From 510f27e13fea59b82c112fa19eaf16a0b7b79a86 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 23 May 2025 07:47:39 -0600 Subject: [PATCH] Port the compiler from Zig to C++ --- .gitea/workflows/ci.yml | 13 +- CMakeLists.txt | 52 + build.bat | 32 - build.sh | 63 +- build.zig | 367 - generate.sh | 51 + src/LLVM.zig | 1901 ---- src/bootstrap.zig | 10784 ---------------------- src/compiler.bbb | 2 +- src/compiler.cpp | 506 + src/compiler.hpp | 1718 ++++ src/emitter.cpp | 8681 +++++++++++++++++ src/entry_point.cpp | 13 + src/entry_point.hpp | 2 + src/lib.cpp | 212 + src/lib.hpp | 718 ++ src/lib.zig | 3159 ------- src/llvm.cpp | 1522 ++- src/llvm.hpp | 645 ++ src/llvm_api.zig | 296 - src/main.zig | 335 - src/parser.cpp | 3641 ++++++++ src/stack_trace.zig | 20 - tests/basic_bool_call.bbb | 16 + tests/basic_shortcircuiting_if.bbb | 12 + tests/concat_logical_or.bbb | 9 + tests/forward_declared_type.bbb | 17 + tests/generic_pointer_array.bbb | 15 + tests/pointer_struct_initialization.bbb | 32 + tests/self_referential_struct.bbb | 11 + tests/slice_array_literal.bbb | 13 + tests/slice_only_start.bbb | 11 + tests/strict_array_type.bbb | 5 + 33 files changed, 16936 insertions(+), 17938 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 build.bat delete mode 100644 build.zig create mode 100755 generate.sh delete mode 100644 src/LLVM.zig delete mode 100644 src/bootstrap.zig create mode 100644 src/compiler.cpp create mode 100644 src/compiler.hpp create mode 100644 src/emitter.cpp create mode 100644 src/entry_point.cpp create mode 100644 src/entry_point.hpp create mode 100644 src/lib.cpp create mode 100644 src/lib.hpp delete mode 100644 src/lib.zig create mode 100644 src/llvm.hpp delete mode 100644 src/llvm_api.zig delete mode 100644 src/main.zig create mode 100644 src/parser.cpp delete mode 100644 src/stack_trace.zig create mode 100644 tests/basic_bool_call.bbb create mode 100644 tests/basic_shortcircuiting_if.bbb create mode 100644 tests/concat_logical_or.bbb create mode 100644 tests/forward_declared_type.bbb create mode 100644 tests/generic_pointer_array.bbb create mode 100644 tests/pointer_struct_initialization.bbb create mode 100644 tests/self_referential_struct.bbb create mode 100644 tests/slice_array_literal.bbb create mode 100644 tests/slice_only_start.bbb create mode 100644 tests/strict_array_type.bbb diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 06f6f69..cd482d2 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -20,11 +20,18 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest ] - BIRTH_ZIG_BUILD_TYPE: [ Debug, ReleaseSafe, ReleaseFast, ReleaseSmall ] + BIRTH_CMAKE_BUILD_TYPE: [ Debug, RelWithDebInfo, Release, MinSizeRel ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Build and test (Packaged LLVM) + shell: bash + env: + BB_CI: 1 + CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}} + CLANG_PATH: clang-19 + CLANGXX_PATH: clang++-19 run: | - ~/zig-linux-x86_64-0.14.0/zig build test -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=false - ldd zig-out/bin/bloat-buster + ./generate.sh + ./build.sh + ./build/bb test diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..34c88aa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.15) +include(CMakePrintHelpers) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE) +endif() +# Set C++ standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +project(bb) + +find_package(LLVM REQUIRED CONFIG) +find_package(LLD REQUIRED CONFIG) + +add_executable(bb + src/lib.cpp + src/entry_point.cpp + src/compiler.cpp + src/parser.cpp + src/emitter.cpp + src/llvm.cpp +) + +add_library(c_abi tests/c_abi.c) + +target_include_directories(bb PUBLIC src) +target_compile_definitions(bb PUBLIC + $<$:BB_DEBUG=1> + $<$>:BB_DEBUG=0> +) + +target_link_libraries(bb PUBLIC ${LLVM_AVAILABLE_LIBS}) + +find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +# find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +# find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +# find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) +# find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) + +target_link_libraries(bb PUBLIC + ${LLD_COMMON} + # ${LLD_COFF} + ${LLD_ELF} + # ${LLD_MACHO} + # ${LLD_MINGW} + # ${LLD_WASM} +) + +target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -funsigned-char -fwrapv -fno-strict-aliasing) +# target_compile_options(bb PRIVATE -fsanitize=address) +# target_link_options(bb PRIVATE -fsanitize=address) diff --git a/build.bat b/build.bat deleted file mode 100644 index 4a037b9..0000000 --- a/build.bat +++ /dev/null @@ -1,32 +0,0 @@ -@echo off -setlocal enableextensions - -if not defined BB_CI ( - set BB_CI=0 -) - -if not defined BB_BUILD_TYPE ( - set BB_BUILD_TYPE=debug -) - -if not defined BB_ERROR_ON_WARNINGS ( - set BB_ERROR_ON_WARNINGS=%BB_CI% -) - -if not defined BB_ERROR_LIMIT ( - set /a BB_ERROR_LIMIT=1-%BB_CI% -) - -set BUILD_DIR=cache -mkdir %BUILD_DIR% > NUL 2>&1 -set BUILD_OUT=cache\build.exe -set BB_ERROR_ON_WARNINGS=%BB_CI% - -REM if "%BB_CI%" == "0" ( -REM %VK_SDK_PATH%\Bin\glslangValidator.exe -V bootstrap\std\shaders\rect.vert -o cache\rect.vert.spv --quiet || exit /b 1 -REM %VK_SDK_PATH%\Bin\glslangValidator.exe -V bootstrap\std\shaders\rect.frag -o cache\rect.frag.spv --quiet || exit /b 1 -REM ) - - -cl /Zi /Y- /Gm- /std:clatest /diagnostics:caret -FC /nologo build.c /Fd%BUILD_DIR%\ /Fo%BUILD_DIR%\ /Fe%BUILD_OUT% -Ibootstrap -DBB_TIMETRACE=0 -DBB_BUILD_TYPE=\"%BB_BUILD_TYPE%\" -DBB_CI=%BB_CI% -DBB_ERROR_ON_WARNINGS=%BB_ERROR_ON_WARNINGS% -DBB_ERROR_LIMIT=%BB_ERROR_LIMIT% /link /INCREMENTAL:NO || exit /b 1 -%BUILD_OUT% diff --git a/build.sh b/build.sh index 09b3c32..eead4ed 100755 --- a/build.sh +++ b/build.sh @@ -1,61 +1,4 @@ -#!/usr/bin/env bash set -eu - -MY_CWD=$PWD - -if [[ -z "${BB_CI-}" ]]; then - BB_CI=0 -fi - -if [[ -z "${BB_BUILD_TYPE-}" ]]; then - BB_BUILD_TYPE=debug -fi - -if [[ -z "${BB_ERROR_ON_WARNINGS-}" ]]; then - BB_ERROR_ON_WARNINGS=$BB_CI -fi - -if [[ -z "${BB_ERROR_LIMIT-}" ]]; then - BB_ERROR_LIMIT=$((1 - BB_CI)) -fi - -BB_COMPILE_SHADERS=0 - -BUILD_DIR=cache -LARGE_ASSET_BASE_URL=https://github.com/birth-software/bloat-buster/releases/download/large-assets -mkdir -p $BUILD_DIR - -if [[ ! -f "$BUILD_DIR/large_assembly.s" ]]; then - cd $BUILD_DIR - wget $LARGE_ASSET_BASE_URL/large_assembly.s -o large_assembly.s - cd $MY_CWD -fi - -if [[ "${BB_COMPILE_SHADERS}" == "1" ]]; then - glslangValidator -V bootstrap/std/shaders/rect.vert -o $BUILD_DIR/rect.vert.spv --quiet - glslangValidator -V bootstrap/std/shaders/rect.frag -o $BUILD_DIR/rect.frag.spv --quiet -fi - -BUILD_OUT=$BUILD_DIR/build -C_COMPILER=clang -TIME_TRACE=1 -BB_TIMETRACE=0 -GCC_ARGS= -CLANG_ARGS= -TIME_TRACE_ARG= - -if [[ $C_COMPILER == "clang"* ]]; then - CLANG_ARGS=-ferror-limit=1 - if [[ "$TIME_TRACE" == "1" ]]; then - CLANG_ARGS="$CLANG_ARGS -ftime-trace" - BB_TIMETRACE=1 - else - CLANG_ARGS="$CLANG_ARGS -ftime-trace" - fi -elif [[ $C_COMPILER == "gcc"* ]]; then - GCC_ARGS=-fmax-errors=1 -fi - -$C_COMPILER build.c -g -o $BUILD_OUT -Ibootstrap -std=gnu2x $CLANG_ARGS $GCC_ARGS -DBB_TIMETRACE=$BB_TIMETRACE -DBB_CI=$BB_CI -DBB_BUILD_TYPE=\"$BB_BUILD_TYPE\" -DBB_ERROR_ON_WARNINGS=$BB_ERROR_ON_WARNINGS -DBB_ERROR_LIMIT=$BB_ERROR_LIMIT -$BUILD_OUT $@ -exit 0 +cd build +ninja --quiet +cd .. diff --git a/build.zig b/build.zig deleted file mode 100644 index 2da4ad2..0000000 --- a/build.zig +++ /dev/null @@ -1,367 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -fn run_process_and_capture_stdout(b: *std.Build, argv: []const []const u8) ![]const u8 { - const result = std.process.Child.run(.{ - .allocator = b.allocator, - .argv = argv, - }) catch |err| return err; - switch (result.term) { - .Exited => |exit_code| { - if (exit_code != 0) { - return error.SpawnError; - } - }, - else => return error.SpawnError, - } - - return result.stdout; -} - -fn file_find_in_path(allocator: std.mem.Allocator, file_name: []const u8, path_env: []const u8, extension: []const u8) ?[]const u8 { - const path_env_separator = switch (builtin.os.tag) { - .windows => ';', - else => ':', - }; - const path_separator = switch (builtin.os.tag) { - .windows => '\\', - else => '/', - }; - var env_it = std.mem.splitScalar(u8, path_env, path_env_separator); - const result: ?[]const u8 = while (env_it.next()) |dir_path| { - const full_path = std.mem.concatWithSentinel(allocator, u8, &.{ dir_path, &[1]u8{path_separator}, file_name, extension }, 0) catch unreachable; - const file = std.fs.cwd().openFile(full_path, .{}) catch continue; - file.close(); - break full_path; - } else null; - return result; -} - -fn executable_find_in_path(allocator: std.mem.Allocator, file_name: []const u8, path_env: []const u8) ?[]const u8 { - const extension = switch (builtin.os.tag) { - .windows => ".exe", - else => "", - }; - return file_find_in_path(allocator, file_name, path_env, extension); -} - -const CmakeBuildType = enum { - Debug, - RelWithDebInfo, - MinSizeRel, - Release, - - fn from_zig_build_type(o: std.builtin.OptimizeMode) CmakeBuildType { - return switch (o) { - .Debug => .Debug, - .ReleaseSafe => .RelWithDebInfo, - .ReleaseSmall => .MinSizeRel, - .ReleaseFast => .Release, - }; - } -}; - -var system_llvm: bool = undefined; -var target: std.Build.ResolvedTarget = undefined; -var optimize: std.builtin.OptimizeMode = undefined; -var env: std.process.EnvMap = undefined; - -const BuildMode = enum { - debug_none, - debug_fast, - debug_size, - soft_optimize, - optimize_for_speed, - optimize_for_size, - aggressively_optimize_for_speed, - aggressively_optimize_for_size, -}; - -pub fn build(b: *std.Build) !void { - env = try std.process.getEnvMap(b.allocator); - target = b.standardTargetOptions(.{}); - optimize = b.standardOptimizeOption(.{}); - system_llvm = b.option(bool, "system_llvm", "Link against system LLVM libraries") orelse false; - - const c_abi = b.addObject(.{ - .name = "c_abi", - .link_libc = true, - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - .link_libc = true, - .sanitize_c = false, - }), - .optimize = optimize, - }); - c_abi.addCSourceFiles(.{ - .files = &.{"tests/c_abi.c"}, - .flags = &.{"-g"}, - }); - - const path = env.get("PATH") orelse unreachable; - - const stack_trace_library = b.addObject(.{ - .name = "stack_trace", - .root_module = b.createModule(.{ - .target = target, - .optimize = .ReleaseFast, - .root_source_file = b.path("src/stack_trace.zig"), - .link_libc = true, - }), - }); - - const exe_mod = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - .link_libc = true, - .sanitize_c = false, - }); - const configuration = b.addOptions(); - configuration.addOptionPath("c_abi_object_path", c_abi.getEmittedBin()); - exe_mod.addOptions("configuration", configuration); - - const exe = b.addExecutable(.{ - .name = "bloat-buster", - .root_module = exe_mod, - .link_libc = true, - }); - exe.addObject(stack_trace_library); - var llvm_libs = std.ArrayList([]const u8).init(b.allocator); - var flags = std.ArrayList([]const u8).init(b.allocator); - const llvm_config_path = if (b.option([]const u8, "llvm_prefix", "LLVM prefix")) |llvm_prefix| blk: { - const full_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/bin/llvm-config" }); - const f = std.fs.cwd().openFile(full_path, .{}) catch return error.llvm_not_found; - f.close(); - break :blk full_path; - } else if (system_llvm) executable_find_in_path(b.allocator, "llvm-config", path) orelse return error.llvm_not_found else blk: { - const home_env = switch (@import("builtin").os.tag) { - .windows => "USERPROFILE", - else => "HOME", - }; - const home_path = env.get(home_env) orelse unreachable; - const is_ci = std.mem.eql(u8, (env.get("BB_CI") orelse "0"), "1"); - const download_dir = try std.mem.concat(b.allocator, u8, &.{ home_path, "/Downloads" }); - std.fs.makeDirAbsolute(download_dir) catch {}; - const cmake_build_type = if (is_ci) CmakeBuildType.from_zig_build_type(optimize) else CmakeBuildType.Release; - const version_string = "20.1.2"; - const llvm_base = try std.mem.concat(b.allocator, u8, &.{ "llvm_", version_string, "_", @tagName(target.result.cpu.arch), "-", @tagName(target.result.os.tag), "-", @tagName(cmake_build_type) }); - const base = try std.mem.concat(b.allocator, u8, &.{ download_dir, "/", llvm_base }); - const full_path = try std.mem.concat(b.allocator, u8, &.{ base, "/bin/llvm-config" }); - - const f = std.fs.cwd().openFile(full_path, .{}) catch { - const url = try std.mem.concat(b.allocator, u8, &.{ "https://github.com/birth-software/llvm/releases/download/v", version_string, "/", llvm_base, ".7z" }); - var result = try std.process.Child.run(.{ - .allocator = b.allocator, - .argv = &.{ "wget", "-P", download_dir, url }, - .max_output_bytes = std.math.maxInt(usize), - }); - var success = false; - switch (result.term) { - .Exited => |exit_code| { - success = exit_code == 0; - }, - else => {}, - } - - if (!success) { - std.debug.print("{s}\n{s}\n", .{ result.stdout, result.stderr }); - } - - if (success) { - const file_7z = try std.mem.concat(b.allocator, u8, &.{ base, ".7z" }); - result = try std.process.Child.run(.{ - .allocator = b.allocator, - .argv = &.{ "7z", "x", try std.mem.concat(b.allocator, u8, &.{ "-o", download_dir }), file_7z }, - .max_output_bytes = std.math.maxInt(usize), - }); - success = false; - switch (result.term) { - .Exited => |exit_code| { - success = exit_code == 0; - }, - else => {}, - } - - if (!success) { - std.debug.print("{s}\n{s}\n", .{ result.stdout, result.stderr }); - } - - break :blk full_path; - } - - return error.llvm_not_found; - }; - - f.close(); - break :blk full_path; - }; - - const llvm_config_invocation = try run_process_and_capture_stdout(b, &.{ - llvm_config_path, - "--cxxflags", - "--libdir", - "--build-mode", - "--libs", - }); - var it = std.mem.splitScalar(u8, llvm_config_invocation, '\n'); - - const llvm_cxx_flags_chunk = it.next() orelse unreachable; - var llvm_cxx_flags_it = std.mem.splitScalar(u8, llvm_cxx_flags_chunk, ' '); - while (llvm_cxx_flags_it.next()) |llvm_cxx_flag| { - try flags.append(llvm_cxx_flag); - } - - const llvm_lib_dir = it.next() orelse unreachable; - const llvm_build_mode_string = it.next() orelse unreachable; - const llvm_build_mode = inline for (@typeInfo(CmakeBuildType).@"enum".fields) |field| { - if (std.mem.eql(u8, llvm_build_mode_string, field.name)) { - break @field(CmakeBuildType, field.name); - } - } else unreachable; - - const llvm_lib_chunk = it.next() orelse unreachable; - var llvm_lib_it = std.mem.splitScalar(u8, llvm_lib_chunk, ' '); - while (llvm_lib_it.next()) |llvm_lib| { - const llvm_lib_arg = std.mem.trimLeft(u8, llvm_lib, "-l"); - try llvm_libs.append(llvm_lib_arg); - } - - if (llvm_build_mode == .Debug) { - try flags.append("-g"); - } - - try flags.append("-fno-rtti"); - - exe.addLibraryPath(.{ .cwd_relative = llvm_lib_dir }); - - const a = std.fs.cwd().openDir("/usr/lib/x86_64-linux-gnu/", .{}); - if (a) |_| { - var dir = a catch unreachable; - dir.close(); - exe.addLibraryPath(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/" }); - } else |err| { - err catch {}; - } - - exe.addCSourceFiles(.{ - .files = &.{"src/llvm.cpp"}, - .flags = flags.items, - }); - - var dir = try std.fs.cwd().openDir("/usr/include/c++", .{ - .iterate = true, - }); - var iterator = dir.iterate(); - const gcc_version = while (try iterator.next()) |entry| { - if (entry.kind == .directory) { - break entry.name; - } - } else return error.include_cpp_dir_not_found; - dir.close(); - const general_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/c++/", gcc_version }); - exe.addIncludePath(.{ .cwd_relative = general_cpp_include_dir }); - - { - const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ general_cpp_include_dir, "/x86_64-pc-linux-gnu" }); - const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); - if (d2) |_| { - var d = d2 catch unreachable; - d.close(); - exe.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); - } else |err| err catch {}; - } - - { - const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/x86_64-linux-gnu/c++/", gcc_version }); - const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); - if (d2) |_| { - var d = d2 catch unreachable; - d.close(); - exe.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); - } else |err| err catch {}; - } - - var found_libcpp = false; - - if (std.fs.cwd().openFile("/usr/lib/libstdc++.so.6", .{})) |file| { - file.close(); - found_libcpp = true; - exe.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); - } else |err| { - err catch {}; - } - - if (std.fs.cwd().openFile("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", .{})) |file| { - file.close(); - found_libcpp = true; - exe.addObjectFile(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" }); - } else |err| { - err catch {}; - } - - if (!found_libcpp) { - return error.libcpp_not_found; - } - - const needed_libraries: []const []const u8 = &.{ "unwind", "z", "zstd" }; - for (needed_libraries) |lib| { - exe.linkSystemLibrary(lib); - } - - for (llvm_libs.items) |lib| { - exe.linkSystemLibrary(lib); - } - - const lld_libs: []const []const u8 = &.{ "lldCommon", "lldCOFF", "lldELF", "lldMachO", "lldMinGW", "lldWasm" }; - for (lld_libs) |lib| { - exe.linkSystemLibrary(lib); - } - - b.installArtifact(exe); - - for ([_]bool{ false, true }) |is_test| { - const run_step_name = switch (is_test) { - true => "test", - false => "run", - }; - - const debug_step_name = switch (is_test) { - true => "debug_test", - false => "debug", - }; - - const command = b.addRunArtifact(exe); - command.step.dependOn(b.getInstallStep()); - - if (is_test) { - command.addArg("test"); - } - - if (b.args) |args| { - command.addArgs(args); - } - - const run_step = b.step(run_step_name, ""); - run_step.dependOn(&command.step); - - const debug_command = std.Build.Step.Run.create(b, b.fmt("{s} {s}", .{ debug_step_name, exe.name })); - debug_command.addArg("gdb"); - debug_command.addArg("-ex"); - debug_command.addArg("r"); - debug_command.addArg("--args"); - debug_command.addArtifactArg(exe); - - if (is_test) { - debug_command.addArg("test"); - } - - if (b.args) |args| { - debug_command.addArgs(args); - } - - const debug_step = b.step(debug_step_name, ""); - debug_step.dependOn(&debug_command.step); - } -} diff --git a/generate.sh b/generate.sh new file mode 100755 index 0000000..2ab0b3f --- /dev/null +++ b/generate.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -eu + +if [[ -z "${BB_CI:-}" ]]; then + CMAKE_BUILD_TYPE=Debug + LLVM_CMAKE_BUILD_TYPE=Release +else + LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE +fi + +BUILD_DIR=build + +BIRTH_NATIVE_OS_STRING=$OSTYPE + +case "$BIRTH_NATIVE_OS_STRING" in + darwin*) BIRTH_OS="macos";; + linux*) BIRTH_OS="linux";; + msys*) BIRTH_OS="windows";; + *) exit 1 +esac + +BIRTH_NATIVE_ARCH_STRING="$(uname -m)" + +case "$BIRTH_NATIVE_ARCH_STRING" in + x86_64) BIRTH_ARCH="x86_64";; + arm64) BIRTH_ARCH="aarch64";; + *) exit 1 +esac + +case "$BIRTH_OS" in + linux) LINKER_TYPE=MOLD;; + *) LINKER_TYPE=DEFAULT;; +esac + +rm -rf $BUILD_DIR +mkdir $BUILD_DIR +cd $BUILD_DIR +LLVM_PREFIX_PATH=$HOME/dev/llvm/install/llvm_20.1.3_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE + +if [[ -z "${CLANG_PATH:-}" ]]; then + CLANG_PATH=clang + CLANGXX_PATH=clang++ +fi + + +if [[ -n "${BB_CI+x}" ]]; then + echo $LLVM_PREFIX_PATH +fi + +cmake .. -G Ninja -DCMAKE_C_COMPILER=$CLANG_PATH -DCMAKE_CXX_COMPILER=$CLANGXX_PATH -DCMAKE_LINKER_TYPE=$LINKER_TYPE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH=$LLVM_PREFIX_PATH -DCMAKE_COLOR_DIAGNOSTICS=ON +cd .. diff --git a/src/LLVM.zig b/src/LLVM.zig deleted file mode 100644 index 6bb352f..0000000 --- a/src/LLVM.zig +++ /dev/null @@ -1,1901 +0,0 @@ -const lib = @import("lib.zig"); -const Arena = lib.Arena; -const assert = lib.assert; -const os = lib.os; -const builtin = @import("builtin"); -const api = @import("llvm_api.zig"); - -/// This is a String which ABI-compatible with C++ -pub const String = extern struct { - pointer: ?[*]const u8 = null, - length: usize = 0, - - pub fn from_slice(slice: []const u8) String { - return String{ - .pointer = slice.ptr, - .length = slice.len, - }; - } - - pub fn to_slice(string: String) ?[]const u8 { - if (string.length != 0) { - return string.pointer.?[0..string.length]; - } else { - return null; - } - } -}; - -pub const Intrinsic = enum { - pub const Id = enum(c_uint) { - _, - }; -}; - -pub const Attribute = opaque { - pub const List = opaque { - pub const Options = extern struct { - function: Attribute.Function, - @"return": Attribute.Argument, - argument_pointer: [*]const Attribute.Argument, - argument_length: u64, - - comptime { - assert(@sizeOf(Options) == @sizeOf(Attribute.Function) + @sizeOf(Attribute.Argument) + @sizeOf([*]const Attribute.Argument) + @sizeOf(u64)); - } - }; - - pub fn build(context: *Context, function_attributes: Attribute.Function, return_attributes: Attribute.Argument, argument_attributes: []const Attribute.Argument, call_site: bool) *Attribute.List { - return api.llvm_attribute_list_build(context, &Options{ - .function = function_attributes, - .@"return" = return_attributes, - .argument_pointer = argument_attributes.ptr, - .argument_length = argument_attributes.len, - }, call_site); - } - }; - - pub const Index = enum(c_uint) { - @"return" = 0, - function = 0xffff_ffff, - _, - }; - - pub const Kind = enum(c_uint) { - _, - }; - - pub const FramePointerKind = enum(u2) { - none = 0, - reserved = 1, - non_leaf = 2, - all = 3, - }; - - pub const ZeroCallUsedRegsKind = enum(u4) { - all = 0, - skip = 1 << 0, - used_gpr_arg = only_used | only_gpr | only_arg, - used_gpr = only_used | only_gpr, - used_arg = only_used | only_arg, - used = only_used, - all_gpr_arg = only_gpr | only_arg, - all_gpr = only_gpr, - all_arg = only_arg, - - const only_used = 1 << 1; - const only_gpr = 1 << 2; - const only_arg = 1 << 3; - }; - - pub const FPClassTest = packed struct(u10) { - s_nan: bool = false, - q_nan: bool = false, - neg_inf: bool = false, - neg_normal: bool = false, - neg_subnormal: bool = false, - neg_zero: bool = false, - pos_zero: bool = false, - pos_subnormal: bool = false, - pos_normal: bool = false, - pos_inf: bool = false, - }; - - pub const UnwindTableKind = enum(u2) { - none = 0, - sync = 1, - @"async" = 2, - - pub const default = UnwindTableKind.@"async"; - }; - - pub const Argument = extern struct { - semantic_type: *Type, - abi_type: *Type, - dereferenceable_bytes: u64, - alignment: u32, - flags: packed struct(u32) { - no_alias: bool, - non_null: bool, - no_undef: bool, - sign_extend: bool, - zero_extend: bool, - in_reg: bool, - no_fp_class: FPClassTest, - struct_return: bool, - writable: bool, - dead_on_unwind: bool, - in_alloca: bool, - dereferenceable: bool, - dereferenceable_or_null: bool, - nest: bool, - by_value: bool, - by_reference: bool, - no_capture: bool, - _: u6 = 0, - }, - - comptime { - assert(@sizeOf(Attribute.Argument) == 2 * @sizeOf(*Type) + 2 * @sizeOf(u64)); - } - }; - - pub const Function = extern struct { - prefer_vector_width: String, - stack_protector_buffer_size: String, - definition_probe_stack: String, - definition_stack_probe_size: String, - flags0: packed struct(u64) { - noreturn: bool, - cmse_ns_call: bool, - nounwind: bool, - returns_twice: bool, - cold: bool, - hot: bool, - no_duplicate: bool, - convergent: bool, - no_merge: bool, - will_return: bool, - no_caller_saved_registers: bool, - no_cf_check: bool, - no_callback: bool, - alloc_size: bool, - uniform_work_group_size: bool, - aarch64_pstate_sm_body: bool, - aarch64_pstate_sm_enabled: bool, - aarch64_pstate_sm_compatible: bool, - aarch64_preserves_za: bool, - aarch64_in_za: bool, - aarch64_out_za: bool, - aarch64_inout_za: bool, - aarch64_preserves_zt0: bool, - aarch64_in_zt0: bool, - aarch64_out_zt0: bool, - aarch64_inout_zt0: bool, - optimize_for_size: bool, - min_size: bool, - no_red_zone: bool, - indirect_tls_seg_refs: bool, - no_implicit_floats: bool, - sample_profile_suffix_elision_policy: bool, - memory_none: bool, - memory_readonly: bool, - memory_inaccessible_or_arg_memory_only: bool, - memory_arg_memory_only: bool, - strict_fp: bool, - no_inline: bool, - always_inline: bool, - guard_no_cf: bool, - // TODO: branch protection function attributes - // TODO: cpu features - - // CALL-SITE ATTRIBUTES - call_no_builtins: bool, - - // DEFINITION-SITE ATTRIBUTES - definition_frame_pointer_kind: FramePointerKind, - definition_less_precise_fpmad: bool, - definition_null_pointer_is_valid: bool, - definition_no_trapping_fp_math: bool, - definition_no_infs_fp_math: bool, - definition_no_nans_fp_math: bool, - definition_approx_func_fp_math: bool, - definition_unsafe_fp_math: bool, - definition_use_soft_float: bool, - definition_no_signed_zeroes_fp_math: bool, - definition_stack_realignment: bool, - definition_backchain: bool, - definition_split_stack: bool, - definition_speculative_load_hardening: bool, - definition_zero_call_used_registers: ZeroCallUsedRegsKind, - // TODO: denormal builtins - definition_non_lazy_bind: bool, - definition_cmse_nonsecure_entry: bool, - definition_unwind_table_kind: UnwindTableKind, - }, - flags1: packed struct(u64) { - definition_disable_tail_calls: bool, - definition_stack_protect_strong: bool, - definition_stack_protect: bool, - definition_stack_protect_req: bool, - definition_aarch64_new_za: bool, - definition_aarch64_new_zt0: bool, - definition_optimize_none: bool, - definition_naked: bool, - definition_inline_hint: bool, - reserved: u55 = 0, - }, - - comptime { - assert(@sizeOf(Attribute.Function) == 10 * @sizeOf(u64)); - } - }; -}; - -pub const CodeModel = enum(u8) { - none = 0, - tiny = 1, - small = 2, - kernel = 3, - medium = 4, - large = 5, -}; - -pub const RelocationModel = enum(u8) { - default = 0, - static = 1, - pic = 2, - dynamic_no_pic = 3, - ropi = 4, - rwpi = 5, - ropi_rwpi = 6, -}; - -pub const CodeGenerationOptimizationLevel = enum(u8) { - none = 0, // -O0 - less = 1, // -O1 - default = 2, // -O2, -Os - aggressive = 3, // -O3 -}; - -pub const Target = opaque { - /// This is ABI-compatible with C++ - pub const Options = extern struct { - flags0: packed struct(u64) { - unsafe_fp_math: bool, - no_infs_fp_math: bool, - no_nans_fp_math: bool, - no_trapping_fp_math: bool, - no_signed_zeroes_fp_math: bool, - approx_func_fp_math: bool, - enable_aix_extended_altivec_abi: bool, - honor_sign_dependent_rounding_fp_math: bool, - no_zeroes_in_bss: bool, - guaranteed_tail_call_optimization: bool, - stack_symbol_ordering: bool, - enable_fast_isel: bool, - enable_global_isel: bool, - global_isel_abort_mode: enum(u2) { - disable = 0, - enable = 1, - disable_with_diag = 2, - }, - swift_async_frame_pointer: enum(u2) { - deployment_based = 0, - always = 1, - never = 2, - }, - use_init_array: bool, - disable_integrated_assembler: bool, - function_sections: bool, - data_sections: bool, - ignore_xcoff_visibility: bool, - xcoff_traceback_table: bool, - unique_section_names: bool, - unique_basic_block_section_names: bool, - separate_named_sections: bool, - trap_unreachable: bool, - no_trap_after_noreturn: bool, - tls_size: u8, - emulated_tls: bool, - enable_tls_descriptors: bool, - enable_ipra: bool, - emit_stack_size_section: bool, - enable_machine_outliner: bool, - enable_machine_function_splitter: bool, - supports_default_outlining: bool, - emit_address_significance_table: bool, - bb_address_map: bool, - bb_sections: enum(u3) { - all = 0, - list = 1, - labels = 2, - preset = 3, - none = 4, - }, - emit_call_site_information: bool, - supports_debug_entry_values: bool, - enable_debug_entry_values: bool, - value_tracking_variable_locations: bool, - force_dwarf_frame_section: bool, - xray_function_index: bool, - debug_strict_dwarf: bool, - hotpatch: bool, - ppc_gen_scalar_mass_entries: bool, - jmc_instrument: bool, - enable_cfi_fixup: bool, - mis_expect: bool, - xcoff_read_only_pointers: bool, - float_abi: enum(u2) { - default = 0, - soft = 1, - hard = 2, - }, - thread_model: enum(u1) { - posix = 0, - single = 1, - }, - }, - flags1: packed struct(u32) { - fp_op_fusion_mode: enum(u2) { - fast = 0, - standard = 1, - strict = 2, - }, - eabi_version: enum(u3) { - unknown = 0, - default = 1, - eabi4 = 2, - eabi5 = 3, - gnu = 4, - }, - debugger_kind: enum(u3) { - default = 0, - gdb = 1, - lldb = 2, - sce = 3, - dbx = 4, - }, - exception_handling: enum(u3) { - none = 0, - dwarf_cfi = 1, - setjmp_longjmp = 2, - arm = 3, - win_eh = 4, - wasm = 5, - aix = 6, - zos = 7, - }, - reserved: PaddingType = 0, - }, - loop_alignment: c_uint, - binutils_version: [2]c_int, - mc: MCTargetOptions, - - const padding_bit_count = 21; - const PaddingType = @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = padding_bit_count, - }, - }); - comptime { - assert(@sizeOf(Target.Options) == 136); - assert(padding_bit_count == 21); - } - - pub fn default() Target.Options { - return .{ - .binutils_version = .{ 0, 0 }, - .flags0 = .{ - .unsafe_fp_math = false, - .no_infs_fp_math = false, - .no_nans_fp_math = false, - .no_trapping_fp_math = true, - .no_signed_zeroes_fp_math = false, - .approx_func_fp_math = false, - .enable_aix_extended_altivec_abi = false, - .honor_sign_dependent_rounding_fp_math = false, - .no_zeroes_in_bss = false, - .guaranteed_tail_call_optimization = false, - .stack_symbol_ordering = true, - .enable_fast_isel = false, - .enable_global_isel = false, - .global_isel_abort_mode = .enable, - .swift_async_frame_pointer = .always, - .use_init_array = false, - .disable_integrated_assembler = false, - .function_sections = false, - .data_sections = false, - .ignore_xcoff_visibility = false, - .xcoff_traceback_table = true, - .unique_section_names = true, - .unique_basic_block_section_names = false, - .separate_named_sections = false, - .trap_unreachable = false, - .no_trap_after_noreturn = false, - .tls_size = 0, - .emulated_tls = false, - .enable_tls_descriptors = false, - .enable_ipra = false, - .emit_stack_size_section = false, - .enable_machine_outliner = false, - .enable_machine_function_splitter = false, - .supports_default_outlining = false, - .emit_address_significance_table = false, - .bb_address_map = false, - .bb_sections = .none, - .emit_call_site_information = false, - .supports_debug_entry_values = false, - .enable_debug_entry_values = false, - .value_tracking_variable_locations = false, - .force_dwarf_frame_section = false, - .xray_function_index = true, - .debug_strict_dwarf = false, - .hotpatch = false, - .ppc_gen_scalar_mass_entries = false, - .jmc_instrument = false, - .enable_cfi_fixup = false, - .mis_expect = false, - .xcoff_read_only_pointers = false, - .float_abi = .default, - .thread_model = .posix, - }, - .flags1 = .{ - .fp_op_fusion_mode = .standard, - .eabi_version = .default, - .debugger_kind = .default, - .exception_handling = .none, - }, - .loop_alignment = 0, - .mc = .{ - .abi_name = .{}, - .assembly_language = .{}, - .split_dwarf_file = .{}, - .as_secure_log_file = .{}, - .argv0 = null, - .argv_pointer = null, - .argv_count = 0, - .integrated_assembler_search_path_pointer = null, - .integrated_assembler_search_path_count = 0, - .flags = .{ - .relax_all = false, - .no_exec_stack = false, - .fatal_warnings = false, - .no_warn = false, - .no_deprecated_warn = false, - .no_type_check = false, - .save_temp_labels = false, - .incremental_linker_compatible = false, - .fdpic = false, - .show_mc_encoding = false, - .show_mc_inst = false, - .asm_verbose = false, - .preserve_asm_comments = true, - .dwarf64 = false, - .crel = false, - .x86_relax_relocations = true, - .x86_sse2_avx = false, - .emit_dwarf_unwind = .default, - .use_dwarf_directory = .default, - .debug_compression_type = .none, - .emit_compact_unwind_non_canonical = false, - .ppc_use_full_register_names = false, - }, - }, - }; - } - }; - - pub const Machine = opaque { - /// This is ABI-compatible with C++ - pub const Create = extern struct { - target_options: Target.Options, - cpu_triple: String, - cpu_model: String, - cpu_features: String, - code_model: CodeModel, - relocation_model: RelocationModel, - optimization_level: CodeGenerationOptimizationLevel, - jit: bool, - reserved: [padding_byte_count]u8 = [1]u8{0} ** padding_byte_count, - - const padding_byte_count = 4; - comptime { - assert(@sizeOf(Create) == 192); - assert(padding_byte_count == 4); - } - }; - - pub fn create(options: Create, error_message: *String) ?*Target.Machine { - const target_machine = api.llvm_create_target_machine(&options, error_message); - return target_machine; - } - }; -}; - -pub const MCTargetOptions = extern struct { - abi_name: String, - assembly_language: String, - split_dwarf_file: String, - as_secure_log_file: String, - argv0: ?[*:0]const u8, - argv_pointer: ?[*]const String, - argv_count: u64, - integrated_assembler_search_path_pointer: ?[*]const String, - integrated_assembler_search_path_count: u64, - flags: packed struct(u32) { - relax_all: bool, - no_exec_stack: bool, - fatal_warnings: bool, - no_warn: bool, - no_deprecated_warn: bool, - no_type_check: bool, - save_temp_labels: bool, - incremental_linker_compatible: bool, - fdpic: bool, - show_mc_encoding: bool, - show_mc_inst: bool, - asm_verbose: bool, - preserve_asm_comments: bool, - dwarf64: bool, - crel: bool, - x86_relax_relocations: bool, - x86_sse2_avx: bool, - emit_dwarf_unwind: enum(u2) { - always = 0, - no_compact_unwind = 1, - default = 2, - }, - use_dwarf_directory: enum(u2) { - disable = 0, - enable = 1, - default = 2, - }, - debug_compression_type: enum(u2) { - none = 0, - zlib = 1, - zstd = 2, - }, - emit_compact_unwind_non_canonical: bool, - ppc_use_full_register_names: bool, - reserved: PaddingType = 0, - }, - - const padding_bit_count = 7; - const PaddingType = @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = 7, - }, - }); - comptime { - assert(@sizeOf(MCTargetOptions) == 112); - assert(padding_bit_count == 7); - } -}; - -pub const OptimizationLevel = enum(u3) { - O0 = 0, - O1 = 1, - O2 = 2, - O3 = 3, - Os = 4, - Oz = 5, - - fn prefers_size(optimization_level: OptimizationLevel) bool { - return switch (optimization_level) { - .O0, .O1, .Os, .Oz => true, - .O2, .O3 => false, - }; - } - - fn prefers_speed(optimization_level: OptimizationLevel) bool { - return !prefers_size(optimization_level); - } -}; - -/// This is ABI-compatible with C++ -pub const OptimizationPipelineOptions = packed struct(u64) { - optimization_level: OptimizationLevel, - debug_info: bool, - loop_unrolling: bool, - loop_interleaving: bool, - loop_vectorization: bool, - slp_vectorization: bool, - merge_functions: bool, - call_graph_profile: bool, - unified_lto: bool, - assignment_tracking: bool, - verify_module: bool, - reserved: PaddingType = 0, - - const padding_bit_count = 51; - const PaddingType = @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = padding_bit_count, - }, - }); - - comptime { - assert(@sizeOf(OptimizationPipelineOptions) == @sizeOf(u64)); - assert(padding_bit_count == 51); - } - - const Create = packed struct { - optimization_level: OptimizationLevel, - debug_info: bool, - }; - - pub fn default(create: Create) OptimizationPipelineOptions { - const pref_speed = create.optimization_level.prefers_speed(); - return .{ - .optimization_level = create.optimization_level, - .debug_info = create.debug_info, - .loop_unrolling = pref_speed, - .loop_interleaving = pref_speed, - .loop_vectorization = pref_speed, - .slp_vectorization = pref_speed, - .merge_functions = pref_speed, - .call_graph_profile = false, - .unified_lto = false, - .assignment_tracking = create.debug_info, - .verify_module = lib.optimization_mode == .ReleaseSafe or lib.optimization_mode == .Debug, - }; - } -}; - -/// This is ABI-compatible with C++ -pub const CodeGenerationPipelineOptions = extern struct { - output_dwarf_file_path: String, - output_file_path: String, - flags: packed struct(u64) { - code_generation_file_type: enum(u2) { - assembly_file = 0, - object_file = 1, - null = 2, - }, - optimize_when_possible: bool, - verify_module: bool, - reserved: PaddingType = 0, - }, - - const padding_bit_count = 60; - const PaddingType = @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = padding_bit_count, - }, - }); - - comptime { - assert(@sizeOf(CodeGenerationPipelineOptions) == 5 * @sizeOf(u64)); - assert(padding_bit_count == 60); - } -}; - -pub const CodeGenerationPipelineResult = enum(u8) { - success = 0, - failed_to_create_file = 1, - failed_to_add_emit_passes = 2, -}; - -pub const Architecture = enum { - X86, -}; - -pub const TargetInitializerOptions = struct { - asm_parser: bool = true, - asm_printer: bool = true, - disassembler: bool = false, -}; - -const targets = [@typeInfo(Architecture).@"enum".fields.len]type{ - api.get_initializer(.X86), -}; - -pub const Context = opaque { - pub const create = api.LLVMContextCreate; - - pub fn create_module(context: *Context, name: []const u8) *Module { - return api.llvm_context_create_module(context, String.from_slice(name)); - } - - pub const create_builder = api.LLVMCreateBuilderInContext; - - pub fn create_basic_block(context: *Context, name: []const u8, parent: ?*Function) *BasicBlock { - return api.llvm_context_create_basic_block(context, String.from_slice(name), parent); - } - - pub fn create_forward_declared_struct_type(context: *Context, name: []const u8) *Type.Struct { - return api.llvm_context_create_forward_declared_struct_type(context, String.from_slice(name)); - } - - pub fn create_struct_type(context: *Context, element_types: []const *Type, name: []const u8) *Type.Struct { - const is_packed = false; - return api.llvm_context_create_struct_type(context, element_types.ptr, @intCast(element_types.len), String.from_slice(name), is_packed); - } - - pub fn get_struct_type(context: *Context, element_types: []const *Type) *Type.Struct { - const is_packed = false; - return api.llvm_context_get_struct_type(context, element_types.ptr, element_types.len, is_packed); - } - - pub const get_void_type = api.LLVMVoidTypeInContext; - pub const get_integer_type = api.LLVMIntTypeInContext; - pub const get_pointer_type = api.LLVMPointerTypeInContext; - - pub fn get_intrinsic_type(context: *Context, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Type.Function { - return api.LLVMIntrinsicGetType(context, intrinsic_id, parameter_types.ptr, parameter_types.len); - } - - pub fn get_anonymous_constant_struct(context: *Context, constant_values: []const *Constant, is_packed: bool) *Constant { - return api.LLVMConstStructInContext(context, constant_values.ptr, @intCast(constant_values.len), @intFromBool(is_packed)); - } - - pub fn get_constant_string(context: *Context, string: []const u8, null_terminated: bool) *Constant { - return api.LLVMConstStringInContext2(context, string.ptr, string.len, @intFromBool(!null_terminated)); - } - - // pub fn create_string_attribute(context: *Context, attribute_name: []const u8, attribute_value: []const u8) *Attribute { - // return api.LLVMCreateStringAttribute(context, attribute_name.ptr, @intCast(attribute_name.len), attribute_value.ptr, @intCast(attribute_value.len)); - // } - // - // pub const create_enum_attribute = api.LLVMCreateEnumAttribute; - // pub const create_type_attribute = api.LLVMCreateTypeAttribute; -}; - -pub const BasicBlock = opaque { - pub const delete = api.llvm_basic_block_delete; - pub const erase_from_parent = api.LLVMDeleteBasicBlock; - pub const get_terminator = api.LLVMGetBasicBlockTerminator; - pub const is_empty = api.llvm_basic_block_is_empty; - pub const user_begin = api.llvm_basic_block_user_begin; - pub const get_next = api.LLVMGetNextBasicBlock; - pub const get_parent = api.LLVMGetBasicBlockParent; - - pub fn to_value(basic_block: *BasicBlock) *Value { - return @ptrCast(basic_block); - } -}; - -pub const Module = opaque { - pub const create_di_builder = api.LLVMCreateDIBuilder; - pub const set_target = api.llvm_module_set_target; - pub const run_optimization_pipeline = api.llvm_module_run_optimization_pipeline; - pub const run_code_generation_pipeline = api.llvm_module_run_code_generation_pipeline; - - pub fn to_string(module: *Module) []const u8 { - return api.llvm_module_to_string(module).to_slice().?; - } - - pub const FunctionCreate = struct { - type: *Type.Function, - linkage: LinkageType, - address_space: c_uint = 0, - name: []const u8, - }; - - pub fn create_function(module: *Module, create: FunctionCreate) *Function { - return api.llvm_module_create_function(module, create.type, create.linkage, create.address_space, String.from_slice(create.name)); - } - - pub const GlobalCreate = struct { - type: *Type, - initial_value: *Constant, - name: []const u8, - before: ?*GlobalVariable = null, - address_space: c_uint = 0, - linkage: LinkageType, - thread_local_mode: ThreadLocalMode = .none, - is_constant: bool = false, - externally_initialized: bool = false, - }; - - pub fn create_global_variable(module: *Module, create: GlobalCreate) *GlobalVariable { - return api.llvm_module_create_global_variable(module, create.type, create.is_constant, create.linkage, create.initial_value, String.from_slice(create.name), create.before, create.thread_local_mode, create.address_space, create.externally_initialized); - } - - pub fn verify(module: *Module) VerifyResult { - var result: VerifyResult = undefined; - var string: String = undefined; - result.success = api.llvm_module_verify(module, &string); - result.error_message = string.to_slice(); - return result; - } - - pub fn get_intrinsic_declaration(module: *Module, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Value { - return api.LLVMGetIntrinsicDeclaration(module, intrinsic_id, parameter_types.ptr, parameter_types.len); - } - - pub fn get_named_function(module: *Module, name: [*:0]const u8) ?*Function { - return api.LLVMGetNamedFunction(module, name); - } -}; - -pub const VerifyResult = struct { - error_message: ?[]const u8, - success: bool, -}; - -pub const Builder = opaque { - pub const position_at_end = api.LLVMPositionBuilderAtEnd; - pub const clear_insertion_position = api.LLVMClearInsertionPosition; - pub const get_insert_block = api.LLVMGetInsertBlock; - pub const insert_basic_block_after_insert_block = api.LLVMInsertExistingBasicBlockAfterInsertBlock; - - pub const create_ret = api.LLVMBuildRet; - - pub fn create_ret_void(builder: *Builder) void { - builder.create_ret(null); - } - - pub fn create_add(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildAdd(builder, left, right, ""); - } - - pub fn create_sub(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildSub(builder, left, right, ""); - } - - pub fn create_mul(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildMul(builder, left, right, ""); - } - - pub fn create_sdiv(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildSDiv(builder, left, right, ""); - } - - pub fn create_udiv(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildUDiv(builder, left, right, ""); - } - - pub fn create_srem(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildSRem(builder, left, right, ""); - } - - pub fn create_urem(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildURem(builder, left, right, ""); - } - - pub fn create_shl(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildShl(builder, left, right, ""); - } - - pub fn create_ashr(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildAShr(builder, left, right, ""); - } - - pub fn create_lshr(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildLShr(builder, left, right, ""); - } - - pub fn create_and(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildAnd(builder, left, right, ""); - } - - pub fn create_or(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildOr(builder, left, right, ""); - } - - pub fn create_xor(builder: *Builder, left: *Value, right: *Value) *Value { - return api.LLVMBuildXor(builder, left, right, ""); - } - - pub fn create_alloca(builder: *Builder, ty: *Type, name: []const u8) *Value { - return api.llvm_builder_create_alloca(builder, ty, 0, String.from_slice(name)); - } - - pub const create_store = api.LLVMBuildStore; - - pub fn create_load(builder: *Builder, ty: *Type, pointer: *Value) *Value { - return api.LLVMBuildLoad2(builder, ty, pointer, ""); - } - - pub fn clear_current_debug_location(builder: *Builder) void { - return builder.set_current_debug_location(null); - } - - pub const get_current_debug_location = api.LLVMGetCurrentDebugLocation2; - - pub const set_current_debug_location = api.LLVMSetCurrentDebugLocation2; - - pub fn create_integer_compare(builder: *Builder, predicate: IntPredicate, left: *Value, right: *Value) *Value { - return api.LLVMBuildICmp(builder, predicate, left, right, ""); - } - - pub const create_branch = api.LLVMBuildBr; - pub const create_conditional_branch = api.LLVMBuildCondBr; - - pub fn create_call(builder: *Builder, function_type: *Type.Function, function_value: *Value, arguments: []const *Value) *Value { - return api.LLVMBuildCall2(builder, function_type, function_value, arguments.ptr, @intCast(arguments.len), ""); - } - - pub fn create_struct_gep(builder: *Builder, struct_type: *Type.Struct, pointer: *Value, index: c_uint) *Value { - return api.LLVMBuildStructGEP2(builder, struct_type, pointer, index, ""); - } - - const GEP = struct { - type: *Type, - aggregate: *Value, - indices: []const *Value, - inbounds: bool = true, - }; - pub fn create_gep(builder: *Builder, gep: GEP) *Value { - const gep_function = switch (gep.inbounds) { - true => &api.LLVMBuildInBoundsGEP2, - false => &api.LLVMBuildGEP2, - }; - return gep_function(builder, gep.type, gep.aggregate, gep.indices.ptr, @intCast(gep.indices.len), ""); - } - - pub fn create_insert_value(builder: *Builder, aggregate: *Value, element: *Value, index: c_uint) *Value { - return api.LLVMBuildInsertValue(builder, aggregate, element, index, ""); - } - - pub fn create_extract_value(builder: *Builder, aggregate: *Value, index: c_uint) *Value { - return api.LLVMBuildExtractValue(builder, aggregate, index, ""); - } - - pub fn create_zero_extend(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildZExt(builder, value, destination_type, ""); - } - - pub fn create_sign_extend(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildSExt(builder, value, destination_type, ""); - } - - pub fn create_int_to_ptr(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildIntToPtr(builder, value, destination_type, ""); - } - - pub fn create_ptr_to_int(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildPtrToInt(builder, value, destination_type, ""); - } - - pub fn create_pointer_cast(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildPointerCast(builder, value, destination_type, ""); - } - - pub fn create_truncate(builder: *Builder, value: *Value, destination_type: *Type) *Value { - return api.LLVMBuildTrunc(builder, value, destination_type, ""); - } - - pub fn create_int_cast(builder: *Builder, value: *Value, destination_type: *Type, is_signed: bool) *Value { - return api.LLVMBuildIntCast2(builder, value, destination_type, @intFromBool(is_signed), ""); - } - - pub const create_unreachable = api.LLVMBuildUnreachable; - - pub const create_memcpy = api.LLVMBuildMemCpy; - pub const create_memset = api.LLVMBuildMemSet; - - pub fn create_vaarg(builder: *Builder, va_list: *Value, arg_type: *Type) *Value { - return api.LLVMBuildVAArg(builder, va_list, arg_type, ""); - } - - pub const find_return_value_dominating_store = api.llvm_find_return_value_dominating_store; - - pub fn create_phi(builder: *Builder, ty: *Type) *Instruction.Phi { - return api.LLVMBuildPhi(builder, ty, ""); - } - - pub fn create_select(builder: *Builder, condition: *Value, true_value: *Value, false_value: *Value) *Value { - return api.LLVMBuildSelect(builder, condition, true_value, false_value, ""); - } - - pub fn create_neg(builder: *Builder, value: *Value) *Value { - return api.LLVMBuildNeg(builder, value, ""); - } - - pub fn create_not(builder: *Builder, value: *Value) *Value { - return api.LLVMBuildNot(builder, value, ""); - } - - pub const create_switch = api.LLVMBuildSwitch; -}; - -pub const GlobalValue = opaque { - pub const get_type = api.LLVMGlobalGetValueType; -}; - -pub const GlobalVariable = opaque { - pub const add_debug_info = api.llvm_global_variable_add_debug_info; - pub const get_initializer = api.LLVMGetInitializer; - pub const set_initializer = api.LLVMSetInitializer; - pub const erase_from_parent = api.LLVMDeleteGlobal; - pub const delete = api.llvm_global_variable_delete; - - pub fn to_value(global_variable: *GlobalVariable) *Value { - return @ptrCast(global_variable); - } - - pub fn to_constant(global_variable: *GlobalVariable) *Constant { - return @ptrCast(global_variable); - } - - pub const UnnamedAddress = enum(c_uint) { - none, - local, - global, - }; - - pub const set_unnamed_address = api.LLVMSetUnnamedAddress; -}; - -pub const Function = opaque { - pub fn get_type(function: *Function) *Type.Function { - return function.to_global_value().get_type().to_function(); - } - - pub fn to_value(function: *Function) *Value { - return @ptrCast(function); - } - - pub fn to_global_value(function: *Function) *GlobalValue { - return @ptrCast(function); - } - - pub fn verify(function: *Function) VerifyResult { - var result: VerifyResult = undefined; - var string: String = undefined; - result.success = api.llvm_function_verify(function, &string); - result.error_message = string.to_slice(); - return result; - } - pub const set_subprogram = api.LLVMSetSubprogram; - pub const get_subprogram = api.LLVMGetSubprogram; - - pub fn to_string(function: *Function) []const u8 { - return api.llvm_function_to_string(function).to_slice().?; - } - - pub fn dump(function: *Function) void { - return lib.print_string(function.to_string()); - } - - pub const set_calling_convention = api.LLVMSetFunctionCallConv; - pub const get_calling_convention = api.LLVMGetFunctionCallConv; - - pub const get_arguments = api.LLVMGetParams; - - pub const set_attributes = api.llvm_function_set_attributes; - pub const get_last_basic_block = api.LLVMGetLastBasicBlock; - pub const append_basic_block = api.LLVMAppendExistingBasicBlock; -}; - -pub const Constant = opaque { - pub fn to_value(constant: *Constant) *Value { - return @ptrCast(constant); - } - - pub fn to_global_variable(constant: *Constant) *GlobalVariable { - assert(constant.to_value().get_kind() == .global_variable); - return @ptrCast(constant); - } - - pub const Integer = opaque { - pub fn to_value(constant: *Constant.Integer) *Value { - return @ptrCast(constant); - } - - pub fn to_constant(constant: *Constant.Integer) *Constant { - return @ptrCast(constant); - } - }; - - pub const get_sign_extended_value = api.LLVMConstIntGetSExtValue; - pub const get_zero_extended_value = api.LLVMConstIntGetZExtValue; - pub const negate = api.LLVMConstNeg; -}; - -pub const Argument = opaque { - pub fn to_value(argument: *Argument) *Value { - return @ptrCast(argument); - } -}; - -pub const Value = opaque { - pub const get_type = api.LLVMTypeOf; - pub const get_kind = api.LLVMGetValueKind; - pub const set_alignment = api.LLVMSetAlignment; - - pub fn set_name(value: *Value, name: []const u8) void { - api.LLVMSetValueName2(value, name.ptr, name.len); - } - - // The operand API is from the User class, but it would work nonetheless - pub const get_operand = api.LLVMGetOperand; - - pub const is_call_instruction = api.LLVMIsACallInst; - pub const use_empty = api.llvm_value_use_empty; - pub const has_one_use = api.llvm_value_has_one_use; - - pub const replace_all_uses_with = api.LLVMReplaceAllUsesWith; - - pub const to_branch = api.llvm_value_to_branch; - - pub fn is_constant(value: *Value) bool { - return api.LLVMIsConstant(value) != 0; - } - - pub fn to_constant(value: *Value) *Constant { - assert(value.is_constant()); - return @ptrCast(value); - } - - pub fn to_instruction(value: *Value) *Instruction { - assert(value.get_kind() == .Instruction); - return @ptrCast(value); - } - - pub fn to_function(value: *Value) *Function { - assert(value.get_kind() == .Function); - return @ptrCast(value); - } - - pub fn to_global_variable(value: *Value) *GlobalVariable { - assert(value.get_kind() == .GlobalVariable); - return @ptrCast(value); - } - - pub const Kind = enum(c_uint) { - Argument, - BasicBlock, - MemoryUse, - MemoryDef, - MemoryPhi, - - Function, - GlobalAlias, - GlobalIFunc, - GlobalVariable, - BlockAddress, - ConstantExpr, - ConstantArray, - ConstantStruct, - ConstantVector, - - UndefValue, - ConstantAggregateZero, - ConstantDataArray, - ConstantDataVector, - ConstantInt, - ConstantFP, - ConstantPointerNull, - ConstantTokenNone, - - MetadataAsValue, - InlineAsm, - - Instruction, - PoisonValue, - ConstantTargetNone, - ConstantPtrAuth, - }; -}; - -pub const Instruction = opaque { - pub fn to_value(instruction: *Instruction) *Value { - return @ptrCast(instruction); - } - - pub fn to_call_base(instruction: *Instruction) *Instruction.CallBase { - assert(instruction.is_call_base()); - return @ptrCast(instruction); - } - - pub const is_call_base = api.llvm_instruction_is_call_base; - - pub const erase_from_parent = api.LLVMInstructionEraseFromParent; - pub const get_parent = api.LLVMGetInstructionParent; - - pub const Branch = opaque { - pub const is_conditional = api.LLVMIsConditional; - pub const get_successor = api.LLVMGetSuccessor; - - pub fn to_instruction(branch: *Branch) *Instruction { - return @ptrCast(branch); - } - }; - - pub const CallBase = opaque { - pub const set_calling_convention = api.LLVMSetInstructionCallConv; - pub const set_attributes = api.llvm_call_base_set_attributes; - }; - - pub const Store = opaque { - pub fn to_instruction(store: *Store) *Instruction { - return @ptrCast(store); - } - }; - - pub const Phi = opaque { - pub fn to_value(phi: *Phi) *Value { - return @ptrCast(phi); - } - pub fn add_incoming(phi: *Phi, values: []const *Value, basic_blocks: []const *BasicBlock) void { - assert(values.len == basic_blocks.len); - return api.LLVMAddIncoming(phi, values.ptr, basic_blocks.ptr, @intCast(values.len)); - } - }; - - pub const Switch = opaque { - pub fn add_case(switchi: *Switch, case_value: *Value, case_block: *BasicBlock) void { - return api.LLVMAddCase(switchi, case_value, case_block); - } - }; -}; - -pub const DI = struct { - pub const Builder = opaque { - pub fn create_file(builder: *DI.Builder, file_name: []const u8, directory: []const u8) *File { - return api.LLVMDIBuilderCreateFile(builder, String.from_slice(file_name), String.from_slice(directory)); - } - - pub fn create_compile_unit(builder: *DI.Builder, file: *DI.File, optimized: bool) *DI.CompileUnit { - return api.LLVMDIBuilderCreateCompileUnit(builder, .C17, file, String.from_slice("bloat buster"), @intFromBool(optimized), String{}, 0, String{}, .full, 0, 0, @intFromBool(optimized), String{}, String{}); - } - - pub const finalize = api.LLVMDIBuilderFinalize; - - pub fn create_subroutine_type(builder: *DI.Builder, file: *DI.File, parameter_types: []const *DI.Type, flags: DI.Flags) *DI.Type.Subroutine { - return api.LLVMDIBuilderCreateSubroutineType(builder, file, parameter_types.ptr, @intCast(parameter_types.len), flags); - } - - pub fn create_function(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, linkage_name: []const u8, file: *DI.File, line_number: c_uint, subroutine_type: ?*DI.Type.Subroutine, local_to_unit: bool, is_definition: bool, scope_line: c_uint, flags: DI.Flags, is_optimized: bool) *DI.Subprogram { - return api.LLVMDIBuilderCreateFunction(builder, scope, String.from_slice(name), String.from_slice(linkage_name), file, line_number, subroutine_type, @intFromBool(local_to_unit), @intFromBool(is_definition), scope_line, flags, @intFromBool(is_optimized)); - } - - pub fn create_basic_type(builder: *DI.Builder, name: []const u8, bit_count: u64, dwarf_type: Dwarf.Type, flags: DI.Flags) *DI.Type { - return api.LLVMDIBuilderCreateBasicType(builder, name.ptr, name.len, bit_count, dwarf_type, flags); - } - - pub const finalize_subprogram = api.LLVMDIBuilderFinalizeSubprogram; - - pub fn create_expression(builder: *DI.Builder, slice: []const u64) *DI.Expression { - return api.LLVMDIBuilderCreateExpression(builder, slice.ptr, slice.len); - } - - pub fn null_expression(builder: *DI.Builder) *DI.Expression { - return api.LLVMDIBuilderCreateExpression(builder, null, 0); - } - - pub fn create_auto_variable(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, auto_type: *DI.Type, always_preserve: bool, flags: DI.Flags, alignment_in_bits: u32) *DI.LocalVariable { - return api.LLVMDIBuilderCreateAutoVariable(builder, scope, name.ptr, name.len, file, line, auto_type, @intFromBool(always_preserve), flags, alignment_in_bits); - } - - pub fn create_parameter_variable(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, argument_number: c_uint, file: *DI.File, line: c_uint, parameter_type: *DI.Type, always_preserve: bool, flags: DI.Flags) *DI.LocalVariable { - return api.LLVMDIBuilderCreateParameterVariable(builder, scope, name.ptr, name.len, argument_number, file, line, parameter_type, @intFromBool(always_preserve), flags); - } - - pub const insert_declare_record_at_end = api.LLVMDIBuilderInsertDeclareRecordAtEnd; - - pub fn create_global_variable(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, linkage_name: []const u8, file: *DI.File, line: c_uint, global_type: *DI.Type, local_to_unit: bool, expression: *DI.Expression, align_in_bits: u32) *DI.GlobalVariableExpression { - const declaration: ?*DI.Metadata = null; - return api.LLVMDIBuilderCreateGlobalVariableExpression(builder, scope, name.ptr, name.len, linkage_name.ptr, linkage_name.len, file, line, global_type, @intFromBool(local_to_unit), expression, declaration, align_in_bits); - } - - pub const create_lexical_block = api.LLVMDIBuilderCreateLexicalBlock; - - pub fn create_replaceable_composite_type(builder: *DI.Builder, tag: c_uint, name: []const u8, scope: *DI.Scope, file: *DI.File, line: c_uint) *DI.Type.Composite { - return api.LLVMDIBuilderCreateReplaceableCompositeType(builder, tag, name.ptr, name.len, scope, file, line, 0, 0, 0, .{}, null, 0); - } - - pub fn create_array_type(builder: *DI.Builder, element_count: u64, align_in_bits: u32, element_type: *DI.Type, subscripts: []const *DI.Metadata) *DI.Type.Composite { - return api.LLVMDIBuilderCreateArrayType(builder, element_count, align_in_bits, element_type, subscripts.ptr, @intCast(subscripts.len)); - } - - pub fn create_struct_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: DI.Flags, members: []const *DI.Type.Derived) *DI.Type.Composite { - const derived_from: ?*DI.Type = null; - const runtime_language: c_uint = 0; - const vtable_holder: ?*DI.Metadata = null; - const unique_id_pointer: ?[*]const u8 = null; - const unique_id_length: usize = 0; - return api.LLVMDIBuilderCreateStructType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, flags, derived_from, members.ptr, @intCast(members.len), runtime_language, vtable_holder, unique_id_pointer, unique_id_length); - } - - pub fn create_union_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: DI.Flags, members: []const *DI.Type.Derived) *DI.Type.Composite { - const runtime_language: c_uint = 0; - const unique_id_pointer: ?[*]const u8 = null; - const unique_id_length: usize = 0; - return api.LLVMDIBuilderCreateUnionType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, flags, members.ptr, @intCast(members.len), runtime_language, unique_id_pointer, unique_id_length); - } - - pub fn create_member_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, bit_offset: u64, flags: DI.Flags, member_type: *DI.Type) *DI.Type.Derived { - return api.LLVMDIBuilderCreateMemberType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, bit_offset, flags, member_type); - } - - pub fn create_bit_field_member_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, bit_offset: u64, bit_storage_offset: u64, flags: DI.Flags, member_type: *DI.Type) *DI.Type.Derived { - return api.LLVMDIBuilderCreateBitFieldMemberType(builder, scope, name.ptr, name.len, file, line, bit_size, bit_offset, bit_storage_offset, flags, member_type); - } - - pub fn create_pointer_type(builder: *DI.Builder, element_type: *DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name: []const u8) *DI.Type.Derived { - return api.LLVMDIBuilderCreatePointerType(builder, element_type, bit_size, align_in_bits, address_space, name.ptr, name.len); - } - - pub fn create_enumerator(builder: *DI.Builder, name: []const u8, value: i64, is_signed: bool) *DI.Enumerator { - return api.LLVMDIBuilderCreateEnumerator(builder, name.ptr, name.len, value, @intFromBool(is_signed)); - } - - pub fn create_enumeration_type(builder: *DI.Builder, scope: *DI.Scope, name: []const u8, file: *DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, enumeration_types: []const *DI.Enumerator, backing_type: *DI.Type) *DI.Type.Composite { - return api.LLVMDIBuilderCreateEnumerationType(builder, scope, name.ptr, name.len, file, line, bit_size, align_in_bits, enumeration_types.ptr, @intCast(enumeration_types.len), backing_type); - } - - pub fn create_typedef(builder: *DI.Builder, ty: *DI.Type, name: []const u8, file: *DI.File, line: c_uint, scope: *DI.Scope, align_in_bits: u32) *DI.Type.Derived { - return api.LLVMDIBuilderCreateTypedef(builder, ty, name.ptr, name.len, file, line, scope, align_in_bits); - } - }; - - pub const create_debug_location = api.LLVMDIBuilderCreateDebugLocation; - - pub const CompileUnit = opaque { - pub fn to_scope(compile_unit: *DI.CompileUnit) *DI.Scope { - return @ptrCast(compile_unit); - } - }; - pub const File = opaque { - pub fn to_scope(file: *File) *Scope { - return @ptrCast(file); - } - }; - pub const Scope = opaque {}; - pub const Subprogram = opaque { - pub const replace_type = api.llvm_subprogram_replace_type; - }; - pub const Expression = opaque {}; - pub const GlobalVariableExpression = opaque {}; - pub const LexicalBlock = opaque { - pub fn to_scope(lexical_block: *LexicalBlock) *Scope { - return @ptrCast(lexical_block); - } - }; - pub const LocalVariable = opaque {}; - pub const Location = opaque {}; - pub const Metadata = opaque {}; - pub const Record = opaque {}; - - pub const Type = opaque { - // TODO: typecheck - pub fn to_subroutine(ty: *DI.Type) *Subroutine { - return @ptrCast(ty); - } - pub const Subroutine = opaque { - pub fn to_type(subroutine: *Subroutine) *DI.Type { - return @ptrCast(subroutine); - } - }; - pub const Composite = opaque { - pub fn to_type(composite: *Composite) *DI.Type { - return @ptrCast(composite); - } - - pub const replace_all_uses_with = api.LLVMMetadataReplaceAllUsesWith; - }; - pub const Derived = opaque { - pub fn to_type(derived: *Derived) *DI.Type { - return @ptrCast(derived); - } - }; - }; - - pub const Enumerator = opaque {}; - - pub const Flags = packed struct(u32) { - visibility: Visibility = .none, - forward_declaration: bool = false, - apple_block: bool = false, - block_by_ref_struct: bool = false, - virtual: bool = false, - artificial: bool = false, - explicit: bool = false, - prototyped: bool = false, - objective_c_class_complete: bool = false, - object_pointer: bool = false, - vector: bool = false, - static_member: bool = false, - lvalue_reference: bool = false, - rvalue_reference: bool = false, - reserved: bool = false, - inheritance: Inheritance = .none, - introduced_virtual: bool = false, - bit_field: bool = false, - no_return: bool = false, - type_pass_by_value: bool = false, - type_pass_by_reference: bool = false, - enum_class: bool = false, - thunk: bool = false, - non_trivial: bool = false, - big_endian: bool = false, - little_endian: bool = false, - all_calls_described: bool = false, - _: u3 = 0, - - const Visibility = enum(u2) { - none = 0, - private = 1, - protected = 2, - public = 3, - }; - const Inheritance = enum(u2) { - none = 0, - single = 1, - multiple = 2, - virtual = 3, - }; - }; -}; - -pub const Type = opaque { - pub const Kind = enum(c_uint) { - Void, - Half, - Float, - Double, - X86_FP80, - FP128, - PPC_FP128, - Label, - Integer, - Function, - Struct, - Array, - Pointer, - Vector, - Metadata, - X86_MMX, - Token, - ScalableVector, - BFloat, - X86_AMX, - TargetExt, - }; - - pub const get_kind = api.LLVMGetTypeKind; - pub const get_poison = api.LLVMGetPoison; - - pub fn to_integer(ty: *Type) *Type.Integer { - assert(ty.get_kind() == .Integer); - return @ptrCast(ty); - } - - pub fn to_function(ty: *Type) *Type.Function { - assert(ty.get_kind() == .Function); - return @ptrCast(ty); - } - - pub fn to_struct(ty: *Type) *Type.Struct { - assert(ty.get_kind() == .Struct); - return @ptrCast(ty); - } - - pub const Function = opaque { - pub const get_return_type = api.LLVMGetReturnType; - pub fn get(return_type: *Type, parameter_types: []const *Type, is_var_args: bool) *Type.Function { - return api.LLVMFunctionType(return_type, parameter_types.ptr, @intCast(parameter_types.len), @intFromBool(is_var_args)); - } - - pub fn to_type(function_type: *Type.Function) *Type { - return @ptrCast(function_type); - } - }; - - pub const Integer = opaque { - pub const get_constant = api.LLVMConstInt; - pub fn to_type(integer: *Type.Integer) *Type { - return @ptrCast(integer); - } - pub const get_bit_count = api.llvm_integer_type_get_bit_count; - }; - - pub const Struct = opaque { - pub fn get_constant(struct_type: *Type.Struct, constant_values: []const *Constant) *Constant { - return api.LLVMConstNamedStruct(struct_type, constant_values.ptr, @intCast(constant_values.len)); - } - - pub fn to_type(struct_type: *Type.Struct) *Type { - return @ptrCast(struct_type); - } - - pub fn set_body(struct_type: *Type.Struct, element_types: []const *Type) void { - const is_packed = false; - api.LLVMStructSetBody(struct_type, element_types.ptr, @intCast(element_types.len), @intFromBool(is_packed)); - } - }; - - pub const Array = opaque { - pub fn to_type(array_type: *Type.Array) *Type { - return @ptrCast(array_type); - } - }; - - pub const Pointer = opaque { - pub fn to_type(pointer_type: *Type.Pointer) *Type { - return @ptrCast(pointer_type); - } - }; - - pub fn get_array_type(element_type: *Type, element_count: u64) *Type.Array { - return api.LLVMArrayType2(element_type, element_count); - } - - pub fn get_constant_array(element_type: *Type, values: []const *Constant) *Constant { - return api.LLVMConstArray2(element_type, values.ptr, values.len); - } - - pub const get_size = api.LLVMSizeOf; - pub const get_alignment = api.LLVMAlignOf; - pub const get_zero = api.LLVMConstNull; -}; - -pub const Dwarf = struct { - pub const Type = enum(c_uint) { - void = 0x0, - address = 0x1, - boolean = 0x2, - complex_float = 0x3, - float = 0x4, - signed = 0x5, - signed_char = 0x6, - unsigned = 0x7, - unsigned_char = 0x8, - - // DWARF 3. - imaginary_float = 0x9, - packed_decimal = 0xa, - numeric_string = 0xb, - edited = 0xc, - signed_fixed = 0xd, - unsigned_fixed = 0xe, - decimal_float = 0xf, - - // DWARF 4. - UTF = 0x10, - - // DWARF 5. - UCS = 0x11, - ASCII = 0x12, - - // HP extensions. - HP_float80 = 0x80, // Floating-point (80 bit). - HP_complex_float80 = 0x81, // Complex floating-point (80 bit). - HP_float128 = 0x82, // Floating-point (128 bit). - HP_complex_float128 = 0x83, // Complex fp (128 bit). - HP_floathpintel = 0x84, // Floating-point (82 bit IA64). - HP_imaginary_float80 = 0x85, - HP_imaginary_float128 = 0x86, - HP_VAX_float = 0x88, // F or G floating. - HP_VAX_float_d = 0x89, // D floating. - HP_packed_decimal = 0x8a, // Cobol. - HP_zoned_decimal = 0x8b, // Cobol. - HP_edited = 0x8c, // Cobol. - HP_signed_fixed = 0x8d, // Cobol. - HP_unsigned_fixed = 0x8e, // Cobol. - HP_VAX_complex_float = 0x8f, // F or G floating complex. - HP_VAX_complex_float_d = 0x90, // D floating complex. - }; - - pub const EmissionKind = enum(c_int) { - none, - full, - line_tables_only, - }; - - pub const SourceLanguage = enum(c_int) { - C89, - C, - Ada83, - C_plus_plus, - Cobol74, - Cobol85, - Fortran77, - Fortran90, - Pascal83, - Modula2, - // New in DWARF v3: - Java, - C99, - Ada95, - Fortran95, - PLI, - ObjC, - ObjC_plus_plus, - UPC, - D, - // New in DWARF v4: - Python, - // New in DWARF v5: - OpenCL, - Go, - Modula3, - Haskell, - C_plus_plus_03, - C_plus_plus_11, - OCaml, - Rust, - C11, - Swift, - Julia, - Dylan, - C_plus_plus_14, - Fortran03, - Fortran08, - RenderScript, - BLISS, - Kotlin, - Zig, - Crystal, - C_plus_plus_17, - C_plus_plus_20, - C17, - Fortran18, - Ada2005, - Ada2012, - HIP, - Assembly, - C_sharp, - Mojo, - GLSL, - GLSL_ES, - HLSL, - OpenCL_CPP, - CPP_for_OpenCL, - SYCL, - Ruby, - Move, - Hylo, - - // Vendor extensions: - Mips_Assembler, - GOOGLE_RenderScript, - BORLAND_Delphi, - }; -}; - -pub fn lookup_intrinsic_id(name: []const u8) Intrinsic.Id { - return api.LLVMLookupIntrinsicID(name.ptr, name.len); -} - -// pub fn lookup_attribute_kind(name: []const u8) Attribute.Kind { -// return api.LLVMGetEnumAttributeKindForName(name.ptr, name.len); -// } - -pub const IntPredicate = enum(c_int) { - eq = 32, - ne, - ugt, - uge, - ult, - ule, - sgt, - sge, - slt, - sle, -}; - -pub const LinkageType = enum(c_int) { - ExternalLinkage, - AvailableExternallyLinkage, - LinkOnceAnyLinkage, - LinkOnceODRLinkage, - WeakAnyLinkage, - WeakODRLinkage, - AppendingLinkage, - InternalLinkage, - PrivateLinkage, - ExternalWeakLinkage, - CommonLinkage, -}; - -pub const ThreadLocalMode = enum(c_uint) { - none = 0, -}; - -pub const CallingConvention = enum(c_uint) { - c = 0, - fast = 8, - cold = 9, - ghc = 10, - hipe = 11, - anyreg = 13, - preserve_most = 14, - preserve_all = 15, - swift = 16, - cxx_fast_tls = 17, - x86_stdcall = 64, - x86_fastcall = 65, - arm_apcs = 66, - arm_aapcs = 67, - arm_aapcsvfp = 68, - msp430_interrupt = 69, - x86_thiscall = 70, - ptx_kernel = 71, - ptx_device = 72, - spir_func = 75, - spir_kernel = 76, - intel_oclbi = 77, - x86_64_system_v = 78, - win64 = 79, - x86_vector = 80, - hhvm = 81, - hhvmc = 82, - x86_interrupt = 83, - avr_interrupt = 84, - avr_signal = 85, - avr_builtin = 86, - amdgpu_vs = 87, - amdgpu_gs = 88, - amdgpu_ps = 89, - amdgpu_cs = 90, - amdgpu_kernel = 91, - x86_regcall = 92, - amdgpu_hs = 93, - msp430_builtin = 94, - amgpu_ls = 95, - amdgpu_es = 96, -}; - -pub const lld = struct { - pub const Result = extern struct { - stdout: String, - stderr: String, - success: bool, - }; -}; - -pub const Global = struct { - host_triple: []const u8, - host_cpu_model: []const u8, - host_cpu_features: []const u8, -}; -pub var global: Global = undefined; - -pub var initialized = false; - -// This is meant to call globally, only once per execution -pub fn initialize_all() void { - assert(!initialized); - defer initialized = true; - inline for (targets) |target| { - target.initialize(.{}); - } - - global = .{ - .host_triple = api.llvm_default_target_triple().to_slice() orelse unreachable, - .host_cpu_model = api.llvm_host_cpu_name().to_slice() orelse unreachable, - .host_cpu_features = api.llvm_host_cpu_features().to_slice() orelse unreachable, - }; -} - -const LldArgvBuilder = struct { - buffer: [1024]?[*:0]const u8 = undefined, - count: usize = 0, - - pub fn add(builder: *LldArgvBuilder, arg: [*:0]const u8) void { - builder.buffer[builder.count] = arg; - builder.count += 1; - } - - pub fn flush(builder: *LldArgvBuilder) [:null]const ?[*:0]const u8 { - builder.buffer[builder.count] = null; - return builder.buffer[0..builder.count :null]; - } -}; - -pub fn default_initialize() void { - if (!initialized) { - initialize_all(); - } -} - -pub const GenerateObject = struct { - path: []const u8, - target_triple: []const u8, - cpu_model: []const u8, - cpu_features: []const u8, - target_options: Target.Options, -}; - -pub const ObjectGenerate = struct { - path: []const u8, - optimization_level: ?OptimizationLevel, - debug_info: bool, - optimize_when_possible: bool, -}; - -pub fn object_generate(module: *Module, target_machine: *Target.Machine, generate: ObjectGenerate) CodeGenerationPipelineResult { - module.set_target(target_machine); - - if (generate.optimization_level) |optimization_level| { - module.run_optimization_pipeline(target_machine, OptimizationPipelineOptions.default(.{ .optimization_level = optimization_level, .debug_info = generate.debug_info })); - } - - // const mod_string = module.to_string(); - // lib.print_string_stderr(mod_string); - - const result = module.run_code_generation_pipeline(target_machine, CodeGenerationPipelineOptions{ - .output_file_path = String.from_slice(generate.path), - .output_dwarf_file_path = .{}, - .flags = .{ - .code_generation_file_type = .object_file, - .optimize_when_possible = generate.optimize_when_possible, - .verify_module = true, - }, - }); - - return result; -} - -pub const LinkOptions = struct { - objects: []const [:0]const u8, - output_path: [:0]const u8, -}; - -pub fn link(arena: *Arena, options: LinkOptions) lld.Result { - var arg_builder = LldArgvBuilder{}; - arg_builder.add("ld.lld"); - arg_builder.add("--error-limit=0"); - arg_builder.add("-o"); - arg_builder.add(options.output_path); - for (options.objects) |object| { - arg_builder.add(object); - } - - const library_paths = [_][:0]const u8{ "/usr/lib", "/usr/lib/x86_64-linux-gnu" }; - - const scrt1_object_directory_path = inline for (library_paths) |library_path| { - const scrt1_path = library_path ++ "/" ++ "Scrt1.o"; - const file = lib.os.File.open(scrt1_path, .{ .read = 1 }, .{}); - if (file.is_valid()) { - file.close(); - break library_path; - } - } else { - lib.print_string_stderr("Failed to find directory for Scrt1.o\n"); - lib.os.abort(); - }; - - arg_builder.add(arena.join_string(&.{ "-L", scrt1_object_directory_path })); - - const link_libcpp = false; - if (link_libcpp) { - arg_builder.add("-lstdc++"); - } - - const link_libc = true; - - const dynamic_linker = true; - if (dynamic_linker) { - arg_builder.add("-dynamic-linker"); - - const dynamic_linker_path = "/usr/lib64/ld-linux-x86-64.so.2"; - arg_builder.add(dynamic_linker_path); - } - - if (link_libc) { - arg_builder.add(arena.join_string(&.{ scrt1_object_directory_path, "/", "Scrt1.o" })); - arg_builder.add("-lc"); - } - - const lld_args = arg_builder.flush(); - const lld_result = api.lld_elf_link(lld_args.ptr, lld_args.len, true, false); - const success = lld_result.success and lld_result.stderr.length == 0; - if (!success) { - for (lld_args) |lld_arg| { - lib.print_string_stderr(lib.cstring.to_slice(lld_arg.?)); - lib.print_string_stderr(" "); - } - lib.print_string_stderr("\n"); - - if (lld_result.stdout.length != 0) { - lib.print_string_stderr(lld_result.stdout.to_slice() orelse unreachable); - } - - if (lld_result.stderr.length != 0) { - lib.print_string_stderr(lld_result.stderr.to_slice() orelse unreachable); - } - } - - return lld_result; -} diff --git a/src/bootstrap.zig b/src/bootstrap.zig deleted file mode 100644 index 53df3a0..0000000 --- a/src/bootstrap.zig +++ /dev/null @@ -1,10784 +0,0 @@ -const lib = @import("lib.zig"); -const assert = lib.assert; -const builtin = lib.builtin; -const Arena = lib.Arena; - -const llvm = @import("LLVM.zig"); - -pub const CPUArchitecture = enum { - x86_64, -}; - -pub const OperatingSystem = enum { - linux, -}; - -fn array_type_name(arena: *Arena, element_type: *Type, element_count: u64) [:0]const u8 { - var buffer: [256]u8 = undefined; - var i: u64 = 0; - buffer[i] = left_bracket; - i += 1; - i += lib.string_format.integer_decimal(buffer[i..], element_count); - buffer[i] = right_bracket; - i += 1; - const element_name = element_type.name; - @memcpy(buffer[i..][0..element_name.len], element_name); - i += element_name.len; - return arena.duplicate_string(buffer[0..i]); -} - -pub const BuildMode = enum { - debug_none, - debug_fast, - debug_size, - soft_optimize, - optimize_for_speed, - optimize_for_size, - aggressively_optimize_for_speed, - aggressively_optimize_for_size, - - fn is_optimized(build_mode: BuildMode) bool { - return @intFromEnum(build_mode) >= @intFromEnum(BuildMode.soft_optimize); - } - - fn to_llvm_ir(build_mode: BuildMode) llvm.OptimizationLevel { - return switch (build_mode) { - .debug_none => unreachable, - .debug_fast, .debug_size => .O0, - .soft_optimize => .O1, - .optimize_for_speed => .O2, - .optimize_for_size => .Os, - .aggressively_optimize_for_speed => .O3, - .aggressively_optimize_for_size => .Oz, - }; - } - - fn to_llvm_machine(build_mode: BuildMode) llvm.CodeGenerationOptimizationLevel { - return switch (build_mode) { - .debug_none => .none, - .debug_fast, .debug_size => .none, - .soft_optimize => .less, - .optimize_for_speed => .default, - .optimize_for_size => .default, - .aggressively_optimize_for_speed => .aggressive, - .aggressively_optimize_for_size => .aggressive, - }; - } -}; - -pub const Target = struct { - cpu: CPUArchitecture, - os: OperatingSystem, - - pub fn get_native() Target { - return Target{ - .cpu = switch (builtin.cpu.arch) { - .x86_64 => .x86_64, - else => @compileError("CPU not supported"), - }, - .os = switch (builtin.os.tag) { - .linux => .linux, - else => @compileError("OS not supported"), - }, - }; - } -}; - -fn is_space(ch: u8) bool { - return ((@intFromBool(ch == ' ') | @intFromBool(ch == '\n')) | ((@intFromBool(ch == '\t') | @intFromBool(ch == '\r')))) != 0; -} - -const left_bracket = '['; -const right_bracket = ']'; -const left_brace = '{'; -const right_brace = '}'; -const left_parenthesis = '('; -const right_parenthesis = ')'; - -const GlobalKeyword = enum { - @"export", - @"extern", -}; -fn is_identifier_start_ch(ch: u8) bool { - return (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_'; -} - -fn is_decimal_ch(ch: u8) bool { - return ch >= '0' and ch <= '9'; -} - -fn is_octal_ch(ch: u8) bool { - return ch >= '0' and ch <= '7'; -} - -fn is_binary_ch(ch: u8) bool { - return ch == '0' or ch == '1'; -} - -fn is_identifier_ch(ch: u8) bool { - return is_identifier_start_ch(ch) or is_decimal_ch(ch); -} - -pub const Variable = struct { - storage: ?*Value = null, - initial_value: *Value, - type: ?*Type, - scope: *Scope, - name: []const u8, - line: u32, - column: u32, - - fn resolve_type(variable: *Variable, auxiliary_type: *Type) void { - const resolved_type: *Type = variable.type orelse auxiliary_type; - variable.type = resolved_type; - } -}; - -pub const Local = struct { - variable: Variable, - argument_index: ?u32, - - pub const Buffer = struct { - buffer: lib.VirtualBuffer(Local), - - pub fn initialize() Buffer { - return .{ - .buffer = .initialize(), - }; - } - - pub fn find_by_name(locals: *Buffer, name: []const u8) ?*Local { - const slice = locals.buffer.get_slice(); - for (slice) |*local| { - if (lib.string.equal(local.variable.name, name)) { - return local; - } - } else { - return null; - } - } - - pub fn get_slice(locals: *Buffer) []Local { - return locals.buffer.get_slice(); - } - - pub fn add(locals: *Buffer) *Local { - return locals.buffer.add(); - } - }; -}; - -pub const Linkage = enum { - external, - internal, -}; - -pub const Global = struct { - variable: Variable, - linkage: Linkage, - - pub const Buffer = struct { - buffer: lib.VirtualBuffer(Global), - - pub fn get_slice(buffer: Buffer) []Global { - return buffer.buffer.get_slice(); - } - - pub fn initialize() Buffer { - return .{ - .buffer = .initialize(), - }; - } - - pub fn find_by_name(globals: *Buffer, name: []const u8) ?*Global { - const slice = globals.buffer.get_slice(); - for (slice) |*global| { - if (lib.string.equal(global.variable.name, name)) { - return global; - } - } else { - return null; - } - } - - pub fn add(globals: *Buffer) *Global { - return globals.buffer.add(); - } - }; -}; - -const ConstantArgument = struct { - kind: Kind, - index: u8, - const Kind = enum { - type, - value, - }; -}; - -pub const Macro = struct { - arguments: []const *Local, - argument_types: []const *Type, - constant_argument_names: []const []const u8, - constant_argument_values: []const *Value, - type_arguments: []const *Type, - constant_arguments: []const ConstantArgument, - return_type: *Type, - block: *LexicalBlock, - name: []const u8, - scope: Scope, - is_generic: bool, - - pub const Instantiation = struct { - declaration: *Macro, - function: *Global, - declaration_arguments: []*Local, - instantiation_arguments: []const *Value, - constant_argument_values: []const *Value, - type_arguments: []*Type, - return_type: *Type, - block: *LexicalBlock, - return_alloca: *llvm.Value, - return_block: *llvm.BasicBlock, - function_scope: Scope, - instantiation_line: u32, - instantiation_column: u32, - }; - - pub const Buffer = struct { - buffer: lib.VirtualBuffer(Macro), - - pub fn get_slice(buffer: Buffer) []Macro { - return buffer.buffer.get_slice(); - } - - pub fn initialize() Buffer { - return .{ - .buffer = .initialize(), - }; - } - - pub fn find_by_name(globals: *Buffer, name: []const u8) ?*Macro { - const slice = globals.buffer.get_slice(); - for (slice) |*global| { - if (lib.string.equal(global.variable.name, name)) { - return global; - } - } else { - return null; - } - } - - pub fn add(globals: *Buffer) *Macro { - return globals.buffer.add(); - } - }; -}; - -pub const ResolvedType = struct { - handle: *llvm.Type, - debug: *llvm.DI.Type, -}; - -pub const Enumerator = struct { - fields: []const Enumerator.Field, - string_to_enum: ?StringToEnum = null, - enum_to_string: ?*llvm.Function = null, - name_array_global: ?*Global = null, - backing_type: *Type, - line: u32, - implicit_backing_type: bool, - - pub const Field = struct { - name: []const u8, - value: u64, - }; - - pub const StringToEnum = struct { - function: *llvm.Function, - struct_type: *Type, - }; -}; - -pub const Type = struct { - bb: union(enum) { - void, - noreturn, - integer: Type.Integer, - enumerator: Enumerator, - float: Float, - bits: Type.Bits, - pointer: Type.Pointer, - function: Type.Function, - array: Type.Array, - structure: Type.Struct, - @"union": Type.Union, - vector, - forward_declaration, - alias: Type.Alias, - unresolved, - }, - name: []const u8, - llvm: LLVM = .{}, - - const Kind = enum { - abi, - memory, - }; - - const LLVM = struct { - abi: ?*llvm.Type = null, - memory: ?*llvm.Type = null, - debug: ?*llvm.DI.Type = null, - - const Type = struct { - handle: ?*llvm.Type = null, - debug: ?*llvm.DI.Type = null, - }; - }; - - pub const Union = struct { - fields: []Union.Field, - byte_size: u64, - byte_alignment: u32, - line: u32, - biggest_field: u32, - - const Field = struct { - type: *Type, - name: []const u8, - line: u32, - }; - }; - - pub const Alias = struct { - type: *Type, - line: u32, - scope: *Scope, - }; - - pub const Float = struct { - const Kind = enum { - half, - bfloat, - float, - double, - fp128, - }; - kind: Float.Kind, - }; - - pub const Intrinsic = struct { - const Id = enum { - ReturnType, - foo, - }; - }; - - pub fn is_slice(ty: *Type) bool { - return switch (ty.bb) { - .structure => |structure| structure.is_slice, - else => false, - }; - } - - pub fn is_integer_backing(ty: *Type) bool { - return switch (ty.bb) { - .enumerator, .integer, .bits, .pointer => true, - else => false, - }; - } - - pub fn get_llvm(ty: *Type, kind: Type.Kind) *llvm.Type { - return switch (kind) { - .abi => ty.llvm.abi.?, - .memory => ty.llvm.memory.?, - }; - } - - fn resolve(ty: *Type, module: *Module) void { - if (ty.llvm.abi == null) { - const abi_type = switch (ty.bb) { - .void, .noreturn => module.llvm.void_type, - .integer => |integer| module.llvm.context.get_integer_type(integer.bit_count).to_type(), - .pointer => module.llvm.pointer_type, - .array => |array| blk: { - array.element_type.resolve(module); - const array_type = array.element_type.llvm.memory.?.get_array_type(array.element_count); - break :blk array_type.to_type(); - }, - .enumerator => |enumerator| blk: { - enumerator.backing_type.resolve(module); - break :blk enumerator.backing_type.llvm.abi.?; - }, - .structure => |structure| blk: { - var llvm_type_buffer: [64]*llvm.Type = undefined; - const llvm_types = llvm_type_buffer[0..structure.fields.len]; - for (llvm_types, structure.fields) |*llvm_type, *field| { - field.type.resolve(module); - llvm_type.* = field.type.llvm.memory.?; - } - const struct_type = module.llvm.context.get_struct_type(llvm_types); - break :blk struct_type.to_type(); - }, - .bits => |bits| blk: { - bits.backing_type.resolve(module); - const t = bits.backing_type.llvm.abi.?; - break :blk t; - }, - .alias => |alias| blk: { - alias.type.resolve(module); - break :blk alias.type.llvm.abi.?; - }, - .@"union" => |union_type| blk: { - const biggest_type = union_type.fields[union_type.biggest_field].type; - biggest_type.resolve(module); - const result_type = module.llvm.context.get_struct_type(&.{biggest_type.llvm.memory.?}); - break :blk result_type.to_type(); - }, - else => @trap(), - }; - ty.llvm.abi = abi_type; - - const memory_type = switch (ty.bb) { - .void, - .noreturn, - .pointer, - .array, - .structure, - .@"union", - => abi_type, - .integer => module.llvm.context.get_integer_type(@intCast(ty.get_byte_size() * 8)).to_type(), - .enumerator => |enumerator| enumerator.backing_type.llvm.memory.?, - .bits => |bits| bits.backing_type.llvm.memory.?, // TODO: see assert below - .alias => |alias| alias.type.llvm.memory.?, - else => @trap(), - }; - ty.llvm.memory = memory_type; - if (ty.bb == .bits) assert(ty.llvm.memory == ty.llvm.abi); - - if (module.has_debug_info) { - const debug_type = switch (ty.bb) { - .void, .noreturn => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{ .no_return = ty.bb == .noreturn }), - .integer => |integer| module.llvm.di_builder.create_basic_type(ty.name, @max(lib.next_power_of_two(integer.bit_count), 8), switch (integer.bit_count) { - 1 => .boolean, - else => switch (integer.signed) { - true => .signed, - false => .unsigned, - }, - }, .{}), - .pointer => |pointer| b: { - pointer.type.resolve(module); - break :b module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(); - }, - .array => |array| module.llvm.di_builder.create_array_type(array.element_count, 0, array.element_type.llvm.debug.?, &.{}).to_type(), - .enumerator => |enumerator| blk: { - var enumerator_buffer: [64]*llvm.DI.Enumerator = undefined; - const enumerators = enumerator_buffer[0..enumerator.fields.len]; - for (enumerators, enumerator.fields) |*enumerator_pointer, *field| { - enumerator_pointer.* = module.llvm.di_builder.create_enumerator(field.name, @bitCast(field.value), false); - } - const alignment = 0; // TODO - const enumeration_type = module.llvm.di_builder.create_enumeration_type(module.scope.llvm.?, ty.name, module.llvm.file, enumerator.line, enumerator.backing_type.get_bit_size(), alignment, enumerators, enumerator.backing_type.llvm.debug.?); - break :blk enumeration_type.to_type(); - }, - .structure => |structure| blk: { - const struct_type = module.llvm.di_builder.create_replaceable_composite_type(module.llvm.debug_tag, ty.name, module.scope.llvm.?, module.llvm.file, structure.line); - ty.llvm.debug = struct_type.to_type(); - module.llvm.debug_tag += 1; - - var llvm_debug_member_type_buffer: [64]*llvm.DI.Type.Derived = undefined; - const llvm_debug_member_types = llvm_debug_member_type_buffer[0..structure.fields.len]; - - for (structure.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| { - field.type.resolve(module); - const member_type = module.llvm.di_builder.create_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_byte_size() * 8, @intCast(field.type.get_byte_alignment() * 8), field.bit_offset, .{}, field.type.llvm.debug.?); - llvm_debug_member_type.* = member_type; - } - - const debug_struct_type = module.llvm.di_builder.create_struct_type(module.scope.llvm.?, ty.name, module.llvm.file, structure.line, structure.bit_size, @intCast(structure.bit_alignment), .{}, llvm_debug_member_types); - const forward_declared: *llvm.DI.Type.Composite = @ptrCast(ty.llvm.debug); - forward_declared.replace_all_uses_with(debug_struct_type); - break :blk debug_struct_type.to_type(); - }, - .bits => |bits| blk: { - var llvm_debug_member_type_buffer: [64]*llvm.DI.Type.Derived = undefined; - const llvm_debug_member_types = llvm_debug_member_type_buffer[0..bits.fields.len]; - for (bits.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| { - llvm_debug_member_type.* = module.llvm.di_builder.create_bit_field_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_bit_size(), field.bit_offset, 0, .{}, bits.backing_type.llvm.debug.?); - } - - const struct_type = module.llvm.di_builder.create_struct_type(module.scope.llvm.?, ty.name, module.llvm.file, bits.line, ty.get_bit_size(), @intCast(ty.get_bit_alignment()), .{}, llvm_debug_member_types); - break :blk struct_type.to_type(); - }, - .alias => |alias| blk: { - const typedef = module.llvm.di_builder.create_typedef(alias.type.llvm.debug.?, ty.name, module.llvm.file, alias.line, alias.scope.llvm.?, 0); - break :blk typedef.to_type(); - }, - .@"union" => |union_type| blk: { - const result_type = module.llvm.di_builder.create_replaceable_composite_type(module.llvm.debug_tag, ty.name, module.scope.llvm.?, module.llvm.file, union_type.line); - ty.llvm.debug = result_type.to_type(); - module.llvm.debug_tag += 1; - - var llvm_debug_member_type_buffer: [64]*llvm.DI.Type.Derived = undefined; - const llvm_debug_member_types = llvm_debug_member_type_buffer[0..union_type.fields.len]; - - for (union_type.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| { - field.type.resolve(module); - const member_type = module.llvm.di_builder.create_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_byte_size() * 8, @intCast(field.type.get_byte_alignment() * 8), 0, .{}, field.type.llvm.debug.?); - llvm_debug_member_type.* = member_type; - } - - const debug_struct_type = module.llvm.di_builder.create_union_type(module.scope.llvm.?, ty.name, module.llvm.file, union_type.line, union_type.byte_size * 8, @intCast(union_type.byte_alignment * 8), .{}, llvm_debug_member_types); - const forward_declared: *llvm.DI.Type.Composite = @ptrCast(ty.llvm.debug); - forward_declared.replace_all_uses_with(debug_struct_type); - break :blk debug_struct_type.to_type(); - }, - else => @trap(), - }; - ty.llvm.debug = debug_type; - } - } - } - - const Bits = struct { - fields: []const Struct.Field, - backing_type: *Type, - line: u32, - implicit_backing_type: bool, - }; - - pub const Integer = struct { - bit_count: u32, - signed: bool, - }; - - pub const Pointer = struct { - type: *Type, - alignment: ?u32, - - pub fn get_alignment(p: *Pointer) u32 { - if (p.alignment) |a| return a else { - const type_alignment = p.type.get_byte_alignment(); - p.alignment = type_alignment; - return type_alignment; - } - } - }; - - pub const Function = struct { - semantic_return_type: *Type, - semantic_argument_types: []const *Type, - calling_convention: CallingConvention, - is_var_args: bool, - // Filled during codegen - return_abi: Abi.Information = undefined, - argument_abis: []Abi.Information = undefined, - abi_argument_types: []*Type = undefined, - abi_return_type: *Type = undefined, - available_registers: Abi.RegisterCount = undefined, - }; - - pub const Struct = struct { - fields: []Struct.Field, - byte_size: u64, - bit_size: u64, - byte_alignment: u32, - bit_alignment: u32, - line: u32, - is_slice: bool, - - const Field = struct { - name: []const u8, - type: *Type, - bit_offset: u64, - byte_offset: u64, - line: u32, - }; - }; - - pub const Array = struct { - element_type: *Type, - element_count: u64, - }; - - pub const Buffer = struct { - buffer: lib.VirtualBuffer(Type), - - pub fn initialize() Buffer { - return .{ - .buffer = .initialize(), - }; - } - - pub fn find_by_name(types: *Buffer, name: []const u8) ?*Type { - const slice = types.buffer.get_slice(); - for (slice) |*ty| { - if (lib.string.equal(ty.name, name)) { - return ty; - } - } else { - return null; - } - } - - pub fn get(types: *Buffer, index: u64) *Type { - const slice = types.get_slice(); - assert(index < slice.len); - return &slice[index]; - } - - pub fn get_slice(types: *Buffer) []Type { - const slice = types.buffer.get_slice(); - return slice; - } - - pub fn append(types: *Buffer, ty: Type) *Type { - return types.buffer.append(ty); - } - }; - - pub fn is_signed(ty: *const Type) bool { - return switch (ty.bb) { - .integer => |integer| integer.signed, - .bits => |bits| bits.backing_type.is_signed(), - .enumerator => |enumerator| enumerator.backing_type.is_signed(), - .alias => |alias| alias.type.is_signed(), - else => @trap(), - }; - } - - pub fn is_integral_or_enumeration_type(ty: *const Type) bool { - return switch (ty.bb) { - .integer => true, - .bits => true, - .structure => false, - .alias => |alias| alias.type.is_integral_or_enumeration_type(), - else => @trap(), - }; - } - - pub fn is_arbitrary_bit_integer(ty: *Type) bool { - return switch (ty.bb) { - .integer => |integer| switch (integer.bit_count) { - 8, 16, 32, 64, 128 => false, - else => true, - }, - .bits => |bits| bits.backing_type.is_arbitrary_bit_integer(), - .enumerator => |enumerator| enumerator.backing_type.is_arbitrary_bit_integer(), - else => false, - }; - } - - pub fn is_promotable_integer_type_for_abi(ty: *Type) bool { - return switch (ty.bb) { - .integer => |integer| integer.bit_count < 32, - .bits => |bits| bits.backing_type.is_promotable_integer_type_for_abi(), - .alias => |alias| alias.type.is_promotable_integer_type_for_abi(), - else => @trap(), - }; - } - - pub fn get_byte_alignment(ty: *const Type) u32 { - const result: u32 = switch (ty.bb) { - .void => unreachable, - .integer => |integer| @intCast(@min(@divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8), 16)), - .pointer => 8, - .function => 1, - .array => |array| array.element_type.get_byte_alignment(), - .enumerator => |enumerator| enumerator.backing_type.get_byte_alignment(), - .structure => |structure| structure.byte_alignment, - .bits => |bits| bits.backing_type.get_byte_alignment(), - .alias => |alias| alias.type.get_byte_alignment(), - .unresolved => unreachable, - .@"union" => |union_type| union_type.byte_alignment, - else => @trap(), - }; - return result; - } - - pub fn get_bit_alignment(ty: *const Type) u32 { - // TODO: fix - return switch (ty.bb) { - .integer => |integer| integer.bit_count, // TODO: is this correct? - .pointer => 64, - .bits => |bits| bits.backing_type.get_bit_alignment(), - .array => |array| array.element_type.get_bit_alignment(), - .structure => |structure| structure.bit_alignment, - .enumerator => |enumerator| enumerator.backing_type.get_bit_alignment(), - else => @trap(), - }; - } - - pub fn get_byte_size(ty: *const Type) u64 { - const byte_size: u64 = switch (ty.bb) { - .integer => |integer| @divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8), - .structure => |structure| structure.byte_size, - .pointer => 8, - .array => |array| array.element_type.get_byte_size() * array.element_count, - .bits => |bits| bits.backing_type.get_byte_size(), - .enumerator => |enumerator| enumerator.backing_type.get_byte_size(), - .alias => |alias| alias.type.get_byte_size(), - .@"union" => |union_type| union_type.byte_size, - else => @trap(), - }; - return byte_size; - } - pub fn get_bit_size(ty: *const Type) u64 { - // TODO: fix - const bit_size: u64 = switch (ty.bb) { - .integer => |integer| integer.bit_count, - .pointer => 64, - .bits => |bits| bits.backing_type.get_bit_size(), - .array => |array| array.element_type.get_bit_size() * array.element_count, - .structure => |structure| structure.bit_size, - .enumerator => |enumerator| enumerator.backing_type.get_bit_size(), - .alias => |alias| alias.type.get_bit_size(), - else => @trap(), - }; - return bit_size; - } - - pub fn get_byte_allocation_size(ty: *const Type) u64 { - return lib.align_forward_u64(ty.get_byte_size(), ty.get_byte_alignment()); - } - - pub fn is_aggregate_type_for_abi(ty: *Type) bool { - const ev_kind = ty.get_evaluation_kind(); - const is_member_function_pointer_type = false; // TODO - return ev_kind != .scalar or is_member_function_pointer_type; - } - - pub const EvaluationKind = enum { - scalar, - complex, - aggregate, - }; - - pub fn get_evaluation_kind(ty: *const Type) EvaluationKind { - return switch (ty.bb) { - .structure, .array, .@"union" => .aggregate, - .integer, .bits, .pointer, .enumerator => .scalar, - .alias => |alias| alias.type.get_evaluation_kind(), - else => @trap(), - }; - } - - pub fn is_abi_equal(ty: *Type, other: *Type, module: *Module) bool { - ty.resolve(module); - other.resolve(module); - - return ty == other or ty.llvm.abi == other.llvm.abi; - } -}; - -const ConstantInteger = struct { - value: u64, - signed: bool, -}; - -pub const Statement = struct { - bb: union(enum) { - local: *Local, - @"return": ?*Value, - assignment: Assignment, - expression: *Value, - @"if": If, - @"while": While, - for_each: ForEach, - @"switch": Switch, - block: *LexicalBlock, - break_statement, - continue_statement, - }, - line: u32, - column: u32, - - const ForEach = struct { - locals: []const *Local, - left_values: []const Value.Kind, - right_values: []const *Value, - predicate: *Statement, - kind: Kind, - scope: Scope, - - const Kind = enum { - slice, - range, - }; - }; - - const Assignment = struct { - left: *Value, - right: *Value, - kind: Operator, - - const Operator = enum { - @"=", - @"+=", - @"-=", - @"*=", - @"/=", - @"%=", - @">>=", - @"<<=", - @"&=", - @"|=", - @"^=", - }; - }; - - const If = struct { - condition: *Value, - if_statement: *Statement, - else_statement: ?*Statement, - }; - - const While = struct { - condition: *Value, - block: *LexicalBlock, - }; - - const Switch = struct { - discriminant: *Value, - clauses: []Clause, - - const Clause = struct { - values: []const *Value, - block: *LexicalBlock, - basic_block: *llvm.BasicBlock = undefined, - }; - }; -}; - -const Unary = struct { - value: *Value, - id: Id, - - const Id = enum { - @"-", - @"+", - @"&", - @"!", - @"~", - - pub fn is_boolean(id: Id) bool { - return switch (id) { - .@"+", .@"-", .@"&", .@"~" => false, - .@"!" => true, - }; - } - }; -}; - -const Binary = struct { - left: *Value, - right: *Value, - id: Id, - - const Id = enum { - @"+", - @"-", - @"*", - @"/", - @"%", - @"&", - @"|", - @"^", - @"<<", - @">>", - @"==", - @"!=", - @">", - @"<", - @">=", - @"<=", - @"and", - @"or", - @"and?", - @"or?", - - fn is_boolean(id: Binary.Id) bool { - return switch (id) { - .@"==", - .@"!=", - .@">", - .@"<", - .@">=", - .@"<=", - => true, - else => false, - }; - } - - fn is_shortcircuiting(id: Binary.Id) bool { - return id == .@"and?" or id == .@"or?"; - } - }; -}; - -pub const Call = struct { - callable: *Value, - arguments: []const *Value, - function_type: *Type = undefined, -}; - -pub const FieldAccess = struct { - aggregate: *Value, - field: []const u8, -}; - -pub const Value = struct { - bb: union(enum) { - external_function, - function: Function, - constant_integer: ConstantInteger, - unary: Unary, - binary: Binary, - variable_reference: *Variable, - local, - argument, - global, - intrinsic: Intrinsic, - dereference: *Value, - call: Call, - infer_or_ignore, - array_initialization: ArrayInitialization, - array_expression: ArrayExpression, - enum_literal: []const u8, - field_access: FieldAccess, - string_literal: []const u8, - aggregate_initialization: AggregateInitialization, - zero, - slice_expression: SliceExpression, - @"unreachable", - undefined, - macro_reference: *Macro, - macro_instantiation: Macro.Instantiation, - }, - type: ?*Type = null, - llvm: ?*llvm.Value = null, - kind: Value.Kind = .right, - - pub const SliceExpression = struct { - array_like: *Value, - start: ?*Value, - end: ?*Value, - }; - - pub const ArrayExpression = struct { - array_like: *Value, - index: *Value, - }; - - pub const ArrayInitialization = struct { - values: []const *Value, - is_constant: bool, - }; - - pub const AggregateInitialization = struct { - names: []const []const u8, - values: []const *Value, - is_constant: bool, - zero: bool, - }; - - const Intrinsic = union(Id) { - alignof: *Type, - byte_size: *Type, - enum_name: *Value, - extend: *Value, - integer_max: *Type, - int_from_enum: *Value, - int_from_pointer: *Value, - pointer_cast: *Value, - select: Select, - string_to_enum: StringToEnum, - trap, - truncate: *Value, - va_start, - va_end: *Value, - va_copy, - va_arg: VaArg, - - const Id = enum { - alignof, - byte_size, - enum_name, - extend, - integer_max, - int_from_enum, - int_from_pointer, - pointer_cast, - select, - string_to_enum, - trap, - truncate, - va_start, - va_end, - va_copy, - va_arg, - }; - - const Select = struct { - condition: *Value, - true_value: *Value, - false_value: *Value, - }; - - const VaArg = struct { - list: *Value, - type: *Type, - }; - - const StringToEnum = struct { - enum_type: *Type, - string_value: *Value, - }; - }; - - fn is_constant(value: *Value) bool { - // TODO: do some comptime evaluation? - return switch (value.bb) { - .constant_integer => true, - .variable_reference => |variable| switch (value.kind) { - .left => switch (variable.scope.kind) { - .global => true, - else => false, - }, - .right => false, - }, - .aggregate_initialization => |aggregate_initialization| aggregate_initialization.is_constant, - .field_access => false, - .binary => false, - .array_initialization => |array_initialization| array_initialization.is_constant, - .intrinsic => |intrinsic| switch (intrinsic) { - .byte_size, - .integer_max, - => true, - else => false, - }, - .undefined => true, - .call => false, - .enum_literal => true, - .unary => false, - .array_expression => false, - .string_literal => true, - .dereference => false, - else => @trap(), - }; - } - - pub const Buffer = struct { - buffer: lib.VirtualBuffer(Value), - pub fn initialize() Buffer { - return .{ - .buffer = .initialize(), - }; - } - - pub fn add(values: *Buffer) *Value { - return values.buffer.add(); - } - }; - - const Kind = enum { - left, - right, - }; - - const Keyword = enum { - undefined, - @"unreachable", - zero, - }; - - const Builder = struct { - kind: Value.Kind = .right, - precedence: Precedence = .none, - left: ?*Value = null, - token: Token = .none, - allow_assignment_operators: bool = false, - - fn with_token(vb: Builder, token: Token) Builder { - var v = vb; - v.token = token; - return v; - } - - fn with_precedence(vb: Builder, precedence: Precedence) Builder { - var v = vb; - v.precedence = precedence; - return v; - } - - fn with_left(vb: Builder, left: ?*Value) Builder { - var v = vb; - v.left = left; - return v; - } - - fn with_kind(vb: Builder, kind: Value.Kind) Builder { - var v = vb; - v.kind = kind; - return v; - } - }; -}; - -const Precedence = enum { - none, - assignment, - @"or", - @"and", - comparison, - bitwise, - shifting, - add_like, - div_like, - prefix, - aggregate_initialization, - postfix, - - pub fn increment(precedence: Precedence) Precedence { - return @enumFromInt(@intFromEnum(precedence) + 1); - } -}; - -const GlobalKind = enum { - bits, - @"enum", - @"fn", - macro, - @"struct", - typealias, - @"union", -}; - -const CallingConvention = enum { - c, - - pub fn to_llvm(calling_convention: CallingConvention) llvm.CallingConvention { - return switch (calling_convention) { - .c => .c, - }; - } - - pub fn resolve(calling_convention: CallingConvention, target: Target) ResolvedCallingConvention { - return switch (calling_convention) { - .c => switch (target.cpu) { - .x86_64 => switch (target.os) { - .linux => .system_v, - }, - }, - }; - } -}; - -pub const Scope = struct { - line: u32, - column: u32, - llvm: ?*llvm.DI.Scope = null, - kind: Scope.Kind, - parent: ?*Scope, - - pub const Kind = enum { - global, - function, - local, - for_each, - macro_declaration, - macro_instantiation_function, - macro_instantiation_block, - }; -}; - -pub const LexicalBlock = struct { - locals: lib.VirtualBuffer(*Local), - statements: lib.VirtualBuffer(*Statement), - scope: Scope, -}; - -pub const Function = struct { - arguments: []const *Local, - attributes: Attributes, - main_block: *LexicalBlock, - scope: Scope, - - return_alloca: ?*llvm.Value = null, - exit_block: ?*llvm.BasicBlock = null, - return_block: ?*llvm.BasicBlock = null, - current_scope: ?*llvm.DI.Scope = null, - return_pointer: ?*Value = null, - - const Keyword = enum { - cc, - }; - - const Attributes = struct { - inline_behavior: enum { - default, - always_inline, - no_inline, - inline_hint, - } = .default, - naked: bool = false, - }; -}; - -pub const ResolvedCallingConvention = enum { - system_v, - win64, -}; - -pub const IndexBuffer = lib.VirtualBuffer(u32); - -pub const Module = struct { - arena: *Arena, - content: []const u8, - offset: u64, - line_offset: u64, - line_character_offset: u64, - types: Type.Buffer, - locals: Local.Buffer, - globals: Global.Buffer, - values: Value.Buffer, - macros: Macro.Buffer, - pointer_types: IndexBuffer, - slice_types: IndexBuffer, - pair_struct_types: IndexBuffer, - array_types: IndexBuffer, - void_type: *Type, - noreturn_type: *Type, - va_list_type: ?*Type = null, - void_value: *Value, - lexical_blocks: lib.VirtualBuffer(LexicalBlock), - statements: lib.VirtualBuffer(Statement), - current_function: ?*Global = null, - current_macro_declaration: ?*Macro = null, - current_macro_instantiation: ?*Value = null, - exit_block: ?*llvm.BasicBlock = null, - continue_block: ?*llvm.BasicBlock = null, - inline_at_debug_location: ?*llvm.DI.Location = null, - name: []const u8, - path: []const u8, - executable: [:0]const u8, - objects: []const [:0]const u8, - scope: Scope, - llvm: LLVM = undefined, - build_mode: BuildMode, - target: Target, - has_debug_info: bool, - silent: bool, - - const LLVM = struct { - context: *llvm.Context, - module: *llvm.Module, - builder: *llvm.Builder, - di_builder: *llvm.DI.Builder, - file: *llvm.DI.File, - compile_unit: *llvm.DI.CompileUnit, - pointer_type: *llvm.Type, - void_type: *llvm.Type, - intrinsic_table: IntrinsicTable, - memcmp: ?*llvm.Function = null, - debug_tag: u32, - - const IntrinsicTable = struct { - trap: llvm.Intrinsic.Id, - va_start: llvm.Intrinsic.Id, - va_end: llvm.Intrinsic.Id, - va_copy: llvm.Intrinsic.Id, - }; - }; - - pub fn integer_type(module: *Module, bit_count: u32, sign: bool) *Type { - switch (bit_count) { - 1...64 => { - const index = @as(u64, @intFromBool(sign)) * 64 + bit_count; - const result = module.types.get(index); - assert(result.bb == .integer); - assert(result.bb.integer.bit_count == bit_count); - assert(result.bb.integer.signed == sign); - return result; - }, - 128 => @trap(), - else => @trap(), - } - } - - fn parse_hexadecimal(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!lib.is_hex_digit(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_hexadecimal(value, ch); - } - - return value; - } - - fn parse_decimal(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!is_decimal_ch(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_decimal(value, ch); - } - - return value; - } - - fn parse_octal(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!is_octal_ch(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_octal(value, ch); - } - - return value; - } - - fn parse_binary(noalias module: *Module) u64 { - var value: u64 = 0; - while (true) { - const ch = module.content[module.offset]; - if (!is_binary_ch(ch)) { - break; - } - - module.offset += 1; - value = lib.parse.accumulate_binary(value, ch); - } - - return value; - } - - const AttributeBuildOptions = struct { - return_type_abi: Abi.Information, - abi_argument_types: []const *Type, - argument_type_abis: []const Abi.Information, - abi_return_type: *Type, - attributes: Function.Attributes, - call_site: bool, - }; - - pub fn build_attribute_list(module: *Module, options: AttributeBuildOptions) *llvm.Attribute.List { - options.return_type_abi.semantic_type.resolve(module); - const return_attributes = llvm.Attribute.Argument{ - .semantic_type = options.return_type_abi.semantic_type.llvm.memory.?, - .abi_type = options.abi_return_type.llvm.abi.?, - .dereferenceable_bytes = 0, - .alignment = 0, - .flags = .{ - .no_alias = false, - .non_null = false, - .no_undef = false, - .sign_extend = options.return_type_abi.flags.kind == .extend and options.return_type_abi.flags.sign_extension, - .zero_extend = options.return_type_abi.flags.kind == .extend and !options.return_type_abi.flags.sign_extension, - .in_reg = false, - .no_fp_class = .{}, - .struct_return = false, - .writable = false, - .dead_on_unwind = false, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = false, - .by_reference = false, - .no_capture = false, - }, - }; - var argument_attribute_buffer: [128]llvm.Attribute.Argument = undefined; - const argument_attributes = argument_attribute_buffer[0..options.abi_argument_types.len]; - - if (options.return_type_abi.flags.kind == .indirect) { - const abi_index = @intFromBool(options.return_type_abi.flags.sret_after_this); - const argument_attribute = &argument_attributes[abi_index]; - argument_attribute.* = .{ - .semantic_type = options.return_type_abi.semantic_type.llvm.memory.?, - .abi_type = options.abi_argument_types[abi_index].llvm.abi.?, - .dereferenceable_bytes = 0, - .alignment = options.return_type_abi.semantic_type.get_byte_alignment(), - .flags = .{ - .no_alias = true, - .non_null = false, - .no_undef = false, - .sign_extend = false, - .zero_extend = false, - .in_reg = options.return_type_abi.flags.in_reg, - .no_fp_class = .{}, - .struct_return = true, - .writable = true, - .dead_on_unwind = true, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = false, - .by_reference = false, - .no_capture = false, - }, - }; - } - - for (options.argument_type_abis) |argument_type_abi| { - for (argument_type_abi.abi_start..argument_type_abi.abi_start + argument_type_abi.abi_count) |abi_index| { - const argument_attribute = &argument_attributes[abi_index]; - argument_type_abi.semantic_type.resolve(module); - const abi_type = options.abi_argument_types[abi_index]; - abi_type.resolve(module); - - argument_attribute.* = .{ - .semantic_type = argument_type_abi.semantic_type.llvm.memory.?, - .abi_type = abi_type.llvm.abi.?, - .dereferenceable_bytes = 0, - .alignment = if (argument_type_abi.flags.kind == .indirect) 8 else 0, - .flags = .{ - .no_alias = false, - .non_null = false, - .no_undef = false, - .sign_extend = argument_type_abi.flags.kind == .extend and argument_type_abi.flags.sign_extension, - .zero_extend = argument_type_abi.flags.kind == .extend and !argument_type_abi.flags.sign_extension, - .in_reg = argument_type_abi.flags.in_reg, - .no_fp_class = .{}, - .struct_return = false, - .writable = false, - .dead_on_unwind = false, - .in_alloca = false, - .dereferenceable = false, - .dereferenceable_or_null = false, - .nest = false, - .by_value = argument_type_abi.flags.indirect_by_value, - .by_reference = false, - .no_capture = false, - }, - }; - } - } - - return llvm.Attribute.List.build(module.llvm.context, llvm.Attribute.Function{ - .prefer_vector_width = llvm.String{}, - .stack_protector_buffer_size = llvm.String{}, - .definition_probe_stack = llvm.String{}, - .definition_stack_probe_size = llvm.String{}, - .flags0 = .{ - .noreturn = options.return_type_abi.semantic_type == module.noreturn_type, - .cmse_ns_call = false, - .returns_twice = false, - .cold = false, - .hot = false, - .no_duplicate = false, - .convergent = false, - .no_merge = false, - .will_return = false, - .no_caller_saved_registers = false, - .no_cf_check = false, - .no_callback = false, - .alloc_size = false, // TODO - .uniform_work_group_size = false, - .nounwind = true, - .aarch64_pstate_sm_body = false, - .aarch64_pstate_sm_enabled = false, - .aarch64_pstate_sm_compatible = false, - .aarch64_preserves_za = false, - .aarch64_in_za = false, - .aarch64_out_za = false, - .aarch64_inout_za = false, - .aarch64_preserves_zt0 = false, - .aarch64_in_zt0 = false, - .aarch64_out_zt0 = false, - .aarch64_inout_zt0 = false, - .optimize_for_size = false, - .min_size = false, - .no_red_zone = false, - .indirect_tls_seg_refs = false, - .no_implicit_floats = false, - .sample_profile_suffix_elision_policy = false, - .memory_none = false, - .memory_readonly = false, - .memory_inaccessible_or_arg_memory_only = false, - .memory_arg_memory_only = false, - .strict_fp = false, - .no_inline = options.attributes.inline_behavior == .no_inline, - .always_inline = options.attributes.inline_behavior == .always_inline, - .guard_no_cf = false, - // TODO: branch protection function attributes - // TODO: cpu features - - // CALL-SITE ATTRIBUTES - .call_no_builtins = false, - - // DEFINITION-SITE ATTRIBUTES - .definition_frame_pointer_kind = switch (module.has_debug_info) { - true => .all, - false => .none, - }, - .definition_less_precise_fpmad = false, - .definition_null_pointer_is_valid = false, - .definition_no_trapping_fp_math = false, - .definition_no_infs_fp_math = false, - .definition_no_nans_fp_math = false, - .definition_approx_func_fp_math = false, - .definition_unsafe_fp_math = false, - .definition_use_soft_float = false, - .definition_no_signed_zeroes_fp_math = false, - .definition_stack_realignment = false, - .definition_backchain = false, - .definition_split_stack = false, - .definition_speculative_load_hardening = false, - .definition_zero_call_used_registers = .all, - // TODO: denormal builtins - .definition_non_lazy_bind = false, - .definition_cmse_nonsecure_entry = false, - .definition_unwind_table_kind = .none, - }, - .flags1 = .{ - .definition_disable_tail_calls = false, - .definition_stack_protect_strong = false, - .definition_stack_protect = false, - .definition_stack_protect_req = false, - .definition_aarch64_new_za = false, - .definition_aarch64_new_zt0 = false, - .definition_optimize_none = false, - .definition_naked = !options.call_site and options.attributes.naked, - .definition_inline_hint = !options.call_site and options.attributes.inline_behavior == .inline_hint, - }, - }, return_attributes, argument_attributes, options.call_site); - } - - const Pointer = struct { - type: *Type, - alignment: ?u32 = null, - }; - - pub fn get_pointer_type(module: *Module, pointer: Pointer) *Type { - const p = Type.Pointer{ - .type = pointer.type, - .alignment = if (pointer.alignment) |a| a else if (pointer.type.bb != .unresolved) pointer.type.get_byte_alignment() else null, - }; - const all_types = module.types.get_slice(); - const pointer_type = for (module.pointer_types.get_slice()) |pointer_type_index| { - const ty = &all_types[pointer_type_index]; - const pointer_type = &all_types[pointer_type_index].bb.pointer; - if (pointer_type.type == p.type and pointer_type.alignment == p.alignment) { - break ty; - } - } else blk: { - const pointer_name = module.arena.join_string(&.{ "&", p.type.name }); - const pointer_type = module.types.append(.{ - .name = pointer_name, - .bb = .{ - .pointer = p, - }, - }); - - const index: u32 = @intCast(pointer_type - module.types.get_slice().ptr); - _ = module.pointer_types.append(index); - break :blk pointer_type; - }; - - return pointer_type; - } - - fn parse_type(module: *Module) *Type { - const start_character = module.content[module.offset]; - switch (start_character) { - 'a'...'z', 'A'...'Z', '_' => { - const identifier = module.parse_identifier(); - var int_type = identifier.len > 1 and identifier[0] == 's' or identifier[0] == 'u'; - if (int_type) { - for (identifier[1..]) |ch| { - int_type = int_type and is_decimal_ch(ch); - } - } - - if (int_type) { - const signedness = switch (identifier[0]) { - 's' => true, - 'u' => false, - else => unreachable, - }; - const bit_count: u32 = @intCast(lib.parse.integer_decimal(identifier[1..])); - const ty = module.integer_type(bit_count, signedness); - return ty; - } else if (lib.string.equal(identifier, "noreturn")) { - return module.noreturn_type; - } else { - const ty = module.types.find_by_name(identifier) orelse @trap(); - return ty; - } - }, - '&' => { - module.offset += 1; - module.skip_space(); - const element_type = module.parse_type(); - const pointer_type = module.get_pointer_type(.{ - .type = element_type, - }); - return pointer_type; - }, - left_bracket => { - module.offset += 1; - module.skip_space(); - - const is_slice = module.consume_character_if_match(right_bracket); - switch (is_slice) { - true => { - module.skip_space(); - const element_type = module.parse_type(); - const slice_type = module.get_slice_type(.{ .type = element_type }); - return slice_type; - }, - false => { - var length_inferred = false; - const offset = module.offset; - if (module.consume_character_if_match('_')) { - module.skip_space(); - if (module.consume_character_if_match(']')) { - length_inferred = true; - } else { - module.offset = offset; - } - } - - const element_count: u64 = switch (length_inferred) { - true => 0, - false => b: { - const v = module.parse_integer_value(false); - if (v == 0) { - module.report_error(); - } - break :b v; - }, - }; - - if (!length_inferred) { - module.skip_space(); - module.expect_character(right_bracket); - } - - module.skip_space(); - - const element_type = module.parse_type(); - - const array_type = switch (element_count) { - 0 => blk: { - const array_type = module.types.append(.{ - .name = "", - .bb = .{ - .array = .{ - .element_type = element_type, - .element_count = element_count, - }, - }, - }); - break :blk array_type; - }, - else => module.get_array_type(element_type, element_count), - }; - - return array_type; - }, - } - }, - '#' => { - module.offset += 1; - - const identifier = module.parse_identifier(); - if (lib.string.to_enum(Type.Intrinsic.Id, identifier)) |intrinsic| switch (intrinsic) { - .ReturnType => { - const return_type = module.current_function.?.variable.type.?.bb.function.semantic_return_type; - return return_type; - }, - else => @trap(), - } else module.report_error(); - @trap(); - }, - else => @trap(), - } - } - - pub fn get_array_type(module: *Module, element_type: *Type, element_count: u64) *Type { - const all_types = module.types.get_slice(); - const array_type = for (module.array_types.get_slice()) |array_type_index| { - const array_type = &all_types[array_type_index]; - assert(array_type.bb == .array); - if (array_type.bb.array.element_count == element_count and array_type.bb.array.element_type == element_type) { - break array_type; - } - } else module.types.append(.{ - .name = array_type_name(module.arena, element_type, element_count), - .bb = .{ - .array = .{ - .element_type = element_type, - .element_count = element_count, - }, - }, - }); - - return array_type; - } - - const Slice = struct { - type: *Type, - alignment: ?u32 = null, - }; - - pub fn get_slice_type(module: *Module, slice: Slice) *Type { - const alignment = if (slice.alignment) |a| a else slice.type.get_byte_alignment(); - const all_types = module.types.get_slice(); - - for (module.slice_types.get_slice()) |slice_type_index| { - const ty = &all_types[slice_type_index]; - const struct_type = &all_types[slice_type_index].bb.structure; - assert(struct_type.is_slice); - assert(struct_type.fields.len == 2); - const pointer_type = struct_type.fields[0].type; - if (pointer_type.bb.pointer.type == slice.type and pointer_type.bb.pointer.alignment == alignment) { - return ty; - } - } else { - const pointer_type = module.get_pointer_type(.{ - .type = slice.type, - .alignment = slice.alignment, - }); - const length_type = module.integer_type(64, false); - - const name = module.arena.join_string(&.{ "[]", slice.type.name }); - - const fields = module.arena.allocate(Type.Struct.Field, 2); - fields[0] = .{ - .bit_offset = 0, - .byte_offset = 0, - .type = pointer_type, - .name = "pointer", - .line = 0, - }; - fields[1] = .{ - .bit_offset = 64, - .byte_offset = 8, - .type = length_type, - .name = "length", - .line = 0, - }; - - const slice_type = module.types.append(.{ - .bb = .{ - .structure = .{ - .fields = fields, - .byte_size = 16, - .bit_size = 128, - .byte_alignment = 8, - .bit_alignment = 64, - .line = 0, - .is_slice = true, - }, - }, - .name = name, - }); - const index = slice_type - module.types.get_slice().ptr; - _ = module.slice_types.append(@intCast(index)); - return slice_type; - } - } - - fn consume_character_if_match(noalias module: *Module, expected_ch: u8) bool { - var is_ch = false; - if (module.offset < module.content.len) { - const ch = module.content[module.offset]; - is_ch = expected_ch == ch; - module.offset += @intFromBool(is_ch); - } - - return is_ch; - } - - fn expect_character(noalias module: *Module, expected_ch: u8) void { - if (!module.consume_character_if_match(expected_ch)) { - module.report_error(); - } - } - - fn report_error(noalias module: *Module) noreturn { - @branchHint(.cold); - _ = module; - lib.os.abort(); - } - - fn get_line(module: *const Module) u32 { - return @intCast(module.line_offset + 1); - } - - fn get_column(module: *const Module) u32 { - return @intCast(module.offset - module.line_character_offset + 1); - } - - pub fn parse_identifier(noalias module: *Module) []const u8 { - const start = module.offset; - - if (is_identifier_start_ch(module.content[start])) { - module.offset += 1; - - while (module.offset < module.content.len) { - if (is_identifier_ch(module.content[module.offset])) { - module.offset += 1; - } else { - break; - } - } - } - - if (module.offset - start == 0) { - module.report_error(); - } - - return module.content[start..module.offset]; - } - - fn skip_space(noalias module: *Module) void { - while (true) { - const offset = module.offset; - while (module.offset < module.content.len and is_space(module.content[module.offset])) { - module.line_offset += @intFromBool(module.content[module.offset] == '\n'); - module.line_character_offset = if (module.content[module.offset] == '\n') module.offset else module.line_character_offset; - module.offset += 1; - } - - if (module.offset + 1 < module.content.len) { - const i = module.offset; - const is_comment = module.content[i] == '/' and module.content[i + 1] == '/'; - if (is_comment) { - while (module.offset < module.content.len and module.content[module.offset] != '\n') { - module.offset += 1; - } - - if (module.offset < module.content.len) { - module.line_offset += 1; - module.line_character_offset = module.offset; - module.offset += 1; - } - } - } - - if (module.offset - offset == 0) { - break; - } - } - } - - const StatementStartKeyword = enum { - @"_", - @"return", - @"if", - // TODO: make `unreachable` a statement start keyword? - @"for", - @"while", - @"switch", - @"break", - @"continue", - }; - - const rules = blk: { - var r: [@typeInfo(Token.Id).@"enum".fields.len]Rule = undefined; - var count: u32 = 0; - r[@intFromEnum(Token.Id.none)] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.end_of_statement)] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.identifier)] = .{ - .before = &rule_before_identifier, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.string_literal)] = .{ - .before = &rule_before_string_literal, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.value_keyword)] = .{ - .before = &rule_before_value_keyword, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.value_intrinsic)] = .{ - .before = &rule_before_value_intrinsic, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.integer)] = .{ - .before = &rule_before_integer, - .after = null, - .precedence = .none, - }; - count += 1; - - const assignment_operators = [_]Token.Id{ - .@"=", - .@"+=", - .@"-=", - .@"*=", - .@"/=", - .@"%=", - .@"&=", - .@"|=", - .@"^=", - .@"<<=", - .@">>=", - }; - - for (assignment_operators) |assignment_operator| { - r[@intFromEnum(assignment_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .assignment, - }; - count += 1; - } - - const comparison_operators = [_]Token.Id{ - .@"==", - .@"!=", - .@"<", - .@">", - .@"<=", - .@">=", - }; - - for (comparison_operators) |comparison_operator| { - r[@intFromEnum(comparison_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .comparison, - }; - count += 1; - } - - const and_operators = [_]Token.Id{ - .@"and", - .@"and?", - }; - - for (and_operators) |and_operator| { - r[@intFromEnum(and_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .@"or", - }; - count += 1; - } - - const or_operators = [_]Token.Id{ - .@"or", - .@"or?", - }; - - for (or_operators) |or_operator| { - r[@intFromEnum(or_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .@"or", - }; - count += 1; - } - - const add_like_operators = [_]Token.Id{ - .@"+", - .@"-", - }; - - for (add_like_operators) |add_like_operator| { - r[@intFromEnum(add_like_operator)] = .{ - .before = rule_before_unary, - .after = rule_after_binary, - .precedence = .add_like, - }; - count += 1; - } - - const div_like_operators = [_]Token.Id{ - .@"*", - .@"/", - .@"%", - }; - - for (div_like_operators) |div_like_operator| { - r[@intFromEnum(div_like_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .div_like, - }; - count += 1; - } - - r[@intFromEnum(Token.Id.@"&")] = .{ - .before = rule_before_unary, - .after = rule_after_binary, - .precedence = .bitwise, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"!")] = .{ - .before = rule_before_unary, - .after = null, - .precedence = .none, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"~")] = .{ - .before = rule_before_unary, - .after = null, - .precedence = .none, - }; - count += 1; - - const bitwise_operators = [_]Token.Id{ - .@"|", - .@"^", - }; - - for (bitwise_operators) |bitwise_operator| { - r[@intFromEnum(bitwise_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .bitwise, - }; - count += 1; - } - - const shifting_operators = [_]Token.Id{ - .@"<<", - .@">>", - }; - - for (shifting_operators) |shifting_operator| { - r[@intFromEnum(shifting_operator)] = .{ - .before = null, - .after = rule_after_binary, - .precedence = .shifting, - }; - count += 1; - } - - r[@intFromEnum(Token.Id.@".&")] = .{ - .before = null, - .after = rule_after_dereference, - .precedence = .postfix, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"(")] = .{ - .before = rule_before_parenthesis, - .after = rule_after_call, - .precedence = .postfix, - }; - count += 1; - r[@intFromEnum(Token.Id.@")")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"[")] = .{ - .before = rule_before_bracket, - .after = rule_after_bracket, - .precedence = .postfix, - }; - count += 1; - r[@intFromEnum(Token.Id.@"]")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - - r[@intFromEnum(Token.Id.@"{")] = .{ - .before = rule_before_brace, - .after = null, // TODO: is this correct? - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.@"}")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - - r[@intFromEnum(Token.Id.@",")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.@".")] = .{ - .before = rule_before_dot, - .after = rule_after_dot, - .precedence = .postfix, - }; - count += 1; - r[@intFromEnum(Token.Id.@"..")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - r[@intFromEnum(Token.Id.@"...")] = .{ - .before = null, - .after = null, - .precedence = .none, - }; - count += 1; - - assert(count == r.len); - break :blk r; - }; - - const OperatorKeyword = enum { - @"and", - @"or", - }; - - fn tokenize(module: *Module) Token { - module.skip_space(); - - const start_index = module.offset; - if (start_index == module.content.len) { - module.report_error(); - } - - const start_character = module.content[start_index]; - const result: Token = switch (start_character) { - ';' => blk: { - module.offset += 1; - break :blk .end_of_statement; - }, - 'a'...'z', 'A'...'Z', '_' => blk: { - assert(is_identifier_start_ch(start_character)); - const identifier = module.parse_identifier(); - const token: Token = if (lib.string.to_enum(Value.Keyword, identifier)) |value_keyword| - .{ .value_keyword = value_keyword } - else if (lib.string.to_enum(OperatorKeyword, identifier)) |operator_keyword| switch (operator_keyword) { - .@"and" => switch (module.content[module.offset]) { - '?' => b: { - module.offset += 1; - break :b .@"and?"; - }, - else => .@"and", - }, - .@"or" => switch (module.content[module.offset]) { - '?' => b: { - module.offset += 1; - break :b .@"or?"; - }, - else => .@"or", - }, - } else .{ .identifier = identifier }; - break :blk token; - }, - '#' => if (is_identifier_start_ch(module.content[module.offset + 1])) blk: { - module.offset += 1; - const value_intrinsic_identifier = module.parse_identifier(); - const value_intrinsic = lib.string.to_enum(Value.Intrinsic.Id, value_intrinsic_identifier) orelse module.report_error(); - break :blk .{ - .value_intrinsic = value_intrinsic, - }; - } else { - @trap(); - }, - '0' => blk: { - const next_ch = module.content[start_index + 1]; - const token_integer_kind: Token.Integer.Kind = switch (next_ch) { - 'x' => .hexadecimal, - 'o' => .octal, - 'b' => .binary, - 'd' => .decimal, - else => .decimal, - }; - const value: u64 = switch (token_integer_kind) { - .binary => b: { - module.offset += 2; - const v = module.parse_binary(); - break :b v; - }, - .octal => b: { - module.offset += 2; - const v = module.parse_octal(); - break :b v; - }, - .decimal => switch (next_ch) { - 0...9 => module.report_error(), - else => switch (next_ch) { - 'd' => b: { - module.offset += 2; - const v = module.parse_decimal(); - break :b v; - }, - else => b: { - module.offset += 1; - break :b 0; - }, - }, - }, - .hexadecimal => b: { - module.offset += 2; - const v = module.parse_hexadecimal(); - break :b v; - }, - }; - - if (module.content[module.offset] == '.' and module.content[module.offset + 1] != '.') { - @trap(); - } else { - break :blk .{ .integer = .{ .value = value, .kind = token_integer_kind } }; - } - }, - '1'...'9' => blk: { - const decimal = module.parse_decimal(); - if (module.content[module.offset] == '.' and module.content[module.offset + 1] != '.') { - @trap(); - } else { - break :blk .{ .integer = .{ .value = decimal, .kind = .decimal } }; - } - }, - '+', '-', '*', '/', '%', '&', '|', '^', '!' => |c| blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '=' => switch (c) { - '!' => .@"!=", - '+' => .@"+=", - '-' => .@"-=", - '*' => .@"*=", - '/' => .@"/=", - '%' => .@"%=", - '^' => .@"^=", - '|' => .@"|=", - '&' => .@"&=", - else => @trap(), - }, - else => switch (c) { - '+' => .@"+", - '-' => .@"-", - '*' => .@"*", - '/' => .@"/", - '%' => .@"%", - '&' => .@"&", - '|' => .@"|", - '^' => .@"^", - '!' => .@"!", - else => unreachable, - }, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@"+", - .@"-", - .@"*", - .@"/", - .@"%", - .@"&", - .@"|", - .@"^", - .@"!=", - .@"+=", - .@"-=", - .@"*=", - .@"/=", - .@"%=", - .@"&=", - .@"|=", - .@"^=", - .@"!", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - - module.offset += @as(u32, 1) + @intFromBool(next_ch == '='); - - break :blk token; - }, - '<' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '<' => switch (module.content[start_index + 2]) { - '=' => .@"<<=", - else => .@"<<", - }, - '=' => .@"<=", - else => .@"<", - }; - - module.offset += switch (token_id) { - .@"<<=" => 3, - .@"<<", .@"<=" => 2, - .@"<" => 1, - else => unreachable, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@"<<=", - .@"<<", - .@"<=", - .@"<", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '>' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '>' => switch (module.content[start_index + 2]) { - '=' => .@">>=", - else => .@">>", - }, - '=' => .@">=", - else => .@">", - }; - - module.offset += switch (token_id) { - .@">>=" => 3, - .@">>", .@">=" => 2, - .@">" => 1, - else => unreachable, - }; - - const token = switch (token_id) { - else => unreachable, - inline .@">>=", - .@">>", - .@">=", - .@">", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '.' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - else => .@".", - '.' => switch (module.content[start_index + 2]) { - '.' => .@"...", - else => .@"..", - }, - '&' => .@".&", - }; - - module.offset += switch (token_id) { - .@"." => 1, - .@".&" => 2, - .@".." => 2, - .@"..." => 3, - else => @trap(), - }; - const token = switch (token_id) { - else => unreachable, - inline .@".&", - .@".", - .@"..", - .@"...", - => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - '=' => blk: { - const next_ch = module.content[start_index + 1]; - const token_id: Token.Id = switch (next_ch) { - '=' => .@"==", - else => .@"=", - }; - module.offset += switch (token_id) { - .@"==" => 2, - .@"=" => 1, - else => @trap(), - }; - const token = switch (token_id) { - else => unreachable, - inline .@"==", .@"=" => |tid| @unionInit(Token, @tagName(tid), {}), - }; - break :blk token; - }, - left_parenthesis => blk: { - module.offset += 1; - break :blk .@"("; - }, - right_parenthesis => blk: { - module.offset += 1; - break :blk .@")"; - }, - left_bracket => blk: { - module.offset += 1; - break :blk .@"["; - }, - right_bracket => blk: { - module.offset += 1; - break :blk .@"]"; - }, - left_brace => blk: { - module.offset += 1; - break :blk .@"{"; - }, - right_brace => blk: { - module.offset += 1; - break :blk .@"}"; - }, - ',' => blk: { - module.offset += 1; - break :blk .@","; - }, - '"' => blk: { - module.offset += 1; - - const string_literal_start = start_index + 1; - while (module.offset < module.content.len) : (module.offset += 1) { - if (module.consume_character_if_match('"')) { - break; - } - } - - const string_slice = module.content[string_literal_start..][0 .. (module.offset - 1) - string_literal_start]; - - break :blk .{ - .string_literal = string_slice, - }; - }, - '\'' => blk: { - module.offset += 1; - const ch_after_quote = module.content[module.offset]; - const ch: u8 = switch (ch_after_quote) { - '\\' => switch (module.content[module.offset + 1]) { - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - else => @trap(), - }, - else => ch_after_quote, - }; - module.offset += @as(u64, @intFromBool(ch_after_quote == '\\')) + 1; - - module.expect_character('\''); - - break :blk .{ - .integer = .{ - .value = ch, - .kind = .decimal, - }, - }; - }, - '~' => blk: { - module.offset += 1; - - break :blk .@"~"; - }, - else => @trap(), - }; - - assert(start_index != module.offset); - - return result; - } - - const Rule = struct { - before: ?*const Rule.Function, - after: ?*const Rule.Function, - precedence: Precedence, - - const Function = fn (noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value; - }; - - fn parse_value(module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - assert(value_builder.precedence == .none); - assert(value_builder.left == null); - const value = module.parse_precedence(scope, value_builder.with_precedence(.assignment)); - return value; - } - - fn parse_precedence(module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - assert(value_builder.token == .none); - const token = module.tokenize(); - const rule = &rules[@intFromEnum(token)]; - if (rule.before) |before| { - const left = before(module, scope, value_builder.with_precedence(.none).with_token(token)); - const result = module.parse_precedence_left(scope, value_builder.with_left(left)); - return result; - } else { - module.report_error(); - } - } - - fn parse_precedence_left(module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - var result = value_builder.left; - const precedence = value_builder.precedence; - while (true) { - const checkpoint = module.offset; - const token = module.tokenize(); - const token_rule = &rules[@intFromEnum(token)]; - const token_precedence: Precedence = switch (token_rule.precedence) { - .assignment => switch (value_builder.allow_assignment_operators) { - true => .assignment, - false => .none, - }, - else => |p| p, - }; - if (@intFromEnum(precedence) > @intFromEnum(token_precedence)) { - module.offset = checkpoint; - break; - } - - const after_rule = token_rule.after orelse module.report_error(); - const old = result; - const new = after_rule(module, scope, value_builder.with_token(token).with_precedence(.none).with_left(old)); - result = new; - } - - return result.?; - } - - fn parse_statement(module: *Module, scope: *Scope) *Statement { - const statement_line = module.get_line(); - const statement_column = module.get_column(); - - const statement_start_character = module.content[module.offset]; - const statement = module.statements.add(); - var require_semicolon = true; - statement.* = .{ - .bb = switch (statement_start_character) { - '>' => blk: { - module.offset += 1; - module.skip_space(); - const local_name = module.parse_identifier(); - module.skip_space(); - const local_type: ?*Type = if (module.consume_character_if_match(':')) b: { - module.skip_space(); - const t = module.parse_type(); - module.skip_space(); - break :b t; - } else null; - module.expect_character('='); - const local_value = module.parse_value(scope, .{}); - const local = module.locals.add(); - local.* = .{ - .variable = .{ - .initial_value = local_value, - .type = local_type, - .name = local_name, - .line = statement_line, - .column = statement_column, - .scope = scope, - }, - .argument_index = null, - }; - switch (scope.kind) { - .local => { - const block: *LexicalBlock = @fieldParentPtr("scope", scope); - _ = block.locals.append(local); - }, - else => @trap(), - } - break :blk .{ - .local = local, - }; - }, - '#' => .{ - .expression = module.parse_value(scope, .{}), - }, - 'A'...'Z', 'a'...'z' => blk: { - const statement_start_identifier = module.parse_identifier(); - - if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) { - ._ => @trap(), - .@"return" => break :blk .{ - .@"return" = module.parse_value(scope, .{}), - }, - .@"if" => { - module.skip_space(); - - module.expect_character(left_parenthesis); - module.skip_space(); - - const condition = module.parse_value(scope, .{}); - - module.skip_space(); - module.expect_character(right_parenthesis); - - module.skip_space(); - - const if_statement = module.parse_statement(scope); - - module.skip_space(); - - var is_else = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const identifier = module.parse_identifier(); - is_else = lib.string.equal(identifier, "else"); - if (!is_else) { - module.offset -= identifier.len; - } else { - module.skip_space(); - } - } - - const else_block = switch (is_else) { - true => module.parse_statement(scope), - false => null, - }; - - require_semicolon = false; - - break :blk .{ - .@"if" = .{ - .condition = condition, - .if_statement = if_statement, - .else_statement = else_block, - }, - }; - }, - .@"while" => { - module.skip_space(); - - module.expect_character(left_parenthesis); - module.skip_space(); - - const condition = module.parse_value(scope, .{}); - - module.skip_space(); - module.expect_character(right_parenthesis); - - module.skip_space(); - - const while_block = module.parse_block(scope); - - require_semicolon = false; - - break :blk .{ - .@"while" = .{ - .condition = condition, - .block = while_block, - }, - }; - }, - .@"for" => { - module.skip_space(); - - module.expect_character(left_parenthesis); - module.skip_space(); - - statement.* = .{ - .line = statement_line, - .column = statement_column, - .bb = .{ - .for_each = .{ - .locals = &.{}, - .left_values = &.{}, - .right_values = &.{}, - .predicate = undefined, - .scope = .{ - .line = statement_line, - .column = statement_column, - .kind = .for_each, - .parent = scope, - }, - .kind = undefined, - }, - }, - }; - - var local_buffer: [64]*Local = undefined; - var left_value_buffer: [64]Value.Kind = undefined; - var left_value_count: u64 = 0; - - while (true) { - module.skip_space(); - - const is_left = switch (module.content[module.offset]) { - '&' => true, - else => false, - }; - module.offset += @intFromBool(is_left); - - const for_local_line = module.get_line(); - const for_local_column = module.get_column(); - - if (is_identifier_start_ch(module.content[module.offset])) { - const identifier = module.parse_identifier(); - const local = module.locals.add(); - local.* = .{ - .variable = .{ - .type = null, - .initial_value = undefined, - .scope = &statement.bb.for_each.scope, - .name = identifier, - .line = for_local_line, - .column = for_local_column, - }, - .argument_index = null, - }; - local_buffer[left_value_count] = local; - left_value_buffer[left_value_count] = switch (is_left) { - true => .left, - false => .right, - }; - left_value_count += 1; - } else { - @trap(); - } - - module.skip_space(); - - if (!module.consume_character_if_match(',')) { - module.expect_character(':'); - break; - } - } - - module.skip_space(); - - var right_value_buffer: [64]*Value = undefined; - var right_value_count: u64 = 0; - - right_value_buffer[right_value_count] = module.parse_value(scope, .{ - .kind = .left, - }); - right_value_count += 1; - - module.skip_space(); - - const token = module.tokenize(); - const kind: Statement.ForEach.Kind = switch (token) { - .@")" => b: { - module.offset += 1; - break :b .slice; - }, - .@".." => b: { - if (left_value_count != 1) { - module.report_error(); - } - right_value_buffer[0].kind = .right; - - right_value_buffer[right_value_count] = module.parse_value(scope, .{ - .kind = .right, - }); - right_value_count += 1; - - module.expect_character(right_parenthesis); - break :b .range; - }, - else => @trap(), - }; - statement.bb.for_each.kind = kind; - - module.skip_space(); - // if (!module.consume_character_if_match(',')) { - // module.expect_character(right_parenthesis); - // @trap(); - // } - // - // while (true) { - // module.skip_space(); - // - // right_value_buffer[right_value_count] = module.parse_value(scope, .{ - // .kind = .left, - // }); - // right_value_count += 1; - // - // module.skip_space(); - // - // if (!module.consume_character_if_match(',')) { - // module.expect_character(right_parenthesis); - // break; - // } - // } - - if (kind == .slice and left_value_count != right_value_count) { - module.report_error(); - } - - const locals = module.arena.allocate(*Local, left_value_count); - @memcpy(locals, local_buffer[0..left_value_count]); - const left_values = module.arena.allocate(Value.Kind, left_value_count); - @memcpy(left_values, left_value_buffer[0..left_value_count]); - const right_values = module.arena.allocate(*Value, right_value_count); - @memcpy(right_values, right_value_buffer[0..right_value_count]); - - statement.bb.for_each.locals = locals; - statement.bb.for_each.left_values = left_values; - statement.bb.for_each.right_values = right_values; - - const predicate = module.parse_statement(&statement.bb.for_each.scope); - statement.bb.for_each.predicate = predicate; - - module.skip_space(); - - require_semicolon = false; - - break :blk statement.bb; - }, - .@"switch" => { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - - const discriminant = module.parse_value(scope, .{}); - - module.skip_space(); - module.expect_character(right_parenthesis); - - module.skip_space(); - module.expect_character(left_brace); - - var clause_buffer: [64]Statement.Switch.Clause = undefined; - var clause_count: u64 = 0; - - while (true) { - module.skip_space(); - - const is_else = is_else_blk: { - var is_else = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const i = module.parse_identifier(); - is_else = lib.string.equal(i, "else"); - if (!is_else) { - module.offset -= i.len; - } - } - - break :is_else_blk is_else; - }; - - const clause_values: []const *Value = if (is_else) b: { - module.skip_space(); - - module.expect_character('='); - module.expect_character('>'); - break :b &.{}; - } else b: { - var case_buffer: [64]*Value = undefined; - var case_count: u64 = 0; - - while (true) { - const case_value = module.parse_value(scope, .{}); - case_buffer[case_count] = case_value; - case_count += 1; - - _ = module.consume_character_if_match(','); - - module.skip_space(); - - if (module.consume_character_if_match('=')) { - module.expect_character('>'); - break; - } - } - - const clause_values = module.arena.allocate(*Value, case_count); - @memcpy(clause_values, case_buffer[0..case_count]); - break :b clause_values; - }; - - module.skip_space(); - - const clause_block = module.parse_block(scope); - - clause_buffer[clause_count] = .{ - .values = clause_values, - .block = clause_block, - }; - clause_count += 1; - - _ = module.consume_character_if_match(','); - - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - } - - const clauses = module.arena.allocate(Statement.Switch.Clause, clause_count); - @memcpy(clauses, clause_buffer[0..clause_count]); - - require_semicolon = false; - - break :blk .{ - .@"switch" = .{ - .discriminant = discriminant, - .clauses = clauses, - }, - }; - }, - .@"break" => break :blk .break_statement, - .@"continue" => break :blk .continue_statement, - } else { - module.offset -= statement_start_identifier.len; - - const left = module.parse_value(scope, .{ - .kind = .left, - }); - - module.skip_space(); - - if (module.consume_character_if_match(';')) { - require_semicolon = false; - break :blk .{ - .expression = left, - }; - } else { - const operator_start_character = module.content[module.offset]; - const operator_next_character = module.content[module.offset + 1]; - const operator_next_next_character = module.content[module.offset + 2]; - const operator: Statement.Assignment.Operator = switch (operator_start_character) { - '=' => .@"=", - '+' => switch (operator_next_character) { - '=' => .@"+=", - else => @trap(), - }, - '-' => switch (operator_next_character) { - '=' => .@"-=", - else => @trap(), - }, - '*' => switch (operator_next_character) { - '=' => .@"*=", - else => @trap(), - }, - '/' => switch (operator_next_character) { - '=' => .@"/=", - else => @trap(), - }, - '%' => switch (operator_next_character) { - '=' => .@"%=", - else => @trap(), - }, - '>' => switch (operator_next_character) { - '>' => switch (operator_next_next_character) { - '=' => .@">>=", - else => @trap(), - }, - else => @trap(), - }, - '<' => switch (operator_next_character) { - '<' => switch (operator_next_next_character) { - '=' => .@"<<=", - else => @trap(), - }, - else => @trap(), - }, - '&' => switch (operator_next_character) { - '=' => .@"&=", - else => @trap(), - }, - '|' => switch (operator_next_character) { - '=' => .@"|=", - else => @trap(), - }, - '^' => switch (operator_next_character) { - '=' => .@"^=", - else => @trap(), - }, - else => @trap(), - }; - module.offset += switch (operator) { - .@"=" => 1, - .@"+=", - .@"-=", - .@"*=", - .@"/=", - .@"%=", - .@"&=", - .@"|=", - .@"^=", - => 2, - .@">>=", - .@"<<=", - => 3, - }; - - module.skip_space(); - - const right = module.parse_value(scope, .{}); - - break :blk .{ - .assignment = .{ - .left = left, - .right = right, - .kind = operator, - }, - }; - } - } - }, - left_brace => blk: { - require_semicolon = false; - break :blk .{ - .block = module.parse_block(scope), - }; - }, - else => @trap(), - }, - .line = statement_line, - .column = statement_column, - }; - - if (require_semicolon) { - module.expect_character(';'); - } - - return statement; - } - - fn parse_block(module: *Module, parent_scope: *Scope) *LexicalBlock { - const block = module.lexical_blocks.append(.{ - .statements = .initialize(), - .locals = .initialize(), - .scope = .{ - .kind = .local, - .parent = parent_scope, - .line = module.get_line(), - .column = module.get_column(), - }, - }); - const scope = &block.scope; - - module.expect_character(left_brace); - - while (true) { - module.skip_space(); - - if (module.offset == module.content.len) { - break; - } - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const statement = module.parse_statement(scope); - _ = block.statements.append(statement); - } - - return block; - } - - fn rule_before_dot(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = scope; - _ = value_builder; - module.skip_space(); - const identifier = module.parse_identifier(); - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .enum_literal = identifier, - }, - }; - return value; - } - - fn rule_after_dot(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = scope; - module.skip_space(); - const left = value_builder.left orelse module.report_error(); - left.kind = .left; - const identifier = module.parse_identifier(); - const value = module.values.add(); - value.* = .{ - .bb = .{ - .field_access = .{ - .aggregate = left, - .field = identifier, - }, - }, - .kind = value_builder.kind, - }; - return value; - } - - fn rule_before_string_literal(noalias module: *Module, parent_scope: *Scope, value_builder: Value.Builder) *Value { - _ = parent_scope; - const value = module.values.add(); - value.* = .{ - .bb = .{ - .string_literal = value_builder.token.string_literal, - }, - }; - return value; - } - - fn reference_identifier(noalias module: *Module, current_scope: *Scope, identifier: []const u8, kind: Value.Kind) *Value { - assert(!lib.string.equal(identifier, "")); - assert(!lib.string.equal(identifier, "_")); - - var scope_it: ?*Scope = current_scope; - const variable = blk: while (scope_it) |scope| : (scope_it = scope.parent) { - switch (scope.kind) { - .global => { - assert(scope.parent == null); - const m: *Module = @fieldParentPtr("scope", scope); - assert(m == module); - for (module.globals.get_slice()) |*global| { - if (lib.string.equal(global.variable.name, identifier)) { - break :blk &global.variable; - } - } - - for (module.macros.get_slice()) |*macro| { - if (lib.string.equal(macro.name, identifier)) { - const value = module.values.add(); - value.* = .{ - .bb = .{ - .macro_reference = macro, - }, - }; - return value; - } - } - }, - .function => { - assert(scope.parent != null); - const f: *Function = @fieldParentPtr("scope", scope); - for (f.arguments) |argument| { - if (lib.string.equal(argument.variable.name, identifier)) { - break :blk &argument.variable; - } - } - }, - .local => { - assert(scope.parent != null); - assert(scope.parent.?.kind != .global); - const block: *LexicalBlock = @fieldParentPtr("scope", scope); - for (block.locals.get_slice()) |local| { - if (lib.string.equal(local.variable.name, identifier)) { - break :blk &local.variable; - } - } - }, - .for_each => { - assert(scope.parent != null); - const for_each: *Statement.ForEach = @fieldParentPtr("scope", scope); - for (for_each.locals) |local| { - if (lib.string.equal(local.variable.name, identifier)) { - break :blk &local.variable; - } - } - }, - .macro_declaration => { - assert(scope.parent != null); - const macro: *Macro = @fieldParentPtr("scope", scope); - for (macro.arguments) |argument| { - if (lib.string.equal(argument.variable.name, identifier)) { - break :blk &argument.variable; - } - } - }, - .macro_instantiation_function => { - assert(scope.parent != null); - const macro: *Macro.Instantiation = @fieldParentPtr("function_scope", scope); - for (macro.declaration_arguments) |argument| { - if (lib.string.equal(argument.variable.name, identifier)) { - break :blk &argument.variable; - } - } - }, - .macro_instantiation_block => unreachable, - } - } else { - module.report_error(); - }; - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .variable_reference = variable, - }, - .kind = kind, - }; - - return value; - } - - fn rule_before_identifier(noalias module: *Module, current_scope: *Scope, value_builder: Value.Builder) *Value { - const identifier = value_builder.token.identifier; - const result = module.reference_identifier(current_scope, identifier, value_builder.kind); - return result; - } - - fn rule_before_value_keyword(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = scope; - const value = module.values.add(); - const new_value: Value = switch (value_builder.token.value_keyword) { - .zero => .{ - .bb = .zero, - }, - .@"unreachable" => .{ - .bb = .@"unreachable", - }, - .undefined => .{ - .bb = .undefined, - }, - }; - value.* = new_value; - return value; - } - - fn rule_before_value_intrinsic(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - const intrinsic = value_builder.token.value_intrinsic; - const value = module.values.add(); - - value.* = switch (intrinsic) { - .alignof => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const ty = module.parse_type(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .alignof = ty, - }, - }, - }; - }, - .byte_size => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const ty = module.parse_type(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .byte_size = ty, - }, - }, - }; - }, - .enum_name => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const arg_value = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .enum_name = arg_value, - }, - }, - }; - }, - .extend => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const arg_value = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .extend = arg_value, - }, - }, - }; - }, - .integer_max => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const ty = module.parse_type(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .integer_max = ty, - }, - }, - }; - }, - .int_from_enum => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const arg_value = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .int_from_enum = arg_value, - }, - }, - }; - }, - .int_from_pointer => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const arg_value = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .int_from_pointer = arg_value, - }, - }, - }; - }, - .pointer_cast => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const v = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .pointer_cast = v, - }, - }, - }; - }, - .select => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const condition = module.parse_value(scope, .{}); - module.expect_character(','); - module.skip_space(); - const true_value = module.parse_value(scope, .{}); - module.expect_character(','); - module.skip_space(); - const false_value = module.parse_value(scope, .{}); - module.skip_space(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .select = .{ - .condition = condition, - .true_value = true_value, - .false_value = false_value, - }, - }, - }, - }; - }, - .string_to_enum => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const enum_type = module.parse_type(); - module.expect_character(','); - module.skip_space(); - const string_value = module.parse_value(scope, .{}); - module.skip_space(); - module.expect_character(right_parenthesis); - - break :blk .{ - .bb = .{ - .intrinsic = .{ - .string_to_enum = .{ - .enum_type = enum_type, - .string_value = string_value, - }, - }, - }, - }; - }, - .trap => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .trap, - }, - }; - }, - .truncate => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const v = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .truncate = v, - }, - }, - }; - }, - .va_start => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .va_start, - }, - }; - }, - .va_end => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const va_list = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .va_end = va_list, - }, - }, - }; - }, - .va_arg => blk: { - module.skip_space(); - module.expect_character(left_parenthesis); - module.skip_space(); - const va_list = module.parse_value(scope, .{}); - module.skip_space(); - module.expect_character(','); - module.skip_space(); - const ty = module.parse_type(); - module.expect_character(right_parenthesis); - break :blk .{ - .bb = .{ - .intrinsic = .{ - .va_arg = .{ - .list = va_list, - .type = ty, - }, - }, - }, - }; - }, - else => @trap(), - }; - - return value; - } - - fn rule_before_integer(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = scope; - const v = value_builder.token.integer.value; - const value = module.values.add(); - value.* = .{ - .bb = .{ - .constant_integer = .{ - .value = v, - .signed = false, - }, - }, - }; - return value; - } - - fn rule_after_binary(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - const binary_operator_token = value_builder.token; - const binary_operator_token_precedence = rules[@intFromEnum(binary_operator_token)].precedence; - const left = value_builder.left orelse module.report_error(); - assert(binary_operator_token_precedence != .assignment); // TODO: this may be wrong. Assignment operator is not allowed in expressions - const right_precedence = if (binary_operator_token_precedence == .assignment) .assignment else binary_operator_token_precedence.increment(); - const right = module.parse_precedence(scope, value_builder.with_precedence(right_precedence).with_token(.none).with_left(null)); - - const binary_operation_kind: Binary.Id = switch (binary_operator_token) { - .none => unreachable, - .@"+" => .@"+", - .@"-" => .@"-", - .@"*" => .@"*", - .@"/" => .@"/", - .@"%" => .@"%", - .@"&" => .@"&", - .@"|" => .@"|", - .@"^" => .@"^", - .@"<<" => .@"<<", - .@">>" => .@">>", - .@"==" => .@"==", - .@"!=" => .@"!=", - .@">=" => .@">=", - .@"<=" => .@"<=", - .@">" => .@">", - .@"<" => .@"<", - .@"and" => .@"and", - .@"and?" => .@"and?", - .@"or" => .@"or", - .@"or?" => .@"or?", - else => @trap(), - }; - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .binary = .{ - .left = left, - .right = right, - .id = binary_operation_kind, - }, - }, - }; - - return value; - } - - fn rule_before_unary(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - assert(value_builder.left == null); - const unary_token = value_builder.token; - const unary_id: Unary.Id = switch (unary_token) { - .none => unreachable, - .@"-" => .@"-", - .@"+" => .@"+", - .@"&" => .@"&", - .@"!" => .@"!", - .@"~" => .@"~", - else => @trap(), - }; - - const right = module.parse_precedence(scope, value_builder.with_precedence(.prefix).with_token(.none).with_kind(if (unary_id == .@"&") .left else value_builder.kind)); - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .unary = .{ - .id = unary_id, - .value = right, - }, - }, - }; - return value; - } - - fn rule_after_dereference(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = scope; - const value = module.values.add(); - value.* = .{ - .bb = .{ - .dereference = value_builder.left orelse unreachable, - }, - }; - return value; - } - - fn rule_before_brace(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - assert(value_builder.left == null); - - var name_buffer: [64][]const u8 = undefined; - var value_buffer: [64]*Value = undefined; - var field_count: u32 = 0; - var zero = false; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - if (module.consume_character_if_match('.')) { - const name = module.parse_identifier(); - name_buffer[field_count] = name; - - module.skip_space(); - - module.expect_character('='); - - module.skip_space(); - - const value = module.parse_value(scope, .{}); - value_buffer[field_count] = value; - module.skip_space(); - - _ = module.consume_character_if_match(','); - } else { - const token = module.tokenize(); - switch (token) { - .value_keyword => |vkw| switch (vkw) { - .zero => { - zero = true; - module.skip_space(); - - if (module.consume_character_if_match(',')) { - module.skip_space(); - } - - module.expect_character(right_brace); - break; - }, - else => module.report_error(), - }, - else => module.report_error(), - } - } - } - - const blob = module.arena.allocate_bytes(field_count * @sizeOf([]const u8) + field_count * @sizeOf(*Value), @max(@alignOf([]const u8), @alignOf(*Value))); - const names = @as([*][]const u8, @alignCast(@ptrCast(blob)))[0..field_count]; - @memcpy(names, name_buffer[0..field_count]); - const values = @as([*]*Value, @alignCast(@ptrCast(blob + (@sizeOf([]const u8) * field_count))))[0..field_count]; - @memcpy(values, value_buffer[0..field_count]); - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .aggregate_initialization = .{ - .names = names, - .values = values, - .is_constant = false, - .zero = zero, - }, - }, - }; - return value; - } - - fn rule_after_brace(noalias module: *Module, value_builder: Value.Builder) *Value { - _ = module; - _ = value_builder; - @trap(); - } - - // Array initialization - fn rule_before_bracket(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - assert(value_builder.left == null); - - var value_buffer: [64]*Value = undefined; - var element_count: u64 = 0; - - while (true) : (element_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_bracket)) { - break; - } - const v = module.parse_value(scope, .{}); - value_buffer[element_count] = v; - - _ = module.consume_character_if_match(','); - } - - const values = module.arena.allocate(*Value, element_count); - @memcpy(values, value_buffer[0..element_count]); - - const value = module.values.add(); - value.* = .{ - .bb = .{ - .array_initialization = .{ - .values = values, - .is_constant = false, - }, - }, - }; - - return value; - } - - // Array-like subscript - fn rule_after_bracket(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - const left = value_builder.left orelse module.report_error(); - module.skip_space(); - - const value = module.values.add(); - - switch (left.bb) { - .macro_reference => |macro| { - if (!macro.is_generic) { - module.report_error(); - } - - const type_arguments = module.arena.allocate(*Type, macro.type_arguments.len); - var type_argument_count: u64 = 0; - const constant_values = module.arena.allocate(*Value, macro.constant_argument_values.len); - var constant_value_count: u64 = 0; - var constant_argument_count: u64 = 0; - - const instantiation_line = module.get_line(); - const instantiation_column = module.get_column(); - - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_bracket)) { - break; - } - - const constant_argument = macro.constant_arguments[constant_argument_count]; - constant_argument_count += 1; - - switch (constant_argument.kind) { - .value => { - _ = &constant_value_count; - @trap(); - }, - .type => { - if (type_argument_count >= macro.type_arguments.len) { - module.report_error(); - } - - if (constant_argument.index != type_argument_count) { - module.report_error(); - } - - const argument_type = module.parse_type(); - type_arguments[constant_argument.index] = argument_type; - - type_argument_count += 1; - }, - } - - module.skip_space(); - - _ = module.consume_character_if_match(','); - } - - module.skip_space(); - - module.expect_character(left_parenthesis); - - const instantiation_arguments = module.parse_call_arguments(scope); - - value.* = .{ - .bb = .{ - .macro_instantiation = .{ - .declaration = macro, - .function = module.current_function.?, - .declaration_arguments = &.{}, - .instantiation_arguments = instantiation_arguments, - .constant_argument_values = constant_values, - .type_arguments = type_arguments, - .return_type = macro.return_type, - .block = undefined, - .return_alloca = undefined, - .return_block = undefined, - .function_scope = .{ - .line = macro.scope.line, - .column = macro.scope.column, - .kind = .macro_instantiation_function, - .parent = scope, - }, - .instantiation_line = instantiation_line, - .instantiation_column = instantiation_column, - }, - }, - }; - }, - else => { - left.kind = .left; - - const is_start = !(module.content[module.offset] == '.' and module.content[module.offset + 1] == '.'); - const start = if (is_start) module.parse_value(scope, .{}) else null; - value.* = .{ - .bb = if (module.consume_character_if_match(right_bracket)) .{ - .array_expression = .{ - .array_like = left, - .index = start orelse module.report_error(), - }, - } else blk: { - module.expect_character('.'); - module.expect_character('.'); - - const end = switch (module.consume_character_if_match(right_bracket)) { - true => null, - false => b: { - const end = module.parse_value(scope, .{}); - module.expect_character(right_bracket); - break :b end; - }, - }; - break :blk .{ - .slice_expression = .{ - .array_like = left, - .start = start, - .end = end, - }, - }; - }, - .kind = value_builder.kind, - }; - }, - } - - return value; - } - - fn rule_before_parenthesis(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - _ = value_builder; - module.skip_space(); - const v = module.parse_value(scope, .{}); - module.expect_character(right_parenthesis); - return v; - } - - fn parse_call_arguments(noalias module: *Module, scope: *Scope) []const *Value { - var semantic_argument_count: u32 = 0; - var semantic_argument_buffer: [64]*Value = undefined; - - while (true) : (semantic_argument_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_parenthesis)) { - break; - } - - const argument = module.parse_value(scope, .{}); - const argument_index = semantic_argument_count; - semantic_argument_buffer[argument_index] = argument; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - } - - const arguments: []const *Value = if (semantic_argument_count != 0) blk: { - const arguments = module.arena.allocate(*Value, semantic_argument_count); - @memcpy(arguments, semantic_argument_buffer[0..semantic_argument_count]); - break :blk arguments; - } else &.{}; - return arguments; - } - - fn rule_after_call(noalias module: *Module, scope: *Scope, value_builder: Value.Builder) *Value { - const may_be_callable = value_builder.left orelse module.report_error(); - assert(value_builder.token == .@"("); - - switch (may_be_callable.bb) { - .macro_reference => |macro| { - if (macro.is_generic) { - module.report_error(); - } - - const instantiation_line = module.get_line(); - const instantiation_column = module.get_column(); - - const arguments = module.parse_call_arguments(scope); - const value = module.values.add(); - - value.* = .{ - .bb = .{ - .macro_instantiation = .{ - .declaration = macro, - .instantiation_arguments = arguments, - .type_arguments = &.{}, - .function = module.current_function.?, - .return_alloca = undefined, - .return_block = undefined, - .block = undefined, - .return_type = macro.return_type, - .constant_argument_values = &.{}, - .declaration_arguments = &.{}, - .function_scope = .{ - .line = macro.scope.line, - .column = macro.scope.column, - .kind = .macro_instantiation_function, - .parent = scope, - }, - .instantiation_line = instantiation_line, - .instantiation_column = instantiation_column, - }, - }, - }; - - return value; - }, - else => { - const arguments = module.parse_call_arguments(scope); - - const call = module.values.add(); - call.* = .{ - .bb = .{ - .call = .{ - .arguments = arguments, - .callable = may_be_callable, - }, - }, - }; - return call; - }, - } - } - - pub fn get_anonymous_struct_pair(module: *Module, pair: [2]*Type) *Type { - const all_types = module.types.get_slice(); - const struct_type = for (module.pair_struct_types.get_slice()) |struct_type_index| { - const struct_type = &all_types[struct_type_index]; - assert(struct_type.bb.structure.fields.len == 2); - if (struct_type.bb.structure.fields[0].type == pair[0] and struct_type.bb.structure.fields[1].type == pair[1]) { - break struct_type; - } - } else blk: { - const byte_alignment = @max(pair[0].get_byte_alignment(), pair[1].get_byte_alignment()); - const byte_size = lib.align_forward_u64(pair[0].get_byte_size() + pair[1].get_byte_size(), byte_alignment); - - const fields = module.arena.allocate(Type.Struct.Field, 2); - fields[0] = .{ - .bit_offset = 0, - .byte_offset = 0, - .type = pair[0], - .name = "", - .line = 0, - }; - fields[1] = .{ - .bit_offset = byte_alignment * 8, - .byte_offset = byte_alignment, - .type = pair[1], - .name = "", - .line = 0, - }; - const pair_type = module.types.append(.{ - .name = "", - .bb = .{ - .structure = .{ - .bit_alignment = byte_alignment * 8, - .byte_alignment = byte_alignment, - .byte_size = byte_size, - .bit_size = byte_size * 8, - .fields = fields, - .line = 0, - .is_slice = false, - }, - }, - }); - const struct_type_index = pair_type - all_types.ptr; - _ = module.pair_struct_types.append(@intCast(struct_type_index)); - - break :blk pair_type; - }; - return struct_type; - } - - pub fn parse(module: *Module) void { - while (true) { - module.skip_space(); - - if (module.offset == module.content.len) { - break; - } - - var is_export = false; - var is_extern = false; - - const global_line = module.get_line(); - const global_column = module.get_column(); - - if (module.consume_character_if_match(left_bracket)) { - while (module.offset < module.content.len) { - const global_keyword_string = module.parse_identifier(); - - const global_keyword = lib.string.to_enum(GlobalKeyword, global_keyword_string) orelse module.report_error(); - switch (global_keyword) { - .@"export" => is_export = true, - .@"extern" => is_extern = true, - } - - switch (module.content[module.offset]) { - right_bracket => break, - else => module.report_error(), - } - } - - module.expect_character(right_bracket); - - module.skip_space(); - } - - const global_name = module.parse_identifier(); - - if (module.types.find_by_name(global_name) != null) { - module.report_error(); - } - - if (module.globals.find_by_name(global_name) != null) { - module.report_error(); - } - - module.skip_space(); - - var global_type: ?*Type = null; - if (module.consume_character_if_match(':')) { - module.skip_space(); - - global_type = module.parse_type(); - - module.skip_space(); - } - - module.expect_character('='); - - module.skip_space(); - - var global_keyword = false; - if (is_identifier_start_ch(module.content[module.offset])) { - const identifier_offset = module.offset; - const global_string = module.parse_identifier(); - module.skip_space(); - - if (lib.string.to_enum(GlobalKind, global_string)) |global_kind| { - global_keyword = true; - switch (global_kind) { - .@"fn" => { - var calling_convention = CallingConvention.c; - const function_attributes = Function.Attributes{}; - _ = function_attributes; - var is_var_args = false; - - if (module.consume_character_if_match(left_bracket)) { - while (module.offset < module.content.len) { - const function_identifier = module.parse_identifier(); - - const function_keyword = lib.string.to_enum(Function.Keyword, function_identifier) orelse module.report_error(); - - module.skip_space(); - - switch (function_keyword) { - .cc => { - module.expect_character(left_parenthesis); - - module.skip_space(); - - const calling_convention_string = module.parse_identifier(); - - calling_convention = lib.string.to_enum(CallingConvention, calling_convention_string) orelse module.report_error(); - - module.skip_space(); - - module.expect_character(right_parenthesis); - }, - } - - module.skip_space(); - - switch (module.content[module.offset]) { - right_bracket => break, - else => module.report_error(), - } - } - - module.expect_character(right_bracket); - } - - module.skip_space(); - - module.expect_character(left_parenthesis); - - var semantic_argument_count: u32 = 0; - var semantic_argument_type_buffer: [64]*Type = undefined; - var semantic_argument_name_buffer: [64][]const u8 = undefined; - - while (module.offset < module.content.len) : (semantic_argument_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match('.')) { - module.expect_character('.'); - module.expect_character('.'); - module.skip_space(); - module.expect_character(right_parenthesis); - is_var_args = true; - break; - } - - if (module.consume_character_if_match(right_parenthesis)) { - break; - } - - const argument_name = module.parse_identifier(); - semantic_argument_name_buffer[semantic_argument_count] = argument_name; - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const argument_type = module.parse_type(); - semantic_argument_type_buffer[semantic_argument_count] = argument_type; - - module.skip_space(); - - if (module.consume_character_if_match(',')) { - module.skip_space(); - } - } - - module.skip_space(); - - const return_type = module.parse_type(); - const argument_types: []const *Type = if (semantic_argument_count == 0) &.{} else blk: { - const argument_types = module.arena.allocate(*Type, semantic_argument_count); - @memcpy(argument_types, semantic_argument_type_buffer[0..argument_types.len]); - break :blk argument_types; - }; - - module.skip_space(); - - const is_declaration = module.consume_character_if_match(';'); - - const function_type = module.types.append(.{ - .bb = .{ - .function = .{ - .semantic_return_type = return_type, - .semantic_argument_types = argument_types, - .calling_convention = calling_convention, - .is_var_args = is_var_args, - }, - }, - .name = "", - }); - const storage = module.values.add(); - storage.* = .{ - .bb = undefined, - .type = module.get_pointer_type(.{ - .type = function_type, - }), - }; - - const global = module.globals.add(); - global.* = .{ - .variable = .{ - .storage = storage, - .initial_value = undefined, - .type = function_type, - .name = global_name, - .line = global_line, - .column = global_column, - .scope = &module.scope, - }, - .linkage = if (is_export or is_extern) .external else .internal, - }; - module.current_function = global; - defer module.current_function = null; - - if (!is_declaration) { - storage.bb = .{ - .function = .{ - .main_block = undefined, - .arguments = &.{}, - .attributes = .{}, - .scope = .{ - .kind = .function, - .line = global_line, - .column = global_column, - .parent = &module.scope, - }, - }, - }; - - if (semantic_argument_count != 0) { - const arguments = module.arena.allocate(*Local, semantic_argument_count); - storage.bb.function.arguments = arguments; - for (argument_types, semantic_argument_name_buffer[0..semantic_argument_count], arguments, 0..) |argument_type, argument_name, *argument, argument_index| { - const result = module.locals.add(); - argument.* = result; - result.* = .{ - .variable = .{ - .line = 0, - .column = 0, - .name = argument_name, - .scope = &storage.bb.function.scope, - .type = argument_type, - .initial_value = undefined, - }, - .argument_index = @intCast(argument_index), - }; - } - } - - storage.bb.function.main_block = module.parse_block(&storage.bb.function.scope); - } else { - storage.bb = .external_function; - } - }, - .@"enum" => { - const is_implicit_type = module.content[module.offset] == left_brace; - const maybe_backing_type: ?*Type = switch (is_implicit_type) { - true => null, - false => module.parse_type(), - }; - - module.skip_space(); - - module.expect_character(left_brace); - - var highest_value: u64 = 0; - var lowest_value = ~@as(u64, 0); - - var field_buffer: [64]Enumerator.Field = undefined; - var field_count: u64 = 0; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_index = field_count; - const field_name = module.parse_identifier(); - module.skip_space(); - const has_explicit_value = module.consume_character_if_match('='); - const field_value = if (has_explicit_value) blk: { - module.skip_space(); - const field_value = module.parse_integer_value(false); - break :blk field_value; - } else field_index; - - field_buffer[field_index] = .{ - .name = field_name, - .value = field_value, - }; - - highest_value = @max(highest_value, field_value); - lowest_value = @min(lowest_value, field_value); - - module.skip_space(); - module.expect_character(','); - } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const backing_type = maybe_backing_type orelse blk: { - const bits_needed = 64 - @clz(highest_value); - const int_type = module.integer_type(if (bits_needed == 0) 1 else bits_needed, false); - break :blk int_type; - }; - - if (maybe_backing_type) |bt| { - const bits_needed = 64 - @clz(highest_value); - if (bits_needed > bt.get_bit_size()) { - module.report_error(); - } - } - - const fields = module.arena.allocate(Enumerator.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - _ = module.types.append(.{ - .bb = .{ - .enumerator = .{ - .backing_type = backing_type, - .fields = fields, - .implicit_backing_type = is_implicit_type, - .line = global_line, - }, - }, - .name = global_name, - }); - }, - .bits => { - const is_implicit_type = module.content[module.offset] == left_brace; - const maybe_backing_type: ?*Type = switch (is_implicit_type) { - true => null, - false => module.parse_type(), - }; - - module.skip_space(); - - module.expect_character(left_brace); - - var field_buffer: [128]Type.Struct.Field = undefined; - var field_line_buffer: [128]u32 = undefined; - var field_count: u64 = 0; - - var field_bit_offset: u64 = 0; - - while (true) : (field_count += 1) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_line = module.get_line(); - field_line_buffer[field_count] = field_line; - - const field_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const field_type = module.parse_type(); - - field_buffer[field_count] = .{ - .name = field_name, - .type = field_type, - .bit_offset = field_bit_offset, - .byte_offset = 0, - .line = field_line, - }; - - const field_bit_size = field_type.get_bit_size(); - - field_bit_offset += field_bit_size; - - module.skip_space(); - - _ = module.consume_character_if_match(','); - } - - _ = module.consume_character_if_match(';'); - - const fields = module.arena.allocate(Type.Struct.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - // const field_lines = field_line_buffer[0..field_count]; - - const backing_type = if (maybe_backing_type) |bt| bt else module.integer_type(@intCast(@max(8, lib.next_power_of_two(field_bit_offset))), false); - if (backing_type.bb != .integer) { - module.report_error(); - } - - if (backing_type.get_bit_size() > 64) { - module.report_error(); - } - - _ = module.types.append(.{ - .name = global_name, - .bb = .{ - .bits = .{ - .fields = fields, - .backing_type = backing_type, - .line = global_line, - .implicit_backing_type = is_implicit_type, - }, - }, - }); - }, - .@"struct" => { - module.skip_space(); - - module.expect_character(left_brace); - - if (module.types.find_by_name(global_name) != null) { - @trap(); - } - - const struct_type = module.types.append(.{ - .name = global_name, - .bb = .forward_declaration, - }); - - var field_buffer: [256]Type.Struct.Field = undefined; - var field_count: u64 = 0; - var byte_offset: u64 = 0; - var byte_alignment: u32 = 1; - var bit_alignment: u32 = 1; - - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_line = module.get_line(); - const field_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const field_type = module.parse_type(); - - const field_byte_alignment = field_type.get_byte_alignment(); - const field_bit_alignment = field_byte_alignment * 8; - const field_byte_size = field_type.get_byte_size(); - - const field_byte_offset = lib.align_forward_u64(byte_offset, field_byte_alignment); - const field_bit_offset = field_byte_offset * 8; - - field_buffer[field_count] = .{ - .byte_offset = field_byte_offset, - .bit_offset = field_bit_offset, - .type = field_type, - .name = field_name, - .line = field_line, - }; - - byte_alignment = @max(byte_alignment, field_byte_alignment); - bit_alignment = @max(bit_alignment, field_bit_alignment); - byte_offset = field_byte_offset + field_byte_size; - - field_count += 1; - - module.skip_space(); - - switch (module.content[module.offset]) { - ',' => module.offset += 1, - else => {}, - } - } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const byte_size = byte_offset; - - const fields = module.arena.allocate(Type.Struct.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - struct_type.bb = .{ - .structure = .{ - .bit_size = byte_size * 8, - .byte_size = byte_size, - .bit_alignment = bit_alignment, - .byte_alignment = byte_alignment, - .fields = fields, - .is_slice = false, - .line = global_line, - }, - }; - }, - .typealias => { - const aliased_type = module.parse_type(); - if (!module.consume_character_if_match(';')) { - module.report_error(); - } - const alias_type = module.types.append(.{ - .bb = .{ - .alias = .{ - .type = aliased_type, - .line = global_line, - .scope = &module.scope, - }, - }, - .name = global_name, - }); - _ = alias_type; - }, - .macro => { - var type_argument_buffer: [64]*Type = undefined; - var type_argument_count: u64 = 0; - - var constant_argument_value_buffer: [64]*Value = undefined; - var constant_argument_name_buffer: [64][]const u8 = undefined; - var constant_argument_buffer: [64]ConstantArgument = undefined; - var constant_argument_value_count: u64 = 0; - var constant_argument_count: u64 = 0; - - const is_generic = module.consume_character_if_match(left_bracket); - if (is_generic) { - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_bracket)) { - break; - } - - const argument_name = module.parse_identifier(); - - module.skip_space(); - - const has_value = module.consume_character_if_match(':'); - const index: u64 = if (has_value) { - _ = &constant_argument_value_buffer; - _ = &constant_argument_name_buffer; - _ = &constant_argument_value_count; - @trap(); - } else blk: { - const ty = module.types.append(.{ - .bb = .unresolved, - .name = argument_name, - }); - const index = type_argument_count; - type_argument_buffer[index] = ty; - type_argument_count = index + 1; - break :blk index; - }; - - constant_argument_buffer[constant_argument_count] = .{ - .kind = switch (has_value) { - true => .value, - false => .type, - }, - .index = @intCast(index), - }; - - constant_argument_count += 1; - } - - module.skip_space(); - } - - module.expect_character(left_parenthesis); - - const type_arguments = module.arena.allocate(*Type, type_argument_count); - @memcpy(type_arguments, type_argument_buffer[0..type_argument_count]); - - const constant_argument_names = module.arena.allocate([]const u8, constant_argument_value_count); - @memcpy(constant_argument_names, constant_argument_name_buffer[0..constant_argument_value_count]); - - const constant_argument_values = module.arena.allocate(*Value, constant_argument_value_count); - @memcpy(constant_argument_values, constant_argument_value_buffer[0..constant_argument_value_count]); - - const constant_arguments = module.arena.allocate(ConstantArgument, constant_argument_count); - @memcpy(constant_arguments, constant_argument_buffer[0..constant_argument_count]); - - const macro = module.macros.add(); - macro.* = .{ - .arguments = &.{}, - .argument_types = &.{}, - .constant_argument_names = constant_argument_names, - .constant_argument_values = constant_argument_values, - .constant_arguments = constant_arguments, - .type_arguments = type_arguments, - .return_type = undefined, - .block = undefined, - .name = global_name, - .scope = .{ - .parent = &module.scope, - .kind = .macro_declaration, - .line = global_line, - .column = global_column, - }, - .is_generic = is_generic, - }; - - module.current_macro_declaration = macro; - defer module.current_macro_declaration = null; - - var argument_buffer: [64]*Local = undefined; - var argument_count: u32 = 0; - - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_parenthesis)) { - break; - } - - const argument_line = module.get_line(); - const argument_column = module.get_column(); - - const argument_name = module.parse_identifier(); - - module.skip_space(); - module.expect_character(':'); - module.skip_space(); - - const argument_type = module.parse_type(); - - const argument = module.locals.add(); - argument.* = .{ - .variable = .{ - .initial_value = undefined, - .scope = ¯o.scope, - .type = argument_type, - .name = argument_name, - .line = argument_line, - .column = argument_column, - }, - .argument_index = argument_count, - }; - argument_buffer[argument_count] = argument; - argument_count += 1; - - module.skip_space(); - _ = module.consume_character_if_match(','); - } - - module.skip_space(); - - const arguments = module.arena.allocate(*Local, argument_count); - @memcpy(arguments, argument_buffer[0..argument_count]); - macro.arguments = arguments; - - const argument_types = module.arena.allocate(*Type, argument_count); - for (arguments, argument_types) |argument, *argument_type| { - argument_type.* = argument.variable.type.?; - } - macro.argument_types = argument_types; - - const return_type = module.parse_type(); - macro.return_type = return_type; - - module.skip_space(); - - const block = module.parse_block(¯o.scope); - macro.block = block; - }, - .@"union" => { - module.skip_space(); - - module.expect_character(left_brace); - - if (module.types.find_by_name(global_name) != null) { - @trap(); - } - - const union_type = module.types.append(.{ - .name = global_name, - .bb = .forward_declaration, - }); - - var field_buffer: [256]Type.Union.Field = undefined; - var field_count: u64 = 0; - var byte_size: u64 = 0; - var byte_alignment: u32 = 1; - var bit_alignment: u32 = 1; - var biggest_field: u32 = 0; - - while (true) { - module.skip_space(); - - if (module.consume_character_if_match(right_brace)) { - break; - } - - const field_line = module.get_line(); - const field_name = module.parse_identifier(); - - module.skip_space(); - - module.expect_character(':'); - - module.skip_space(); - - const field_type = module.parse_type(); - - const field_byte_alignment = field_type.get_byte_alignment(); - const field_bit_alignment = field_byte_alignment * 8; - const field_byte_size = field_type.get_byte_size(); - - field_buffer[field_count] = .{ - .type = field_type, - .name = field_name, - .line = field_line, - }; - - biggest_field = if (byte_size > field_byte_size) @intCast(field_count) else biggest_field; - byte_alignment = @max(byte_alignment, field_byte_alignment); - bit_alignment = @max(bit_alignment, field_bit_alignment); - byte_size = @max(field_byte_size, byte_size); - - field_count += 1; - - module.skip_space(); - - switch (module.content[module.offset]) { - ',' => module.offset += 1, - else => {}, - } - } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const fields = module.arena.allocate(Type.Union.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - union_type.bb = .{ - .@"union" = .{ - .byte_size = byte_size, - .byte_alignment = byte_alignment, - .fields = fields, - .line = global_line, - .biggest_field = biggest_field, - }, - }; - }, - } - } else { - module.offset = identifier_offset; - } - } - - if (!global_keyword) { - const v = module.parse_value(&module.scope, .{}); - module.skip_space(); - module.expect_character(';'); - - const global = module.globals.add(); - const global_storage = module.values.add(); - global_storage.* = .{ - .bb = .global, - }; - global.* = .{ - .variable = .{ - .storage = global_storage, - .initial_value = v, - .type = global_type, - .scope = &module.scope, - .name = global_name, - .line = global_line, - .column = global_column, - }, - .linkage = .internal, - }; - } - } - } - - fn parse_integer_value(module: *Module, sign: bool) u64 { - const start = module.offset; - const integer_start_ch = module.content[start]; - assert(!is_space(integer_start_ch)); - assert(is_decimal_ch(integer_start_ch)); - - const absolute_value: u64 = switch (integer_start_ch) { - '0' => blk: { - module.offset += 1; - - const next_ch = module.content[module.offset]; - break :blk switch (sign) { - false => switch (next_ch) { - 'x' => b: { - module.offset += 1; - break :b module.parse_hexadecimal(); - }, - 'd' => b: { - module.offset += 1; - break :b module.parse_decimal(); - }, - 'o' => b: { - module.offset += 1; - break :b module.parse_octal(); - }, - 'b' => b: { - module.offset += 1; - break :b module.parse_binary(); - }, - '0'...'9' => { - module.report_error(); - }, - // Zero literal - else => 0, - }, - true => switch (next_ch) { - 'x', 'o', 'b', '0' => module.report_error(), - '1'...'9' => module.parse_decimal(), - else => unreachable, - }, - }; - }, - '1'...'9' => module.parse_decimal(), - else => unreachable, - }; - - return absolute_value; - } - - fn initialize_llvm(module: *Module) void { - llvm.default_initialize(); - const context = llvm.Context.create(); - const m = context.create_module(module.name); - const di_builder = if (module.has_debug_info) m.create_di_builder() else undefined; - - var compile_unit: *llvm.DI.CompileUnit = undefined; - - const file = if (module.has_debug_info) blk: { - const index = lib.string.last_character(module.path, '/') orelse lib.os.abort(); - const directory = module.path[0..index]; - const file_name = module.path[index + 1 ..]; - const file = di_builder.create_file(file_name, directory); - compile_unit = di_builder.create_compile_unit(file, module.build_mode.is_optimized()); - module.scope.llvm = compile_unit.to_scope(); - break :blk file; - } else undefined; - - module.llvm = LLVM{ - .context = context, - .module = m, - .builder = context.create_builder(), - .di_builder = di_builder, - .file = file, - .compile_unit = compile_unit, - .void_type = context.get_void_type(), - .pointer_type = context.get_pointer_type(0).to_type(), - .intrinsic_table = .{ - .trap = llvm.lookup_intrinsic_id("llvm.trap"), - .va_start = llvm.lookup_intrinsic_id("llvm.va_start"), - .va_end = llvm.lookup_intrinsic_id("llvm.va_end"), - .va_copy = llvm.lookup_intrinsic_id("llvm.va_copy"), - }, - .debug_tag = 0, - }; - } - - pub fn emit_block(module: *Module, block: *llvm.BasicBlock) void { - const maybe_current_block = module.llvm.builder.get_insert_block(); - - var emit_branch = false; - if (maybe_current_block) |current_block| { - emit_branch = current_block.get_terminator() == null; - } - - if (emit_branch) { - _ = module.llvm.builder.create_branch(block); - } - - if (maybe_current_block != null and maybe_current_block.?.get_parent() != null) { - module.llvm.builder.insert_basic_block_after_insert_block(block); - } else { - module.current_function.?.variable.storage.?.llvm.?.to_function().append_basic_block(block); - } - - module.llvm.builder.position_at_end(block); - } - - pub fn emit_va_arg(module: *Module, value: *Value, left_llvm: ?*llvm.Value, left_type: ?*Type) *llvm.Value { - switch (value.bb) { - .intrinsic => |intrinsic| switch (intrinsic) { - .va_arg => |va_arg| { - const raw_va_list_type = module.get_va_list_type(); - module.emit_value(va_arg.list, .memory); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const va_list = module.llvm.builder.create_gep(.{ - .type = raw_va_list_type.llvm.memory.?, - .aggregate = va_arg.list.llvm.?, - .indices = &([1]*llvm.Value{uint64.llvm.memory.?.to_integer().get_constant(0, @intFromBool(false)).to_value()} ** 2), - }); - const r = Abi.SystemV.classify_argument_type(module, va_arg.type, .{ - .available_gpr = 0, - .is_named_argument = false, - .is_reg_call = false, - }); - const abi = r[0]; - const needed_register_count = r[1]; - const abi_kind = abi.flags.kind; - assert(abi_kind != .ignore); - - const va_list_struct = raw_va_list_type.bb.array.element_type; - const llvm_address = switch (needed_register_count.gpr == 0 and needed_register_count.sse == 0) { - true => Abi.SystemV.emit_va_arg_from_memory(module, va_list, va_list_struct, va_arg.type), - false => c: { - const va_list_struct_llvm = va_list_struct.llvm.memory.?.to_struct(); - const gpr_offset_pointer = if (needed_register_count.gpr != 0) module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 0) else undefined; - const gpr_offset = if (needed_register_count.gpr != 0) module.create_load(.{ .type = va_list_struct.bb.structure.fields[0].type, .value = gpr_offset_pointer, .alignment = 16 }) else undefined; - const raw_in_regs = 48 - needed_register_count.gpr * 8; - const int32 = module.integer_type(32, false); - const int32_llvm = int32.llvm.abi.?.to_integer(); - var in_regs = if (needed_register_count.gpr != 0) int32_llvm.get_constant(raw_in_regs, @intFromBool(false)).to_value() else @trap(); - in_regs = if (needed_register_count.gpr != 0) module.llvm.builder.create_integer_compare(.ule, gpr_offset, in_regs) else in_regs; - - const fp_offset_pointer = if (needed_register_count.sse != 0) module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 1) else undefined; - const fp_offset = if (needed_register_count.sse != 0) module.create_load(.{ .type = va_list_struct.bb.structure.fields[1].type, .value = fp_offset_pointer }) else undefined; - const raw_fits_in_fp = 176 - needed_register_count.sse * 16; - var fits_in_fp = if (needed_register_count.sse != 0) int32_llvm.get_constant(raw_fits_in_fp, @intFromBool(false)).to_value() else undefined; - fits_in_fp = if (needed_register_count.sse != 0) module.llvm.builder.create_integer_compare(.ule, fp_offset, fits_in_fp) else undefined; - in_regs = if (needed_register_count.sse != 0 and needed_register_count.gpr != 0) @trap() else in_regs; - - const in_reg_block = module.llvm.context.create_basic_block("va_arg.in_reg", null); - const in_mem_block = module.llvm.context.create_basic_block("va_arg.in_mem", null); - const end_block = module.llvm.context.create_basic_block("va_arg.end", null); - _ = module.llvm.builder.create_conditional_branch(in_regs, in_reg_block, in_mem_block); - module.emit_block(in_reg_block); - - const reg_save_area = module.create_load(.{ .type = va_list_struct.bb.structure.fields[3].type, .value = module.llvm.builder.create_struct_gep(va_list_struct_llvm, va_list, 3), .alignment = 16 }); - - const register_address = if (needed_register_count.gpr != 0 and needed_register_count.sse != 0) { - @trap(); - } else if (needed_register_count.gpr != 0) b: { - const t = va_list_struct.bb.structure.fields[3].type.bb.pointer.type; - t.resolve(module); - const register_address = module.llvm.builder.create_gep(.{ - .type = t.llvm.abi.?, - .aggregate = reg_save_area, - .indices = &.{gpr_offset}, - .inbounds = false, - }); - if (va_arg.type.get_byte_alignment() > 8) { - @trap(); - } - break :b register_address; - } else if (needed_register_count.sse == 1) { - @trap(); - } else { - assert(needed_register_count.sse == 2); - @trap(); - }; - - if (needed_register_count.gpr != 0) { - const raw_offset = needed_register_count.gpr * 8; - const new_offset = module.llvm.builder.create_add(gpr_offset, int32_llvm.get_constant(raw_offset, @intFromBool(false)).to_value()); - _ = module.create_store(.{ .destination_value = gpr_offset_pointer, .source_value = new_offset, .type = int32, .alignment = 16 }); - } - - if (needed_register_count.sse != 0) { - @trap(); - } - - _ = module.llvm.builder.create_branch(end_block); - - module.emit_block(in_mem_block); - - const memory_address = Abi.SystemV.emit_va_arg_from_memory(module, va_list, va_list_struct, va_arg.type); - module.emit_block(end_block); - - const values = &.{ register_address, memory_address }; - const blocks = &.{ in_reg_block, in_mem_block }; - const phi = module.llvm.builder.create_phi(module.llvm.pointer_type); - phi.add_incoming(values, blocks); - break :c phi.to_value(); - }, - }; - const result = switch (va_arg.type.get_evaluation_kind()) { - .scalar => module.create_load(.{ .type = va_arg.type, .value = llvm_address }), - .aggregate => if (left_llvm) |l| b: { - uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(l, left_type.?.bb.pointer.alignment.?, llvm_address, va_arg.type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(va_arg.type.get_byte_size(), 0).to_value()); - break :b l; - } else llvm_address, - .complex => @trap(), - }; - return result; - }, - else => unreachable, - }, - else => unreachable, - } - } - - pub fn emit_call(module: *Module, value: *Value, left_llvm: ?*llvm.Value, left_type: ?*Type) *llvm.Value { - switch (value.bb) { - .call => |call| { - const raw_function_type = call.function_type; - // TODO: improve this code, which works for now - const llvm_callable = switch (call.callable.bb) { - .variable_reference => |variable| switch (variable.type.?.bb) { - .pointer => |pointer| switch (pointer.type.bb) { - .function => module.create_load(.{ .type = module.get_pointer_type(.{ .type = raw_function_type }), .value = variable.storage.?.llvm.? }), - else => @trap(), - }, - .function => variable.storage.?.llvm.?, - else => @trap(), - }, - else => @trap(), - }; - - const function_type = &raw_function_type.bb.function; - const calling_convention = function_type.calling_convention; - const llvm_calling_convention = calling_convention.to_llvm(); - var llvm_abi_argument_value_buffer: [64]*llvm.Value = undefined; - var llvm_abi_argument_type_buffer: [64]*llvm.Type = undefined; - var abi_argument_type_buffer: [64]*Type = undefined; - var argument_type_abi_buffer: [64]Abi.Information = undefined; - - var abi_argument_count: u16 = 0; - const function_semantic_argument_count = function_type.argument_abis.len; - - // TODO - const uses_in_alloca = false; - if (uses_in_alloca) { - @trap(); - } - - const llvm_indirect_return_value: *llvm.Value = switch (function_type.return_abi.flags.kind) { - .indirect, .in_alloca, .coerce_and_expand => blk: { - // TODO: handle edge cases: - // - virtual function pointer thunk - // - return alloca already exists - const semantic_return_type = function_type.return_abi.semantic_type; - const pointer = if (left_llvm) |l| b: { - assert(left_type.?.bb.pointer.type == semantic_return_type); - break :b l; - } else b: { - const temporal_alloca = module.create_alloca(.{ .type = semantic_return_type, .name = "tmp" }); - break :b temporal_alloca; - }; - const has_sret = function_type.return_abi.flags.kind == .indirect; - if (has_sret) { - llvm_abi_argument_value_buffer[abi_argument_count] = pointer; - abi_argument_type_buffer[abi_argument_count] = module.void_type; - llvm_abi_argument_type_buffer[abi_argument_count] = module.void_type.llvm.abi.?; - abi_argument_count += 1; - break :blk pointer; - } else if (function_type.return_abi.flags.kind == .in_alloca) { - @trap(); - } else { - @trap(); - } - }, - else => undefined, - }; - - var available_registers = function_type.available_registers; - - for (call.arguments, 0..) |semantic_argument_value, semantic_argument_index| { - const is_named_argument = semantic_argument_index < function_semantic_argument_count; - const semantic_argument_type = switch (is_named_argument) { - true => function_type.argument_abis[semantic_argument_index].semantic_type, - false => semantic_argument_value.type.?, - }; - semantic_argument_type.resolve(module); - - const argument_abi = if (is_named_argument) function_type.argument_abis[semantic_argument_index] else Abi.SystemV.classify_argument(module, &available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{ - .type = semantic_argument_type, - .abi_start = abi_argument_count, - .is_named_argument = true, - }); - if (semantic_argument_type.get_byte_size() > 60 and argument_abi.flags.kind != .indirect) { - @trap(); - } - if (is_named_argument) { - for (llvm_abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], function_type.abi_argument_types[argument_abi.abi_start..][0..argument_abi.abi_count]) |*llvm_t, *t, abi_argument_type| { - llvm_t.* = abi_argument_type.llvm.abi.?; - t.* = abi_argument_type; - } - } - argument_type_abi_buffer[semantic_argument_index] = argument_abi; - - if (argument_abi.padding.type) |padding_type| { - _ = padding_type; - @trap(); - } - assert(abi_argument_count == argument_abi.abi_start); - const argument_abi_kind = argument_abi.flags.kind; - switch (argument_abi_kind) { - .direct, .extend => { - const coerce_to_type = argument_abi.get_coerce_to_type(); - coerce_to_type.resolve(module); - if (coerce_to_type.bb != .structure and semantic_argument_type.is_abi_equal(coerce_to_type, module) and argument_abi.attributes.direct.offset == 0) { - module.emit_value(semantic_argument_value, .memory); - const v = switch (argument_abi.semantic_type.get_evaluation_kind()) { - .aggregate => @trap(), - else => semantic_argument_value, - }; - - if (!coerce_to_type.is_abi_equal(v.type.?, module)) { - switch (v.type.?) { - else => @trap(), - } - } - - // TODO: bitcast - // if (argument_abi.abi_start < function_type.argument_type_abis.len and v.type.llvm.handle != abi_arguments - - // TODO: fill types - llvm_abi_argument_value_buffer[abi_argument_count] = v.llvm.?; - abi_argument_count += 1; - } else { - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) { - @trap(); - } - - const evaluation_kind = semantic_argument_type.get_evaluation_kind(); - var src = switch (evaluation_kind) { - .aggregate => semantic_argument_value, - .scalar => { - @trap(); - }, - .complex => @trap(), - }; - - src = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => src, - }; - - if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) { - const source_type_size_is_scalable = false; // TODO - if (source_type_size_is_scalable) { - @trap(); - } else { - if (src.kind == .right) { - if (src.bb == .variable_reference) { - src.type = null; - src.kind = .left; - module.analyze_value_type(src, .{}); - } - } - module.emit_value(semantic_argument_value, .memory); - const destination_size = coerce_to_type.get_byte_size(); - const source_size = argument_abi.semantic_type.get_byte_size(); - - const alignment = argument_abi.semantic_type.get_byte_alignment(); - const source = switch (source_size < destination_size) { - true => blk: { - const temporal_alloca = module.create_alloca(.{ .type = coerce_to_type, .name = "coerce", .alignment = alignment }); - const destination = temporal_alloca; - const source = semantic_argument_value.llvm.?; - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(destination, alignment, source, alignment, uint64.llvm.abi.?.to_integer().get_constant(semantic_argument_type.get_byte_size(), @intFromBool(false)).to_value()); - break :blk temporal_alloca; - }, - false => src.llvm.?, - }; - - // TODO: - assert(argument_abi.attributes.direct.offset == 0); - - switch (semantic_argument_value.kind) { - .left => { - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.memory.?.to_struct(), source, @intCast(field_index)); - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - const load = module.create_load(.{ .value = gep, .type = field.type, .alignment = alignment }); - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - }, - .right => { - if (coerce_to_type.is_abi_equal(semantic_argument_type, module)) { - for (0..coerce_to_type.bb.structure.fields.len) |field_index| { - const extract_value = module.llvm.builder.create_extract_value(source, @intCast(field_index)); - llvm_abi_argument_value_buffer[abi_argument_count] = extract_value; - abi_argument_count += 1; - } - } else { - switch (semantic_argument_value.bb) { - .aggregate_initialization => |aggregate_initialization| switch (aggregate_initialization.is_constant) { - true => { - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "conststruct", // TODO: format properly - .initial_value = semantic_argument_value.llvm.?.to_constant(), - .type = semantic_argument_type.llvm.abi.?, - }); - global_variable.set_unnamed_address(.global); - global_variable.to_value().set_alignment(semantic_argument_type.get_byte_alignment()); - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.abi.?.to_struct(), global_variable.to_value(), @intCast(field_index)); - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - const load = module.create_load(.{ .value = gep, .type = field.type, .alignment = alignment }); - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - }, - false => @trap(), - }, - else => @trap(), - } - } - }, - } - } - } else { - assert(argument_abi.abi_count == 1); - // TODO: handmade change - if (src.type.?.bb != .pointer) { - assert(src.kind == .right); - assert(src.type.?.bb == .structure); - src.type = null; - src.kind = .left; - module.analyze_value_type(src, .{}); - } - module.emit_value(src, .memory); - - assert(src.type.?.bb == .pointer); - const source_type = src.type.?.bb.pointer.type; - assert(source_type == argument_abi.semantic_type); - const destination_type = argument_abi.get_coerce_to_type(); - const load = module.create_coerced_load(src.llvm.?, source_type, destination_type); - - const is_cmse_ns_call = false; - if (is_cmse_ns_call) { - @trap(); - } - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } - - llvm_abi_argument_value_buffer[abi_argument_count] = load; - abi_argument_count += 1; - } - } - }, - .indirect, .indirect_aliased => indirect: { - if (semantic_argument_type.get_evaluation_kind() == .aggregate) { - const same_address_space = true; - assert(argument_abi.abi_start >= function_type.abi_argument_types.len or same_address_space); - - // TODO: handmade code, may contain bugs - assert(argument_abi.abi_count == 1); - const abi_argument_type = abi_argument_type_buffer[argument_abi.abi_start]; - - if (abi_argument_type == semantic_argument_value.type) { - @trap(); - } else if (abi_argument_type.bb == .pointer and abi_argument_type.bb.pointer.type == semantic_argument_value.type) switch (semantic_argument_value.is_constant()) { - true => { - module.emit_value(semantic_argument_value, .memory); - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "conststruct", // TODO: format properly - .initial_value = semantic_argument_value.llvm.?.to_constant(), - .type = semantic_argument_type.llvm.abi.?, - }); - global_variable.set_unnamed_address(.global); - const alignment = semantic_argument_type.get_byte_alignment(); - global_variable.to_value().set_alignment(alignment); - llvm_abi_argument_value_buffer[abi_argument_count] = global_variable.to_value(); - abi_argument_count += 1; - break :indirect; - }, - false => switch (semantic_argument_value.bb) { - .variable_reference => { - const pointer_type = module.get_pointer_type(.{ .type = semantic_argument_value.type.? }); - semantic_argument_value.type = null; - semantic_argument_value.kind = .left; - module.analyze(semantic_argument_value, .{ .type = pointer_type }, .memory); - llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm.?; - abi_argument_count += 1; - break :indirect; - }, - else => { - assert(abi_argument_type.bb.pointer.type == semantic_argument_value.type); - const alloca = module.create_alloca(.{ - .type = semantic_argument_value.type.?, - }); - const pointer_type = module.get_pointer_type(.{ .type = semantic_argument_value.type.? }); - module.emit_assignment(alloca, pointer_type, semantic_argument_value); - llvm_abi_argument_value_buffer[abi_argument_count] = alloca; - abi_argument_count += 1; - break :indirect; - }, - }, - } else { - @trap(); - } - - // const indirect_alignment = argument_abi.attributes.indirect.alignment; - // const address_alignment = semantic_argument_type.get_byte_alignment(); - // const get_or_enforce_known_alignment = indirect_alignment; - // llvm::getOrEnforceKnownAlignment(Addr.emitRawPointer(*this), - // Align.getAsAlign(), - // *TD) < Align.getAsAlign()) { - - // TODO - // const need_copy = switch (address_alignment < indirect_alignment and get_or_enforce_known_alignment < indirect_alignment) { - // true => @trap(), - // false => b: { - // const is_lvalue = !(semantic_argument_value.type.?.bb == .pointer and semantic_argument_type == semantic_argument_value.type.?.bb.pointer.type); - // if (is_lvalue) { - // var need_copy = false; - // const is_by_val_or_by_ref = argument_abi.flags.kind == .indirect_aliased or argument_abi.flags.indirect_by_value; - // - // const lv_alignment = semantic_argument_value.type.?.get_byte_alignment(); - // const arg_type_alignment = argument_abi.semantic_type.get_byte_alignment(); - // if (!is_by_val_or_by_ref or lv_alignment < arg_type_alignment) { - // need_copy = true; - // } - // - // break :b need_copy; - // } else { - // break :b false; - // } - // }, - // }; - // - // if (!need_copy) { - // const abi_argument_type = abi_argument_type_buffer[argument_abi.abi_start]; - // assert(abi_argument_type == semantic_argument_value.type); - // llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm.?; - // abi_argument_count += 1; - // break :indirect; - // } - } - - @trap(); - }, - .ignore => unreachable, - else => @trap(), - } - - assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); - } - - if (function_type.is_var_args) { - assert(abi_argument_count >= function_type.abi_argument_types.len); - } else { - // TODO - assert(abi_argument_count == function_type.abi_argument_types.len); - } - - const llvm_abi_argument_values = llvm_abi_argument_value_buffer[0..abi_argument_count]; - const llvm_call = module.llvm.builder.create_call(raw_function_type.llvm.abi.?.to_function(), llvm_callable, llvm_abi_argument_values); - - const attribute_list = module.build_attribute_list(.{ - .return_type_abi = function_type.return_abi, - .abi_return_type = function_type.abi_return_type, - .abi_argument_types = abi_argument_type_buffer[0..abi_argument_count], - .argument_type_abis = argument_type_abi_buffer[0..call.arguments.len], - .attributes = .{}, - .call_site = true, - }); - - const call_base = llvm_call.to_instruction().to_call_base(); - call_base.set_calling_convention(llvm_calling_convention); - call_base.set_attributes(attribute_list); - - const return_type_abi = &function_type.return_abi; - const return_abi_kind = return_type_abi.flags.kind; - - switch (return_abi_kind) { - .ignore => { - assert(return_type_abi.semantic_type == module.noreturn_type or return_type_abi.semantic_type == module.void_type); - return llvm_call; - }, - .direct, .extend => { - const coerce_to_type = return_type_abi.get_coerce_to_type(); - - if (return_type_abi.semantic_type.is_abi_equal(coerce_to_type, module) and return_type_abi.attributes.direct.offset == 0) { - const coerce_to_type_kind = coerce_to_type.get_evaluation_kind(); - switch (coerce_to_type_kind) { - .aggregate => {}, - .complex => @trap(), - .scalar => { - return llvm_call; - // TODO: maybe a bug? - // const v = module.values.add(); - // v.* = .{ - // .llvm = llvm_call, - // .bb = .instruction, - // .type = return_type_abi.semantic_type, - // .lvalue = false, - // .dereference_to_assign = false, - // }; - // break :c v; - }, - } - } - - // TODO: if - const fixed_vector_type = false; - if (fixed_vector_type) { - @trap(); - } - - const coerce_alloca = if (left_llvm) |l| b: { - assert(left_type.?.bb.pointer.type == return_type_abi.semantic_type); - break :b l; - } else module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "coerce" }); - const destination_pointer = switch (return_type_abi.attributes.direct.offset == 0) { - true => coerce_alloca, - false => @trap(), - }; - - var destination_type = return_type_abi.semantic_type; - if (return_type_abi.semantic_type.bb.structure.fields.len > 0) { - // CreateCoercedStore( - // CI, StorePtr, - // llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()), - // DestIsVolatile); - - const source_value = llvm_call; - const source_type = function_type.abi_return_type; - // const source_size = source_type.get_byte_size(); - const destination_size = destination_type.get_byte_size(); - // const destination_alignment = destination_type.get_byte_alignment(); - const left_destination_size = destination_size - return_type_abi.attributes.direct.offset; - - const is_destination_volatile = false; // TODO - module.create_coerced_store(source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); - } else { - @trap(); - } - - if (left_llvm) |l| { - assert(destination_pointer == l); - return destination_pointer; - } else return switch (value.kind) { - .left => @trap(), - .right => module.create_load(.{ .type = destination_type, .value = destination_pointer }), - }; - }, - .indirect => { - return llvm_indirect_return_value; - // TODO - // const v = module.values.add(); - // v.* = .{ - // .llvm = llvm_indirect_return_value, - // .bb = .instruction, - // .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }), - // .lvalue = true, - // .dereference_to_assign = true, - // }; - // break :c v; - }, - else => @trap(), - } - }, - else => unreachable, - } - } - - pub fn emit(module: *Module) void { - module.initialize_llvm(); - - for (module.globals.get_slice()) |*global| { - switch (global.variable.storage.?.bb) { - .function, .external_function => { - const function_type = &global.variable.storage.?.type.?.bb.pointer.type.bb.function; - function_type.argument_abis = module.arena.allocate(Abi.Information, function_type.semantic_argument_types.len); - - const resolved_calling_convention = function_type.calling_convention.resolve(module.target); - const is_reg_call = resolved_calling_convention == .system_v and false; // TODO: regcall calling_convention - - var llvm_abi_argument_type_buffer: [64]*llvm.Type = undefined; - var abi_argument_type_buffer: [64]*Type = undefined; - var abi_argument_type_count: u16 = 0; - - switch (resolved_calling_convention) { - .system_v => { - function_type.available_registers = switch (resolved_calling_convention) { - .system_v => .{ - .system_v = .{ - .gpr = if (is_reg_call) 11 else 6, - .sse = if (is_reg_call) 16 else 8, - }, - }, - .win64 => @trap(), - }; - - function_type.return_abi = Abi.SystemV.classify_return_type(module, function_type.semantic_return_type); - const return_abi_kind = function_type.return_abi.flags.kind; - function_type.abi_return_type = switch (return_abi_kind) { - .direct, .extend => function_type.return_abi.coerce_to_type.?, - .ignore, .indirect => module.void_type, - else => |t| @panic(@tagName(t)), - }; - function_type.abi_return_type.resolve(module); - - if (function_type.return_abi.flags.kind == .indirect) { - assert(!function_type.return_abi.flags.sret_after_this); - function_type.available_registers.system_v.gpr -= 1; - const indirect_type = module.get_pointer_type(.{ .type = function_type.return_abi.semantic_type }); - indirect_type.resolve(module); - abi_argument_type_buffer[abi_argument_type_count] = indirect_type; - llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_type.llvm.abi.?; - abi_argument_type_count += 1; - } - - const required_arguments = function_type.semantic_argument_types.len; - - for (function_type.argument_abis, function_type.semantic_argument_types, 0..) |*argument_type_abi, semantic_argument_type, semantic_argument_index| { - const is_named_argument = semantic_argument_index < required_arguments; - assert(is_named_argument); - - argument_type_abi.* = Abi.SystemV.classify_argument(module, &function_type.available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{ - .type = semantic_argument_type, - .abi_start = abi_argument_type_count, - .is_named_argument = is_named_argument, - }); - - abi_argument_type_count += argument_type_abi.abi_count; - } - - function_type.abi_argument_types = module.arena.allocate(*Type, abi_argument_type_count); - @memcpy(function_type.abi_argument_types, abi_argument_type_buffer[0..function_type.abi_argument_types.len]); - }, - .win64 => { - @trap(); - }, - } - - const llvm_abi_argument_types = llvm_abi_argument_type_buffer[0..abi_argument_type_count]; - const llvm_function_type = llvm.Type.Function.get(function_type.abi_return_type.llvm.abi.?, llvm_abi_argument_types, function_type.is_var_args); - - const subroutine_type_flags = llvm.DI.Flags{}; - const subroutine_type = if (module.has_debug_info) blk: { - var debug_argument_type_buffer: [64 + 1]*llvm.DI.Type = undefined; - const semantic_debug_argument_types = debug_argument_type_buffer[0 .. function_type.argument_abis.len + 1 + @intFromBool(function_type.is_var_args)]; - semantic_debug_argument_types[0] = function_type.return_abi.semantic_type.llvm.debug.?; - - for (function_type.argument_abis, semantic_debug_argument_types[1..][0..function_type.argument_abis.len]) |argument_abi, *debug_argument_type| { - debug_argument_type.* = argument_abi.semantic_type.llvm.debug.?; - } - - if (function_type.is_var_args) { - semantic_debug_argument_types[function_type.argument_abis.len + 1] = module.void_type.llvm.debug.?; - } - - const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, subroutine_type_flags); - break :blk subroutine_type; - } else undefined; - global.variable.storage.?.type.?.bb.pointer.type.llvm.abi = llvm_function_type.to_type(); - global.variable.storage.?.type.?.bb.pointer.type.llvm.debug = subroutine_type.to_type(); - - const llvm_function_value = module.llvm.module.create_function(.{ - .name = global.variable.name, - // TODO: make it better - .linkage = switch (global.linkage) { - .external => .ExternalLinkage, - .internal => .InternalLinkage, - }, - .type = global.variable.storage.?.type.?.bb.pointer.type.llvm.abi.?.to_function(), - }); - - global.variable.storage.?.llvm = llvm_function_value.to_value(); - - llvm_function_value.set_calling_convention(function_type.calling_convention.to_llvm()); - - const attribute_list = module.build_attribute_list(.{ - .abi_return_type = function_type.abi_return_type, - .abi_argument_types = function_type.abi_argument_types, - .argument_type_abis = function_type.argument_abis, - .return_type_abi = function_type.return_abi, - .attributes = switch (global.variable.storage.?.bb) { - .function => |function| function.attributes, - else => .{}, - }, - .call_site = false, - }); - - llvm_function_value.set_attributes(attribute_list); - - const function_scope: *llvm.DI.Scope = if (module.has_debug_info) blk: { - const scope_line: u32 = @intCast(module.line_offset + 1); - const local_to_unit = switch (global.linkage) { - .internal => true, - .external => false, - }; - const flags = llvm.DI.Flags{}; - const is_definition = switch (global.variable.storage.?.bb) { - .function => true, - .external_function => false, - else => @trap(), - }; - const name = global.variable.name; - const linkage_name = name; - const subprogram = module.llvm.di_builder.create_function(module.scope.llvm.?, name, linkage_name, module.llvm.file, global.variable.line, subroutine_type, local_to_unit, is_definition, scope_line, flags, module.build_mode.is_optimized()); - llvm_function_value.set_subprogram(subprogram); - - break :blk @ptrCast(subprogram); - } else undefined; - - if (global.variable.storage.?.bb == .function) { - module.current_function = global; - defer module.current_function = null; - - global.variable.storage.?.bb.function.scope.llvm = function_scope; - - const entry_block = module.llvm.context.create_basic_block("entry", llvm_function_value); - global.variable.storage.?.bb.function.return_block = module.llvm.context.create_basic_block("ret_block", null); - - module.llvm.builder.position_at_end(entry_block); - module.llvm.builder.set_current_debug_location(null); - - var llvm_abi_argument_buffer: [64]*llvm.Argument = undefined; - llvm_function_value.get_arguments(&llvm_abi_argument_buffer); - - const llvm_abi_arguments = llvm_abi_argument_buffer[0..function_type.abi_argument_types.len]; - - const return_abi_kind = function_type.return_abi.flags.kind; - switch (return_abi_kind) { - .ignore => {}, - .indirect => { - const indirect_argument_index = @intFromBool(function_type.return_abi.flags.sret_after_this); - if (function_type.return_abi.flags.sret_after_this) { - @trap(); - } - global.variable.storage.?.bb.function.return_alloca = llvm_abi_arguments[indirect_argument_index].to_value(); - if (!function_type.return_abi.flags.indirect_by_value) { - @trap(); - } - }, - .in_alloca => { - @trap(); - }, - else => { - const alloca = module.create_alloca(.{ .type = function_type.return_abi.semantic_type, .name = "retval" }); - global.variable.storage.?.bb.function.return_alloca = alloca; - }, - } - - const argument_variables = global.variable.storage.?.bb.function.arguments; - for (function_type.argument_abis, argument_variables, 0..) |argument_abi, argument_variable, argument_index| { - const abi_arguments = llvm_abi_arguments[argument_abi.abi_start..][0..argument_abi.abi_count]; - assert(argument_abi.flags.kind == .ignore or argument_abi.abi_count != 0); - const argument_abi_kind = argument_abi.flags.kind; - const semantic_argument_storage = switch (argument_abi_kind) { - .direct, .extend => blk: { - const first_argument = abi_arguments[0]; - const coerce_to_type = argument_abi.get_coerce_to_type(); - if (coerce_to_type.bb != .structure and coerce_to_type.is_abi_equal(argument_abi.semantic_type, module) and argument_abi.attributes.direct.offset == 0) { - assert(argument_abi.abi_count == 1); - const is_promoted = false; - var v = first_argument.to_value(); - v = switch (coerce_to_type.llvm.abi.? == v.get_type()) { - true => v, - false => @trap(), - }; - if (is_promoted) { - @trap(); - } - - switch (argument_abi.semantic_type.is_arbitrary_bit_integer()) { - true => { - const bit_count = argument_abi.semantic_type.get_bit_size(); - const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count))); - const is_signed = argument_abi.semantic_type.is_signed(); - const destination_type = module.align_integer_type(argument_abi.semantic_type); - const alloca = module.create_alloca(.{ .type = destination_type, .name = argument_variable.variable.name }); - const result = switch (bit_count < abi_bit_count) { - true => switch (is_signed) { - true => module.llvm.builder.create_sign_extend(first_argument.to_value(), destination_type.llvm.memory.?), - false => module.llvm.builder.create_zero_extend(first_argument.to_value(), destination_type.llvm.memory.?), - }, - false => @trap(), - }; - _ = module.create_store(.{ .source_value = result, .destination_value = alloca, .type = destination_type }); - break :blk alloca; - }, - false => { // TODO: ExtVectorBoolType - const alloca = module.create_alloca(.{ .type = argument_abi.semantic_type, .name = argument_variable.variable.name }); - _ = module.create_store(.{ .source_value = first_argument.to_value(), .destination_value = alloca, .type = argument_abi.semantic_type }); - break :blk alloca; - }, - } - } else { - const is_fixed_vector_type = false; - if (is_fixed_vector_type) { - @trap(); - } - - if (coerce_to_type.bb == .structure and coerce_to_type.bb.structure.fields.len > 1 and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) { - const contains_homogeneous_scalable_vector_types = false; - if (contains_homogeneous_scalable_vector_types) { - @trap(); - } - } - - const alloca = module.create_alloca(.{ .type = argument_abi.semantic_type }); - const pointer = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => alloca, - }; - const pointer_type = switch (argument_abi.attributes.direct.offset > 0) { - true => @trap(), - false => argument_abi.semantic_type, - }; - - if (coerce_to_type.bb == .structure and coerce_to_type.bb.structure.fields.len > 1 and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) { - const struct_size = coerce_to_type.get_byte_size(); - const pointer_element_size = pointer_type.get_byte_size(); // TODO: fix - const is_scalable = false; - - switch (is_scalable) { - true => @trap(), - false => { - const source_size = struct_size; - const destination_size = pointer_element_size; - const address_alignment = argument_abi.semantic_type.get_byte_alignment(); - const address = switch (source_size <= destination_size) { - true => alloca, - false => module.create_alloca(.{ .type = coerce_to_type, .alignment = address_alignment, .name = "coerce" }), - }; - assert(coerce_to_type.bb.structure.fields.len == argument_abi.abi_count); - coerce_to_type.resolve(module); - for (coerce_to_type.bb.structure.fields, abi_arguments, 0..) |field, abi_argument, field_index| { - const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.abi.?.to_struct(), address, @intCast(field_index)); - // TODO: check if alignment is right - _ = module.create_store(.{ .source_value = abi_argument.to_value(), .destination_value = gep, .type = field.type }); - } - - if (source_size > destination_size) { - _ = module.llvm.builder.create_memcpy(pointer, pointer_type.get_byte_alignment(), address, address_alignment, module.integer_type(64, false).llvm.abi.?.to_integer().get_constant(destination_size, @intFromBool(false)).to_value()); - } - }, - } - } else { - assert(argument_abi.abi_count == 1); - const abi_argument_type = function_type.abi_argument_types[argument_abi.abi_start]; - const destination_size = pointer_type.get_byte_size() - argument_abi.attributes.direct.offset; - const is_volatile = false; - module.create_coerced_store(abi_arguments[0].to_value(), abi_argument_type, pointer, pointer_type, destination_size, is_volatile); - } - - switch (argument_abi.semantic_type.get_evaluation_kind()) { - .scalar => @trap(), - else => { - // TODO - }, - } - - break :blk alloca; - } - }, - .indirect, .indirect_aliased => blk: { - assert(argument_abi.abi_count == 1); - switch (argument_abi.semantic_type.get_evaluation_kind()) { - .scalar => @trap(), - else => { - if (argument_abi.flags.indirect_realign or argument_abi.flags.kind == .indirect_aliased) { - @trap(); - } - - const use_indirect_debug_address = !argument_abi.flags.indirect_by_value; - if (use_indirect_debug_address) { - @trap(); - } - - const llvm_argument = abi_arguments[0]; - break :blk llvm_argument.to_value(); - }, - } - }, - else => @trap(), - }; - - const storage = module.values.add(); - storage.* = .{ - .bb = .argument, - .type = module.get_pointer_type(.{ - .type = argument_variable.variable.type.?, - }), - .llvm = semantic_argument_storage, - }; - argument_variable.variable.storage = storage; - - // no pointer - const argument_type = argument_variable.variable.storage.?.type.?.bb.pointer.type; - if (module.has_debug_info) { - const always_preserve = true; - const flags = llvm.DI.Flags{}; - const parameter_variable = module.llvm.di_builder.create_parameter_variable(function_scope, argument_variable.variable.name, @intCast(argument_index + 1), module.llvm.file, argument_variable.variable.line, argument_type.llvm.debug.?, always_preserve, flags); - const inlined_at: ?*llvm.DI.Metadata = @ptrCast(module.inline_at_debug_location); - const debug_location = llvm.DI.create_debug_location(module.llvm.context, argument_variable.variable.line, argument_variable.variable.column, function_scope, inlined_at); - _ = module.llvm.di_builder.insert_declare_record_at_end(semantic_argument_storage, parameter_variable, module.llvm.di_builder.null_expression(), debug_location, entry_block); - } - } - - module.analyze_block(global.variable.storage.?.bb.function.main_block); - - // Handle jump to the return block - const return_block = global.variable.storage.?.bb.function.return_block orelse module.report_error(); - - if (module.llvm.builder.get_insert_block()) |current_basic_block| { - assert(current_basic_block.get_terminator() == null); - - if (current_basic_block.is_empty() or current_basic_block.to_value().use_empty()) { - return_block.to_value().replace_all_uses_with(current_basic_block.to_value()); - return_block.delete(); - } else { - module.emit_block(return_block); - } - } else { - var is_reachable = false; - - if (return_block.to_value().has_one_use()) { - if (llvm.Value.to_branch(return_block.user_begin())) |branch| { - is_reachable = !branch.is_conditional() and branch.get_successor(0) == return_block; - - if (is_reachable) { - module.llvm.builder.position_at_end(branch.to_instruction().get_parent()); - branch.to_instruction().erase_from_parent(); - return_block.delete(); - } - } - } - - if (!is_reachable) { - module.emit_block(return_block); - } - } - - // End function debug info - if (llvm_function_value.get_subprogram()) |subprogram| { - module.llvm.di_builder.finalize_subprogram(subprogram); - } - - if (function_type.return_abi.semantic_type == module.noreturn_type or global.variable.storage.?.bb.function.attributes.naked) { - _ = module.llvm.builder.create_unreachable(); - } else if (function_type.return_abi.semantic_type == module.void_type) { - module.llvm.builder.create_ret_void(); - } else { - const abi_kind = function_type.return_abi.flags.kind; - const return_value: ?*llvm.Value = switch (abi_kind) { - .direct, .extend => blk: { - const coerce_to_type = function_type.return_abi.get_coerce_to_type(); - const return_alloca = global.variable.storage.?.bb.function.return_alloca orelse unreachable; - - if (function_type.return_abi.semantic_type.is_abi_equal(coerce_to_type, module) and function_type.return_abi.attributes.direct.offset == 0) { - if (module.llvm.builder.find_return_value_dominating_store(return_alloca, function_type.return_abi.semantic_type.llvm.abi.?)) |store| { - const store_instruction = store.to_instruction(); - const return_value = store_instruction.to_value().get_operand(0); - const alloca = store_instruction.to_value().get_operand(1); - assert(alloca == return_alloca); - store_instruction.erase_from_parent(); - assert(alloca.use_empty()); - alloca.to_instruction().erase_from_parent(); - break :blk return_value; - } else { - const load_value = module.create_load(.{ .type = function_type.return_abi.semantic_type, .value = return_alloca }); - break :blk load_value; - } - } else { - const source = switch (function_type.return_abi.attributes.direct.offset == 0) { - true => return_alloca, - false => @trap(), - }; - - const source_type = function_type.return_abi.semantic_type; - const destination_type = coerce_to_type; - const result = module.create_coerced_load(source, source_type, destination_type); - break :blk result; - } - }, - .indirect => switch (function_type.return_abi.semantic_type.get_evaluation_kind()) { - .complex => @trap(), - .aggregate => null, - .scalar => @trap(), - }, - else => @trap(), - }; - - if (return_value) |rv| { - module.llvm.builder.create_ret(rv); - } else { - module.llvm.builder.create_ret_void(); - } - } - } - - if (lib.optimization_mode == .Debug) { - const verify_result = llvm_function_value.verify(); - if (!verify_result.success) { - lib.print_string(module.llvm.module.to_string()); - lib.print_string("============================\n"); - lib.print_string(llvm_function_value.to_string()); - lib.print_string("============================\n"); - lib.print_string(verify_result.error_message orelse unreachable); - lib.print_string("\n============================\n"); - lib.os.abort(); - } - } - }, - .global => { - module.analyze(global.variable.initial_value, .{ .type = global.variable.type }, .memory); - - if (global.variable.type == null) { - global.variable.type = global.variable.initial_value.type; - } - - if (global.variable.type != global.variable.initial_value.type) { - module.report_error(); - } - - global.variable.type.?.resolve(module); - - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = switch (global.linkage) { - .internal => .InternalLinkage, - .external => .ExternalLinkage, - }, - .name = global.variable.name, - .initial_value = global.variable.initial_value.llvm.?.to_constant(), - .type = global.variable.type.?.llvm.memory.?, - }); - global_variable.to_value().set_alignment(global.variable.type.?.get_byte_alignment()); - global.variable.storage.?.llvm = global_variable.to_value(); - global.variable.storage.?.type = module.get_pointer_type(.{ .type = global.variable.type.? }); - - if (module.has_debug_info) { - const linkage_name = global.variable.name; - const local_to_unit = global.linkage == .internal; - const alignment = 0; // TODO - const global_variable_expression = module.llvm.di_builder.create_global_variable(module.scope.llvm.?, global.variable.name, linkage_name, module.llvm.file, global.variable.line, global.variable.type.?.llvm.debug.?, local_to_unit, module.llvm.di_builder.null_expression(), alignment); - global_variable.add_debug_info(global_variable_expression); - } - }, - else => @trap(), - } - } - - if (module.has_debug_info) { - module.llvm.di_builder.finalize(); - } - - const verify_result = module.llvm.module.verify(); - if (!verify_result.success) { - lib.print_string(module.llvm.module.to_string()); - lib.print_string("============================\n"); - lib.print_string(verify_result.error_message orelse unreachable); - lib.os.abort(); - } - - if (!module.silent) { - const module_string = module.llvm.module.to_string(); - lib.print_string_stderr(module_string); - } - - var error_message: llvm.String = undefined; - var target_options = llvm.Target.Options.default(); - target_options.flags0.trap_unreachable = switch (module.build_mode) { - .debug_none, .debug_fast, .debug_size => true, - else => false, - }; - const target_machine = llvm.Target.Machine.create(.{ - .target_options = target_options, - .cpu_triple = llvm.String.from_slice(llvm.global.host_triple), - .cpu_model = llvm.String.from_slice(llvm.global.host_cpu_model), - .cpu_features = llvm.String.from_slice(llvm.global.host_cpu_features), - .optimization_level = module.build_mode.to_llvm_machine(), - .relocation_model = .default, - .code_model = .none, - .jit = false, - }, &error_message) orelse { - lib.os.abort(); - }; - - const object_generate_result = llvm.object_generate(module.llvm.module, target_machine, .{ - .optimize_when_possible = @intFromEnum(module.build_mode) > @intFromEnum(BuildMode.soft_optimize), - .debug_info = module.has_debug_info, - .optimization_level = if (module.build_mode != .debug_none) module.build_mode.to_llvm_ir() else null, - .path = module.objects[0], - }); - - switch (object_generate_result) { - .success => { - const result = llvm.link(module.arena, .{ - .output_path = module.executable, - .objects = module.objects, - }); - - switch (result.success) { - true => {}, - false => lib.os.abort(), - } - }, - else => lib.os.abort(), - } - } - - pub fn get_va_list_type(module: *Module) *Type { - if (module.va_list_type) |va_list_type| { - @branchHint(.likely); - return va_list_type; - } else { - @branchHint(.unlikely); - const unsigned_int = module.integer_type(32, false); - const void_pointer = module.get_pointer_type(.{ - .type = module.integer_type(8, false), - }); - const va_list_name = "va_list"; - - const field_buffer = [_]Type.Struct.Field{ - .{ .name = "gp_offset", .type = unsigned_int, .bit_offset = 0, .byte_offset = 0, .line = 0 }, - .{ .name = "fp_offset", .type = unsigned_int, .bit_offset = 32, .byte_offset = 4, .line = 0 }, - .{ .name = "overflow_arg_area", .type = void_pointer, .bit_offset = 64, .byte_offset = 8, .line = 0 }, - .{ .name = "reg_save_area", .type = void_pointer, .bit_offset = 128, .byte_offset = 16, .line = 0 }, - }; - const fields = module.arena.allocate(Type.Struct.Field, 4); - @memcpy(fields, &field_buffer); - - const result = module.types.append(.{ - .name = va_list_name, - .bb = .{ - .structure = .{ - .bit_alignment = 64, - .byte_alignment = 16, - .byte_size = 24, - .bit_size = 24 * 8, - .fields = fields, - .is_slice = false, - .line = 0, - }, - }, - }); - - const element_count = 1; - const element_type = result; - const ty = module.types.append(.{ - .name = array_type_name(module.arena, element_type, element_count), - .bb = .{ - .array = .{ - .element_type = element_type, - .element_count = element_count, - }, - }, - }); - module.va_list_type = ty; - return ty; - } - } - - const ValueAnalysis = struct { - type: ?*Type = null, - }; - - pub fn analyze(module: *Module, value: *Value, analysis: ValueAnalysis, type_kind: Type.Kind) void { - module.analyze_value_type(value, analysis); - module.emit_value(value, type_kind); - } - - pub fn analyze_binary(module: *Module, left: *Value, right: *Value, is_boolean: bool, a: ValueAnalysis) void { - var analysis = a; - const is_left_constant = left.is_constant(); - const is_right_constant = right.is_constant(); - if (analysis.type == null) { - if (is_left_constant and is_right_constant) { - if (left.type == null and right.type == null) { - const are_string_literal = left.bb == .string_literal and right.bb == .string_literal; - if (are_string_literal) { - analysis.type = module.get_slice_type(.{ .type = module.integer_type(8, false) }); - } else { - module.report_error(); - } - } - } - } - - if (is_boolean or analysis.type == null) { - if (is_left_constant) { - module.analyze_value_type(right, .{}); - module.analyze_value_type(left, .{ - .type = right.type, - }); - } else if (is_right_constant) { - module.analyze_value_type(left, .{}); - module.analyze_value_type(right, .{ - .type = left.type, - }); - } else { - module.analyze_value_type(left, .{}); - module.analyze_value_type(right, .{ .type = left.type }); - } - } else if (!is_boolean and analysis.type != null) { - const expected_type = analysis.type.?; - module.analyze_value_type(left, .{ - .type = expected_type, - }); - module.analyze_value_type(right, .{ - .type = expected_type, - }); - } else { - @trap(); - } - - assert(left.type != null); - assert(right.type != null); - } - - pub fn typecheck(module: *Module, analysis: ValueAnalysis, ty: *Type) void { - if (analysis.type) |expected_type| { - module.check_types(expected_type, ty); - } - } - - fn fully_resolve_alias(module: *Module, ty: *Type) *Type { - const result_type = switch (ty.bb) { - .bits, - .structure, - .@"union", - .integer, - .enumerator, - .array, - .noreturn, - .void, - .function, - => ty, - .alias => |alias| alias.type, - .pointer => |pointer| module.get_pointer_type(.{ .type = module.fully_resolve_alias(pointer.type) }), - else => @trap(), - }; - - return result_type; - } - - pub fn check_types(module: *Module, expected_type: *Type, source_type: *Type) void { - if (expected_type != source_type) { - const dst_p_src_i = expected_type.bb == .pointer and source_type.bb == .integer; - if (!dst_p_src_i) { - const source = module.fully_resolve_alias(source_type); - const expected = module.fully_resolve_alias(expected_type); - if (source != expected) { - module.report_error(); - } - } - } - } - - pub fn copy_statement(module: *Module, scope: *Scope, old_statement: *Statement) *Statement { - const new_statement = module.statements.add(); - new_statement.line = old_statement.line; - new_statement.column = old_statement.column; - - new_statement.bb = switch (old_statement.bb) { - .@"return" => |rv| if (rv) |v| .{ - .@"return" = module.clone_value(scope, v), - } else old_statement.bb, - .@"if" => |if_stmt| blk: { - const condition = module.clone_value(scope, if_stmt.condition); - const if_statement = module.copy_statement(scope, if_stmt.if_statement); - const else_statement = if (if_stmt.else_statement) |else_statement| module.copy_statement(scope, else_statement) else null; - break :blk .{ - .@"if" = .{ - .condition = condition, - .if_statement = if_statement, - .else_statement = else_statement, - }, - }; - }, - .block => |block| blk: { - const lexical_block = module.lexical_blocks.add(); - module.copy_block(scope, .{ - .source = block, - .destination = lexical_block, - }); - - break :blk .{ - .block = lexical_block, - }; - }, - .expression => |v| .{ - .expression = module.clone_value(scope, v), - }, - else => @trap(), - }; - - return new_statement; - } - - const BlockCopy = struct { - source: *LexicalBlock, - destination: *LexicalBlock, - }; - - pub fn copy_block(module: *Module, parent_scope: *Scope, block_copy: BlockCopy) void { - const source = block_copy.source; - const destination = block_copy.destination; - destination.* = .{ - .locals = .initialize(), - .statements = .initialize(), - .scope = .{ - .line = source.scope.line, - .column = source.scope.column, - .kind = source.scope.kind, - .parent = parent_scope, - }, - }; - - const scope = &destination.scope; - - for (source.statements.get_slice()) |old_statement| { - const statement = module.copy_statement(scope, old_statement); - _ = destination.statements.append(statement); - } - } - - pub fn clone_value(module: *Module, scope: *Scope, source: *Value) *Value { - const result = switch (source.bb) { - .variable_reference => |variable| module.reference_identifier(scope, variable.name, source.kind), - else => blk: { - const result = module.values.add(); - - result.* = .{ - .bb = switch (source.bb) { - .unary => |unary| .{ - .unary = .{ - .value = module.clone_value(scope, unary.value), - .id = unary.id, - }, - }, - .binary => |binary| .{ - .binary = .{ - .left = module.clone_value(scope, binary.left), - .right = module.clone_value(scope, binary.right), - .id = binary.id, - }, - }, - .variable_reference => unreachable, - .call => |call| b: { - const callable = module.clone_value(scope, call.callable); - const arguments = module.arena.allocate(*Value, call.arguments.len); - for (arguments, call.arguments) |*new_argument, old_argument| { - new_argument.* = module.clone_value(scope, old_argument); - } - break :b .{ - .call = .{ - .callable = callable, - .arguments = arguments, - .function_type = call.function_type, - }, - }; - }, - .intrinsic => |intrinsic| .{ - .intrinsic = switch (intrinsic) { - .alignof => |ty| .{ - .alignof = module.resolve_type(ty), - }, - .byte_size => |ty| .{ - .byte_size = module.resolve_type(ty), - }, - .pointer_cast => |value| .{ - .pointer_cast = module.clone_value(scope, value), - }, - else => @trap(), - }, - }, - .@"unreachable" => .@"unreachable", - else => @trap(), - }, - .kind = source.kind, - }; - - break :blk result; - }, - }; - - return result; - } - - pub fn analyze_value_type(module: *Module, value: *Value, a: ValueAnalysis) void { - assert(value.type == null); - assert(value.llvm == null); - - var analysis = a; - if (analysis.type) |expected_type| switch (expected_type.bb) { - .unresolved => { - const macro_instantiation = (module.current_macro_instantiation orelse module.report_error()).bb.macro_instantiation; - const macro_declaration = macro_instantiation.declaration; - - const resolved_type = for (macro_declaration.type_arguments, macro_instantiation.type_arguments) |t, result_type| { - if (t == expected_type) { - result_type.resolve(module); - break result_type; - } - } else unreachable; - analysis.type = resolved_type; - @trap(); - }, - else => {}, - }; - // .unresolved => blk: { - // // TODO: nest macros - // }, - - const value_type = switch (value.bb) { - .unary => |unary| b: { - if (unary.id.is_boolean()) { - module.analyze_value_type(unary.value, .{}); - const boolean_type = module.integer_type(1, false); - module.typecheck(analysis, boolean_type); - break :b boolean_type; - } else { - module.analyze_value_type(unary.value, analysis); - const result_type = unary.value.type.?; - module.typecheck(analysis, result_type); - - break :b result_type; - } - }, - .binary => |binary| blk: { - const is_boolean = binary.id.is_boolean(); - - module.analyze_binary(binary.left, binary.right, is_boolean, analysis); - module.check_types(binary.left.type.?, binary.right.type.?); - - const result_type = if (is_boolean) module.integer_type(1, false) else binary.left.type.?; - module.typecheck(analysis, result_type); - break :blk result_type; - }, - .constant_integer => |constant_integer| blk: { - const expected_type = analysis.type orelse module.report_error(); - expected_type.resolve(module); - const et = switch (expected_type.bb) { - .alias => b: { - var it = expected_type; - while (it.bb == .alias) { - it = it.bb.alias.type; - } - break :b it; - }, - else => expected_type, - }; - const ty = switch (et.bb) { - .integer => |integer| switch (constant_integer.signed) { - true => { - if (!integer.signed) { - module.report_error(); - } - - @trap(); - }, - false => { - const bit_count = integer.bit_count; - const max_value = if (bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(bit_count - @intFromBool(integer.signed))) - 1; - - if (constant_integer.value > max_value) { - module.report_error(); - } - - break :blk expected_type; - }, - }, - .pointer => module.integer_type(64, false), - else => @trap(), - }; - module.typecheck(analysis, ty); - break :blk ty; - }, - .variable_reference => |variable| b: { - const ty = switch (value.kind) { - .left => variable.storage.?.type.?, - .right => variable.type.?, - }; - module.typecheck(analysis, ty); - break :b ty; - }, - .@"unreachable" => module.noreturn_type, - .call => |*call| blk: { - module.analyze_value_type(call.callable, .{}); - call.function_type = switch (call.callable.bb) { - .variable_reference => |variable| switch (variable.type.?.bb) { - .function => variable.type.?, - .pointer => |pointer| switch (pointer.type.bb) { - .function => pointer.type, - else => @trap(), - }, - else => @trap(), - }, - else => @trap(), - }; - - const declaration_argument_types = call.function_type.bb.function.semantic_argument_types; - - switch (call.function_type.bb.function.is_var_args) { - true => if (call.arguments.len < declaration_argument_types.len) { - module.report_error(); - }, - false => if (call.arguments.len != declaration_argument_types.len) { - module.report_error(); - }, - } - - for (declaration_argument_types, call.arguments[0..declaration_argument_types.len]) |argument_type, call_argument| { - module.analyze_value_type(call_argument, .{ .type = argument_type }); - module.check_types(argument_type, call_argument.type.?); - } - - for (call.arguments[declaration_argument_types.len..]) |call_argument| { - module.analyze_value_type(call_argument, .{}); - } - - const semantic_return_type = call.function_type.bb.function.semantic_return_type; - module.typecheck(analysis, semantic_return_type); - break :blk semantic_return_type; - }, - .intrinsic => |intrinsic| switch (intrinsic) { - .alignof => |ty| blk: { - const expected_type = analysis.type orelse module.report_error(); - // TODO - if (expected_type.bb != .integer) { - module.report_error(); - } - - const alignment = ty.get_byte_alignment(); - - const max_value = if (expected_type.bb.integer.bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(expected_type.bb.integer.bit_count - @intFromBool(expected_type.bb.integer.signed))) - 1; - if (alignment > max_value) { - module.report_error(); - } - - break :blk expected_type; - }, - .byte_size => |ty| blk: { - const expected_type = analysis.type orelse module.report_error(); - // TODO - if (expected_type.bb != .integer) { - module.report_error(); - } - - const size = ty.get_byte_size(); - const max_value = if (expected_type.bb.integer.bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(expected_type.bb.integer.bit_count - @intFromBool(expected_type.bb.integer.signed))) - 1; - if (size > max_value) { - module.report_error(); - } - - break :blk expected_type; - }, - .enum_name => |enum_value| blk: { - const string_type = module.get_slice_type(.{ .type = module.integer_type(8, false) }); - module.typecheck(analysis, string_type); - module.analyze_value_type(enum_value, .{}); - const enum_type = enum_value.type.?; - switch (enum_type.bb) { - .enumerator => |*enumerator| { - const enum_array_name_global = module.get_enum_name_array_global(enum_value.type.?); - if (enumerator.enum_to_string == null) { - const current_block = module.llvm.builder.get_insert_block(); - const llvm_function_type = llvm.Type.Function.get(string_type.llvm.memory.?, &.{enum_type.llvm.abi.?}, false); - const llvm_function_value = module.llvm.module.create_function(.{ - .name = module.arena.join_string(&.{ "enum_to_string.", enum_type.name }), - .linkage = .InternalLinkage, - .type = llvm_function_type, - }); - llvm_function_value.set_calling_convention(.fast); - var llvm_function_arguments: [1]*llvm.Argument = undefined; - llvm_function_value.get_arguments(&llvm_function_arguments); - const llvm_arg = llvm_function_arguments[0]; - - const function_entry_block = module.llvm.context.create_basic_block("entry", llvm_function_value); - module.llvm.builder.position_at_end(function_entry_block); - - const alloca = module.create_alloca(.{ - .type = string_type, - .name = "retval", - }); - - const return_block = module.llvm.context.create_basic_block("return_block", llvm_function_value); - const else_block = module.llvm.context.create_basic_block("else_block", llvm_function_value); - - const switch_i = module.llvm.builder.create_switch(llvm_arg.to_value(), else_block, @intCast(enumerator.fields.len)); - const backing_type = enumerator.backing_type.llvm.abi.?.to_integer(); - const uint64 = module.integer_type(64, false).llvm.abi.?.to_integer(); - - for (enumerator.fields, 0..) |field, field_index| { - const case_block = module.llvm.context.create_basic_block(module.arena.join_string(&.{ "case_block.", field.name }), llvm_function_value); - const case_value = backing_type.get_constant(field.value, 0).to_value(); - switch_i.add_case(case_value, case_block); - module.llvm.builder.position_at_end(case_block); - const case_value_result_pointer = module.llvm.builder.create_gep(.{ - .type = enum_array_name_global.variable.type.?.llvm.memory.?, - .aggregate = enum_array_name_global.variable.storage.?.llvm.?, - .indices = &.{ uint64.get_constant(0, 0).to_value(), uint64.get_constant(field_index, 0).to_value() }, - }); - const case_value_result = module.create_load(.{ - .type = string_type, - .value = case_value_result_pointer, - }); - _ = module.create_store(.{ - .type = string_type, - .destination_value = alloca, - .source_value = case_value_result, - }); - _ = module.llvm.builder.create_branch(return_block); - } - - module.llvm.builder.position_at_end(else_block); - _ = module.llvm.builder.create_unreachable(); - - module.llvm.builder.position_at_end(return_block); - const function_result = module.create_load(.{ - .type = string_type, - .value = alloca, - }); - - module.llvm.builder.create_ret(function_result); - - if (current_block) |bb| { - module.llvm.builder.position_at_end(bb); - } - - enumerator.enum_to_string = llvm_function_value; - } - - break :blk string_type; - }, - else => module.report_error(), - } - }, - .extend => |extended_value| blk: { - const expected_type = analysis.type orelse module.report_error(); - module.analyze_value_type(extended_value, .{}); - assert(extended_value.type != null); - const destination_type = expected_type; - const source_type = extended_value.type.?; - - if (source_type.get_bit_size() > destination_type.get_bit_size()) { - module.report_error(); - } else if (source_type.get_bit_size() == destination_type.get_bit_size() and source_type.is_signed() == destination_type.is_signed()) { - module.report_error(); - } - - break :blk expected_type; - }, - .integer_max => |integer_max_type| blk: { - if (integer_max_type.bb != .integer) { - module.report_error(); - } - if (analysis.type) |expected_type| { - if (expected_type.bb != .integer) { - module.report_error(); - } - } - - const result_type = if (analysis.type) |et| et else integer_max_type; - module.typecheck(analysis, result_type); - - break :blk result_type; - }, - .int_from_enum => |enum_value| blk: { - module.analyze_value_type(enum_value, .{}); - if (enum_value.type.?.bb != .enumerator) { - module.report_error(); - } - - const enum_backing_type = enum_value.type.?.bb.enumerator.backing_type; - module.typecheck(analysis, enum_backing_type); - break :blk enum_backing_type; - }, - .int_from_pointer => |pointer_value| blk: { - module.analyze_value_type(pointer_value, .{}); - assert(pointer_value.type != null); - if (pointer_value.type.?.bb != .pointer) { - module.report_error(); - } - - const result_type = module.integer_type(64, false); - module.typecheck(analysis, result_type); - - break :blk result_type; - }, - .pointer_cast => |pointer_value| blk: { - const expected_type = analysis.type orelse module.report_error(); - if (expected_type.bb != .pointer) { - module.report_error(); - } - module.analyze_value_type(pointer_value, .{}); - const pointer_type = pointer_value.type orelse module.report_error(); - - if (pointer_type == expected_type) { - module.report_error(); - } - - if (pointer_type.bb != .pointer) { - module.report_error(); - } - - break :blk expected_type; - }, - .select => |select| blk: { - module.analyze_value_type(select.condition, .{}); - const is_boolean = false; // This indicates that the result type must not be a boolean type - module.analyze_binary(select.true_value, select.false_value, is_boolean, analysis); - - const left_type = select.true_value.type.?; - const right_type = select.false_value.type.?; - module.check_types(left_type, right_type); - assert(left_type == right_type); - const result_type = left_type; - module.typecheck(analysis, result_type); - - break :blk result_type; - }, - .string_to_enum => |string_to_enum| blk: { - if (string_to_enum.enum_type.bb != .enumerator) { - module.report_error(); - } - - if (string_to_enum.enum_type.bb.enumerator.string_to_enum == null) { - const fields = string_to_enum.enum_type.bb.enumerator.fields; - const array_element_count = fields.len; - - const insert_block = module.llvm.builder.get_insert_block(); - defer module.llvm.builder.position_at_end(insert_block.?); - - const uint1 = module.integer_type(1, false); - uint1.resolve(module); - const uint8 = module.integer_type(8, false); - uint8.resolve(module); - - const alignment = string_to_enum.enum_type.get_byte_alignment(); - const byte_size = lib.align_forward_u64(string_to_enum.enum_type.get_byte_size() + 1, alignment); - - const struct_fields = module.arena.allocate(Type.Struct.Field, 2); - struct_fields[0] = .{ - .bit_offset = 0, - .line = 0, - .type = string_to_enum.enum_type, - .name = "enum_value", - .byte_offset = 0, - }; - struct_fields[1] = .{ - .byte_offset = string_to_enum.enum_type.get_byte_size(), - .bit_offset = string_to_enum.enum_type.get_byte_size() * 8, - .line = 0, - .type = uint1, - .name = "is_valid", - }; - - const struct_type = module.types.append(.{ - .name = "string_to_enum", - .bb = .{ - .structure = .{ - .fields = struct_fields, - .byte_size = byte_size, - .bit_size = byte_size * 8, - .byte_alignment = alignment, - .bit_alignment = alignment * 8, - .line = 0, - .is_slice = false, - }, - }, - }); - struct_type.resolve(module); - - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const llvm_function_type = llvm.Type.Function.get(struct_type.llvm.abi.?, &.{ module.llvm.pointer_type, uint64.llvm.abi.? }, false); - const slice_struct_type = module.get_slice_type(.{ .type = uint8 }); - - const llvm_function_value = module.llvm.module.create_function(.{ - .name = module.arena.join_string(&.{ "string_to_enum.", string_to_enum.enum_type.name }), - .linkage = .InternalLinkage, - .type = llvm_function_type, - }); - llvm_function_value.set_calling_convention(.fast); - - const name_array_global = module.get_enum_name_array_global(string_to_enum.enum_type); - - var value_constant_buffer: [64]*llvm.Constant = undefined; - - for (string_to_enum.enum_type.bb.enumerator.fields, 0..) |field, field_index| { - const value_global = string_to_enum.enum_type.llvm.memory.?.to_integer().get_constant(field.value, 0); - value_constant_buffer[field_index] = value_global.to_constant(); - } - - const value_array = string_to_enum.enum_type.llvm.memory.?.get_constant_array(value_constant_buffer[0..array_element_count]); - const value_array_variable_type = string_to_enum.enum_type.llvm.memory.?.get_array_type(array_element_count); - const value_array_variable = module.llvm.module.create_global_variable(.{ - .type = value_array_variable_type.to_type(), - .linkage = .InternalLinkage, - .initial_value = value_array, - .name = "value.array.enum", - }); - value_array_variable.to_value().set_alignment(string_to_enum.enum_type.get_byte_alignment()); - - const function_entry_block = module.llvm.context.create_basic_block("entry", llvm_function_value); - const return_block = module.llvm.context.create_basic_block("return_block", llvm_function_value); - const loop_entry_block = module.llvm.context.create_basic_block("loop.entry", llvm_function_value); - const loop_body_block = module.llvm.context.create_basic_block("loop.body", llvm_function_value); - const loop_exit_block = module.llvm.context.create_basic_block("loop.exit", llvm_function_value); - module.llvm.builder.position_at_end(function_entry_block); - - var arguments: [2]*llvm.Argument = undefined; - llvm_function_value.get_arguments(&arguments); - - const return_value_alloca = module.create_alloca(.{ - .type = string_to_enum.enum_type, - .name = "retval", - }); - const return_boolean_alloca = module.create_alloca(.{ - .type = uint8, - .name = "retbool", - }); - const index_alloca = module.create_alloca(.{ - .type = uint64, - .name = "idx", - }); - _ = module.create_store(.{ - .type = uint64, - .source_value = uint64.llvm.abi.?.get_zero().to_value(), - .destination_value = index_alloca, - }); - const slice_pointer = arguments[0].to_value(); - const slice_length = arguments[1].to_value(); - _ = module.llvm.builder.create_branch(loop_entry_block); - - module.llvm.builder.position_at_end(loop_entry_block); - const index_load = module.create_load(.{ - .type = uint64, - .value = index_alloca, - }); - const loop_cmp = module.llvm.builder.create_integer_compare(.ult, index_load, uint64.llvm.abi.?.to_integer().get_constant(array_element_count, 0).to_value()); - _ = module.llvm.builder.create_conditional_branch(loop_cmp, loop_body_block, loop_exit_block); - - module.llvm.builder.position_at_end(loop_body_block); - - const body_index_load = module.create_load(.{ - .type = uint64, - .value = index_alloca, - }); - const uint64_zero = uint64.llvm.abi.?.get_zero().to_value(); - - const array_element_pointer = module.llvm.builder.create_gep(.{ - .type = name_array_global.variable.type.?.llvm.memory.?, - .aggregate = name_array_global.variable.storage.?.llvm.?, - .indices = &.{ uint64_zero, body_index_load }, - }); - - const element_length_pointer = module.llvm.builder.create_struct_gep(slice_struct_type.llvm.abi.?.to_struct(), array_element_pointer, 1); - const element_length = module.create_load(.{ - .type = uint64, - .value = element_length_pointer, - }); - - const length_comparison = module.llvm.builder.create_integer_compare(.eq, slice_length, element_length); - - const length_match_block = module.llvm.context.create_basic_block("length.match", llvm_function_value); - const length_mismatch_block = module.llvm.context.create_basic_block("length.mismatch", llvm_function_value); - _ = module.llvm.builder.create_conditional_branch(length_comparison, length_match_block, length_mismatch_block); - - module.llvm.builder.position_at_end(length_match_block); - const s32 = module.integer_type(32, true); - s32.resolve(module); - const memcmp = if (module.llvm.memcmp) |memcmp| memcmp else b: { - if (module.llvm.module.get_named_function("memcmp")) |memcmp| { - module.llvm.memcmp = memcmp; - break :b memcmp; - } else { - const memcmp = module.llvm.module.create_function(.{ - .name = "memcmp", - .linkage = .ExternalLinkage, - .type = llvm.Type.Function.get(s32.llvm.abi.?, &.{ module.llvm.pointer_type, module.llvm.pointer_type, uint64.llvm.abi.? }, false), - }); - module.llvm.memcmp = memcmp; - break :b memcmp; - } - }; - - const length_index_load = module.create_load(.{ - .type = uint64, - .value = index_alloca, - }); - const length_array_element_pointer = module.llvm.builder.create_gep(.{ - .type = name_array_global.variable.type.?.llvm.memory.?, - .aggregate = name_array_global.variable.storage.?.llvm.?, - .indices = &.{ uint64_zero, length_index_load }, - }); - const element_pointer_pointer = module.llvm.builder.create_struct_gep(slice_struct_type.llvm.abi.?.to_struct(), length_array_element_pointer, 0); - const element_pointer = module.create_load(.{ - .type = module.get_pointer_type(.{ .type = uint8 }), - .value = element_pointer_pointer, - }); - const memcmp_return_result = module.llvm.builder.create_call(memcmp.get_type(), memcmp.to_value(), &.{ slice_pointer, element_pointer, slice_length }); - const content_comparison = module.llvm.builder.create_integer_compare(.eq, memcmp_return_result, s32.llvm.abi.?.get_zero().to_value()); - const content_match_block = module.llvm.context.create_basic_block("content.match", llvm_function_value); - _ = module.llvm.builder.create_conditional_branch(content_comparison, content_match_block, length_mismatch_block); - - module.llvm.builder.position_at_end(content_match_block); - const content_index_load = module.create_load(.{ - .type = uint64, - .value = index_alloca, - }); - const value_array_element_pointer = module.llvm.builder.create_gep(.{ - .type = value_array_variable_type.to_type(), - .aggregate = value_array_variable.to_value(), - .indices = &.{ uint64_zero, content_index_load }, - }); - const enum_value_load = module.create_load(.{ - .type = string_to_enum.enum_type, - .value = value_array_element_pointer, - }); - _ = module.create_store(.{ - .type = string_to_enum.enum_type, - .source_value = enum_value_load, - .destination_value = return_value_alloca, - }); - _ = module.create_store(.{ - .type = uint8, - .source_value = uint8.llvm.abi.?.to_integer().get_constant(1, 0).to_value(), - .destination_value = return_boolean_alloca, - }); - _ = module.llvm.builder.create_branch(return_block); - - module.llvm.builder.position_at_end(length_mismatch_block); - const inc_index_load = module.create_load(.{ - .type = uint64, - .value = index_alloca, - }); - const inc = module.llvm.builder.create_add(inc_index_load, uint64.llvm.abi.?.to_integer().get_constant(1, 0).to_value()); - _ = module.create_store(.{ - .type = uint64, - .source_value = inc, - .destination_value = index_alloca, - }); - _ = module.llvm.builder.create_branch(loop_entry_block); - - module.llvm.builder.position_at_end(loop_exit_block); - _ = module.create_store(.{ - .type = string_to_enum.enum_type, - .source_value = string_to_enum.enum_type.llvm.memory.?.get_zero().to_value(), - .destination_value = return_value_alloca, - }); - _ = module.create_store(.{ - .type = uint8, - .source_value = uint8.llvm.memory.?.get_zero().to_value(), - .destination_value = return_boolean_alloca, - }); - _ = module.llvm.builder.create_branch(return_block); - - module.llvm.builder.position_at_end(return_block); - - const value_load = module.create_load(.{ - .type = string_to_enum.enum_type, - .value = return_value_alloca, - .type_kind = .memory, - }); - var return_value = module.llvm.builder.create_insert_value(struct_type.llvm.memory.?.get_poison(), value_load, 0); - const bool_load = module.create_load(.{ - .type = uint8, - .value = return_boolean_alloca, - }); - return_value = module.llvm.builder.create_insert_value(return_value, bool_load, 1); - - module.llvm.builder.create_ret(return_value); - - string_to_enum.enum_type.bb.enumerator.string_to_enum = .{ - .function = llvm_function_value, - .struct_type = struct_type, - }; - } - - const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable; - - const result_type = s2e.struct_type; - module.typecheck(analysis, result_type); - - const string_type = module.get_slice_type(.{ .type = module.integer_type(8, false) }); - - module.analyze_value_type(string_to_enum.string_value, .{ .type = string_type }); - - break :blk result_type; - }, - .truncate => |value_to_truncate| blk: { - // TODO: better typechecking - const expected_type = analysis.type orelse module.report_error(); - module.analyze_value_type(value_to_truncate, .{}); - if (expected_type.get_bit_size() >= value_to_truncate.type.?.get_bit_size()) { - module.report_error(); - } - break :blk expected_type; - }, - .trap => module.noreturn_type, - .va_arg => |va_arg| blk: { - module.analyze_value_type(va_arg.list, .{ - .type = module.get_pointer_type(.{ .type = module.get_va_list_type() }), - }); - const result_type = va_arg.type; - module.typecheck(analysis, result_type); - break :blk result_type; - }, - .va_end => |va_list| blk: { - module.analyze_value_type(va_list, .{ - .type = module.get_pointer_type(.{ .type = module.get_va_list_type() }), - }); - const result_type = module.void_type; - module.typecheck(analysis, result_type); - break :blk result_type; - }, - .va_start => blk: { - const result_type = module.get_va_list_type(); - module.typecheck(analysis, result_type); - break :blk result_type; - }, - else => @trap(), - }, - .dereference => |dereferenced_value| blk: { - module.analyze_value_type(dereferenced_value, .{}); - - const dereference_type = switch (value.kind) { - .left => @trap(), - .right => dereferenced_value.type.?.bb.pointer.type, - }; - - module.typecheck(analysis, dereference_type); - - break :blk dereference_type; - }, - .slice_expression => |slice_expression| blk: { - if (slice_expression.array_like.kind != .left) { - module.report_error(); - } - module.analyze_value_type(slice_expression.array_like, .{}); - - const pointer_type = slice_expression.array_like.type.?; - if (pointer_type.bb != .pointer) { - module.report_error(); - } - - const sliceable_type = pointer_type.bb.pointer.type; - - const element_type = switch (sliceable_type.bb) { - .pointer => |pointer| pointer.type, - .structure => |structure| b: { - if (!structure.is_slice) { - module.report_error(); - } - break :b structure.fields[0].type.bb.pointer.type; - }, - .array => |array| array.element_type, - else => @trap(), - }; - - const slice_type = module.get_slice_type(.{ .type = element_type }); - - module.typecheck(analysis, slice_type); - - const index_type = module.integer_type(64, false); - - if (slice_expression.start) |start| { - module.analyze_value_type(start, .{ .type = index_type }); - - if (start.type.?.bb != .integer) { - module.report_error(); - } - } - - if (slice_expression.end) |end| { - module.analyze_value_type(end, .{ .type = index_type }); - - if (end.type.?.bb != .integer) { - module.report_error(); - } - } - - break :blk slice_type; - }, - .field_access => |field_access| blk: { - module.analyze_value_type(field_access.aggregate, .{}); - const field_name = field_access.field; - - const field_type = switch (field_access.aggregate.kind) { - .left => left: { - if (field_access.aggregate.type.?.bb != .pointer) { - module.report_error(); - } - const pty = field_access.aggregate.type.?.bb.pointer.type; - const ty = module.fully_resolve_alias(switch (pty.bb) { - .pointer => |pointer| pointer.type, - else => pty, - }); - - const result_type = switch (ty.bb) { - .structure => |structure| s: { - const field_type = for (structure.fields) |*field| { - if (lib.string.equal(field_name, field.name)) { - break field.type; - } - } else { - module.report_error(); - }; - - break :s switch (value.kind) { - .left => module.get_pointer_type(.{ .type = field_type }), - .right => field_type, - }; - }, - .@"union" => |union_type| s: { - const field_type = for (union_type.fields) |*field| { - if (lib.string.equal(field_name, field.name)) { - break field.type; - } - } else { - module.report_error(); - }; - - break :s switch (value.kind) { - .left => module.get_pointer_type(.{ .type = field_type }), - .right => field_type, - }; - }, - .bits => |bits| b: { - const field_type = for (bits.fields) |*field| { - if (lib.string.equal(field_name, field.name)) { - break field.type; - } - } else { - module.report_error(); - }; - break :b switch (value.kind) { - .left => module.report_error(), - .right => field_type, - }; - }, - .array => a: { - if (analysis.type) |expected_type| { - if (expected_type.bb != .integer) { - module.report_error(); - } - // TODO: see if the count fits into the integer type - break :a expected_type; - } else { - @trap(); - } - }, - .pointer => module.report_error(), - else => @trap(), - }; - break :left result_type; - }, - .right => module.report_error(), - }; - - module.typecheck(analysis, field_type); - - break :blk field_type; - }, - .array_expression => |array_expression| blk: { - module.analyze_value_type(array_expression.index, .{ - .type = module.integer_type(64, false), - }); - - // Overwrite side of the expression - array_expression.array_like.kind = .left; - module.analyze_value_type(array_expression.array_like, .{}); - const element_type = switch (array_expression.array_like.kind) { - .left => switch (array_expression.array_like.type.?.bb) { - .pointer => |pointer| switch (pointer.type.bb) { - .array => |array| array.element_type, - .structure => |structure| b: { - if (!structure.is_slice) { - module.report_error(); - } - break :b structure.fields[0].type.bb.pointer.type; - }, - .pointer => |p| p.type, - else => @trap(), - }, - else => module.report_error(), - }, - .right => @trap(), - }; - - const result_type = switch (value.kind) { - .left => module.get_pointer_type(.{ .type = element_type }), - .right => element_type, - }; - - module.typecheck(analysis, result_type); - - break :blk result_type; - }, - .aggregate_initialization => |*aggregate_initialization| switch (module.fully_resolve_alias(analysis.type orelse module.report_error()).bb) { - .bits => |bits| blk: { - var is_ordered = true; - var is_constant = true; - for (aggregate_initialization.names, aggregate_initialization.values, 0..) |field_name, field_value, initialization_index| { - const declaration_index = for (bits.fields, 0..) |field, declaration_index| { - if (lib.string.equal(field.name, field_name)) { - break declaration_index; - } - } else module.report_error(); - is_ordered = is_ordered and declaration_index == initialization_index; - const field = &bits.fields[declaration_index]; - const declaration_type = field.type; - module.analyze_value_type(field_value, .{ .type = declaration_type }); - is_constant = is_constant and field_value.is_constant(); - } - - aggregate_initialization.is_constant = is_constant; - - break :blk analysis.type.?; - }, - .structure => |structure| blk: { - var is_ordered = true; - var is_constant = true; - for (aggregate_initialization.names, aggregate_initialization.values, 0..) |field_name, field_value, initialization_index| { - const declaration_index = for (structure.fields, 0..) |field, declaration_index| { - if (lib.string.equal(field.name, field_name)) { - break declaration_index; - } - } else module.report_error(); - is_ordered = is_ordered and declaration_index == initialization_index; - const field = &structure.fields[declaration_index]; - const declaration_type = field.type; - module.analyze_value_type(field_value, .{ .type = declaration_type }); - is_constant = is_constant and field_value.is_constant(); - } - - aggregate_initialization.is_constant = is_constant and is_ordered; - - break :blk analysis.type.?; - }, - .@"union" => |union_type| blk: { - if (aggregate_initialization.values.len != 1) { - module.report_error(); - } - - const initialization_value = aggregate_initialization.values[0]; - assert(aggregate_initialization.names.len == 1); - const initialization_name = aggregate_initialization.names[0]; - - const field = for (union_type.fields) |*field| { - if (lib.string.equal(field.name, initialization_name)) { - break field; - } - } else module.report_error(); - - module.analyze_value_type(initialization_value, .{ .type = field.type }); - - break :blk analysis.type.?; - }, - else => @trap(), - }, - .enum_literal => |enum_literal| blk: { - const expected_type = analysis.type orelse module.report_error(); - _ = enum_literal; - if (expected_type.bb != .enumerator) { - module.report_error(); - } - break :blk expected_type; - }, - .array_initialization => |*array_initialization| blk: { - if (analysis.type) |expected_type| switch (expected_type.bb) { - .array => |*array| { - if (array.element_count == 0) { - array.element_count = array_initialization.values.len; - assert(lib.string.equal(expected_type.name, "")); - expected_type.name = array_type_name(module.arena, array.element_type, array.element_count); - } else { - if (array.element_count != array_initialization.values.len) { - module.report_error(); - } - } - - var is_constant = true; - for (array_initialization.values) |v| { - module.analyze_value_type(v, .{ - .type = array.element_type, - }); - is_constant = is_constant and v.is_constant(); - } - - array_initialization.is_constant = is_constant; - - break :blk switch (value.kind) { - .left => module.report_error(), // TODO: possible? - .right => expected_type, - }; - }, - else => module.report_error(), - } else { - if (array_initialization.values.len == 0) { - module.report_error(); - } - - var expected_type: ?*Type = null; - var is_constant = true; - - for (array_initialization.values) |v| { - module.analyze_value_type(v, .{ - .type = expected_type, - }); - - is_constant = is_constant and v.is_constant(); - - if (expected_type) |ty| { - if (ty != v.type.?) { - module.report_error(); - } - } else { - expected_type = v.type.?; - } - } - - const element_type = expected_type orelse module.report_error(); - const element_count = array_initialization.values.len; - - const array_type = module.get_array_type(element_type, element_count); - - break :blk switch (value.kind) { - .left => module.get_pointer_type(.{ .type = array_type }), - .right => array_type, - }; - } - }, - // TODO: further typechecking - .undefined => analysis.type orelse module.report_error(), - .string_literal => blk: { - const slice_type = module.get_slice_type(.{ .type = module.integer_type(8, false) }); - module.typecheck(analysis, slice_type); - break :blk slice_type; - }, - // TODO: further typecheck: avoid void, noreturn, etc - .zero => analysis.type orelse module.report_error(), - .macro_instantiation => |*macro_instantiation| blk: { - const current_function = module.current_function orelse module.report_error(); - module.current_function = null; - defer module.current_function = current_function; - - const current_macro_instantiation = module.current_macro_instantiation; - assert(current_macro_instantiation == null); - module.current_macro_instantiation = value; - defer module.current_macro_instantiation = current_macro_instantiation; - - const declaration = macro_instantiation.declaration; - - macro_instantiation.declaration_arguments = module.arena.allocate(*Local, declaration.arguments.len); - - const local_to_unit = true; - const is_definition = true; - const flags: llvm.DI.Flags = .{}; - const subprogram = if (module.has_debug_info) module.llvm.di_builder.create_function(module.scope.llvm.?, macro_instantiation.declaration.name, macro_instantiation.declaration.name, module.llvm.file, macro_instantiation.function_scope.line, null, local_to_unit, is_definition, macro_instantiation.function_scope.line, flags, module.build_mode.is_optimized()) else undefined; - macro_instantiation.function_scope.llvm = @ptrCast(subprogram); - - for (declaration.arguments, macro_instantiation.declaration_arguments) |old_argument, *new_argument| { - const argument = module.locals.add(); - argument.* = .{ - .variable = .{ - .initial_value = undefined, - .type = old_argument.variable.type, - .scope = ¯o_instantiation.function_scope, - .name = old_argument.variable.name, - .line = old_argument.variable.line, - .column = old_argument.variable.column, - }, - .argument_index = old_argument.argument_index, - }; - new_argument.* = argument; - } - - for (macro_instantiation.type_arguments, declaration.type_arguments) |*instantiation_type_argument_pointer, declaration_type_argument| { - assert(declaration_type_argument.bb == .unresolved); - const original_instantiation_type_argument = instantiation_type_argument_pointer.*; - const instantiation_type_argument = module.types.append(.{ - .name = declaration_type_argument.name, - .bb = .{ - .alias = .{ - .type = original_instantiation_type_argument, - .line = macro_instantiation.function_scope.line, - .scope = ¯o_instantiation.function_scope, - }, - }, - }); - instantiation_type_argument_pointer.* = instantiation_type_argument; - } - - macro_instantiation.return_type = module.resolve_type(declaration.return_type); - - for (macro_instantiation.declaration_arguments) |argument| { - argument.variable.type = module.resolve_type(argument.variable.type.?); - } - - if (macro_instantiation.instantiation_arguments.len != declaration.arguments.len) { - module.report_error(); - } - - const argument_count = macro_instantiation.declaration_arguments.len; - if (module.has_debug_info) { - for (macro_instantiation.instantiation_arguments, macro_instantiation.declaration_arguments) |instantiation_argument, declaration_argument| { - module.analyze_value_type(instantiation_argument, .{ .type = declaration_argument.variable.type.? }); - } - - var debug_argument_type_buffer: [64 + 1]*llvm.DI.Type = undefined; - const semantic_debug_argument_types = debug_argument_type_buffer[0 .. argument_count + 1]; - macro_instantiation.return_type.resolve(module); - semantic_debug_argument_types[0] = macro_instantiation.return_type.llvm.debug.?; - - for (macro_instantiation.declaration_arguments, semantic_debug_argument_types[1..][0..argument_count]) |declaration_argument, *debug_argument_type| { - declaration_argument.variable.type.?.resolve(module); - debug_argument_type.* = declaration_argument.variable.type.?.llvm.debug.?; - } - - module.llvm.builder.set_current_debug_location(null); - const subroutine_type_flags = llvm.DI.Flags{}; - const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, subroutine_type_flags); - assert(macro_instantiation.function_scope.llvm != null); - subprogram.replace_type(subroutine_type); - } - - value.bb.macro_instantiation.block = module.lexical_blocks.add(); - module.copy_block(¯o_instantiation.function_scope, .{ - .source = declaration.block, - .destination = value.bb.macro_instantiation.block, - }); - - const result_type = macro_instantiation.return_type; - result_type.resolve(module); - module.typecheck(analysis, result_type); - - if (!module.has_debug_info) { - for (macro_instantiation.declaration_arguments, macro_instantiation.instantiation_arguments) |declaration_argument, instantiation_argument| { - module.analyze_value_type(instantiation_argument, .{ .type = declaration_argument.variable.type }); - } - } - - break :blk result_type; - }, - else => @trap(), - }; - - value.type = value_type; - } - - pub fn get_enum_name_array_global(module: *Module, enum_type: *Type) *Global { - switch (enum_type.bb) { - .enumerator => |*enumerator| { - if (enumerator.name_array_global) |name_array| { - return name_array; - } else { - const fields = enumerator.fields; - var name_before: ?*llvm.GlobalVariable = null; - var name_constant_buffer: [64]*llvm.Constant = undefined; - const uint8 = module.integer_type(8, false); - const uint64 = module.integer_type(64, false); - uint8.resolve(module); - uint64.resolve(module); - - for (fields, 0..) |field, field_index| { - const null_terminate = true; - const name_global = module.llvm.module.create_global_variable(.{ - .type = uint8.llvm.abi.?.get_array_type(field.name.len + @intFromBool(null_terminate)).to_type(), - .linkage = .InternalLinkage, - .name = module.arena.join_string(&.{ "string.", enum_type.name, ".", field.name }), - .initial_value = module.llvm.context.get_constant_string(field.name, null_terminate), - .is_constant = true, - .before = name_before, - }); - name_before = name_global; - - const slice_constant = module.llvm.context.get_anonymous_constant_struct(&.{ - name_global.to_constant(), - uint64.llvm.abi.?.to_integer().get_constant(field.name.len, 0).to_constant(), - }, false); - name_constant_buffer[field_index] = slice_constant; - } - - const slice_struct_type = module.get_slice_type(.{ .type = uint8 }); - const array_element_count = fields.len; - - const name_array = slice_struct_type.llvm.abi.?.get_constant_array(name_constant_buffer[0..array_element_count]); - - const name_array_variable_type = slice_struct_type.llvm.abi.?.get_array_type(array_element_count); - - const name_array_variable = module.llvm.module.create_global_variable(.{ - .type = name_array_variable_type.to_type(), - .linkage = .InternalLinkage, - .initial_value = name_array, - .name = "name.array.enum", - }); - name_array_variable.to_value().set_alignment(slice_struct_type.get_byte_alignment()); - - const global_type = module.get_array_type(slice_struct_type, array_element_count); - global_type.resolve(module); - - const storage_type = module.get_pointer_type(.{ .type = global_type }); - storage_type.resolve(module); - - const global_storage = module.values.add(); - global_storage.* = .{ - .bb = .global, - .type = storage_type, - .llvm = name_array_variable.to_value(), - .kind = .left, - }; - - const global = module.globals.add(); - global.* = .{ - .variable = .{ - .storage = global_storage, - .initial_value = undefined, - .type = global_type, - .scope = &module.scope, - .name = module.arena.join_string(&.{ "name.array.enum.", enum_type.name }), - .line = 0, - .column = 0, - }, - .linkage = .internal, - }; - - enumerator.name_array_global = global; - - return global; - } - }, - else => unreachable, - } - } - - pub fn emit_slice_expression(module: *Module, value: *Value) struct { *llvm.Value, *llvm.Value } { - const value_type = value.type.?; - assert(value_type.is_slice()); - - const slice_pointer_type = value_type.bb.structure.fields[0].type; - const slice_element_type = slice_pointer_type.bb.pointer.type; - - const index_type = module.integer_type(64, false); - index_type.resolve(module); - const llvm_integer_index_type = index_type.llvm.abi.?.to_integer(); - const index_zero = llvm_integer_index_type.get_constant(0, 0).to_value(); - - switch (value.bb) { - .slice_expression => |slice_expression| { - assert(slice_expression.array_like.kind == .left); - module.emit_value(slice_expression.array_like, .memory); - - const pointer_type = slice_expression.array_like.type.?; - assert(pointer_type.bb == .pointer); - const sliceable_type = pointer_type.bb.pointer.type; - const has_start = if (slice_expression.start) |start| switch (start.bb) { - .constant_integer => |constant_integer| constant_integer.value != 0, - else => true, - } else false; - - if (slice_expression.start) |start| { - module.emit_value(start, .memory); - } - - if (slice_expression.end) |end| { - module.emit_value(end, .memory); - } - - switch (sliceable_type.bb) { - .pointer => |pointer| { - const pointer_load = module.create_load(.{ - .type = sliceable_type, - .value = slice_expression.array_like.llvm.?, - }); - const slice_pointer = switch (has_start) { - true => module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.memory.?, - .aggregate = pointer_load, - .indices = &.{slice_expression.start.?.llvm.?}, - }), - false => pointer_load, - }; - const slice_length = if (has_start) module.llvm.builder.create_sub(slice_expression.end.?.llvm.?, slice_expression.start.?.llvm.?) else slice_expression.end.?.llvm.?; - return .{ slice_pointer, slice_length }; - }, - .structure => |structure| { - assert(structure.is_slice); - - const slice_load = module.create_load(.{ - .type = sliceable_type, - .value = slice_expression.array_like.llvm.?, - }); - const old_slice_pointer = module.llvm.builder.create_extract_value(slice_load, 0); - - const slice_pointer = switch (has_start) { - true => module.llvm.builder.create_gep(.{ - .type = slice_element_type.llvm.memory.?, - .aggregate = old_slice_pointer, - .indices = &.{slice_expression.start.?.llvm.?}, - }), - false => old_slice_pointer, - }; - - const slice_end = if (slice_expression.end) |end| end.llvm.? else module.llvm.builder.create_extract_value(slice_load, 1); - const slice_length = if (has_start) module.llvm.builder.create_sub(slice_end, slice_expression.start.?.llvm.?) else slice_end; - - return .{ slice_pointer, slice_length }; - }, - .array => |array| { - assert(array.element_type == slice_element_type); - const slice_pointer = switch (has_start) { - true => module.llvm.builder.create_gep(.{ - .type = sliceable_type.llvm.memory.?, - .aggregate = slice_expression.array_like.llvm.?, - .indices = &.{ index_zero, slice_expression.start.?.llvm.? }, - }), - false => slice_expression.array_like.llvm.?, - }; - - const slice_length = if (has_start) { - @trap(); - } else if (slice_expression.end) |end| end.llvm.? else llvm_integer_index_type.get_constant(array.element_count, 0).to_value(); - - return .{ slice_pointer, slice_length }; - }, - else => @trap(), - } - }, - else => unreachable, - } - } - - pub fn emit_value(module: *Module, value: *Value, type_kind: Type.Kind) void { - const value_type = value.type orelse unreachable; - const resolved_type = module.fully_resolve_alias(value_type); - assert(value.llvm == null); - value_type.resolve(module); - - const must_be_constant = module.current_function == null and module.current_macro_instantiation == null; - - const llvm_value: *llvm.Value = switch (value.bb) { - .constant_integer => |constant_integer| value_type.get_llvm(type_kind).to_integer().get_constant(constant_integer.value, @intFromBool(constant_integer.signed)).to_value(), - .unary => |unary| switch (unary.id) { - .@"-" => blk: { - const unary_value = unary.value.llvm orelse b: { - module.emit_value(unary.value, type_kind); - break :b unary.value.llvm orelse unreachable; - }; - break :blk module.negate_llvm_value(unary_value, unary.value.is_constant()); - }, - .@"&" => blk: { - assert(value_type == unary.value.type); - module.emit_value(unary.value, type_kind); - break :blk unary.value.llvm orelse unreachable; - }, - .@"!" => switch (unary.value.type == value_type) { - true => b: { - module.emit_value(unary.value, type_kind); - break :b module.llvm.builder.create_not(unary.value.llvm.?); - }, - false => switch (unary.value.type.?.bb) { - .pointer => b: { - module.emit_value(unary.value, type_kind); - break :b module.llvm.builder.create_integer_compare(.eq, unary.value.llvm.?, unary.value.type.?.llvm.abi.?.get_zero().to_value()); - }, - else => @trap(), - }, - }, - .@"~" => b: { - module.emit_value(unary.value, type_kind); - break :b module.llvm.builder.create_not(unary.value.llvm.?); - }, - else => @trap(), - }, - .binary => |binary| blk: { - if (binary.id.is_shortcircuiting()) { - const ShortcircuitingOperation = enum { - @"and", - @"or", - }; - const op: ShortcircuitingOperation = switch (binary.id) { - .@"and?" => .@"and", - .@"or?" => .@"or", - else => unreachable, - }; - const left = if (binary.left.llvm) |left_llvm| left_llvm else b: { - module.emit_value(binary.left, .abi); - break :b binary.left.llvm orelse unreachable; - }; - const left_condition = switch (binary.left.type.?.bb) { - .integer => |integer| switch (integer.bit_count) { - 1 => left, - else => @trap(), - }, - else => @trap(), - }; - const llvm_function = module.current_function.?.variable.storage.?.llvm.?.to_function(); - const current_bb = module.llvm.builder.get_insert_block().?; - const right_block = module.llvm.context.create_basic_block(switch (op) { - inline else => |o| @tagName(o) ++ ".right", - }, llvm_function); - const end_block = module.llvm.context.create_basic_block(switch (op) { - inline else => |o| @tagName(o) ++ ".end", - }, llvm_function); - _ = module.llvm.builder.create_conditional_branch(left_condition, switch (op) { - .@"and" => right_block, - .@"or" => end_block, - }, switch (op) { - .@"and" => end_block, - .@"or" => right_block, - }); - - module.llvm.builder.position_at_end(right_block); - const right = if (binary.right.llvm) |right_llvm| right_llvm else b: { - module.emit_value(binary.right, .abi); - break :b binary.right.llvm orelse unreachable; - }; - const right_condition = switch (binary.left.type.?.bb) { - .integer => |integer| switch (integer.bit_count) { - 1 => right, - else => @trap(), - }, - else => @trap(), - }; - _ = module.llvm.builder.create_branch(end_block); - module.llvm.builder.position_at_end(end_block); - const boolean_type = module.integer_type(1, false).llvm.abi.?; - - const phi = module.llvm.builder.create_phi(boolean_type); - phi.add_incoming(&.{ switch (op) { - .@"and" => boolean_type.get_zero().to_value(), - .@"or" => boolean_type.to_integer().get_constant(1, 0).to_value(), - }, right_condition }, &.{ current_bb, right_block }); - - break :blk switch (type_kind) { - .abi => phi.to_value(), - .memory => @trap(), - }; - } else { - const left = if (binary.left.llvm) |left_llvm| left_llvm else b: { - module.emit_value(binary.left, .abi); - break :b binary.left.llvm orelse unreachable; - }; - const right = if (binary.right.llvm) |right_llvm| right_llvm else b: { - module.emit_value(binary.right, .abi); - break :b binary.right.llvm orelse unreachable; - }; - const result = switch (resolved_type.bb) { - .integer => |integer| switch (binary.id) { - .@"+" => module.llvm.builder.create_add(left, right), - .@"-" => module.llvm.builder.create_sub(left, right), - .@"*" => module.llvm.builder.create_mul(left, right), - .@"/" => switch (integer.signed) { - true => module.llvm.builder.create_sdiv(left, right), - false => module.llvm.builder.create_udiv(left, right), - }, - .@"%" => switch (integer.signed) { - true => module.llvm.builder.create_srem(left, right), - false => module.llvm.builder.create_urem(left, right), - }, - .@"&", .@"and" => module.llvm.builder.create_and(left, right), - .@"|", .@"or" => module.llvm.builder.create_or(left, right), - .@"^" => module.llvm.builder.create_xor(left, right), - .@"<<" => module.llvm.builder.create_shl(left, right), - .@">>" => switch (integer.signed) { - true => module.llvm.builder.create_ashr(left, right), - false => module.llvm.builder.create_lshr(left, right), - }, - .@"==" => module.llvm.builder.create_integer_compare(.eq, left, right), - .@"!=" => module.llvm.builder.create_integer_compare(.ne, left, right), - .@">" => switch (integer.signed) { - true => module.llvm.builder.create_integer_compare(.sgt, left, right), - false => module.llvm.builder.create_integer_compare(.ugt, left, right), - }, - .@"<" => switch (integer.signed) { - true => module.llvm.builder.create_integer_compare(.slt, left, right), - false => module.llvm.builder.create_integer_compare(.ult, left, right), - }, - .@">=" => switch (integer.signed) { - true => module.llvm.builder.create_integer_compare(.sge, left, right), - false => module.llvm.builder.create_integer_compare(.uge, left, right), - }, - .@"<=" => switch (integer.signed) { - true => module.llvm.builder.create_integer_compare(.sle, left, right), - false => module.llvm.builder.create_integer_compare(.ule, left, right), - }, - else => module.report_error(), - }, - .pointer => |pointer| switch (b: { - pointer.type.resolve(module); - break :b binary.id; - }) { - .@"+" => module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.abi.?, - .aggregate = left, - .indices = &.{right}, - }), - .@"-" => module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.abi.?, - .aggregate = left, - .indices = &.{module.negate_llvm_value(right, binary.right.is_constant())}, - }), - else => module.report_error(), - }, - else => @trap(), - }; - break :blk result; - } - }, - .variable_reference => |variable| switch (value.kind) { - .left => switch (variable.storage.?.type == value_type) { - true => variable.storage.?.llvm.?, - false => switch (value_type.bb) { - .structure => |structure| switch (structure.is_slice) { - true => switch (variable.storage.?.type.?.bb) { - .pointer => |pointer| switch (pointer.type.bb) { - .array => |array| blk: { - value.kind = .right; // TODO: TODO: TODO: WARN: is this wise? - const slice_poison = value_type.llvm.memory.?.get_poison(); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const pointer_insert = module.llvm.builder.create_insert_value(slice_poison, variable.storage.?.llvm.?, 0); - const length_insert = module.llvm.builder.create_insert_value(pointer_insert, uint64.llvm.abi.?.to_integer().get_constant(array.element_count, @intFromBool(false)).to_value(), 1); - const slice_value = length_insert; - break :blk slice_value; - }, - else => @trap(), - }, - else => @trap(), - }, - false => @trap(), - }, - else => module.report_error(), - }, - }, - .right => switch (variable.type == value_type) { - true => switch (must_be_constant) { - true => b: { - if (variable.scope.kind != .global) { - module.report_error(); - } - break :b variable.initial_value.llvm.?; - }, - false => switch (value_type.get_evaluation_kind()) { - .scalar => module.create_load(.{ - .type = value_type, - .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, - }), - // TODO: this might be wrong - .aggregate => module.create_load(.{ - .type = value_type, - .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, - }), - .complex => @trap(), - }, - }, - false => module.report_error(), - }, - }, - .intrinsic => |intrinsic| switch (intrinsic) { - .alignof => |ty| blk: { - const alignment = ty.get_byte_alignment(); - const constant_integer = value_type.llvm.abi.?.to_integer().get_constant(alignment, @intFromBool(false)); - break :blk constant_integer.to_value(); - }, - .byte_size => |ty| blk: { - const byte_size = ty.get_byte_size(); - const constant_integer = value_type.llvm.abi.?.to_integer().get_constant(byte_size, @intFromBool(false)); - break :blk constant_integer.to_value(); - }, - .enum_name => |enum_value| blk: { - const enum_type = enum_value.type.?; - const enum_to_string = enum_type.bb.enumerator.enum_to_string.?; - module.emit_value(enum_value, .abi); - const call = module.llvm.builder.create_call(enum_to_string.get_type(), enum_to_string.to_value(), &.{enum_value.llvm.?}); - call.to_instruction().to_call_base().set_calling_convention(.fast); - break :blk call; - }, - .extend => |extended_value| blk: { - if (extended_value.llvm == null) { - module.emit_value(extended_value, type_kind); - } - const llvm_value = extended_value.llvm orelse unreachable; - const destination_type = value_type.llvm.abi.?; - const extension_type = switch (extended_value.type.?.bb) { - .alias => |alias| alias.type, - else => extended_value.type.?, - }; - const extension_instruction = switch (extension_type.bb.integer.signed) { - true => module.llvm.builder.create_sign_extend(llvm_value, destination_type), - false => module.llvm.builder.create_zero_extend(llvm_value, destination_type), - }; - break :blk extension_instruction; - }, - .integer_max => |max_type| blk: { - max_type.resolve(module); - const bit_count = max_type.bb.integer.bit_count; - const max_value = if (bit_count == 64) ~@as(u64, 0) else (@as(u64, 1) << @intCast(bit_count - @intFromBool(max_type.bb.integer.signed))) - 1; - const constant_integer = value_type.llvm.abi.?.to_integer().get_constant(max_value, @intFromBool(false)); - break :blk constant_integer.to_value(); - }, - .int_from_enum => |enum_value| blk: { - module.emit_value(enum_value, type_kind); - break :blk enum_value.llvm.?; - }, - .int_from_pointer => |pointer_value| blk: { - module.emit_value(pointer_value, type_kind); - const int = module.llvm.builder.create_ptr_to_int(pointer_value.llvm.?, value_type.llvm.abi.?); - break :blk int; - }, - .pointer_cast => |pointer_value| blk: { - module.emit_value(pointer_value, type_kind); - break :blk pointer_value.llvm.?; - }, - .select => |select| blk: { - module.emit_value(select.condition, type_kind); - const condition = switch (select.condition.type.?.bb) { - .integer => |integer| switch (integer.bit_count) { - 1 => select.condition.llvm.?, - else => @trap(), - }, - else => @trap(), - }; - module.emit_value(select.true_value, type_kind); - module.emit_value(select.false_value, type_kind); - const result = module.llvm.builder.create_select(condition, select.true_value.llvm.?, select.false_value.llvm.?); - break :blk result; - }, - .string_to_enum => |string_to_enum| blk: { - module.emit_value(string_to_enum.string_value, type_kind); - const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable; - const first_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 0); - const second_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 1); - const call = module.llvm.builder.create_call(s2e.function.get_type(), s2e.function.to_value(), &.{ first_field, second_field }); - call.to_instruction().to_call_base().set_calling_convention(.fast); - break :blk call; - }, - .trap => blk: { - // TODO: lookup in advance - const intrinsic_id = module.llvm.intrinsic_table.trap; - const argument_types: []const *llvm.Type = &.{}; - const argument_values: []const *llvm.Value = &.{}; - const intrinsic_function = module.llvm.module.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const llvm_call = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - _ = module.llvm.builder.create_unreachable(); - module.llvm.builder.clear_insertion_position(); - - break :blk llvm_call; - }, - .truncate => |value_to_truncate| blk: { - if (value_to_truncate.llvm == null) { - module.emit_value(value_to_truncate, type_kind); - } - const llvm_value = value_to_truncate.llvm orelse unreachable; - const truncate = module.llvm.builder.create_truncate(llvm_value, value_type.llvm.abi.?); - break :blk truncate; - }, - .va_arg => module.emit_va_arg(value, null, null), - .va_end => |va_list| blk: { - module.emit_value(va_list, .memory); - - const intrinsic_id = module.llvm.intrinsic_table.va_end; - const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type}; - const intrinsic_function = module.llvm.module.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const argument_values: []const *llvm.Value = &.{va_list.llvm.?}; - const llvm_value = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - break :blk llvm_value; - }, - else => @trap(), - }, - .dereference => |dereferenceable_value| blk: { - module.emit_value(dereferenceable_value, .memory); - const result = switch (value.kind) { - .left => @trap(), - .right => module.create_load(.{ - .type = dereferenceable_value.type.?.bb.pointer.type, - .value = dereferenceable_value.llvm.?, - .alignment = dereferenceable_value.type.?.bb.pointer.alignment, - }), - }; - break :blk result; - }, - .call => module.emit_call(value, null, null), - .array_initialization => |array_initialization| switch (array_initialization.is_constant) { - true => blk: { - assert(value.kind == .right); - var llvm_value_buffer: [64]*llvm.Constant = undefined; - const element_count = array_initialization.values.len; - const llvm_values = llvm_value_buffer[0..element_count]; - - for (array_initialization.values, llvm_values) |v, *llvm_value| { - module.emit_value(v, .memory); - llvm_value.* = v.llvm.?.to_constant(); - } - - value_type.bb.array.element_type.resolve(module); - - const array_value = value_type.bb.array.element_type.llvm.memory.?.get_constant_array(llvm_values); - break :blk array_value.to_value(); - }, - false => switch (value.kind) { - .left => blk: { - const array_type = value_type.bb.pointer.type; - const alloca = module.create_alloca(.{ - .type = array_type, - }); - - const pointer_to_element_type = module.get_pointer_type(.{ .type = array_type.bb.array.element_type }); - const uint64 = module.integer_type(64, false).llvm.abi.?.to_integer(); - const u64_zero = uint64.get_constant(0, 0).to_value(); - - for (array_initialization.values, 0..) |v, i| { - const alloca_gep = module.llvm.builder.create_gep(.{ - .type = array_type.llvm.memory.?, - .aggregate = alloca, - .indices = &.{ u64_zero, uint64.get_constant(i, 0).to_value() }, - }); - module.emit_assignment(alloca_gep, pointer_to_element_type, v); - } - - break :blk alloca; - }, - .right => @trap(), - }, - }, - .array_expression => |array_expression| switch (array_expression.array_like.kind) { - .left => switch (array_expression.array_like.type.?.bb) { - .pointer => |pointer| switch (pointer.type.bb) { - .array => |array| blk: { - module.emit_value(array_expression.array_like, .memory); - module.emit_value(array_expression.index, .memory); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const zero_index = uint64.llvm.abi.?.to_integer().get_constant(0, @intFromBool(false)).to_value(); - const gep = module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.memory.?, - .aggregate = array_expression.array_like.llvm.?, - .indices = &.{ zero_index, array_expression.index.llvm.? }, - }); - - const v = switch (value.kind) { - .left => gep, - .right => module.create_load(.{ .type = array.element_type, .value = gep }), - }; - - break :blk v; - }, - .structure => |structure| blk: { - assert(structure.is_slice); - module.emit_value(array_expression.array_like, .memory); - module.emit_value(array_expression.index, .memory); - const pointer_type = structure.fields[0].type; - const element_type = pointer_type.bb.pointer.type; - element_type.resolve(module); - const pointer_load = module.create_load(.{ .type = structure.fields[0].type, .value = array_expression.array_like.llvm.? }); - const gep = module.llvm.builder.create_gep(.{ - .type = element_type.llvm.memory.?, - .aggregate = pointer_load, - .indices = &.{array_expression.index.llvm.?}, - }); - - break :blk switch (value.kind) { - .left => gep, - .right => module.create_load(.{ - .type = element_type, - .value = gep, - }), - }; - }, - .pointer => |real_pointer| blk: { - module.emit_value(array_expression.array_like, .memory); - module.emit_value(array_expression.index, .memory); - // TODO: consider not emitting the and doing straight GEP? - const pointer_load = module.create_load(.{ .type = pointer.type, .value = array_expression.array_like.llvm.? }); - const element_type = real_pointer.type; - const gep = module.llvm.builder.create_gep(.{ - .type = element_type.llvm.memory.?, - .aggregate = pointer_load, - .indices = &.{array_expression.index.llvm.?}, - }); - break :blk switch (value.kind) { - .left => gep, - .right => module.create_load(.{ - .type = element_type, - .value = gep, - }), - }; - }, - else => @trap(), - }, - else => unreachable, - }, - .right => switch (array_expression.array_like.type.?.bb) { - .pointer => |pointer| blk: { - module.emit_value(array_expression.array_like, .memory); - module.emit_value(array_expression.index, .memory); - const gep = module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.memory.?, - .aggregate = array_expression.array_like.llvm.?, - .indices = &.{array_expression.index.llvm.?}, - }); - const v = switch (value.kind) { - .left => gep, - .right => module.create_load(.{ .type = pointer.type, .value = gep }), - }; - - break :blk v; - }, - .structure => |structure| switch (structure.is_slice) { - true => blk: { - module.emit_value(array_expression.array_like, .memory); - module.emit_value(array_expression.index, .memory); - const pointer_extract = module.llvm.builder.create_extract_value(array_expression.array_like.llvm.?, 0); - const element_type = structure.fields[0].type.bb.pointer.type; - const gep = module.llvm.builder.create_gep(.{ - .type = element_type.llvm.memory.?, - .aggregate = pointer_extract, - .indices = &.{array_expression.index.llvm.?}, - }); - const v = switch (value.kind) { - .left => gep, - .right => module.create_load(.{ .type = element_type, .value = gep }), - }; - - break :blk v; - }, - false => module.report_error(), - }, - else => @trap(), - }, - }, - .enum_literal => |enum_literal_name| blk: { - const enum_int_value = for (value_type.bb.enumerator.fields) |*field| { - if (lib.string.equal(enum_literal_name, field.name)) { - break field.value; - } - } else module.report_error(); - const llvm_value = value_type.get_llvm(type_kind).to_integer().get_constant(enum_int_value, @intFromBool(false)); - break :blk llvm_value.to_value(); - }, - .field_access => |field_access| blk: { - module.emit_value(field_access.aggregate, .memory); - const field_name = field_access.field; - switch (field_access.aggregate.kind) { - .left => { - const base_type = field_access.aggregate.type.?; - assert(base_type.bb == .pointer); - const pointer_type = switch (base_type.bb.pointer.type.bb) { - .pointer => base_type.bb.pointer.type, - else => base_type, - }; - const ty = pointer_type.bb.pointer.type; - ty.resolve(module); - const child_resolved_type = module.fully_resolve_alias(ty); - const v = switch (pointer_type == base_type) { - false => module.create_load(.{ .type = pointer_type, .value = field_access.aggregate.llvm.? }), - true => field_access.aggregate.llvm.?, - }; - - switch (child_resolved_type.bb) { - .structure => |structure| { - const field_index: u32 = for (structure.fields, 0..) |field, field_index| { - if (lib.string.equal(field_name, field.name)) { - break @intCast(field_index); - } - } else module.report_error(); - - const gep = module.llvm.builder.create_struct_gep(ty.llvm.memory.?.to_struct(), v, field_index); - break :blk switch (value.kind) { - .left => gep, - .right => module.create_load(.{ - .type = structure.fields[field_index].type, - .value = gep, - }), - }; - }, - .@"union" => |union_type| { - const field_value_type = for (union_type.fields) |field| { - if (lib.string.equal(field_name, field.name)) { - break field.type; - } - } else unreachable; - - const biggest_field_type = union_type.fields[union_type.biggest_field].type; - - const struct_type = if (field_value_type.is_abi_equal(biggest_field_type, module)) ty.llvm.memory.?.to_struct() else module.llvm.context.get_struct_type(&.{field_value_type.llvm.memory.?}); - const gep = module.llvm.builder.create_struct_gep(struct_type, v, 0); - break :blk switch (value.kind) { - .left => gep, - .right => module.create_load(.{ - .type = field_value_type, - .value = gep, - }), - }; - }, - .bits => |bits| { - const field_index: u32 = for (bits.fields, 0..) |field, field_index| { - if (lib.string.equal(field_name, field.name)) { - break @intCast(field_index); - } - } else module.report_error(); - const field = bits.fields[field_index]; - field.type.resolve(module); - - const load = module.create_load(.{ .type = ty, .alignment = pointer_type.bb.pointer.alignment, .value = v }); - const shift = module.llvm.builder.create_lshr(load, bits.backing_type.llvm.abi.?.to_integer().get_constant(field.bit_offset, 0).to_value()); - const trunc = module.llvm.builder.create_truncate(shift, field.type.llvm.abi.?); - break :blk trunc; - }, - .array => |array| break :blk value_type.get_llvm(type_kind).to_integer().get_constant(array.element_count, 0).to_value(), - else => @trap(), - } - }, - .right => switch (field_access.aggregate.type.?.bb) { - else => @trap(), - }, - } - }, - .aggregate_initialization => |aggregate_initialization| switch (module.fully_resolve_alias(value_type).bb) { - .bits => |bits| switch (aggregate_initialization.is_constant) { - true => blk: { - var bits_value: u64 = 0; - for (aggregate_initialization.names, aggregate_initialization.values) |field_name, field_value| { - const declaration_index = for (bits.fields, 0..) |field, declaration_index| { - if (lib.string.equal(field_name, field.name)) { - break declaration_index; - } - } else unreachable; - const field = &bits.fields[declaration_index]; - - const fv = switch (field_value.bb) { - .constant_integer => |ci| ci.value, - else => @trap(), - }; - bits_value |= fv << @intCast(field.bit_offset); - } - - bits.backing_type.resolve(module); - const llvm_value = bits.backing_type.llvm.abi.?.to_integer().get_constant(bits_value, @intFromBool(false)); - break :blk llvm_value.to_value(); - }, - false => blk: { - bits.backing_type.resolve(module); - const llvm_type = bits.backing_type.llvm.abi.?; - const zero_value = llvm_type.get_zero(); - var result = zero_value.to_value(); - for (aggregate_initialization.names, aggregate_initialization.values) |field_name, field_value| { - const declaration_index = for (bits.fields, 0..) |field, declaration_index| { - if (lib.string.equal(field_name, field.name)) { - break declaration_index; - } - } else unreachable; - const field = &bits.fields[declaration_index]; - module.emit_value(field_value, .memory); - const extended = module.llvm.builder.create_zero_extend(field_value.llvm.?, llvm_type); - const shl = module.llvm.builder.create_shl(extended, llvm_type.to_integer().get_constant(field.bit_offset, 0).to_value()); - const or_value = module.llvm.builder.create_or(result, shl); - result = or_value; - } - break :blk result; - }, - }, - .structure => |structure| switch (aggregate_initialization.is_constant) { - true => blk: { - var constant_buffer: [64]*llvm.Constant = undefined; - const constants = constant_buffer[0..structure.fields.len]; - - for (aggregate_initialization.values, constants[0..aggregate_initialization.values.len]) |field_value, *constant| { - module.emit_value(field_value, .memory); - constant.* = field_value.llvm.?.to_constant(); - } - - if (aggregate_initialization.zero) { - if (aggregate_initialization.values.len == structure.fields.len) { - module.report_error(); - } - - for (constants[aggregate_initialization.values.len..], structure.fields[aggregate_initialization.values.len..]) |*constant, *field| { - field.type.resolve(module); - constant.* = field.type.llvm.memory.?.get_zero(); - } - } - const constant_struct = value_type.llvm.abi.?.to_struct().get_constant(constants); - break :blk constant_struct.to_value(); - }, - false => { - @trap(); - }, - }, - .pointer => module.report_error(), - else => @trap(), - }, - .zero => value_type.llvm.abi.?.get_zero().to_value(), - .@"unreachable" => b: { - const unreachable_value = module.llvm.builder.create_unreachable(); - module.llvm.builder.clear_insertion_position(); - break :b unreachable_value; - }, - .slice_expression => blk: { - assert(value.kind == .right); - const slice_values = module.emit_slice_expression(value); - const slice_poison = value_type.llvm.memory.?.get_poison(); - const slice_pointer = module.llvm.builder.create_insert_value(slice_poison, slice_values[0], 0); - const slice_length = module.llvm.builder.create_insert_value(slice_pointer, slice_values[1], 1); - const slice_value = slice_length; - break :blk slice_value; - }, - .undefined => value_type.llvm.abi.?.get_poison(), - .string_literal => |string_literal| blk: { - const null_terminate = true; - const constant_string = module.llvm.context.get_constant_string(string_literal, null_terminate); - - const u8_type = module.integer_type(8, false); - u8_type.resolve(module); - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "conststring", - .initial_value = constant_string, - .type = u8_type.llvm.abi.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(), - }); - global_variable.set_unnamed_address(.global); - - const slice_type = module.get_slice_type(.{ - .type = u8_type, - }); - assert(value_type == slice_type); - slice_type.resolve(module); - const slice_poison = slice_type.llvm.abi.?.get_poison(); - const slice_pointer = module.llvm.builder.create_insert_value(slice_poison, global_variable.to_value(), 0); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const slice_length = module.llvm.builder.create_insert_value(slice_pointer, uint64.llvm.abi.?.to_integer().get_constant(string_literal.len, 0).to_value(), 1); - const slice_value = slice_length; - break :blk slice_value; - }, - .macro_instantiation => |*macro_instantiation| blk: { - const current_function = module.current_function orelse module.report_error(); - module.current_function = null; - defer module.current_function = current_function; - - const current_macro_instantiation = module.current_macro_instantiation; - assert(current_macro_instantiation == null); - module.current_macro_instantiation = value; - defer module.current_macro_instantiation = current_macro_instantiation; - - for (macro_instantiation.instantiation_arguments) |call_argument| { - module.emit_value(call_argument, .abi); - } - - const caller_debug_location = if (module.has_debug_info) llvm.DI.create_debug_location(module.llvm.context, macro_instantiation.instantiation_line, macro_instantiation.instantiation_column, macro_instantiation.function_scope.parent.?.llvm.?, null) else undefined; - defer if (module.has_debug_info) { - module.llvm.builder.set_current_debug_location(caller_debug_location); - }; - module.inline_at_debug_location = caller_debug_location; - defer module.inline_at_debug_location = null; - - const llvm_function = current_function.variable.storage.?.llvm.?.to_function(); - const macro_entry_block = module.llvm.context.create_basic_block("macro.entry", llvm_function); - _ = module.llvm.builder.create_branch(macro_entry_block); - module.llvm.builder.position_at_end(macro_entry_block); - - const valid_return_value = switch (macro_instantiation.return_type.bb) { - .void, .noreturn => false, - else => true, - }; - - macro_instantiation.return_alloca = if (valid_return_value) module.create_alloca(.{ - .type = macro_instantiation.return_type, - .name = "macro.return", - }) else undefined; - - const macro_return_block = module.llvm.context.create_basic_block("macro.return_block", llvm_function); - macro_instantiation.return_block = macro_return_block; - - for (macro_instantiation.instantiation_arguments, macro_instantiation.declaration_arguments) |call_argument, declaration_argument| { - module.emit_local_storage(declaration_argument); - const storage = declaration_argument.variable.storage.?.llvm.?; - - switch (declaration_argument.variable.type.?.get_evaluation_kind()) { - .scalar => { - _ = module.create_store(.{ - .source_value = call_argument.llvm.?, - .destination_value = storage, - .type = declaration_argument.variable.type.?, - }); - }, - .aggregate => @trap(), - .complex => @trap(), - } - } - - module.analyze_block(macro_instantiation.block); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(macro_return_block); - } - - module.llvm.builder.position_at_end(macro_return_block); - - const load = switch (valid_return_value) { - true => module.create_load(.{ - .type = macro_instantiation.return_type, - .value = macro_instantiation.return_alloca, - .type_kind = type_kind, - }), - false => return, - }; - break :blk load; - }, - else => @trap(), - }; - - value.llvm = llvm_value; - } - - pub fn analyze_statement(module: *Module, scope: *Scope, statement: *Statement, last_line: *u32, last_column: *u32, last_statement_debug_location: **llvm.DI.Location) void { - const parent_function_global = if (module.current_function) |cf| cf else if (module.current_macro_instantiation) |mi| mi.bb.macro_instantiation.function else module.report_error(); - const llvm_function = parent_function_global.variable.storage.?.llvm.?.to_function(); - if (module.has_debug_info) { - if (statement.line != last_line.* or statement.column != last_column.*) { - const inlined_at: ?*llvm.DI.Metadata = @ptrCast(module.inline_at_debug_location); - last_statement_debug_location.* = llvm.DI.create_debug_location(module.llvm.context, statement.line, statement.column, scope.llvm.?, inlined_at); - module.llvm.builder.set_current_debug_location(last_statement_debug_location.*); - last_line.* = statement.line; - last_column.* = statement.column; - } - } - - switch (statement.bb) { - .@"return" => |rv| { - if (module.current_function != null) { - const function_type = parent_function_global.variable.storage.?.type.?.bb.pointer.type.bb.function; - const return_abi = function_type.return_abi; - - switch (return_abi.semantic_type.bb) { - .void => { - if (rv != null) { - module.report_error(); - } - }, - .noreturn => module.report_error(), - else => { - const return_value = rv orelse module.report_error(); - module.analyze(return_value, .{ - .type = return_abi.semantic_type, - }, .memory); - - if (module.has_debug_info) { - module.llvm.builder.set_current_debug_location(last_statement_debug_location.*); - } - - // Clang equivalent: CodeGenFunction::EmitReturnStmt - const return_alloca = module.current_function.?.variable.storage.?.bb.function.return_alloca orelse module.report_error(); - - switch (return_abi.semantic_type.get_evaluation_kind()) { - .scalar => { - switch (return_abi.flags.kind) { - .indirect => { - @trap(); - }, - else => { - // assert(!return_value.?.lvalue); - assert(return_value.type.?.is_abi_equal(return_abi.semantic_type, module)); - _ = module.create_store(.{ - .source_value = return_value.llvm.?, - .destination_value = return_alloca, - .type = return_abi.semantic_type, - }); - }, - } - }, - // TODO: handcoded code, might be wrong - .aggregate => switch (return_value.kind) { - .left => @trap(), - .right => { - assert(return_value.type.?.is_abi_equal(return_abi.semantic_type, module)); - _ = module.create_store(.{ - .source_value = return_value.llvm.?, - .destination_value = return_alloca, - .type = return_abi.semantic_type, - }); - }, - // switch (return_value.lvalue) { - // true => { - // const abi_alignment = return_abi.semantic_type.get_byte_alignment(); - // const abi_size = return_abi.semantic_type.get_byte_size(); - // switch (return_abi.flags.kind) { - // .indirect => { - // _ = module.llvm.builder.create_memcpy( - // unreachable, //return_alloca, - // abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - // }, - // else => { - // switch (return_abi.semantic_type.get_evaluation_kind()) { - // .aggregate => { - // // TODO: this is 100% wrong, fix - // assert(abi_alignment == return_abi.semantic_type.get_byte_alignment()); - // assert(abi_size == return_abi.semantic_type.get_byte_size()); - // _ = module.llvm.builder.create_memcpy( - // unreachable, //return_alloca, - // abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - // }, - // .scalar => { - // const destination_type = return_abi.semantic_type; - // const source_type = return_abi.semantic_type; - // assert(return_value.type == source_type); - // const ret_val = switch (return_value.type.bb) { - // .pointer => return_value.llvm, - // // TODO: this feels hacky - // else => switch (return_value.lvalue) { - // true => module.create_load(.{ .type = return_value.type, .value = return_value.llvm }), - // false => return_value.llvm, - // }, - // }; - // _ = module.create_store(.{ .source_value = ret_val, .source_type = source_type, - // .destination_value = unreachable, //return_alloca, - // .destination_type = destination_type }); - // }, - // .complex => @trap(), - // } - // }, - // } - // }, - // false => { - // }, - // } - }, - .complex => @trap(), - } - }, - } - - const return_block = module.current_function.?.variable.storage.?.bb.function.return_block orelse module.report_error(); - - _ = module.llvm.builder.create_branch(return_block); - _ = module.llvm.builder.clear_insertion_position(); - } else if (module.current_macro_instantiation) |m| { - const macro_instantiation = &m.bb.macro_instantiation; - module.analyze_value_type(rv.?, .{ .type = macro_instantiation.return_type }); - module.emit_assignment(macro_instantiation.return_alloca, module.get_pointer_type(.{ .type = macro_instantiation.return_type }), rv.?); - _ = module.llvm.builder.create_branch(macro_instantiation.return_block); - module.llvm.builder.clear_insertion_position(); - } else { - module.report_error(); - } - }, - .local => |local| { - const expected_type = local.variable.type; - assert(local.variable.storage == null); - module.analyze_value_type(local.variable.initial_value, .{ .type = local.variable.type }); - local.variable.resolve_type(local.variable.initial_value.type.?); - if (expected_type) |lvt| assert(lvt == local.variable.type); - module.emit_local_storage(local); - - module.emit_assignment(local.variable.storage.?.llvm.?, local.variable.storage.?.type.?, local.variable.initial_value); - }, - .assignment => |assignment| { - module.analyze(assignment.left, .{}, .memory); - switch (assignment.kind) { - .@"=" => { - module.analyze_value_type(assignment.right, .{ .type = assignment.left.type.?.bb.pointer.type }); - module.emit_assignment(assignment.left.llvm.?, assignment.left.type.?, assignment.right); - }, - else => |kind| { - const pointer_type = assignment.left.type.?.bb.pointer; - const element_type = pointer_type.type; - assert(element_type.get_evaluation_kind() == .scalar); - const load = module.create_load(.{ .type = element_type, .value = assignment.left.llvm.?, .alignment = pointer_type.alignment }); - module.analyze(assignment.right, .{ .type = element_type }, .memory); - const a = load; - const b = assignment.right.llvm.?; - const right = switch (kind) { - .@"+=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_add(a, b), - .pointer => |pointer| module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.abi.?, - .aggregate = a, - .indices = &.{b}, - }), - else => module.report_error(), - }, - .@"-=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_sub(a, b), - .pointer => |pointer| module.llvm.builder.create_gep(.{ - .type = pointer.type.llvm.abi.?, - .aggregate = a, - .indices = &.{module.negate_llvm_value(b, assignment.right.is_constant())}, - }), - else => module.report_error(), - }, - .@"*=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_mul(a, b), - else => module.report_error(), - }, - .@"/=" => switch (element_type.bb) { - .integer => |integer| switch (integer.signed) { - true => module.llvm.builder.create_sdiv(a, b), - false => module.llvm.builder.create_udiv(a, b), - }, - else => module.report_error(), - }, - .@"%=" => switch (element_type.bb) { - .integer => |integer| switch (integer.signed) { - true => module.llvm.builder.create_srem(a, b), - false => module.llvm.builder.create_urem(a, b), - }, - else => module.report_error(), - }, - .@"&=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_and(a, b), - else => module.report_error(), - }, - .@"|=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_or(a, b), - else => module.report_error(), - }, - .@"^=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_xor(a, b), - else => module.report_error(), - }, - .@">>=" => switch (element_type.bb) { - .integer => |integer| switch (integer.signed) { - true => module.llvm.builder.create_ashr(a, b), - false => module.llvm.builder.create_lshr(a, b), - }, - else => module.report_error(), - }, - .@"<<=" => switch (element_type.bb) { - .integer => module.llvm.builder.create_shl(a, b), - else => module.report_error(), - }, - else => @trap(), - }; - - _ = module.create_store(.{ - .source_value = right, - .destination_value = assignment.left.llvm.?, - .type = element_type, - .alignment = pointer_type.alignment, - }); - }, - } - }, - .expression => |expression_value| { - module.analyze(expression_value, .{}, .memory); - }, - .@"if" => |if_statement| { - const taken_block = module.llvm.context.create_basic_block("if.true", llvm_function); - const not_taken_block = module.llvm.context.create_basic_block("if.false", llvm_function); - const exit_block = module.llvm.context.create_basic_block("if.end", llvm_function); - - module.analyze(if_statement.condition, .{}, .abi); - const llvm_condition = switch (if_statement.condition.type.?.bb) { - .integer => |integer| if (integer.bit_count != 1) module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.abi.?.get_zero().to_value()) else if_statement.condition.llvm.?, - .pointer => module.llvm.builder.create_integer_compare(.ne, if_statement.condition.llvm.?, if_statement.condition.type.?.llvm.abi.?.get_zero().to_value()), - else => @trap(), - }; - - _ = module.llvm.builder.create_conditional_branch(llvm_condition, taken_block, not_taken_block); - module.llvm.builder.position_at_end(taken_block); - - module.analyze_statement(scope, if_statement.if_statement, last_line, last_line, last_statement_debug_location); - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(exit_block); - } - - module.llvm.builder.position_at_end(not_taken_block); - if (if_statement.else_statement) |else_statement| { - module.analyze_statement(scope, else_statement, last_line, last_line, last_statement_debug_location); - } - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(exit_block); - } - - module.llvm.builder.position_at_end(exit_block); - }, - .@"while" => |while_loop| { - const loop_entry_block = module.llvm.context.create_basic_block("while.entry", llvm_function); - _ = module.llvm.builder.create_branch(loop_entry_block); - module.llvm.builder.position_at_end(loop_entry_block); - - const loop_body_block = module.llvm.context.create_basic_block("while.body", llvm_function); - - const previous_continue_block = module.continue_block; - defer module.continue_block = previous_continue_block; - const loop_continue_block = module.llvm.context.create_basic_block("while.continue", llvm_function); - module.continue_block = loop_continue_block; - - const previous_exit_block = module.exit_block; - defer module.exit_block = previous_exit_block; - const loop_end_block = module.llvm.context.create_basic_block("while.end", llvm_function); - module.exit_block = loop_end_block; - - if (while_loop.condition.is_constant()) { - switch (while_loop.condition.bb) { - .constant_integer => |constant_integer| { - if (constant_integer.value == 0) { - module.report_error(); - } - }, - else => @trap(), - } - _ = module.llvm.builder.create_branch(loop_body_block); - } else { - module.analyze(while_loop.condition, .{}, .abi); - - const boolean_type = module.integer_type(1, false); - const condition_value = switch (while_loop.condition.type == boolean_type) { - true => while_loop.condition.llvm.?, - false => switch (while_loop.condition.type.?.bb) { - .integer => module.llvm.builder.create_integer_compare(.ne, while_loop.condition.llvm.?, while_loop.condition.type.?.llvm.abi.?.to_integer().get_constant(0, @intFromBool(false)).to_value()), - else => @trap(), - }, - }; - - _ = module.llvm.builder.create_conditional_branch(condition_value, loop_body_block, loop_end_block); - } - - module.llvm.builder.position_at_end(loop_body_block); - - module.analyze_block(while_loop.block); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(loop_continue_block); - } - - module.llvm.builder.position_at_end(loop_continue_block); - _ = module.llvm.builder.create_branch(loop_entry_block); - - if (loop_body_block.to_value().use_empty()) { - @trap(); - } - - if (loop_end_block.to_value().use_empty()) { - @trap(); - } - - module.llvm.builder.position_at_end(loop_end_block); - }, - .@"switch" => |switch_statement| { - const exit_block = module.llvm.context.create_basic_block("exit_block", llvm_function); - - module.analyze(switch_statement.discriminant, .{}, .abi); - const switch_discriminant_type = switch_statement.discriminant.type.?; - - switch (switch_discriminant_type.bb) { - .enumerator => |enumerator| { - _ = enumerator; - var else_clause_index: ?usize = null; - var total_discriminant_cases: u32 = 0; - for (switch_statement.clauses, 0..) |*clause, clause_index| { - clause.basic_block = module.llvm.context.create_basic_block(if (clause.values.len == 0) "switch.else_case_block" else "switch.case_block", llvm_function); - total_discriminant_cases += @intCast(clause.values.len); - if (clause.values.len == 0) { - if (else_clause_index != null) { - module.report_error(); - } - else_clause_index = clause_index; - } else { - for (clause.values) |v| { - module.analyze(v, .{ .type = switch_discriminant_type }, .abi); - if (!v.is_constant()) { - module.report_error(); - } - } - } - } - - const else_block = if (else_clause_index) |i| switch_statement.clauses[i].basic_block else module.llvm.context.create_basic_block("switch.else_case_block", llvm_function); - const switch_instruction = module.llvm.builder.create_switch(switch_statement.discriminant.llvm.?, else_block, total_discriminant_cases); - - var all_blocks_terminated = true; - - for (switch_statement.clauses) |clause| { - for (clause.values) |v| { - switch_instruction.add_case(v.llvm.?, clause.basic_block); - } - - module.llvm.builder.position_at_end(clause.basic_block); - module.analyze_block(clause.block); - if (module.llvm.builder.get_insert_block() != null) { - all_blocks_terminated = false; - _ = module.llvm.builder.create_branch(exit_block); - module.llvm.builder.clear_insertion_position(); - } - } - - if (else_clause_index == null) { - module.llvm.builder.position_at_end(else_block); - _ = module.llvm.builder.create_unreachable(); - module.llvm.builder.clear_insertion_position(); - } - - if (!all_blocks_terminated) { - module.llvm.builder.position_at_end(exit_block); - } - }, - else => @trap(), - } - }, - .block => |child_block| module.analyze_block(child_block), - .for_each => |*for_loop| { - if (module.has_debug_info) { - const lexical_block = module.llvm.di_builder.create_lexical_block(for_loop.scope.parent.?.llvm.?, module.llvm.file, for_loop.scope.line, for_loop.scope.column); - for_loop.scope.llvm = lexical_block.to_scope(); - } - - const index_type = module.integer_type(64, false); - index_type.resolve(module); - - const loop_entry_block = module.llvm.context.create_basic_block("foreach.entry", llvm_function); - const loop_body_block = module.llvm.context.create_basic_block("foreach.body", llvm_function); - - const previous_continue_block = module.continue_block; - defer module.continue_block = previous_continue_block; - const loop_continue_block = module.llvm.context.create_basic_block("foreach.continue", llvm_function); - module.continue_block = loop_continue_block; - - const previous_exit_block = module.exit_block; - defer module.exit_block = previous_exit_block; - const loop_exit_block = module.llvm.context.create_basic_block("foreach.exit", llvm_function); - module.exit_block = loop_exit_block; - - switch (for_loop.kind) { - .slice => { - const index_zero = index_type.llvm.abi.?.get_zero().to_value(); - - for (for_loop.locals, for_loop.left_values, for_loop.right_values) |local, kind, right| { - assert(right.kind == .left); - module.analyze_value_type(right, .{}); - const pointer_type = right.type.?; - if (pointer_type.bb != .pointer) { - module.report_error(); - } - const aggregate_type = pointer_type.bb.pointer.type; - const child_type = switch (aggregate_type.bb) { - .array => |array| array.element_type, - .structure => |structure| b: { - assert(structure.is_slice); - break :b structure.fields[0].type.bb.pointer.type; - }, - else => @trap(), - }; - assert(local.variable.type == null); - const local_type = switch (kind) { - .left => module.get_pointer_type(.{ .type = child_type }), - .right => child_type, - }; - local.variable.type = local_type; - module.emit_local_storage(local); - module.emit_value(right, .memory); - } - - const length_value = for (for_loop.right_values) |right| { - const pointer_type = right.type.?; - if (pointer_type.bb != .pointer) { - module.report_error(); - } - const aggregate_type = pointer_type.bb.pointer.type; - const length = switch (aggregate_type.bb) { - .array => |array| index_type.llvm.abi.?.to_integer().get_constant(array.element_count, 0).to_value(), - .structure => |structure| b: { - assert(structure.is_slice); - const gep = module.llvm.builder.create_struct_gep(aggregate_type.llvm.abi.?.to_struct(), right.llvm.?, 1); - const load = module.create_load(.{ - .type = index_type, - .value = gep, - }); - break :b load; - }, - else => @trap(), - }; - break length; - } else unreachable; - - const index_alloca = module.create_alloca(.{ .type = index_type, .name = "foreach.index" }); - _ = module.create_store(.{ .type = index_type, .source_value = index_zero, .destination_value = index_alloca }); - - _ = module.llvm.builder.create_branch(loop_entry_block); - module.llvm.builder.position_at_end(loop_entry_block); - - const header_index_load = module.create_load(.{ .type = index_type, .value = index_alloca }); - const index_compare = module.llvm.builder.create_integer_compare(.ult, header_index_load, length_value); - _ = module.llvm.builder.create_conditional_branch(index_compare, loop_body_block, loop_exit_block); - - module.llvm.builder.position_at_end(loop_body_block); - const body_index_load = module.create_load(.{ .type = index_type, .value = index_alloca }); - - for (for_loop.locals, for_loop.left_values, for_loop.right_values) |local, kind, right| { - const aggregate_type = right.type.?.bb.pointer.type; - const element_pointer_value = switch (aggregate_type.bb) { - .array => module.llvm.builder.create_gep(.{ - .type = right.type.?.bb.pointer.type.llvm.memory.?, - .aggregate = right.llvm.?, - .indices = &.{ index_zero, body_index_load }, - }), - .structure => |structure| b: { - assert(structure.is_slice); - const load = module.create_load(.{ - .type = aggregate_type, - .value = right.llvm.?, - .alignment = right.type.?.bb.pointer.alignment, - }); - const extract_pointer = module.llvm.builder.create_extract_value(load, 0); - const gep = module.llvm.builder.create_gep(.{ - .type = structure.fields[0].type.bb.pointer.type.llvm.memory.?, - .aggregate = extract_pointer, - .indices = &.{body_index_load}, - }); - break :b gep; - }, - else => @trap(), - }; - - switch (kind) { - .left => { - _ = module.create_store(.{ - .type = local.variable.type.?, - .source_value = element_pointer_value, - .destination_value = local.variable.storage.?.llvm.?, - }); - }, - .right => switch (local.variable.type.?.get_evaluation_kind() == .scalar or (aggregate_type.bb == .structure and aggregate_type.bb.structure.is_slice)) { - true => { - const load = module.create_load(.{ - .type = local.variable.type.?, - .value = element_pointer_value, - }); - _ = module.create_store(.{ - .type = local.variable.type.?, - .source_value = load, - .destination_value = local.variable.storage.?.llvm.?, - }); - }, - false => { - @trap(); - }, - }, - } - } - - module.analyze_statement(&for_loop.scope, for_loop.predicate, last_line, last_column, last_statement_debug_location); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(loop_continue_block); - } - - module.llvm.builder.position_at_end(loop_continue_block); - const continue_index_load = module.create_load(.{ .type = index_type, .value = index_alloca }); - const add = module.llvm.builder.create_add(continue_index_load, index_type.llvm.abi.?.to_integer().get_constant(1, 0).to_value()); - _ = module.create_store(.{ .type = index_type, .source_value = add, .destination_value = index_alloca }); - _ = module.llvm.builder.create_branch(loop_entry_block); - - module.llvm.builder.position_at_end(loop_exit_block); - }, - .range => { - assert(for_loop.locals.len == 1); - assert(for_loop.left_values.len == 1); - if (for_loop.right_values.len == 2) { - const start = for_loop.right_values[0]; - const end = for_loop.right_values[1]; - const local_type = switch (start.bb) { - .constant_integer => |start_constant_integer| switch (end.bb) { - .constant_integer => |end_constant_integer| module.integer_type(64, !(!start_constant_integer.signed and !end_constant_integer.signed)), - else => blk: { - module.analyze_value_type(end, .{}); - start.type = end.type; - break :blk end.type.?; - }, - }, - else => @trap(), - }; - - for (for_loop.right_values) |right_value| { - if (right_value.type == null) { - module.analyze_value_type(right_value, .{ .type = local_type }); - } - } - - const local = for_loop.locals[0]; - local.variable.type = local_type; - module.emit_local_storage(local); - module.emit_value(start, .memory); - - const index_alloca = local.variable.storage.?.llvm.?; - - _ = module.create_store(.{ .type = local_type, .source_value = start.llvm.?, .destination_value = index_alloca }); - - _ = module.llvm.builder.create_branch(loop_entry_block); - module.llvm.builder.position_at_end(loop_entry_block); - - const header_index_load = module.create_load(.{ .type = local_type, .value = index_alloca }); - module.emit_value(end, .abi); - const length_value = end.llvm.?; - const index_compare = module.llvm.builder.create_integer_compare(.ult, header_index_load, length_value); - _ = module.llvm.builder.create_conditional_branch(index_compare, loop_body_block, loop_exit_block); - - module.llvm.builder.position_at_end(loop_body_block); - module.analyze_statement(&for_loop.scope, for_loop.predicate, last_line, last_column, last_statement_debug_location); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(loop_continue_block); - } - - module.llvm.builder.position_at_end(loop_continue_block); - const continue_index_load = module.create_load(.{ .type = local_type, .value = index_alloca }); - const add = module.llvm.builder.create_add(continue_index_load, local_type.llvm.abi.?.to_integer().get_constant(1, 0).to_value()); - _ = module.create_store(.{ .type = local_type, .source_value = add, .destination_value = index_alloca }); - _ = module.llvm.builder.create_branch(loop_entry_block); - - module.llvm.builder.position_at_end(loop_exit_block); - } else { - // TODO: case for reverse range - @trap(); - } - }, - } - }, - .break_statement => { - const exit_block = module.exit_block orelse module.report_error(); - _ = module.llvm.builder.create_branch(exit_block); - module.llvm.builder.clear_insertion_position(); - }, - .continue_statement => { - const continue_block = module.continue_block orelse module.report_error(); - _ = module.llvm.builder.create_branch(continue_block); - module.llvm.builder.clear_insertion_position(); - }, - } - } - - pub fn analyze_block(module: *Module, block: *LexicalBlock) void { - if (module.has_debug_info) { - const lexical_block = module.llvm.di_builder.create_lexical_block(block.scope.parent.?.llvm.?, module.llvm.file, block.scope.line, block.scope.column); - block.scope.llvm = lexical_block.to_scope(); - } - - var last_line: u32 = 0; - var last_column: u32 = 0; - var last_statement_debug_location: *llvm.DI.Location = undefined; - - for (block.statements.get_slice()) |statement| { - module.analyze_statement(&block.scope, statement, &last_line, &last_column, &last_statement_debug_location); - } - } - - fn emit_assignment(module: *Module, left_llvm: *llvm.Value, left_type: *Type, right: *Value) void { - assert(right.llvm == null); - const pointer_type = left_type; - const v_type = right.type.?; - v_type.resolve(module); - pointer_type.resolve(module); - const resolved_value_type = module.fully_resolve_alias(v_type); - const resolved_pointer_type = module.fully_resolve_alias(pointer_type); - assert(resolved_pointer_type.bb == .pointer); - assert(resolved_pointer_type.bb.pointer.type == resolved_value_type); - - switch (resolved_value_type.get_evaluation_kind()) { - .scalar => { - module.emit_value(right, .memory); - _ = module.create_store(.{ - .source_value = right.llvm.?, - .destination_value = left_llvm, - .type = v_type, - .alignment = pointer_type.bb.pointer.alignment, - }); - }, - .aggregate => switch (right.bb) { - .array_initialization => |array_initialization| switch (array_initialization.is_constant) { - true => { - module.emit_value(right, .memory); - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "constarray", // TODO: format properly - .initial_value = right.llvm.?.to_constant(), - .type = v_type.llvm.memory.?, - }); - global_variable.set_unnamed_address(.global); - const element_type = v_type.bb.array.element_type; - const alignment = element_type.get_byte_alignment(); - global_variable.to_value().set_alignment(alignment); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(array_initialization.values.len * pointer_type.bb.pointer.type.bb.array.element_type.get_byte_size(), @intFromBool(false)).to_value()); - }, - false => { - assert(v_type.bb == .array); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - const u64_zero = uint64.llvm.abi.?.to_integer().get_constant(0, 0).to_value(); - const pointer_to_element_type = module.get_pointer_type(.{ .type = v_type.bb.array.element_type }); - for (array_initialization.values, 0..) |v, i| { - const alloca_gep = module.llvm.builder.create_gep(.{ - .type = v_type.llvm.memory.?, - .aggregate = left_llvm, - .indices = &.{ u64_zero, uint64.llvm.abi.?.to_integer().get_constant(i, 0).to_value() }, - }); - module.emit_assignment(alloca_gep, pointer_to_element_type, v); - } - }, - }, - .aggregate_initialization => |aggregate_initialization| switch (aggregate_initialization.is_constant) { - true => { - module.emit_value(right, .memory); - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "conststruct", // TODO: format properly - .initial_value = right.llvm.?.to_constant(), - .type = v_type.llvm.abi.?, - }); - global_variable.set_unnamed_address(.global); - const alignment = v_type.get_byte_alignment(); - global_variable.to_value().set_alignment(alignment); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(v_type.get_byte_size(), @intFromBool(false)).to_value()); - }, - false => switch (resolved_value_type.bb) { - .structure => { - var max_field_index: u64 = 0; - var field_mask: u64 = 0; - const fields = resolved_value_type.bb.structure.fields; - assert(fields.len <= 64); - - for (aggregate_initialization.values, aggregate_initialization.names) |initialization_value, initialization_name| { - const field_index = for (fields, 0..) |*field, field_index| { - if (lib.string.equal(field.name, initialization_name)) { - break field_index; - } - } else module.report_error(); - field_mask |= @as(@TypeOf(field_mask), 1) << @intCast(field_index); - max_field_index = @max(field_index, max_field_index); - const field = &fields[field_index]; - const destination_pointer = module.llvm.builder.create_struct_gep(v_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index)); - module.emit_assignment(destination_pointer, module.get_pointer_type(.{ .type = field.type }), initialization_value); - } - - if (aggregate_initialization.zero) { - const buffer_field_count: u64 = @bitSizeOf(@TypeOf(field_mask)); - const raw_end_uninitialized_field_count = @clz(field_mask); - const unused_buffer_field_count = buffer_field_count - fields.len; - const end_uninitialized_field_count = raw_end_uninitialized_field_count - unused_buffer_field_count; - const initialized_field_count = @popCount(field_mask); - const uninitialized_field_count = fields.len - initialized_field_count; - if (uninitialized_field_count != end_uninitialized_field_count) { - @trap(); - } - - if (end_uninitialized_field_count == 0) { - module.report_error(); - } - - const field_index_offset = fields.len - end_uninitialized_field_count; - const destination_pointer = module.llvm.builder.create_struct_gep(v_type.llvm.abi.?.to_struct(), left_llvm, @intCast(field_index_offset)); - const start_field = &fields[field_index_offset]; - const memset_size = v_type.get_byte_size() - start_field.byte_offset; - const uint8 = module.integer_type(8, false); - uint8.resolve(module); - const uint64 = module.integer_type(64, false); - uint64.resolve(module); - _ = module.llvm.builder.create_memset(destination_pointer, uint8.llvm.abi.?.get_zero().to_value(), uint64.llvm.abi.?.to_integer().get_constant(memset_size, 0).to_value(), pointer_type.bb.pointer.alignment.?); - } - }, - .@"union" => |union_type| { - assert(aggregate_initialization.names.len == 1); - assert(aggregate_initialization.values.len == 1); - const biggest_field_type = union_type.fields[union_type.biggest_field].type; - const value = aggregate_initialization.values[0]; - const field_value_type = value.type.?; - const field_type_size = field_value_type.get_byte_size(); - - const struct_type = if (field_value_type.is_abi_equal(biggest_field_type, module)) v_type.llvm.memory.?.to_struct() else module.llvm.context.get_struct_type(&.{field_value_type.llvm.memory.?}); - - const destination_pointer = module.llvm.builder.create_struct_gep(struct_type, left_llvm, 0); - const field_pointer_type = module.get_pointer_type(.{ .type = field_value_type }); - module.emit_assignment(destination_pointer, field_pointer_type, value); - if (field_type_size < union_type.byte_size) { - @trap(); - } else if (field_type_size > union_type.byte_size) { - unreachable; - } - }, - else => unreachable, - }, - }, - .string_literal => |string_literal| { - const null_terminate = true; - const constant_string = module.llvm.context.get_constant_string(string_literal, null_terminate); - - const u8_type = module.integer_type(8, false); - u8_type.resolve(module); - const global_variable = module.llvm.module.create_global_variable(.{ - .linkage = .InternalLinkage, - .name = "conststring", - .initial_value = constant_string, - .type = u8_type.llvm.abi.?.get_array_type(string_literal.len + @intFromBool(null_terminate)).to_type(), - }); - global_variable.set_unnamed_address(.global); - - const slice_type = module.get_slice_type(.{ - .type = u8_type, - }); - - switch (resolved_value_type.bb) { - .structure => |structure| switch (structure.is_slice) { - true => switch (slice_type == resolved_value_type) { - true => { - const pointer_to_pointer = module.llvm.builder.create_struct_gep(slice_type.llvm.abi.?.to_struct(), left_llvm, 0); - const slice_pointer_type = slice_type.bb.structure.fields[0].type; - _ = module.create_store(.{ - .destination_value = pointer_to_pointer, - .source_value = global_variable.to_value(), - .type = slice_pointer_type, - }); - const pointer_to_length = module.llvm.builder.create_struct_gep(slice_type.llvm.abi.?.to_struct(), left_llvm, 1); - const slice_length_type = slice_type.bb.structure.fields[1].type; - const slice_length_value = slice_length_type.llvm.abi.?.to_integer().get_constant(string_literal.len, @intFromBool(false)).to_value(); - _ = module.create_store(.{ - .destination_value = pointer_to_length, - .source_value = slice_length_value, - .type = slice_length_type, - }); - }, - false => module.report_error(), - }, - false => module.report_error(), - }, - else => @trap(), - } - }, - .intrinsic => |intrinsic| switch (intrinsic) { - .enum_name => { - module.emit_value(right, .memory); - _ = module.create_store(.{ - .type = right.type.?, - .source_value = right.llvm.?, - .destination_value = left_llvm, - }); - }, - .select => { - if (right.type.?.get_evaluation_kind() == .scalar or right.type.?.is_slice()) { - module.emit_value(right, .memory); - _ = module.create_store(.{ - .type = right.type.?, - .source_value = right.llvm.?, - .destination_value = left_llvm, - }); - } else { - @trap(); - } - }, - .string_to_enum => |string_to_enum| { - module.emit_value(string_to_enum.string_value, .memory); - const s2e = string_to_enum.enum_type.bb.enumerator.string_to_enum orelse unreachable; - const first_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 0); - const second_field = module.llvm.builder.create_extract_value(string_to_enum.string_value.llvm.?, 1); - const call = module.llvm.builder.create_call(s2e.function.get_type(), s2e.function.to_value(), &.{ first_field, second_field }); - call.to_instruction().to_call_base().set_calling_convention(.fast); - _ = module.create_store(.{ - .source_value = call, - .destination_value = left_llvm, - .type = s2e.struct_type, - .alignment = pointer_type.bb.pointer.alignment, - }); - }, - .va_start => { - assert(resolved_value_type == module.get_va_list_type()); - assert(pointer_type.bb.pointer.type == module.get_va_list_type()); - const intrinsic_id = module.llvm.intrinsic_table.va_start; - const argument_types: []const *llvm.Type = &.{module.llvm.pointer_type}; - const intrinsic_function = module.llvm.module.get_intrinsic_declaration(intrinsic_id, argument_types); - const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types); - const argument_values: []const *llvm.Value = &.{left_llvm}; - _ = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values); - }, - .va_arg => { - const result = module.emit_va_arg(right, left_llvm, left_type); - switch (result == left_llvm) { - true => {}, - false => switch (resolved_value_type.get_evaluation_kind()) { - .scalar => { - @trap(); - }, - .aggregate => @trap(), - .complex => @trap(), - }, - } - }, - else => @trap(), - }, - .call => { - const result = module.emit_call(right, left_llvm, left_type); - assert(result == left_llvm); - // if (result != left.llvm) { - // const call_ret_type_ev_kind = right.type.?.get_evaluation_kind(); - // switch (call_ret_type_ev_kind) { - // .aggregate => switch (right.kind) { - // .left => @trap(), - // .right => @trap(), - // }, - // else => @trap(), - // } - // @trap(); - // } - }, - .slice_expression => { - const slice_values = module.emit_slice_expression(right); - const slice_pointer_type = resolved_value_type.bb.structure.fields[0].type; - _ = module.create_store(.{ - .source_value = slice_values[0], - .destination_value = left_llvm, - .type = slice_pointer_type, - .alignment = pointer_type.bb.pointer.alignment, - }); - const slice_length_destination = module.llvm.builder.create_struct_gep(resolved_value_type.llvm.abi.?.to_struct(), left_llvm, 1); - _ = module.create_store(.{ - .source_value = slice_values[1], - .destination_value = slice_length_destination, - .type = module.integer_type(64, false), - }); - }, - .zero => { - const u8_type = module.integer_type(8, false); - u8_type.resolve(module); - const u64_type = module.integer_type(64, false); - u64_type.resolve(module); - _ = module.llvm.builder.create_memset(left_llvm, u8_type.llvm.abi.?.get_zero().to_value(), u64_type.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment.?); - }, - .variable_reference => |variable| switch (right.kind) { - .left => @trap(), - .right => { - const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, variable.storage.?.llvm.?, variable.storage.?.type.?.bb.pointer.alignment.?, uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); - }, - }, - .field_access => |field_access| { - const struct_type = field_access.aggregate.type.?.bb.pointer.type; - const fields = struct_type.bb.structure.fields; - const field_index: u32 = for (fields, 0..) |*field, field_index| { - if (lib.string.equal(field_access.field, field.name)) { - break @intCast(field_index); - } - } else module.report_error(); - module.emit_value(field_access.aggregate, .memory); - const gep = module.llvm.builder.create_struct_gep(struct_type.llvm.abi.?.to_struct(), field_access.aggregate.llvm.?, field_index); - const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, gep, resolved_value_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); - }, - .undefined => {}, - else => @trap(), - }, - .complex => @trap(), - } - } - - pub fn emit_local_storage(module: *Module, local: *Local) void { - assert(local.variable.storage == null); - const resolved_type = local.variable.type.?; - resolved_type.resolve(module); - - const pointer_type = module.get_pointer_type(.{ .type = resolved_type }); - const storage = module.values.add(); - storage.* = .{ - .type = pointer_type, - .bb = .local, - .llvm = module.create_alloca(.{ - .type = pointer_type.bb.pointer.type, - .alignment = pointer_type.bb.pointer.alignment, - .name = local.variable.name, - }), - }; - - if (module.has_debug_info) { - const debug_type = resolved_type.llvm.debug.?; - const always_preserve = true; - const flags = llvm.DI.Flags{}; - const inlined_at: ?*llvm.DI.Metadata = @ptrCast(module.inline_at_debug_location); // TODO - - const scope = local.variable.scope.llvm.?; - const local_variable = if (local.argument_index) |argument_index| module.llvm.di_builder.create_parameter_variable(scope, local.variable.name, @intCast(argument_index + 1), module.llvm.file, local.variable.line, local.variable.type.?.llvm.debug.?, always_preserve, flags) else module.llvm.di_builder.create_auto_variable(scope, local.variable.name, module.llvm.file, local.variable.line, debug_type, always_preserve, flags, 0); - const debug_location = llvm.DI.create_debug_location(module.llvm.context, local.variable.line, local.variable.column, scope, inlined_at); - module.llvm.builder.set_current_debug_location(debug_location); - _ = module.llvm.di_builder.insert_declare_record_at_end(storage.llvm.?, local_variable, module.llvm.di_builder.null_expression(), debug_location, module.llvm.builder.get_insert_block().?); - } - - local.variable.storage = storage; - } - - pub fn align_integer_type(module: *Module, ty: *Type) *Type { - assert(ty.bb == .integer or ty.bb == .enumerator); - const bit_count = ty.get_bit_size(); - const abi_bit_count: u32 = @intCast(@max(8, lib.next_power_of_two(bit_count))); - if (bit_count != abi_bit_count) { - const is_signed = ty.is_signed(); - return module.integer_type(abi_bit_count, is_signed); - } else { - return ty; - } - } - - const LoadOptions = struct { - type: *Type, - value: *llvm.Value, - alignment: ?c_uint = null, - type_kind: Type.Kind = .abi, - }; - - pub fn create_load(module: *Module, options: LoadOptions) *llvm.Value { - options.type.resolve(module); - const alignment: c_uint = if (options.alignment) |a| a else @intCast(options.type.get_byte_alignment()); - const v = module.llvm.builder.create_load(options.type.llvm.memory.?, options.value); - v.set_alignment(alignment); - - return switch (options.type_kind) { - .abi => switch (options.type.llvm.memory == options.type.llvm.abi) { - true => v, - false => module.llvm.builder.create_int_cast(v, options.type.llvm.abi.?, options.type.is_signed()), - }, - .memory => v, - }; - } - - const AllocaOptions = struct { - type: *Type, - name: []const u8 = "", - alignment: ?c_uint = null, - }; - - pub fn create_alloca(module: *Module, options: AllocaOptions) *llvm.Value { - const abi_type = options.type; - abi_type.resolve(module); - const alignment: c_uint = if (options.alignment) |a| a else @intCast(abi_type.get_byte_alignment()); - const v = module.llvm.builder.create_alloca(abi_type.llvm.memory.?, options.name); - v.set_alignment(alignment); - return v; - } - - const StoreOptions = struct { - source_value: *llvm.Value, - destination_value: *llvm.Value, - type: *Type, - alignment: ?c_uint = null, - }; - - pub fn create_store(module: *Module, options: StoreOptions) *llvm.Value { - options.type.resolve(module); - const source_value = switch (options.type.llvm.abi == options.type.llvm.memory) { - true => options.source_value, - false => module.llvm.builder.create_int_cast(options.source_value, options.type.llvm.memory.?, options.type.is_signed()), - }; - const alignment = if (options.alignment) |a| a else options.type.get_byte_alignment(); - const v = module.llvm.builder.create_store(source_value, options.destination_value); - v.set_alignment(alignment); - return v; - } - - const IntCast = struct { - type: *llvm.Type, - value: *llvm.Value, - }; - - pub fn raw_int_cast(module: *Module, options: IntCast) *llvm.Value { - assert(options.source_type != options.destination_type); - const source_size = options.source_type.get_bit_size(); - const destination_size = options.destination_type.get_bit_size(); - options.destination_type.resolve(module); - const llvm_destination_type = options.destination_type.llvm.abi.?; - const result = switch (source_size < destination_size) { - true => switch (options.source_type.is_signed()) { - true => module.llvm.builder.create_sign_extend(options.value, llvm_destination_type), - false => module.llvm.builder.create_zero_extend(options.value, llvm_destination_type), - }, - false => module.llvm.builder.create_truncate(options.value, llvm_destination_type), - }; - return result; - } - - fn negate_llvm_value(module: *Module, value: *llvm.Value, is_constant: bool) *llvm.Value { - return switch (is_constant) { - true => value.to_constant().negate().to_value(), - false => module.llvm.builder.create_neg(value), - }; - } - - pub fn dump(module: *Module) void { - lib.print_string(module.llvm.module.to_string()); - } - - pub fn enter_struct_pointer_for_coerced_access(module: *Module, source_value: *llvm.Value, source_ty: *Type, destination_size: u64) struct { - value: *llvm.Value, - type: *Type, - } { - _ = module; - var source_pointer = source_value; - var source_type = source_ty; - assert(source_type.bb == .structure and source_type.bb.structure.fields.len > 0); - const first_field_type = source_type.bb.structure.fields[0].type; - const first_field_size = first_field_type.get_byte_size(); - const source_size = source_type.get_byte_size(); - - source_pointer = switch (first_field_size < destination_size and first_field_size < source_size) { - true => source_pointer, - false => @trap(), // TODO: make sure `source_type` is also updated here - }; - - return .{ .value = source_pointer, .type = source_type }; - } - - pub fn create_coerced_store(module: *Module, source_value: *llvm.Value, source_type: *Type, destination: *llvm.Value, destination_ty: *Type, destination_size: u64, destination_volatile: bool) void { - _ = destination_volatile; - var destination_type = destination_ty; - var destination_pointer = destination; - const source_size = source_type.get_byte_size(); - if (!source_type.is_abi_equal(destination_type, module)) { - const r = module.enter_struct_pointer_for_coerced_access(destination_pointer, destination_type, source_size); - destination_pointer = r.value; - destination_type = r.type; - } - - const is_scalable = false; // TODO - if (is_scalable or source_size <= destination_size) { - const destination_alignment = destination_type.get_byte_alignment(); - if (source_type.bb == .integer and destination_type.bb == .pointer and source_size == lib.align_forward_u64(destination_size, destination_alignment)) { - @trap(); - } else if (source_type.bb == .structure) { - for (source_type.bb.structure.fields, 0..) |field, field_index| { - // TODO: volatile - const gep = module.llvm.builder.create_struct_gep(source_type.llvm.abi.?.to_struct(), destination_pointer, @intCast(field_index)); - const field_value = module.llvm.builder.create_extract_value(source_value, @intCast(field_index)); - _ = module.create_store(.{ - .source_value = field_value, - .type = field.type, - .destination_value = gep, - .alignment = destination_alignment, - }); - } - } else { - _ = module.create_store(.{ - .source_value = source_value, - .type = destination_type, - .destination_value = destination_pointer, - .alignment = destination_alignment, - }); - } - // TODO: is this valid for pointers too? - } else if (source_type.is_integer_backing()) { - const destination_integer_type = module.integer_type(@intCast(destination_size * 8), false); - const value = module.coerce_int_or_pointer_to_int_or_pointer(source_value, source_type, destination_integer_type); - _ = module.create_store(.{ - .type = destination_integer_type, - .source_value = value, - .destination_value = destination_pointer, - }); - } else { - // Coercion through memory - const original_destination_alignment = destination_type.get_byte_alignment(); - const source_alloca_alignment = @max(original_destination_alignment, source_type.get_byte_alignment()); - const source_alloca = module.create_alloca(.{ .type = source_type, .alignment = source_alloca_alignment, .name = "coerce" }); - _ = module.create_store(.{ - .source_value = source_value, - .destination_value = source_alloca, - .type = source_type, - .alignment = source_alloca_alignment, - }); - const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(destination_pointer, original_destination_alignment, source_alloca, source_alloca_alignment, uint64.llvm.abi.?.to_integer().get_constant(destination_size, @intFromBool(false)).to_value()); - } - } - - pub fn create_coerced_load(module: *Module, source: *llvm.Value, source_ty: *Type, destination_type: *Type) *llvm.Value { - var source_pointer = source; - var source_type = source_ty; - - const result = switch (source_type.is_abi_equal(destination_type, module)) { - true => module.create_load(.{ - .type = destination_type, - .value = source_pointer, - }), - false => res: { - const destination_size = destination_type.get_byte_size(); - if (source_type.bb == .structure) { - const src = module.enter_struct_pointer_for_coerced_access(source_pointer, source_type, destination_size); - source_pointer = src.value; - source_type = src.type; - } - - if (source_type.is_integer_backing() and destination_type.is_integer_backing()) { - const load = module.create_load(.{ - .type = destination_type, - .value = source_pointer, - }); - const result = module.coerce_int_or_pointer_to_int_or_pointer(load, source_type, destination_type); - return result; - } else { - const source_size = source_type.get_byte_size(); - - const is_source_type_scalable = false; - const is_destination_type_scalable = false; - if (!is_source_type_scalable and !is_destination_type_scalable and source_size >= destination_size) { - const load = module.create_load(.{ .type = destination_type, .value = source, .alignment = source_type.get_byte_alignment() }); - break :res load; - } else { - const is_destination_scalable_vector_type = false; - if (is_destination_scalable_vector_type) { - @trap(); - } - - // Coercion through memory - const original_destination_alignment = destination_type.get_byte_alignment(); - const source_alignment = source_type.get_byte_alignment(); - const destination_alignment = @max(original_destination_alignment, source_alignment); - const destination_alloca = module.create_alloca(.{ .type = destination_type, .name = "coerce", .alignment = destination_alignment }); - const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(destination_alloca, destination_alignment, source, source_alignment, uint64.llvm.abi.?.to_integer().get_constant(source_size, @intFromBool(false)).to_value()); - const load = module.create_load(.{ .type = destination_type, .value = destination_alloca, .alignment = destination_alignment }); - return load; - } - } - }, - }; - return result; - } - - pub fn coerce_int_or_pointer_to_int_or_pointer(module: *Module, source: *llvm.Value, source_ty: *Type, destination_ty: *Type) *llvm.Value { - const source_type = source_ty; - var destination_type = destination_ty; - switch (source_type == destination_type) { - true => return source, - false => { - if (source_type.bb == .pointer and destination_type.bb == .pointer) { - @trap(); - } else { - if (source_type.bb == .pointer) { - @trap(); - } - - if (destination_type.bb == .pointer) { - destination_type = module.integer_type(64, false); - } - - if (source_type != destination_type) { - @trap(); - } - - // This is the original destination type - if (destination_ty.bb == .pointer) { - @trap(); - } - - @trap(); - } - }, - } - } - - fn resolve_type(module: *Module, ty: *Type) *Type { - const result_type = switch (ty.bb) { - .unresolved => blk: { - const macro_instantiation_value = module.current_macro_instantiation orelse module.report_error(); - const macro_instantiation = ¯o_instantiation_value.bb.macro_instantiation; - - const result_type = for (macro_instantiation.type_arguments, macro_instantiation.declaration.type_arguments) |instantiation_type_argument, declaration_type_argument| { - if (declaration_type_argument == ty) { - assert(lib.string.equal(declaration_type_argument.name, instantiation_type_argument.name)); - break instantiation_type_argument; - } - } else module.report_error(); - - break :blk result_type; - }, - .pointer => |pointer| blk: { - const old_child_type = pointer.type; - const new_child_type = module.resolve_type(pointer.type); - if (old_child_type == new_child_type) { - break :blk ty; - } else { - const p = module.get_pointer_type(.{ .type = new_child_type, .alignment = pointer.alignment }); - break :blk p; - } - }, - .structure => |structure| blk: { - var is_the_same = true; - var new_field_type_buffer: [64]*Type = undefined; - for (structure.fields, new_field_type_buffer[0..structure.fields.len]) |*field, *new_field_type| { - const old_field_type = field.type; - const new = module.resolve_type(old_field_type); - new_field_type.* = new; - is_the_same = is_the_same and old_field_type == new; - } - - if (is_the_same) { - break :blk ty; - } else { - @trap(); - } - }, - .integer => ty, - .array => |array| blk: { - const old_element_type = array.element_type; - const new_element_type = module.resolve_type(old_element_type); - if (old_element_type == new_element_type) { - break :blk ty; - } else { - @trap(); - } - }, - .alias => |alias| blk: { - const old_aliased_type = alias.type; - const new_aliased_type = module.resolve_type(old_aliased_type); - if (old_aliased_type == new_aliased_type) { - break :blk ty; - } else { - // TODO: create new type - @trap(); - } - }, - .@"union" => |union_type| blk: { - var is_the_same = true; - var new_field_type_buffer: [64]*Type = undefined; - for (union_type.fields, new_field_type_buffer[0..union_type.fields.len]) |*field, *new_field_type| { - const old_field_type = field.type; - const new = module.resolve_type(old_field_type); - new_field_type.* = new; - is_the_same = is_the_same and old_field_type == new; - } - - if (is_the_same) { - break :blk ty; - } else { - @trap(); - } - }, - .enumerator => |*enumerator| blk: { - const old_backing_type = enumerator.backing_type; - const new_backing_type = module.resolve_type(old_backing_type); - - if (old_backing_type == new_backing_type) { - break :blk ty; - } else { - @trap(); - } - }, - .void => ty, - else => @trap(), - }; - - assert(result_type.bb != .unresolved); - - return result_type; - } -}; - -pub const Options = struct { - content: []const u8, - path: [:0]const u8, - executable: [:0]const u8, - name: []const u8, - objects: []const [:0]const u8, - target: Target, - build_mode: BuildMode, - has_debug_info: bool, - silent: bool, -}; - -const Token = union(Id) { - none, - end_of_statement, - integer: Integer, - identifier: []const u8, - string_literal: []const u8, - value_keyword: Value.Keyword, - value_intrinsic: Value.Intrinsic.Id, - // Assignment operators - @"=", - @"+=", - @"-=", - @"*=", - @"/=", - @"%=", - @"&=", - @"|=", - @"^=", - @"<<=", - @">>=", - // Comparison operators - @"==", - @"!=", - @"<", - @">", - @"<=", - @">=", - // Logical AND - @"and", - @"and?", - // Logical OR - @"or", - @"or?", - // Add-like operators - @"+", - @"-", - // Div-like operators - @"*", - @"/", - @"%", - // Bitwise operators - @"&", - @"|", - @"^", - // Shifting operators - @"<<", - @">>", - // Logical NOT operator - @"!", - // Bitwise NOT operator - @"~", - // Pointer dereference - @".&", - // Parenthesis - @"(", - @")", - // Bracket - @"[", - @"]", - // Brace - @"{", - @"}", - - @",", - @".", - @"..", - @"...", - - const Id = enum { - none, - end_of_statement, - integer, - identifier, - string_literal, - value_keyword, - value_intrinsic, - // Assignment operators - @"=", - @"+=", - @"-=", - @"*=", - @"/=", - @"%=", - @"&=", - @"|=", - @"^=", - @"<<=", - @">>=", - // Comparison operators - @"==", - @"!=", - @"<", - @">", - @"<=", - @">=", - // Logical AND - @"and", - @"and?", - // Logical OR - @"or", - @"or?", - // Add-like operators - @"+", - @"-", - // Div-like operators - @"*", - @"/", - @"%", - // Bitwise operators - @"&", - @"|", - @"^", - // Shifting operators - @"<<", - @">>", - // Logical NOT operator - @"!", - // Bitwise NOT operator - @"~", - // Pointer dereference - @".&", - // Parenthesis - @"(", - @")", - // Bracket - @"[", - @"]", - // Brace - @"{", - @"}", - - @",", - @".", - @"..", - @"...", - }; - - const Integer = struct { - value: u64, - kind: Integer.Kind, - - const Kind = enum { - hexadecimal, - decimal, - octal, - binary, - }; - }; -}; - -pub const Abi = struct { - const Kind = enum(u3) { - ignore, - direct, - extend, - indirect, - indirect_aliased, - expand, - coerce_and_expand, - in_alloca, - }; - - const RegisterCount = union { - system_v: Abi.SystemV.RegisterCount, - }; - - const Flags = struct { - kind: Abi.Kind, - padding_in_reg: bool = false, - in_alloca_sret: bool = false, - in_alloca_indirect: bool = false, - indirect_by_value: bool = false, - indirect_realign: bool = false, - sret_after_this: bool = false, - in_reg: bool = false, - can_be_flattened: bool = false, - sign_extension: bool = false, - }; - - const Information = struct { - semantic_type: *Type, - coerce_to_type: ?*Type = null, - padding: union { - type: ?*Type, - unpadded_coerce_and_expand_type: ?*Type, - } = .{ .type = null }, - padding_arg_index: u16 = 0, - attributes: union { - direct: DirectAttributes, - indirect: IndirectAttributes, - alloca_field_index: u32, - } = .{ - .direct = .{ - .offset = 0, - .alignment = 0, - }, - }, - flags: Abi.Flags, - abi_start: u16 = 0, - abi_count: u16 = 0, - - const DirectAttributes = struct { - offset: u32, - alignment: u32, - }; - - const IndirectAttributes = struct { - alignment: u32, - address_space: u32, - }; - - const Direct = struct { - semantic_type: *Type, - type: *Type, - padding: ?*Type = null, - offset: u32 = 0, - alignment: u32 = 0, - can_be_flattened: bool = true, - }; - - pub fn get_direct(module: *Module, direct: Direct) Information { - var result = Information{ - .semantic_type = direct.semantic_type, - .flags = .{ - .kind = .direct, - }, - }; - _ = direct.semantic_type.resolve(module); - _ = direct.semantic_type.resolve(module); - if (direct.padding) |p| _ = p.resolve(module); - result.set_coerce_to_type(direct.type); - result.set_padding_type(direct.padding); - result.set_direct_offset(direct.offset); - result.set_direct_alignment(direct.alignment); - result.set_can_be_flattened(direct.can_be_flattened); - return result; - } - - pub const Ignore = struct { - semantic_type: *Type, - }; - - pub fn get_ignore(module: *Module, ignore: Ignore) Information { - _ = ignore.semantic_type.resolve(module); - return Information{ - .semantic_type = ignore.semantic_type, - .flags = .{ - .kind = .ignore, - }, - }; - } - - const Extend = struct { - semantic_type: *Type, - type: ?*Type = null, - sign: bool, - }; - - pub fn get_extend(extend: Extend) Information { - assert(extend.semantic_type.is_integral_or_enumeration_type()); - var result = Information{ - .semantic_type = extend.semantic_type, - .flags = .{ - .kind = .extend, - }, - }; - result.set_coerce_to_type(if (extend.type) |t| t else extend.semantic_type); - result.set_padding_type(null); - result.set_direct_offset(0); - result.set_direct_alignment(0); - result.flags.sign_extension = extend.sign; - return result; - } - - const NaturalAlignIndirect = struct { - semantic_type: *Type, - padding_type: ?*Type = null, - by_value: bool = true, - realign: bool = false, - }; - - pub fn get_natural_align_indirect(nai: NaturalAlignIndirect) Abi.Information { - const alignment = nai.semantic_type.get_byte_alignment(); - return get_indirect(.{ - .semantic_type = nai.semantic_type, - .alignment = alignment, - .by_value = nai.by_value, - .realign = nai.realign, - .padding_type = nai.padding_type, - }); - } - - pub const Indirect = struct { - semantic_type: *Type, - padding_type: ?*Type = null, - alignment: u32, - by_value: bool = true, - realign: bool = false, - }; - - pub fn get_indirect(indirect: Indirect) Abi.Information { - var result = Abi.Information{ - .semantic_type = indirect.semantic_type, - .attributes = .{ - .indirect = .{ - .address_space = 0, - .alignment = 0, - }, - }, - .flags = .{ - .kind = .indirect, - }, - }; - result.set_indirect_align(indirect.alignment); - result.set_indirect_by_value(indirect.by_value); - result.set_indirect_realign(indirect.realign); - result.set_sret_after_this(false); - result.set_padding_type(indirect.padding_type); - return result; - } - - fn set_sret_after_this(abi: *Abi.Information, sret_after_this: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.sret_after_this = sret_after_this; - } - - fn set_indirect_realign(abi: *Abi.Information, realign: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.indirect_realign = realign; - } - - fn set_indirect_by_value(abi: *Abi.Information, by_value: bool) void { - assert(abi.flags.kind == .indirect); - abi.flags.indirect_by_value = by_value; - } - - fn set_indirect_align(abi: *Abi.Information, alignment: u32) void { - assert(abi.flags.kind == .indirect or abi.flags.kind == .indirect_aliased); - abi.attributes.indirect.alignment = alignment; - } - - fn set_coerce_to_type(info: *Information, coerce_to_type: *Type) void { - assert(info.can_have_coerce_to_type()); - info.coerce_to_type = coerce_to_type; - } - - fn get_coerce_to_type(info: *const Information) *Type { - assert(info.can_have_coerce_to_type()); - return info.coerce_to_type.?; - } - - fn can_have_coerce_to_type(info: *const Information) bool { - return switch (info.flags.kind) { - .direct, .extend, .coerce_and_expand => true, - else => false, - }; - } - - fn set_padding_type(info: *Information, padding_type: ?*Type) void { - assert(info.can_have_padding_type()); - info.padding = .{ - .type = padding_type, - }; - } - - fn can_have_padding_type(info: *const Information) bool { - return switch (info.flags.kind) { - .direct, .extend, .indirect, .indirect_aliased, .expand => true, - else => false, - }; - } - - fn get_padding_type(info: *const Information) ?*Type { - return if (info.can_have_padding_type()) info.padding.type else null; - } - - fn set_direct_offset(info: *Information, offset: u32) void { - assert(info.flags.kind == .direct or info.flags.kind == .extend); - info.attributes.direct.offset = offset; - } - - fn set_direct_alignment(info: *Information, alignment: u32) void { - assert(info.flags.kind == .direct or info.flags.kind == .extend); - info.attributes.direct.alignment = alignment; - } - - fn set_can_be_flattened(info: *Information, can_be_flattened: bool) void { - assert(info.flags.kind == .direct); - info.flags.can_be_flattened = can_be_flattened; - } - - fn get_can_be_flattened(info: *const Information) bool { - assert(info.flags.kind == .direct); - return info.flags.can_be_flattened; - } - }; - - pub const SystemV = struct { - pub const RegisterCount = struct { - gpr: u32, - sse: u32, - }; - - pub const Class = enum { - integer, - sse, - sseup, - x87, - x87up, - complex_x87, - none, - memory, - - fn merge(accumulator: Class, field: Class) Class { - // AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is - // classified recursively so that always two fields are - // considered. The resulting class is calculated according to - // the classes of the fields in the eightbyte: - // - // (a) If both classes are equal, this is the resulting class. - // - // (b) If one of the classes is NO_CLASS, the resulting class is - // the other class. - // - // (c) If one of the classes is MEMORY, the result is the MEMORY - // class. - // - // (d) If one of the classes is INTEGER, the result is the - // INTEGER. - // - // (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, - // MEMORY is used as class. - // - // (f) Otherwise class SSE is used. - - // Accum should never be memory (we should have returned) or - // ComplexX87 (because this cannot be passed in a structure). - - assert(accumulator != .memory and accumulator != .complex_x87); - if (accumulator == field or field == .none) { - return accumulator; - } - - if (field == .memory) { - return .memory; - } - - if (accumulator == .none) { - return field; - } - - if (accumulator == .integer or field == .integer) { - return .integer; - } - - if (field == .x87 or field == .x87up or field == .complex_x87 or accumulator == .x87 or accumulator == .x87up) { - return .memory; - } - - return .sse; - } - }; - - const ClassifyOptions = struct { - base_offset: u64, - is_named_argument: bool, - is_register_call: bool = false, - }; - - fn classify(ty: *Type, options: ClassifyOptions) [2]Class { - var result = [2]Class{ .none, .none }; - - const is_memory = options.base_offset >= 8; - const current_index = @intFromBool(is_memory); - const not_current_index = @intFromBool(!is_memory); - assert(current_index != not_current_index); - result[current_index] = .memory; - - switch (ty.bb) { - .void, .noreturn => result[current_index] = .none, - .bits => |bits| return classify(bits.backing_type, options), - .enumerator => |enumerator| return classify(enumerator.backing_type, options), - .pointer => result[current_index] = .integer, - .integer => |integer| { - if (integer.bit_count <= 64) { - result[current_index] = .integer; - } else if (integer.bit_count == 128) { - @trap(); - } else { - @trap(); - } - }, - .structure => |struct_type| { - if (struct_type.byte_size <= 64) { - const has_variable_array = false; - if (!has_variable_array) { - // const struct_type = ty.get_payload(.@"struct"); - result[current_index] = .none; - const is_union = false; - var member_offset: u32 = 0; - for (struct_type.fields) |field| { - const offset = options.base_offset + member_offset; - const member_size = field.type.get_byte_size(); - const member_alignment = field.type.get_byte_alignment(); - member_offset = @intCast(lib.align_forward_u64(member_offset + member_size, ty.get_byte_alignment())); - const native_vector_size = 16; - if (ty.get_byte_size() > 16 and ((!is_union and ty.get_byte_size() != member_size) or ty.get_byte_size() > native_vector_size)) { - result[0] = .memory; - const r = classify_post_merge(ty.get_byte_size(), result); - return r; - } - - if (offset % member_alignment != 0) { - result[0] = .memory; - const r = classify_post_merge(ty.get_byte_size(), result); - return r; - } - - const member_classes = classify(field.type, .{ - .base_offset = offset, - .is_named_argument = false, - }); - for (&result, member_classes) |*r, m| { - const merge_result = r.merge(m); - r.* = merge_result; - } - - if (result[0] == .memory or result[1] == .memory) break; - } - - const final = classify_post_merge(ty.get_byte_size(), result); - result = final; - } - } - }, - .array => |*array_type| { - if (ty.get_byte_size() <= 64) { - if (options.base_offset % ty.get_byte_alignment() == 0) { - result[current_index] = .none; - - const vector_size = 16; - if (ty.get_byte_size() > 16 and (ty.get_byte_size() != array_type.element_type.get_byte_size() or ty.get_byte_size() > vector_size)) { - unreachable; - } else { - var offset = options.base_offset; - - for (0..array_type.element_count) |_| { - const element_classes = classify(array_type.element_type, .{ - .base_offset = offset, - .is_named_argument = false, - }); - offset += array_type.element_type.get_byte_size(); - const merge_result = [2]Class{ result[0].merge(element_classes[0]), result[1].merge(element_classes[1]) }; - result = merge_result; - if (result[0] == .memory or result[1] == .memory) { - break; - } - } - - const final_result = classify_post_merge(ty.get_byte_size(), result); - assert(final_result[1] != .sseup or final_result[0] != .sse); - result = final_result; - } - } - } - }, - .alias => |alias| return classify(alias.type, options), - else => @trap(), - } - - return result; - } - - fn classify_post_merge(aggregate_size: u64, classes: [2]Class) [2]Class { - // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done: - // - // (a) If one of the classes is Memory, the whole argument is passed in - // memory. - // - // (b) If X87UP is not preceded by X87, the whole argument is passed in - // memory. - // - // (c) If the size of the aggregate exceeds two eightbytes and the first - // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole - // argument is passed in memory. NOTE: This is necessary to keep the - // ABI working for processors that don't support the __m256 type. - // - // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE. - // - // Some of these are enforced by the merging logic. Others can arise - // only with unions; for example: - // union { _Complex double; unsigned; } - // - // Note that clauses (b) and (c) were added in 0.98. - - var result = classes; - if (result[1] == .memory) { - result[0] = .memory; - } - - if (result[1] == .x87up) { - @trap(); - } - - if (aggregate_size > 16 and (result[0] != .sse or result[1] != .sseup)) { - result[0] = .memory; - } - - if (result[1] == .sseup and result[0] != .sse) { - result[0] = .sse; - } - - return result; - } - - fn get_int_type_at_offset(module: *Module, ty: *Type, offset: u32, source_type: *Type, source_offset: u32) *Type { - switch (ty.bb) { - .enumerator => |enumerator| { - return get_int_type_at_offset(module, enumerator.backing_type, offset, if (source_type == ty) enumerator.backing_type else source_type, source_offset); - }, - .bits => |bits| { - return get_int_type_at_offset(module, bits.backing_type, offset, if (source_type == ty) bits.backing_type else source_type, source_offset); - }, - .integer => |integer_type| { - switch (integer_type.bit_count) { - 64 => return ty, - 32, 16, 8 => { - if (offset != 0) unreachable; - const start = source_offset + ty.get_byte_size(); - const end = source_offset + 8; - if (contains_no_user_data(source_type, start, end)) { - return ty; - } - }, - else => { - const original_byte_count = ty.get_byte_size(); - assert(original_byte_count != source_offset); - const byte_count = @min(original_byte_count - source_offset, 8); - const bit_count = byte_count * 8; - return module.integer_type(@intCast(bit_count), integer_type.signed); - }, - } - }, - .pointer => return if (offset == 0) ty else @trap(), - .structure => { - if (get_member_at_offset(ty, offset)) |field| { - // TODO: this is a addition of mine, since we don't allow arbitrary-bit fields inside structs - const field_type = switch (field.type.bb) { - .integer, .enumerator => module.align_integer_type(field.type), - else => field.type, - }; - return get_int_type_at_offset(module, field_type, @intCast(offset - field.byte_offset), source_type, source_offset); - } - unreachable; - }, - .array => |array_type| { - const element_type = array_type.element_type; - const element_size = element_type.get_byte_size(); - const element_offset = (offset / element_size) * element_size; - return get_int_type_at_offset(module, element_type, @intCast(offset - element_offset), source_type, source_offset); - }, - .alias => |alias| return get_int_type_at_offset(module, alias.type, offset, if (ty == source_type) alias.type else source_type, source_offset), - else => |t| @panic(@tagName(t)), - } - - if (source_type.get_byte_size() - source_offset > 8) { - return module.integer_type(64, false); - } else { - const byte_count = source_type.get_byte_size() - source_offset; - const bit_count = byte_count * 8; - return module.integer_type(@intCast(bit_count), false); - } - } - - fn get_member_at_offset(ty: *Type, offset: u32) ?*const Type.Struct.Field { - if (ty.get_byte_size() <= offset) { - return null; - } - - var offset_it: u32 = 0; - var last_match: ?*const Type.Struct.Field = null; - - const struct_type = &ty.bb.structure; - for (struct_type.fields) |*field| { - if (offset_it > offset) { - break; - } - - last_match = field; - offset_it = @intCast(lib.align_forward_u64(offset_it + field.type.get_byte_size(), ty.get_byte_alignment())); - } - - assert(last_match != null); - return last_match; - } - - fn contains_no_user_data(ty: *Type, start: u64, end: u64) bool { - if (ty.get_byte_size() <= start) { - return true; - } - - switch (ty.bb) { - .structure => |*struct_type| { - var offset: u64 = 0; - - for (struct_type.fields) |field| { - if (offset >= end) break; - const field_start = if (offset < start) start - offset else 0; - if (!contains_no_user_data(field.type, field_start, end - offset)) return false; - offset += field.type.get_byte_size(); - } - - return true; - }, - .array => |array_type| { - for (0..array_type.element_count) |i| { - const offset = i * array_type.element_type.get_byte_size(); - if (offset >= end) break; - const element_start = if (offset < start) start - offset else 0; - if (!contains_no_user_data(array_type.element_type, element_start, end - offset)) return false; - } - - return true; - }, - else => return false, - } - } - - const ArgumentOptions = struct { - available_gpr: u32, - is_named_argument: bool, - is_reg_call: bool, - }; - - pub fn classify_argument_type(module: *Module, argument_type: *Type, options: ArgumentOptions) struct { Abi.Information, Abi.SystemV.RegisterCount } { - const classes = classify(argument_type, .{ - .base_offset = 0, - .is_named_argument = options.is_named_argument, - }); - assert(classes[1] != .memory or classes[0] == .memory); - assert(classes[1] != .sseup or classes[0] == .sse); - var needed_registers = Abi.SystemV.RegisterCount{ - .gpr = 0, - .sse = 0, - }; - - var low: ?*Type = null; - switch (classes[0]) { - .integer => { - needed_registers.gpr += 1; - - const low_ty = Abi.SystemV.get_int_type_at_offset(module, argument_type, 0, argument_type, 0); - low = low_ty; - - if (classes[1] == .none and low_ty.bb == .integer) { - // TODO: - // if (argument_type.bb == .enumerator) { - // @trap(); - // } - - if (argument_type.is_integral_or_enumeration_type() and argument_type.is_promotable_integer_type_for_abi()) { - return .{ - Abi.Information.get_extend(.{ - .semantic_type = argument_type, - .sign = argument_type.is_signed(), - }), - needed_registers, - }; - } - } - }, - .memory, .x87, .complex_x87 => { - // TODO: CXX ABI: RAA_Indirect - return .{ get_indirect_result(module, argument_type, options.available_gpr), needed_registers }; - }, - else => @trap(), - } - - var high: ?*Type = null; - switch (classes[1]) { - .none => {}, - .integer => { - needed_registers.gpr += 1; - const high_ty = Abi.SystemV.get_int_type_at_offset(module, argument_type, 8, argument_type, 8); - high = high_ty; - - if (classes[0] == .none) { - @trap(); - } - }, - else => @trap(), - } - - const result_type = if (high) |hi| get_by_val_argument_pair(module, low orelse unreachable, hi) else low orelse unreachable; - return .{ - Abi.Information.get_direct(module, .{ - .semantic_type = argument_type, - .type = result_type, - }), - needed_registers, - }; - } - - const ClassifyArgument = struct { - type: *Type, - abi_start: u16, - is_reg_call: bool = false, - is_named_argument: bool, - }; - - pub fn classify_argument(module: *Module, available_registers: *Abi.RegisterCount, llvm_abi_argument_type_buffer: []*llvm.Type, abi_argument_type_buffer: []*Type, options: ClassifyArgument) Abi.Information { - const semantic_argument_type = options.type; - const result = if (options.is_reg_call) @trap() else Abi.SystemV.classify_argument_type(module, semantic_argument_type, .{ - .is_named_argument = options.is_named_argument, - .is_reg_call = options.is_reg_call, - .available_gpr = available_registers.system_v.gpr, - }); - const abi = result[0]; - const needed_registers = result[1]; - - var argument_type_abi = switch (available_registers.system_v.gpr >= needed_registers.gpr and available_registers.system_v.sse >= needed_registers.sse) { - true => blk: { - available_registers.system_v.gpr -= needed_registers.gpr; - available_registers.system_v.sse -= needed_registers.sse; - break :blk abi; - }, - false => Abi.SystemV.get_indirect_result(module, semantic_argument_type, available_registers.system_v.gpr), - }; - - if (argument_type_abi.get_padding_type() != null) { - @trap(); - } - - argument_type_abi.abi_start = options.abi_start; - - const count = switch (argument_type_abi.flags.kind) { - .direct, .extend => blk: { - const coerce_to_type = argument_type_abi.get_coerce_to_type(); - coerce_to_type.resolve(module); - const flattened_struct = argument_type_abi.flags.kind == .direct and argument_type_abi.get_can_be_flattened() and coerce_to_type.bb == .structure; - - const count: u16 = switch (flattened_struct) { - false => 1, - true => @intCast(argument_type_abi.get_coerce_to_type().bb.structure.fields.len), - }; - - switch (flattened_struct) { - false => { - llvm_abi_argument_type_buffer[argument_type_abi.abi_start] = coerce_to_type.llvm.abi.?; - abi_argument_type_buffer[argument_type_abi.abi_start] = coerce_to_type; - }, - true => { - for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| { - field.type.resolve(module); - const index = argument_type_abi.abi_start + field_index; - llvm_abi_argument_type_buffer[index] = field.type.llvm.abi.?; - abi_argument_type_buffer[index] = field.type; - } - }, - } - - break :blk count; - }, - .indirect => blk: { - const indirect_type = module.get_pointer_type(.{ .type = argument_type_abi.semantic_type }); - abi_argument_type_buffer[argument_type_abi.abi_start] = indirect_type; - indirect_type.resolve(module); - llvm_abi_argument_type_buffer[argument_type_abi.abi_start] = indirect_type.llvm.abi.?; - break :blk 1; - }, - else => |t| @panic(@tagName(t)), - }; - - argument_type_abi.abi_count = count; - - return argument_type_abi; - } - - pub fn get_by_val_argument_pair(module: *Module, low: *Type, high: *Type) *Type { - const low_size = low.get_byte_allocation_size(); - const high_alignment = high.get_byte_alignment(); - const high_start = lib.align_forward_u64(low_size, high_alignment); - assert(high_start != 0 and high_start <= 8); - - const new_low = if (high_start != 8) { - @trap(); - } else low; - const result = module.get_anonymous_struct_pair(.{ new_low, high }); - assert(result.bb.structure.fields[1].byte_offset == 8); - return result; - } - - pub fn classify_return_type(module: *Module, return_type: *Type) Abi.Information { - const classes = classify(return_type, .{ - .base_offset = 0, - .is_named_argument = true, - }); - assert(classes[1] != .memory or classes[0] == .memory); - assert(classes[1] != .sseup or classes[0] == .sse); - - var low: ?*Type = null; - - switch (classes[0]) { - .none => { - if (classes[1] == .none) { - return Abi.Information.get_ignore(module, .{ - .semantic_type = return_type, - }); - } - - @trap(); - }, - .integer => { - const low_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, 0, return_type, 0); - low = low_ty; - - if (classes[1] == .none and low_ty.bb == .integer) { - if (return_type.bb == .enumerator) { - @trap(); - } - - if (return_type.is_integral_or_enumeration_type() and return_type.is_promotable_integer_type_for_abi()) { - return Abi.Information.get_extend(.{ - .semantic_type = return_type, - .sign = return_type.is_signed(), - }); - } - } - }, - .memory => { - return Abi.SystemV.get_indirect_return_result(.{ .type = return_type }); - }, - else => @trap(), - } - - var high: ?*Type = null; - - switch (classes[1]) { - .none => {}, - .integer => { - const high_offset = 8; - const high_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, high_offset, return_type, high_offset); - high = high_ty; - if (classes[0] == .none) { - return Abi.Information.get_direct(module, .{ - .semantic_type = return_type, - .type = high_ty, - .offset = high_offset, - }); - } - }, - else => @trap(), - } - - if (high) |hi| { - low = Abi.SystemV.get_byval_argument_pair(module, .{ low orelse unreachable, hi }); - } - - return Abi.Information.get_direct(module, .{ - .semantic_type = return_type, - .type = low orelse unreachable, - }); - } - - pub fn get_byval_argument_pair(module: *Module, pair: [2]*Type) *Type { - const low_size = pair[0].get_byte_size(); - const high_alignment = pair[1].get_byte_alignment(); - const high_offset = lib.align_forward_u64(low_size, high_alignment); - assert(high_offset != 0 and high_offset <= 8); - const low = if (high_offset != 8) - if ((pair[0].bb == .float and pair[0].bb.float.kind == .half) or (pair[0].bb == .float and pair[0].bb.float.kind == .float)) { - @trap(); - } else { - assert(pair[0].is_integer_backing()); - @trap(); - } - else - pair[0]; - const high = pair[1]; - const struct_type = module.get_anonymous_struct_pair(.{ low, high }); - assert(struct_type.bb.structure.fields[1].byte_offset == 8); - - return struct_type; - } - - const IndirectReturn = struct { - type: *Type, - }; - - pub fn get_indirect_return_result(indirect: IndirectReturn) Abi.Information { - if (indirect.type.is_aggregate_type_for_abi()) { - return Abi.Information.get_natural_align_indirect(.{ - .semantic_type = indirect.type, - }); - } else { - @trap(); - } - } - - pub fn get_indirect_result(module: *Module, ty: *Type, free_gpr: u32) Abi.Information { - if (!ty.is_aggregate_type_for_abi() and !is_illegal_vector_type(ty) and !ty.is_arbitrary_bit_integer()) { - return switch (ty.is_promotable_integer_type_for_abi()) { - true => @trap(), - false => Abi.Information.get_direct(module, .{ - .semantic_type = ty, - .type = ty, - }), - }; - } else { - // TODO CXX ABI - const alignment = @max(ty.get_byte_alignment(), 8); - const size = ty.get_byte_size(); - return switch (free_gpr == 0 and alignment == 8 and size <= 8) { - true => @trap(), - false => Abi.Information.get_indirect(.{ - .semantic_type = ty, - .alignment = alignment, - }), - }; - } - } - - pub fn is_illegal_vector_type(ty: *Type) bool { - return switch (ty.bb) { - .vector => @trap(), - else => false, - }; - } - - pub fn emit_va_arg_from_memory(module: *Module, va_list_pointer: *llvm.Value, va_list_struct: *Type, arg_type: *Type) *llvm.Value { - const overflow_arg_area_pointer = module.llvm.builder.create_struct_gep(va_list_struct.llvm.abi.?.to_struct(), va_list_pointer, 2); - const overflow_arg_area_type = va_list_struct.bb.structure.fields[2].type; - const overflow_arg_area = module.create_load(.{ .type = overflow_arg_area_type, .value = overflow_arg_area_pointer }); - if (arg_type.get_byte_alignment() > 8) { - @trap(); - } - const arg_type_size = arg_type.get_byte_size(); - const raw_offset = lib.align_forward_u64(arg_type_size, 8); - const offset = module.integer_type(32, false).llvm.abi.?.to_integer().get_constant(raw_offset, @intFromBool(false)); - const new_overflow_arg_area = module.llvm.builder.create_gep(.{ - .type = module.integer_type(8, false).llvm.abi.?, - .aggregate = overflow_arg_area, - .indices = &.{offset.to_value()}, - .inbounds = false, - }); - _ = module.create_store(.{ .type = overflow_arg_area_type, .source_value = new_overflow_arg_area, .destination_value = overflow_arg_area_pointer }); - return overflow_arg_area; - } - }; -}; - -pub fn compile(arena: *Arena, options: Options) void { - var types = Type.Buffer.initialize(); - const void_type = types.append(.{ - .name = "void", - .bb = .void, - }); - - for ([2]bool{ false, true }) |sign| { - for (1..64 + 1) |bit_count| { - const name_buffer = [3]u8{ if (sign) 's' else 'u', @intCast(if (bit_count < 10) bit_count % 10 + '0' else bit_count / 10 + '0'), if (bit_count > 9) @intCast(bit_count % 10 + '0') else 0 }; - const name_length = @as(u64, 2) + @intFromBool(bit_count > 9); - - const name = arena.duplicate_string(name_buffer[0..name_length]); - - const ty = types.append(.{ - .name = name, - .bb = .{ - .integer = .{ - .bit_count = @intCast(bit_count), - .signed = sign, - }, - }, - }); - _ = ty; - } - } - - for ([2]bool{ false, true }) |sign| { - const name = if (sign) "s128" else "u128"; - const ty = types.append(.{ - .name = name, - .bb = .{ - .integer = .{ - .bit_count = 128, - .signed = sign, - }, - }, - }); - _ = ty; - } - - const noreturn_type = types.append(.{ - .name = "noreturn", - .bb = .noreturn, - }); - - const globals = Global.Buffer.initialize(); - var values = Value.Buffer.initialize(); - const void_value = values.add(); - void_value.* = .{ - .bb = .infer_or_ignore, - .type = void_type, - }; - - var module = Module{ - .arena = arena, - .content = options.content, - .has_debug_info = options.has_debug_info, - .offset = 0, - .line_offset = 0, - .line_character_offset = 0, - .types = types, - .globals = globals, - .locals = .initialize(), - .values = values, - .macros = .initialize(), - .pointer_types = .initialize(), - .slice_types = .initialize(), - .pair_struct_types = .initialize(), - .array_types = .initialize(), - .lexical_blocks = .initialize(), - .statements = .initialize(), - .void_type = void_type, - .noreturn_type = noreturn_type, - .void_value = void_value, - .scope = .{ - .kind = .global, - .column = 0, - .line = 0, - .parent = null, - }, - .name = options.name, - .path = options.path, - .executable = options.executable, - .objects = options.objects, - .target = options.target, - .build_mode = options.build_mode, - .silent = options.silent, - }; - - module.parse(); - module.emit(); -} diff --git a/src/compiler.bbb b/src/compiler.bbb index c30f9a3..6a69b54 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -445,7 +445,7 @@ arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8 arena_allocate = macro [T] (arena: &Arena, count: u64) &T { - return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #alignof(T))); + return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #align_of(T))); } arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8 diff --git a/src/compiler.cpp b/src/compiler.cpp new file mode 100644 index 0000000..45584e3 --- /dev/null +++ b/src/compiler.cpp @@ -0,0 +1,506 @@ +#include + +fn void compile(Arena* arena, Options options) +{ + auto base_allocation_type_count = i128_offset + // 64 * 2 for basic integer types + 2 + // u128, s128 + 2; // void, noreturn + auto base_type_allocation = arena_allocate(arena, base_allocation_type_count); + + auto* type_it = base_type_allocation.pointer; + + bool signs[] = {false, true}; + Type* previous = 0; + + for (bool sign: signs) + { + for (u32 bit_index = 0; bit_index < 64; bit_index += 1) + { + auto bit_count = bit_index + 1; + auto first_digit = (u8)(bit_count < 10 ? bit_count % 10 + '0' : bit_count / 10 + '0'); + auto second_digit = (u8)(bit_count > 9 ? bit_count % 10 + '0' : 0); + u8 name_buffer[] = { u8(sign ? 's' : 'u'), first_digit, second_digit }; + u64 name_length = 2 + (bit_count > 9); + auto name_stack = String{name_buffer, name_length}; + + auto name = arena_duplicate_string(arena, name_stack); + + *type_it = { + .integer = { + .bit_count = bit_count, + .is_signed = sign, + }, + .id = TypeId::integer, + .name = name, + }; + if (previous) previous->next = type_it; + previous = type_it; + type_it += 1; + } + } + + for (bool sign: signs) + { + auto name = sign ? string_literal("s128") : string_literal("u128"); + *type_it = { + .integer = { + .bit_count = 128, + .is_signed = sign, + }, + .id = TypeId::integer, + .name = name, + .next = previous, + }; + if (previous) previous->next = type_it; + previous = type_it; + type_it += 1; + } + + auto void_type = type_it; + type_it += 1; + auto noreturn_type = type_it; + type_it += 1; + assert((u64)(type_it - base_type_allocation.pointer) == base_allocation_type_count); + + previous->next = void_type; + *void_type = { + .id = TypeId::void_type, + .name = string_literal("void"), + .next = noreturn_type, + }; + *noreturn_type = { + .id = TypeId::noreturn, + .name = string_literal("noreturn"), + }; + + auto module = Module{ + .arena = arena, + .content = options.content, + .first_type = base_type_allocation.pointer, + .last_type = noreturn_type, + .scope = { + .kind = ScopeKind::global, + }, + .name = options.name, + .path = options.path, + .executable = options.executable, + .objects = options.objects, + .libraries = options.libraries, + .target = options.target, + .build_mode = options.build_mode, + .has_debug_info = options.has_debug_info, + .silent = options.silent, + }; + module.void_value = new_value(&module); + *module.void_value = { + .type = void_type, + .id = ValueId::infer_or_ignore, + }; + + parse(&module); + emit(&module); +} + +fn String compile_file(Arena* arena, Compile options) +{ + auto relative_file_path = options.relative_file_path; + if (relative_file_path.length < 5) + { + bb_fail(); + } + + auto extension_start = string_last_character(relative_file_path, '.'); + if (extension_start == string_no_match) + { + bb_fail(); + } + + if (!relative_file_path(extension_start).equal(string_literal(".bbb"))) + { + bb_fail(); + } + + auto separator_index = string_last_character(relative_file_path, '/'); + separator_index = separator_index == string_no_match ? 0 : separator_index; + + auto base_start = separator_index + (separator_index != 0 || relative_file_path[separator_index] == '/'); + auto base_name = relative_file_path(base_start, extension_start); + + auto is_compiler = relative_file_path.equal(string_literal("src/compiler.bbb")); + + String output_path_dir_parts[] = { + string_literal(base_cache_dir), + is_compiler ? string_literal("/compiler/") : string_literal("/"), + build_mode_to_string(options.build_mode), + string_literal("_"), + options.has_debug_info ? string_literal("di") : string_literal("nodi"), + }; + auto output_path_dir = arena_join_string(arena, array_to_slice(output_path_dir_parts)); + + make_directory(base_cache_dir); + + if (is_compiler) + { + make_directory(base_cache_dir "/compiler"); + } + + make_directory(cstr(output_path_dir)); + + String output_path_base_parts[] = { + output_path_dir, + string_literal("/"), + base_name, + }; + auto output_path_base = arena_join_string(arena, array_to_slice(output_path_base_parts)); + String output_object_path_parts[] = { + output_path_base, + string_literal(".o"), + }; + auto output_object_path = arena_join_string(arena, array_to_slice(output_object_path_parts)); + auto output_executable_path = output_path_base; + + auto file_content = file_read(arena, relative_file_path); + auto file_path = path_absolute(arena, relative_file_path); + + String objects[] = { + output_object_path, + }; + Slice object_slice = array_to_slice(objects); + + String libraries[] = { + string_literal("build/libc_abi.a"), + }; + Slice library_slice = {}; + if (base_name.equal(string_literal("c_abi"))) + { + library_slice = array_to_slice(libraries); + } + + compile(arena, { + .content = file_content, + .path = file_path, + .executable = output_executable_path, + .name = base_name, + .objects = object_slice, + .libraries = library_slice, + .target = { + .cpu = CPUArchitecture::x86_64, + .os = OperatingSystem::linux_, + }, + .build_mode = options.build_mode, + .has_debug_info = options.has_debug_info, + .silent = options.silent, + }); + + return output_executable_path; +} + +global_variable String names[] = +{ + string_literal("minimal"), + string_literal("comments"), + string_literal("constant_add"), + string_literal("constant_and"), + string_literal("constant_div"), + string_literal("constant_mul"), + string_literal("constant_rem"), + string_literal("constant_or"), + string_literal("constant_sub"), + string_literal("constant_xor"), + string_literal("constant_shift_left"), + string_literal("constant_shift_right"), + string_literal("minimal_stack"), + string_literal("minimal_stack_arithmetic"), + string_literal("minimal_stack_arithmetic2"), + string_literal("minimal_stack_arithmetic3"), + string_literal("stack_negation"), + string_literal("stack_add"), + string_literal("stack_sub"), + string_literal("extend"), + string_literal("integer_max"), + string_literal("integer_hex"), + string_literal("basic_pointer"), + string_literal("basic_call"), + string_literal("basic_branch"), + string_literal("basic_array"), + string_literal("basic_enum"), + string_literal("basic_slice"), + string_literal("basic_string"), + string_literal("basic_varargs"), + string_literal("basic_while"), + string_literal("pointer"), + string_literal("pointer_cast"), + string_literal("u1_return"), + string_literal("local_type_inference"), + string_literal("global"), + string_literal("function_pointer"), + string_literal("extern"), + string_literal("byte_size"), + string_literal("argv"), + string_literal("assignment_operators"), + string_literal("not_pointer"), + string_literal("bits"), + string_literal("bits_no_backing_type"), + string_literal("bits_return_u1"), + string_literal("bits_zero"), + string_literal("comparison"), + string_literal("global_struct"), + string_literal("if_no_else"), + string_literal("if_no_else_void"), + string_literal("indirect"), + string_literal("indirect_struct"), + string_literal("indirect_varargs"), + string_literal("ret_c_bool"), + string_literal("return_type_builtin"), + string_literal("return_u64_u64"), + string_literal("select"), + string_literal("slice"), + string_literal("small_struct_ints"), + string_literal("struct_assignment"), + string_literal("struct"), + string_literal("struct_u64_u64"), + string_literal("struct_varargs"), + string_literal("struct_zero"), + string_literal("unreachable"), + string_literal("varargs"), + string_literal("c_abi0"), + string_literal("c_abi1"), + string_literal("c_med_struct_ints"), + string_literal("c_ret_struct_array"), + string_literal("c_split_struct_ints"), + string_literal("c_string_to_slice"), + string_literal("c_struct_with_array"), + string_literal("c_function_pointer"), + string_literal("basic_bool_call"), + string_literal("abi_enum_bool"), + string_literal("return_small_struct"), + string_literal("c_abi"), + string_literal("string_to_enum"), + string_literal("empty_if"), + string_literal("else_if"), + string_literal("else_if_complicated"), + string_literal("basic_shortcircuiting_if"), + string_literal("shortcircuiting_if"), + string_literal("field_access_left_assign"), + string_literal("for_each"), + string_literal("pointer_decay"), + string_literal("enum_name"), + string_literal("slice_of_slices"), + string_literal("type_alias"), + string_literal("integer_formats"), + string_literal("for_each_int"), + string_literal("bool_array"), + string_literal("basic_union"), + string_literal("break_continue"), + string_literal("constant_global_reference"), + string_literal("concat_logical_or"), + string_literal("strict_array_type"), + string_literal("pointer_struct_initialization"), + string_literal("slice_array_literal"), + string_literal("slice_only_start"), + + string_literal("basic_macro"), + string_literal("generic_macro"), + + string_literal("generic_pointer_macro"), + string_literal("noreturn_macro"), + string_literal("generic_pointer_array"), + + // string_literal("self_referential_struct"), // TODO + // string_literal("forward_declared_type"), +}; + +void entry_point(Slice arguments, Slice environment) +{ + Arena* arena = arena_initialize_default(8 * mb); + + if (arguments.length < 2) + { + bb_fail_with_message(string_literal("error: Not enough arguments\n")); + } + + String command_string = c_string_to_slice(arguments[1]); + String command_strings[] = { + string_literal("compile"), + string_literal("test"), + }; + static_assert(array_length(command_strings) == (u64)Command::count); + + backing_type(Command) i; + for (i = 0; i < (backing_type(Command))Command::count; i += 1) + { + String candidate = command_strings[i]; + if (candidate.equal(command_string)) + { + break; + } + } + + auto command = (Command)i; + + switch (command) + { + case Command::compile: + { + if (arguments.length < 3) + { + bb_fail_with_message(string_literal("Not enough arguments for command 'compile'\n")); + } + + auto build_mode = BuildMode::debug_none; + auto has_debug_info = true; + + if (arguments.length >= 4) + { + auto build_mode_string = c_string_to_slice(arguments[3]); + String build_mode_strings[] = { + string_literal("debug_none"), + string_literal("debug"), + string_literal("soft_optimize"), + string_literal("optimize_for_speed"), + string_literal("optimize_for_size"), + string_literal("aggressively_optimize_for_speed"), + string_literal("aggressively_optimize_for_size"), + }; + + backing_type(BuildMode) i; + for (i = 0; i < (backing_type(BuildMode))BuildMode::count; i += 1) + { + String candidate = build_mode_strings[i]; + if (build_mode_string.equal(candidate)) + { + break; + } + } + + build_mode = (BuildMode)i; + if (build_mode == BuildMode::count) + { + bb_fail_with_message(string_literal("Invalid build mode\n")); + } + } + + if (arguments.length >= 5) + { + auto has_debug_info_string = c_string_to_slice(arguments[3]); + if (has_debug_info_string.equal(string_literal("true"))) + { + has_debug_info = true; + } + else if (has_debug_info_string.equal(string_literal("false"))) + { + has_debug_info = false; + } + else + { + bb_fail_with_message(string_literal("Wrong value for has_debug_info\n")); + } + } + + auto relative_file_path = c_string_to_slice(arguments[2]); + + compile_file(arena, { + .relative_file_path = relative_file_path, + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .silent = false, + }); + } break; + case Command::test: + { + // TODO: provide more arguments + if (arguments.length != 2) + { + bb_fail_with_message(string_literal("error: 'test' command takes no arguments")); + } + + bool has_debug_info_array[] = {true, false}; + + for (auto name: names) + { + for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1)) + { + for (bool has_debug_info : has_debug_info_array) + { + auto position = arena->position; + + String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") }; + auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts)); + + auto executable_path = compile_file(arena, { + .relative_file_path = relative_file_path, + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .silent = true, + }); + + char* const arguments[] = + { + (char*)executable_path.pointer, + 0, + }; + Slice arg_slice = array_to_slice(arguments); + arg_slice.length -= 1; + auto execution = os_execute(arena, arg_slice, environment, {}); + auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; + if (!success) + { + print(string_literal("Test failed: ")); + print(executable_path); + print(string_literal("\n")); + bb_fail(); + } + + arena_restore(arena, position); + } + } + } + + for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1)) + { + for (bool has_debug_info : has_debug_info_array) + { + auto compiler = compile_file(arena, { + .relative_file_path = string_literal("src/compiler.bbb"), + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .silent = true, + }); + + for (auto name: names) + { + BuildMode build_mode = BuildMode::debug_none; + bool has_debug_info = true; + String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") }; + auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts)); + const char* const arguments[] = + { + (char*)compiler.pointer, + "compile", + (char*)relative_file_path.pointer, + (char*)build_mode_to_string(build_mode).pointer, + has_debug_info ? "true" : "false", + 0, + }; + Slice arg_slice = array_to_slice(arguments); + arg_slice.length -= 1; + auto execution = os_execute(arena, arg_slice, environment, {}); + auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; + if (!success) + { + print(string_literal("Standalone test failed: ")); + print(name); + print(string_literal("\n")); + bb_fail(); + } + break; + } + } + } + } break; + case Command::count: + { + bb_fail_with_message(string_literal("error: Invalid command\n")); + } break; + } +} diff --git a/src/compiler.hpp b/src/compiler.hpp new file mode 100644 index 0000000..53adbbc --- /dev/null +++ b/src/compiler.hpp @@ -0,0 +1,1718 @@ +#pragma once + +#include +#include +#define report_error() trap() + +enum class Command +{ + compile, + test, + count, +}; + +enum class BuildMode +{ + debug_none, + debug, + soft_optimize, + optimize_for_speed, + optimize_for_size, + aggressively_optimize_for_speed, + aggressively_optimize_for_size, + count, +}; + +global_variable constexpr u64 build_mode_count = (u64)BuildMode::count; + +fn String build_mode_to_string(BuildMode build_mode) +{ + switch (build_mode) + { + case_to_name(BuildMode, debug_none); + case_to_name(BuildMode, debug); + case_to_name(BuildMode, soft_optimize); + case_to_name(BuildMode, optimize_for_speed); + case_to_name(BuildMode, optimize_for_size); + case_to_name(BuildMode, aggressively_optimize_for_speed); + case_to_name(BuildMode, aggressively_optimize_for_size); + case BuildMode::count: unreachable(); + } +} + +fn bool build_mode_is_optimized(BuildMode build_mode) +{ + switch (build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + return false; + case BuildMode::soft_optimize: + case BuildMode::optimize_for_speed: + case BuildMode::optimize_for_size: + case BuildMode::aggressively_optimize_for_speed: + case BuildMode::aggressively_optimize_for_size: + return true; + case BuildMode::count: unreachable(); + } +} + +enum class ValueKind +{ + right, + left, +}; + +enum class CPUArchitecture +{ + x86_64, +}; + +enum class OperatingSystem +{ + linux_, +}; + +struct Type; +struct Value; +struct Local; +struct Global; +struct Block; +struct Statement; +struct Variable; +struct Argument; +struct Scope; +struct MacroDeclaration; + +struct DirectAttributes +{ + u32 offset; + u32 alignment; +}; + +struct IndirectAttributes +{ + u32 alignment; + u32 address_space; +}; + +enum class AbiKind : u8 +{ + ignore, + direct, + extend, + indirect, + indirect_aliased, + expand, + coerce_and_expand, + in_alloca, +}; + +struct AbiFlags +{ + AbiKind kind; + bool padding_in_reg; + bool in_alloca_sret; + bool in_alloca_indirect; + bool indirect_by_value; + bool indirect_realign; + bool sret_after_this; + bool in_reg; + bool can_be_flattened; + bool sign_extension; +}; + +struct AbiInformation +{ + Type* semantic_type; + Type* coerce_to_type; + union + { + Type* type; + Type* unpadded_coerce_and_expand_type; + } padding; + u16 padding_argument_index; + union + { + DirectAttributes direct; + IndirectAttributes indirect; + u32 alloca_field_index; + } attributes; + AbiFlags flags; + u16 abi_start; + u16 abi_count; + + inline void set_sret_after_this(bool sret_after_this) + { + assert(flags.kind == AbiKind::indirect); + flags.sret_after_this = sret_after_this; + } + + inline void set_indirect_realign(bool realign) + { + assert(flags.kind == AbiKind::indirect); + flags.indirect_realign = realign; + } + + inline void set_indirect_by_value(bool by_value) + { + assert(flags.kind == AbiKind::indirect); + flags.indirect_by_value = by_value; + } + + inline void set_indirect_align(u32 alignment) + { + assert(flags.kind == AbiKind::indirect || flags.kind == AbiKind::indirect_aliased); + attributes.indirect.alignment = alignment; + } + + inline bool can_have_coerce_to_type() + { + switch (flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + case AbiKind::coerce_and_expand: + return true; + default: + return false; + } + } + + inline void set_coerce_to_type(Type* coerce_to_type) + { + assert(can_have_coerce_to_type()); + this->coerce_to_type = coerce_to_type; + } + + inline Type* get_coerce_to_type() + { + assert(can_have_coerce_to_type()); + return coerce_to_type; + } + + inline void set_padding_type(Type* padding_type) + { + assert(can_have_padding_type()); + padding = { + .type = padding_type, + }; + } + + inline bool can_have_padding_type() + { + switch (flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + case AbiKind::indirect: + case AbiKind::indirect_aliased: + case AbiKind::expand: + return true; + default: + return false; + } + } + + inline Type* get_padding_type() + { + return can_have_padding_type() ? padding.type : 0; + } + + inline void set_direct_offset(u32 offset) + { + assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend); + attributes.direct.offset = offset; + } + + inline void set_direct_alignment(u32 alignment) + { + assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend); + attributes.direct.alignment = alignment; + } + + inline void set_can_be_flattened(bool can_be_flattened) + { + assert(flags.kind == AbiKind::direct); + flags.can_be_flattened = can_be_flattened; + } + + inline bool get_can_be_flattened() + { + return flags.can_be_flattened; + } +}; + +struct Target +{ + CPUArchitecture cpu; + OperatingSystem os; +}; + +struct Compile +{ + String relative_file_path; + BuildMode build_mode; + bool has_debug_info; + bool silent; +}; + +#define base_cache_dir "bb-cache" + +enum class CallingConvention +{ + c, + count, +}; + +enum class ResolvedCallingConvention +{ + system_v, + win64, + count, +}; + +fn ResolvedCallingConvention resolve_calling_convention(CallingConvention cc) +{ + switch (cc) + { + case CallingConvention::c: + // TODO: + return ResolvedCallingConvention::system_v; + case CallingConvention::count: unreachable(); + } +} + +enum class InlineBehavior +{ + normal, + always_inline, + no_inline, + inline_hint, +}; + +struct FunctionAttributes +{ + InlineBehavior inline_behavior; + bool naked; +}; + +enum class TypeId +{ + void_type, + noreturn, + forward_declaration, + integer, + function, + pointer, + array, + enumerator, + structure, + bits, + alias, + union_type, + unresolved, + vector, + floating_point, +}; + +struct TypeInteger +{ + u32 bit_count; + bool is_signed; +}; + +struct AbiRegisterCountSystemV +{ + u32 gpr; + u32 sse; +}; + +union AbiRegisterCount +{ + AbiRegisterCountSystemV system_v; +}; + +struct TypeFunction +{ + Type* semantic_return_type; + Slice semantic_argument_types; + CallingConvention calling_convention; + bool is_variable_arguments; + // ABI + Slice abi_argument_types; + Type* abi_return_type; + AbiRegisterCount available_registers; + Slice argument_abis; + AbiInformation return_abi; +}; + +struct TypePointer +{ + Type* element_type; + Type* next; +}; + +struct TypeArray +{ + Type* element_type; + u64 element_count; + Type* next; +}; + +struct UnresolvedEnumField +{ + String name; + Value* value; +}; + +struct EnumField +{ + String name; + u64 value; +}; + +struct UnresolvedTypeEnum +{ + Slice fields; + Type* backing_type; + u32 line; + bool implicit_backing_type; + Type* resolved_type; +}; + +struct TypeEnum +{ + Slice fields; + Type* backing_type; + LLVMValueRef enum_to_string_function; + LLVMValueRef string_to_enum_function; + Type* string_to_enum_struct_type; + Global* name_array; + u32 line; +}; + +struct Field +{ + String name; + Type* type; + u64 offset; + u32 line; +}; + +struct TypeStruct +{ + Slice fields; + u64 byte_size; + u32 byte_alignment; + u32 line; + bool is_slice; + Type* next; +}; + +struct TypeBits +{ + Slice fields; + Type* backing_type; + u32 line; + bool is_implicit_backing_type; +}; + +struct TypeAlias +{ + Type* type; + Scope* scope; + u32 line; +}; + +struct UnionField +{ + Type* type; + String name; + u32 line; +}; + +struct TypeUnion +{ + Slice fields; + u64 byte_size; + u32 byte_alignment; + u32 line; + u32 biggest_field; +}; + +struct LLVMType +{ + LLVMTypeRef abi; + LLVMTypeRef memory; + LLVMMetadataRef debug; +}; + +struct Type +{ + union + { + TypeInteger integer; + TypeFunction function; + TypePointer pointer; + TypeArray array; + TypeEnum enumerator; + TypeStruct structure; + TypeBits bits; + TypeAlias alias; + TypeUnion union_type; + }; + TypeId id; + String name; + Type* next; + LLVMType llvm; +}; + +fn u32 align_bit_count(u32 bit_count) +{ + auto aligned_bit_count = MAX(8, next_power_of_two(bit_count)); + assert(aligned_bit_count % 8 == 0); + return aligned_bit_count; +} + +fn u32 aligned_byte_count_from_bit_count(u32 bit_count) +{ + auto aligned_bit_count = align_bit_count(bit_count); + return aligned_bit_count / 8; +} + +fn u64 get_byte_size(Type* type) +{ + switch (type->id) + { + case TypeId::integer: + { + auto byte_count = aligned_byte_count_from_bit_count(type->integer.bit_count); + assert(byte_count == 1 || byte_count == 2 || byte_count == 4 || byte_count == 8 || byte_count == 16); + return byte_count; + } break; + case TypeId::pointer: + { + return 8; + } break; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto element_size = get_byte_size(element_type); + auto element_count = type->array.element_count; + auto result = element_size * element_count; + return result; + } break; + case TypeId::structure: + { + auto result = type->structure.byte_size; + return result; + } break; + case TypeId::enumerator: + { + auto result = get_byte_size(type->enumerator.backing_type); + return result; + } break; + case TypeId::bits: + { + auto result = get_byte_size(type->bits.backing_type); + return result; + } break; + case TypeId::alias: + { + auto result = get_byte_size(type->alias.type); + return result; + } break; + case TypeId::union_type: + { + auto result = type->union_type.byte_size; + return result; + } break; + default: trap(); + } +} + +fn u32 get_byte_alignment(Type* type) +{ + switch (type->id) + { + case TypeId::integer: + { + auto aligned_byte_count = aligned_byte_count_from_bit_count(type->integer.bit_count); + assert(aligned_byte_count == 1 || aligned_byte_count == 2 || aligned_byte_count == 4 || aligned_byte_count == 8 || aligned_byte_count == 16); + return aligned_byte_count; + } break; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto result = get_byte_alignment(element_type); + return result; + } break; + case TypeId::structure: + { + auto result = type->structure.byte_alignment; + return result; + } break; + case TypeId::enumerator: + { + auto result = get_byte_alignment(type->enumerator.backing_type); + return result; + } break; + case TypeId::pointer: + { + return 8; + } break; + case TypeId::bits: + { + auto result = get_byte_alignment(type->bits.backing_type); + return result; + } break; + case TypeId::union_type: + { + return type->union_type.byte_alignment; + } break; + case TypeId::alias: + { + return get_byte_alignment(type->alias.type); + } break; + default: trap(); + } +} + +fn u64 get_bit_size(Type* type) +{ + switch (type->id) + { + case TypeId::integer: return type->integer.bit_count; + case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type); + case TypeId::alias: return get_bit_size(type->alias.type); + default: trap(); + } +} + +enum class ScopeKind +{ + global, + function, + local, + for_each, + macro_declaration, + macro_instantiation, +}; + +struct Scope +{ + Scope* parent; + u32 line; + u32 column; + ScopeKind kind; + LLVMMetadataRef llvm; +}; + +enum class StatementId +{ + local, + expression, + return_st, + assignment, + if_st, + block, + while_st, + switch_st, + for_each, + break_st, + continue_st, +}; + +enum class StatementAssignmentId +{ + assign, + assign_add, + assign_sub, + assign_mul, + assign_div, + assign_rem, + assign_shift_left, + assign_shift_right, + assign_and, + assign_or, + assign_xor, +}; + +struct StatementAssignment +{ + Value* left; + Value* right; + StatementAssignmentId id; +}; + +struct StatementIf +{ + Value* condition; + Statement* if_statement; + Statement* else_statement; +}; + +struct StatementWhile +{ + Value* condition; + Block* block; +}; + +struct StatementSwitchClause +{ + Slice values; + Block* block; + LLVMBasicBlockRef basic_block; +}; + +struct StatementSwitch +{ + Value* discriminant; + Slice clauses; +}; + +enum class ForEachKind +{ + slice, + range, +}; + +struct StatementForEach +{ + Local* first_local; + Local* last_local; + Slice left_values; + Slice right_values; + Statement* predicate; + Scope scope; + ForEachKind kind; +}; + +struct Statement +{ + union + { + Local* local; + Value* expression; + Value* return_st; + StatementAssignment assignment; + StatementIf if_st; + Block* block; + StatementWhile while_st; + StatementSwitch switch_st; + StatementForEach for_each; + }; + Statement* next; + StatementId id; + u32 line; + u32 column; +}; + +struct Block +{ + Local* first_local; + Local* last_local; + Statement* first_statement; + Scope scope; +}; + +enum class ValueId +{ + infer_or_ignore, + external_function, + function, + constant_integer, + unary, + binary, + unary_type, + variable_reference, + macro_reference, + macro_instantiation, + call, + global, + array_initialization, + array_expression, + slice_expression, + enum_literal, + trap, + field_access, + string_literal, + va_start, + va_arg, + aggregate_initialization, + undefined, + unreachable, + zero, + select, + string_to_enum, + local, + argument, +}; + +struct ValueConstantInteger +{ + u64 value; + bool is_signed; +}; + +struct FunctionLLVM +{ + LLVMBasicBlockRef return_block; + LLVMValueRef return_alloca; +}; + +struct ValueFunction +{ + Slice arguments; + Scope scope; + Block* block; + FunctionAttributes attributes; + FunctionLLVM llvm; +}; + +enum class UnaryId +{ + minus, + plus, + ampersand, + exclamation, + tilde, + enum_name, + extend, + truncate, + pointer_cast, + int_from_enum, + int_from_pointer, + va_end, + bitwise_not, + dereference, + pointer_from_int, +}; + +struct ValueUnary +{ + Value* value; + UnaryId id; +}; + +enum class UnaryTypeId +{ + align_of, + byte_size, + integer_max, +}; + +struct ValueUnaryType +{ + Type* type; + UnaryTypeId id; +}; + +enum class BinaryId +{ + add, + sub, + mul, + div, + rem, + bitwise_and, + bitwise_or, + bitwise_xor, + shift_left, + shift_right, + compare_equal, + compare_not_equal, + compare_greater, + compare_less, + compare_greater_equal, + compare_less_equal, + logical_and, + logical_or, + logical_and_shortcircuit, + logical_or_shortcircuit, +}; + +struct ValueBinary +{ + Value* left; + Value* right; + BinaryId id; +}; + +struct ValueCall +{ + Value* callable; + Slice arguments; + Type* function_type; +}; + +struct ValueArrayInitialization +{ + Slice values; + bool is_constant; +}; + +struct ValueArrayExpression +{ + Value* array_like; + Value* index; +}; + +struct ValueFieldAccess +{ + Value* aggregate; + String field_name; +}; + +struct ValueSliceExpression +{ + Value* array_like; + Value* start; + Value* end; +}; + +struct ValueVaArg +{ + Value* va_list; + Type* type; +}; + +struct ValueAggregateInitialization +{ + Slice names; + Slice values; + bool is_constant; + bool zero; +}; + +struct ValueSelect +{ + Value* condition; + Value* true_value; + Value* false_value; +}; + +struct ValueStringToEnum +{ + Type* type; + Value* string; +}; + +enum class ConstantArgumentId +{ + value, + type, +}; + +struct ConstantArgument +{ + String name; + union + { + Type* type; + Value* value; + }; + ConstantArgumentId id; +}; + +struct MacroDeclaration +{ + Slice arguments; + Slice constant_arguments; + Type* return_type; + Block* block; + String name; + Scope scope; + MacroDeclaration* next; + + bool is_generic() + { + return constant_arguments.length != 0; + } +}; + +struct MacroInstantiation +{ + MacroDeclaration* declaration; + Global* instantiation_function; + Slice declaration_arguments; + Slice instantiation_arguments; + Slice constant_arguments; + Type* return_type; + Block* block; + Scope scope; + u32 line; + u32 column; + LLVMValueRef return_alloca; + LLVMBasicBlockRef return_block; +}; + +fn bool variable_is_constant(Value* value); + +struct Value +{ + union + { + ValueConstantInteger constant_integer; + ValueFunction function; + ValueUnary unary; + ValueBinary binary; + Variable* variable_reference; + ValueUnaryType unary_type; + ValueCall call; + ValueArrayInitialization array_initialization; + ValueArrayExpression array_expression; + String enum_literal; + ValueFieldAccess field_access; + ValueSliceExpression slice_expression; + String string_literal; + ValueVaArg va_arg; + ValueAggregateInitialization aggregate_initialization; + ValueSelect select; + ValueStringToEnum string_to_enum; + MacroDeclaration* macro_reference; + MacroInstantiation macro_instantiation; + }; + Type* type; + ValueId id; + ValueKind kind; + LLVMValueRef llvm; + + bool is_constant() + { + switch (id) + { + case ValueId::constant_integer: + case ValueId::enum_literal: + case ValueId::unary_type: + case ValueId::string_literal: + return true; + case ValueId::unary: + case ValueId::binary: + case ValueId::field_access: + case ValueId::array_expression: + case ValueId::call: + case ValueId::select: + return false; + case ValueId::variable_reference: + { + return variable_is_constant(this); + } break; + case ValueId::array_initialization: + { + assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set + return array_initialization.is_constant; + } break; + case ValueId::aggregate_initialization: + { + assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set + return aggregate_initialization.is_constant; + } break; + default: trap(); + } + } +}; + +struct Variable +{ + Value* storage; + Value* initial_value; + Type* type; + Scope* scope; + String name; + u32 line; + u32 column; +}; + +fn bool variable_is_constant(Value* value) +{ + assert(value->id == ValueId::variable_reference); + auto* variable = value->variable_reference; + + switch (value->kind) + { + case ValueKind::left: + { + switch (variable->scope->kind) + { + case ScopeKind::global: + return true; + default: + return false; + } + } break; + case ValueKind::right: + return false; + } +} + +enum class Linkage +{ + internal, + external, +}; + +struct Global +{ + Variable variable; + Linkage linkage; + bool emitted; + Global* next; +}; + +struct Local +{ + Variable variable; + Local* next; +}; + +struct Argument +{ + Variable variable; + u32 index; +}; + +struct LLVMIntrinsicId +{ + u32 n; +}; + +enum class IntrinsicIndex +{ + trap, + va_start, + va_end, + va_copy, + count, +}; + +global_variable String intrinsic_names[] = { + string_literal("llvm.trap"), + string_literal("llvm.va_start"), + string_literal("llvm.va_end"), + string_literal("llvm.va_copy"), +}; + +static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count); + +struct ModuleLLVM +{ + LLVMContextRef context; + LLVMModuleRef module; + LLVMBuilderRef builder; + LLVMDIBuilderRef di_builder; + LLVMMetadataRef file; + LLVMMetadataRef compile_unit; + LLVMTypeRef pointer_type; + LLVMTypeRef void_type; + LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count]; + LLVMValueRef memcmp; + LLVMMetadataRef inlined_at; + LLVMBasicBlockRef continue_block; + LLVMBasicBlockRef exit_block; + u32 debug_tag; +}; + +struct Module +{ + Arena* arena; + String content; + u64 offset; + u64 line_offset; + u64 line_character_offset; + + Type* first_pointer_type; + Type* first_slice_type; + Type* first_pair_struct_type; + Type* first_array_type; + + Type* first_type; + Type* last_type; + Type* va_list_type; + + Value* void_value; + Global* first_global; + Global* last_global; + Global* current_function; + MacroDeclaration* first_macro_declaration; + MacroDeclaration* last_macro_declaration; + MacroDeclaration* current_macro_declaration; + MacroInstantiation* current_macro_instantiation; + + ModuleLLVM llvm; + Scope scope; + + String name; + String path; + String executable; + Sliceobjects; + Slicelibraries; + + Target target; + BuildMode build_mode; + bool has_debug_info; + bool silent; +}; + +constexpr u64 i128_offset = 64 * 2; +constexpr u64 void_offset = i128_offset + 2; + +fn Type* integer_type(Module* module, TypeInteger integer) +{ + assert(integer.bit_count); + assert(integer.bit_count <= 64 || integer.bit_count == 128); + if (integer.is_signed) + { + assert(integer.bit_count > 1); + } + auto index = integer.bit_count == 128 ? (i128_offset + integer.is_signed) : (integer.bit_count - 1 + (64 * integer.is_signed)); + auto* result_type = module->first_type + index; + assert(result_type->id == TypeId::integer); + assert(result_type->integer.bit_count == integer.bit_count); + assert(result_type->integer.is_signed == integer.is_signed); + return result_type; +} + +fn Type* void_type(Module* module) +{ + return module->first_type + void_offset; +} + +fn Type* noreturn_type(Module* module) +{ + return void_type(module) + 1; +} + +fn Type* uint1(Module* module) +{ + return integer_type(module, { .bit_count = 1, .is_signed = false }); +} + +fn Type* uint8(Module* module) +{ + return integer_type(module, { .bit_count = 8, .is_signed = false }); +} + +fn Type* uint32(Module* module) +{ + return integer_type(module, { .bit_count = 32, .is_signed = false }); +} + +fn Type* uint64(Module* module) +{ + return integer_type(module, { .bit_count = 64, .is_signed = false }); +} + +fn Type* sint32(Module* module) +{ + return integer_type(module, { .bit_count = 32, .is_signed = true }); +} + +struct Options +{ + String content; + String path; + String executable; + String name; + Slice objects; + Slice libraries; + Target target; + BuildMode build_mode; + bool has_debug_info; + bool silent; +}; + +fn Type* type_allocate_init(Module* module, Type type) +{ + auto* result = &arena_allocate(module->arena, 1)[0]; + *result = type; + + if (module->last_type) + { + module->last_type->next = result; + module->last_type = result; + } + else + { + assert(!module->first_type); + module->first_type = result; + module->last_type = result; + } + + return result; +} + +fn Value* new_value(Module* module) +{ + auto* result = &arena_allocate(module->arena, 1)[0]; + return result; +} + +fn Slice new_value_array(Module* module, u64 count) +{ + auto result = arena_allocate(module->arena, count); + return result; +} + +fn Slice new_type_array(Module* module, u64 count) +{ + auto result = arena_allocate(module->arena, count); + return result; +} + +fn Global* new_global(Module* module) +{ + auto* result = &arena_allocate(module->arena, 1)[0]; + + if (module->last_global) + { + module->last_global->next = result; + module->last_global = result; + } + else + { + assert(!module->first_global); + module->first_global = result; + module->last_global = result; + } + + return result; +} + +fn Type* get_pointer_type(Module* module, Type* element_type) +{ + auto last_pointer_type = module->first_pointer_type; + while (last_pointer_type) + { + assert(last_pointer_type->id == TypeId::pointer); + if (last_pointer_type->pointer.element_type == element_type) + { + return last_pointer_type; + } + + if (!last_pointer_type->pointer.next) + { + break; + } + + last_pointer_type = last_pointer_type->pointer.next; + } + + String name_parts[] = { + string_literal("&"), + element_type->name, + }; + + auto result = type_allocate_init(module, { + .pointer = { + .element_type = element_type, + }, + .id = TypeId::pointer, + .name = arena_join_string(module->arena, array_to_slice(name_parts)), + }); + + if (last_pointer_type) + { + assert(module->first_pointer_type); + last_pointer_type->pointer.next = result; + } + else + { + assert(!module->first_pointer_type); + module->first_pointer_type = result; + } + + return result; +} + +fn bool is_slice(Type* type) +{ + switch (type->id) + { + case TypeId::structure: + { + return type->structure.is_slice; + } + default: return false; + } +} + +fn Type* get_slice_type(Module* module, Type* element_type) +{ + Type* slice_type = module->first_slice_type; + + if (slice_type) + { + while (1) + { + assert(is_slice(slice_type)); + assert(slice_type->structure.fields.length == 2); + auto* pointer_type = slice_type->structure.fields[0].type; + assert(pointer_type->id == TypeId::pointer); + auto* candidate_element_type = pointer_type->pointer.element_type; + if (candidate_element_type == element_type) + { + return slice_type; + } + + if (!slice_type->structure.next) + { + break; + } + + slice_type = slice_type->structure.next; + } + } + + Type* last_slice_type = slice_type; + auto fields = arena_allocate(module->arena, 2); + fields[0] = { + .name = string_literal("pointer"), + .type = get_pointer_type(module, element_type), + .offset = 0, + .line = 0, + }; + fields[1] = { + .name = string_literal("length"), + .type = uint64(module), + .offset = 8, + .line = 0, + }; + String name_parts[] = { + string_literal("[]"), + element_type->name, + }; + + auto result = type_allocate_init(module, { + .structure = { + .fields = fields, + .byte_size = 16, + .byte_alignment = 8, + .line = 0, + .is_slice = true, + }, + .id = TypeId::structure, + .name = arena_join_string(module->arena, array_to_slice(name_parts)), + }); + + if (last_slice_type) + { + last_slice_type->structure.next = result; + } + else + { + module->first_slice_type = result; + } + + return result; +} + +fn String array_name(Module* module, Type* element_type, u64 element_count) +{ + u8 buffer[512]; + auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) }; + + u64 i = 0; + + buffer[i] = '['; + i += 1; + + i += format_integer_decimal(buffer_slice(i), element_count); + + buffer[i] = ']'; + i += 1; + + auto element_name = element_type->name; + memcpy(buffer + i, element_name.pointer, element_name.length); + i += element_name.length; + + auto name = arena_duplicate_string(module->arena, buffer_slice(0, i)); + return name; +} + +fn Type* get_array_type(Module* module, Type* element_type, u64 element_count) +{ + assert(element_type); + assert(element_count); + + Type* array_type = module->first_array_type; + + if (array_type) + { + while (1) + { + assert(array_type->id == TypeId::array); + auto* candidate_element_type = array_type->array.element_type; + auto candidate_element_count = array_type->array.element_count; + + if (candidate_element_type == element_type && candidate_element_count == element_count) + { + return array_type; + } + + if (!array_type->array.next) + { + break; + } + + array_type = array_type->array.next; + } + } + + Type* last_array_type = array_type; + + auto result = type_allocate_init(module, { + .array = { + .element_type = element_type, + .element_count = element_count, + }, + .id = TypeId::array, + .name = array_name(module, element_type, element_count), + }); + + if (last_array_type) + { + last_array_type->array.next = result; + } + else + { + module->first_array_type = result; + } + + return result; +} + +fn Block* scope_to_block(Scope* scope) +{ + assert(scope->kind == ScopeKind::local); + auto byte_offset = offsetof(Block, scope); + auto result = (Block*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::local); + return result; +} + +fn StatementForEach* scope_to_for_each(Scope* scope) +{ + assert(scope->kind == ScopeKind::for_each); + auto byte_offset = offsetof(StatementForEach, scope); + auto result = (StatementForEach*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::for_each); + return result; +} + +fn MacroDeclaration* scope_to_macro_declaration(Scope* scope) +{ + assert(scope->kind == ScopeKind::macro_declaration); + auto byte_offset = offsetof(MacroDeclaration, scope); + auto result = (MacroDeclaration*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::macro_declaration); + return result; +} + +fn MacroInstantiation* scope_to_macro_instantiation(Scope* scope) +{ + assert(scope->kind == ScopeKind::macro_instantiation); + auto byte_offset = offsetof(MacroInstantiation, scope); + auto result = (MacroInstantiation*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::macro_instantiation); + return result; +} + +fn ValueFunction* scope_to_function(Scope* scope) +{ + assert(scope->kind == ScopeKind::function); + auto byte_offset = offsetof(ValueFunction, scope); + auto result = (ValueFunction*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::function); + return result; +} + +fn Module* scope_to_module(Scope* scope) +{ + assert(scope->kind == ScopeKind::global); + auto byte_offset = offsetof(Module, scope); + auto result = (Module*)((u8*)scope - byte_offset); + assert(result->scope.kind == ScopeKind::global); + return result; +} + +fn Value* reference_identifier(Module* module, Scope* current_scope, String identifier, ValueKind kind) +{ + assert(!identifier.equal(string_literal(""))); + assert(!identifier.equal(string_literal("_"))); + + Variable* variable = 0; + + for (Scope* scope = current_scope; scope && !variable; scope = scope->parent) + { + switch (scope->kind) + { + case ScopeKind::global: + { + assert(module == scope_to_module(scope)); + + for (Global* global = module->first_global; global; global = global->next) + { + if (identifier.equal(global->variable.name)) + { + variable = &global->variable; + break; + } + } + + for (MacroDeclaration* macro_declaration = module->first_macro_declaration; macro_declaration; macro_declaration = macro_declaration->next) + { + if (identifier.equal(macro_declaration->name)) + { + auto result = new_value(module); + *result = { + .macro_reference = macro_declaration, + .id = ValueId::macro_reference, + }; + return result; + } + } + } break; + case ScopeKind::function: + { + assert(scope->parent); + auto function = scope_to_function(scope); + for (auto& argument: function->arguments) + { + if (identifier.equal(argument.variable.name)) + { + variable = &argument.variable; + break; + } + } + } break; + case ScopeKind::local: + { + assert(scope->parent); + assert(scope->parent->kind != ScopeKind::global); + + auto block = scope_to_block(scope); + for (Local* local = block->first_local; local; local = local->next) + { + assert(!local->next || block->last_local != local); + assert((u64)local->next != 0x6e66203d206e6961); + if (identifier.equal(local->variable.name)) + { + variable = &local->variable; + break; + } + } + } break; + case ScopeKind::for_each: + { + assert(scope->parent); + auto for_each = scope_to_for_each(scope); + + for (Local* local = for_each->first_local; local; local = local->next) + { + if (identifier.equal(local->variable.name)) + { + variable = &local->variable; + break; + } + } + } break; + case ScopeKind::macro_declaration: + { + assert(scope->parent); + auto macro_declaration = scope_to_macro_declaration(scope); + + for (auto& constant_argument: macro_declaration->constant_arguments) + { + if (identifier.equal(constant_argument.name)) + { + trap(); + } + } + + for (auto& argument: macro_declaration->arguments) + { + if (identifier.equal(argument.variable.name)) + { + variable = &argument.variable; + break; + } + } + } break; + case ScopeKind::macro_instantiation: + { + assert(scope->parent); + auto macro_instantiation = scope_to_macro_instantiation(scope); + + for (auto& argument : macro_instantiation->declaration_arguments) + { + if (identifier.equal(argument.variable.name)) + { + variable = &argument.variable; + break; + } + } + } break; + } + } + + if (variable) + { + auto result = new_value(module); + *result = { + .variable_reference = variable, + .id = ValueId::variable_reference, + .kind = kind, + }; + return result; + } + else + { + report_error(); + } +} + +fn Local* new_local(Module* module, Scope* scope) +{ + auto* result = &arena_allocate(module->arena, 1)[0]; + *result = {}; + + switch (scope->kind) + { + case ScopeKind::local: + { + auto block = scope_to_block(scope); + if (block->last_local) + { + block->last_local->next = result; + block->last_local = result; + } + else + { + block->first_local = result; + block->last_local = result; + } + } break; + case ScopeKind::for_each: + { + auto for_each = scope_to_for_each(scope); + if (for_each->last_local) + { + for_each->last_local->next = result; + for_each->last_local = result; + } + else + { + for_each->first_local = result; + for_each->last_local = result; + } + } break; + default: report_error(); + } + + return result; +} + +void parse(Module* module); +void emit(Module* module); diff --git a/src/emitter.cpp b/src/emitter.cpp new file mode 100644 index 0000000..0471cc3 --- /dev/null +++ b/src/emitter.cpp @@ -0,0 +1,8681 @@ +#include +#include + +enum class EvaluationKind +{ + scalar, + aggregate, + complex, +}; + +enum class TypeKind +{ + abi, + memory, +}; +fn void analyze_block(Module* module, Block* block); +fn void emit_local_storage(Module* module, Variable* variable); +fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right); +fn void emit_macro_instantiation(Module* module, Value* value); + +fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) +{ + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + if (current_basic_block) + { + if (!LLVMGetBasicBlockTerminator(current_basic_block)) + { + LLVMBuildBr(module->llvm.builder, basic_block); + } + } + + if (current_basic_block && LLVMGetBasicBlockParent(current_basic_block)) + { + LLVMInsertExistingBasicBlockAfterInsertBlock(module->llvm.builder, basic_block); + } + else + { + LLVMAppendExistingBasicBlock(module->current_function->variable.storage->llvm, basic_block); + + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); +} + +fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice argument_types, Slice argument_values) +{ + auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index]; + auto intrinsic_function = LLVMGetIntrinsicDeclaration(module->llvm.module, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto intrinsic_function_type = LLVMIntrinsicGetType(module->llvm.context, intrinsic_id.n, argument_types.pointer, argument_types.length); + auto call = LLVMBuildCall2(module->llvm.builder, intrinsic_function_type, intrinsic_function, argument_values.pointer, argument_values.length, ""); + return call; +} + +fn EvaluationKind get_evaluation_kind(Type* type) +{ + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + case TypeId::forward_declaration: + case TypeId::unresolved: + case TypeId::function: + case TypeId::alias: + unreachable(); + case TypeId::integer: + case TypeId::pointer: + case TypeId::bits: + case TypeId::enumerator: + return EvaluationKind::scalar; + case TypeId::array: + case TypeId::structure: + case TypeId::union_type: + return EvaluationKind::aggregate; + default: + unreachable(); + } +} + +fn bool type_is_aggregate_type_for_abi(Type* type) +{ + auto evaluation_kind = get_evaluation_kind(type); + auto is_member_function_pointer_type = false; // TODO + return evaluation_kind != EvaluationKind::scalar || is_member_function_pointer_type; +} + +fn u64 get_byte_allocation_size(Type* type) +{ + auto size = get_byte_size(type); + auto alignment = get_byte_alignment(type); + auto result = align_forward(size, alignment); + return result; +} + +struct LLVMGlobal +{ + String host_triple; + String host_cpu_model; + String host_cpu_features; +}; + +global_variable LLVMGlobal llvm_global; + +fn bool type_is_signed(Type* type) +{ + switch (type->id) + { + case TypeId::integer: + return type->integer.is_signed; + case TypeId::enumerator: + return type_is_signed(type->enumerator.backing_type); + case TypeId::bits: + return type_is_signed(type->bits.backing_type); + case TypeId::pointer: // TODO: pointers should be signed? + return false; + case TypeId::alias: + return type_is_signed(type->alias.type); + default: unreachable(); + } +} + +fn bool type_is_slice(Type* type) +{ + return type->id == TypeId::structure && type->structure.is_slice; +} + +fn bool is_integral_or_enumeration_type(Type* type) +{ + switch (type->id) + { + case TypeId::alias: return is_integral_or_enumeration_type(type->alias.type); + case TypeId::integer: + case TypeId::bits: + return true; + case TypeId::structure: + return false; + default: unreachable(); + } +} + +fn Type* align_integer_type(Module* module, Type* type) +{ + auto bit_count = (u32)get_bit_size(type); + auto abi_bit_count = align_bit_count(bit_count); + bool is_signed = type_is_signed(type); + auto result = integer_type(module, { .bit_count = abi_bit_count, .is_signed = is_signed }); + return result; +} + +fn bool is_promotable_integer_type_for_abi(Type* type) +{ + switch (type->id) + { + case TypeId::integer: return type->integer.bit_count < 32; + case TypeId::bits: return is_promotable_integer_type_for_abi(type->bits.backing_type); + case TypeId::alias: return is_promotable_integer_type_for_abi(type->alias.type); + default: unreachable(); + } +} + +fn void llvm_initialize_all_raw() +{ + assert(!llvm_initialized); + + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + + llvm_global = { + .host_triple = llvm_default_target_triple(), + .host_cpu_model = llvm_host_cpu_name(), + .host_cpu_features = llvm_host_cpu_features(), + }; +} + +fn void llvm_initialize_all() +{ + if (!llvm_initialized) + { + llvm_initialize_all_raw(); + } +} + +fn bool is_arbitrary_bit_integer(Type* type) +{ + switch (type->id) + { + case TypeId::integer: switch (type->integer.bit_count) + { + case 8: + case 16: + case 32: + case 64: + case 128: + return false; + default: return true; + } break; + case TypeId::unresolved: unreachable(); + case TypeId::bits: return is_arbitrary_bit_integer(type->bits.backing_type); + case TypeId::enumerator: return is_arbitrary_bit_integer(type->enumerator.backing_type); + default: return false; + } + +} + +fn u64 integer_max_value(u32 bit_count, bool is_signed) +{ + auto max_value = bit_count == 64 ? ~(u64)0 : ((u64)1 << (bit_count - is_signed)) - 1; + return max_value; +} + +fn void dump_module(Module* module) +{ + print(llvm_module_to_string(module->llvm.module)); +} + +fn void emit_value(Module* module, Value* value, TypeKind type_kind); + +fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention) +{ + LLVMCallConv cc; + switch (calling_convention) + { + case CallingConvention::c: cc = LLVMCCallConv; break; + case CallingConvention::count: unreachable(); + } + + return cc; +} + +fn Type* resolve_alias(Module* module, Type* type) +{ + Type* result_type = 0; + switch (type->id) + { + case TypeId::pointer: + { + auto* element_type = type->pointer.element_type; + auto* resolved_element_type = resolve_alias(module, element_type); + result_type = get_pointer_type(module, resolved_element_type); + } break; + case TypeId::array: + { + auto* element_type = type->array.element_type; + auto element_count = type->array.element_count; + assert(element_count); + auto* resolved_element_type = resolve_alias(module, element_type); + result_type = get_array_type(module, resolved_element_type, element_count); + } break; + case TypeId::void_type: + case TypeId::noreturn: + case TypeId::integer: + case TypeId::enumerator: + case TypeId::function: + case TypeId::bits: + case TypeId::union_type: + { + result_type = type; + } break; + case TypeId::structure: + { + if (type->structure.is_slice) + { + auto element_type = resolve_alias(module, type->structure.fields[0].type->pointer.element_type); + result_type = get_slice_type(module, element_type); + } + else + { + result_type = type; + } + } break; + case TypeId::alias: + { + result_type = resolve_alias(module, type->alias.type); + } break; + default: unreachable(); + } + + assert(result_type); + return result_type; +} + +fn void llvm_initialize(Module* module) +{ + llvm_initialize_all(); + + auto context = LLVMContextCreate(); + auto m = llvm_context_create_module(context, module->name); + auto builder = LLVMCreateBuilderInContext(context); + LLVMDIBuilderRef di_builder = 0; + LLVMMetadataRef di_compile_unit = 0; + LLVMMetadataRef di_file = 0; + + if (module->has_debug_info) + { + di_builder = LLVMCreateDIBuilder(m); + auto last_slash = string_last_character(module->path, '/'); + if (last_slash == string_no_match) + { + report_error(); + } + auto directory = module->path(0, last_slash); + auto file_name = module->path(last_slash + 1); + di_file = LLVMDIBuilderCreateFile(di_builder, (char*)file_name.pointer, file_name.length, (char*)directory.pointer, directory.length); + auto producer_name = string_literal("bloat buster"); + auto is_optimized = build_mode_is_optimized(module->build_mode); + auto flags = string_literal(""); + u32 runtime_version = 0; + auto split_name = string_literal(""); + auto sysroot = string_literal(""); + auto sdk = string_literal(""); + di_compile_unit = LLVMDIBuilderCreateCompileUnit(di_builder, LLVMDWARFSourceLanguageC17, di_file, (char*)producer_name.pointer, producer_name.length, is_optimized, (char*)flags.pointer, flags.length, runtime_version, (char*)split_name.pointer, split_name.length, LLVMDWARFEmissionFull, 0, 0, is_optimized, (char*)sysroot.pointer, sysroot.length, (char*)sdk.pointer, sdk.length); + module->scope.llvm = di_compile_unit; + } + + module->llvm = { + .context = context, + .module = m, + .builder = builder, + .di_builder = di_builder, + .file = di_file, + .compile_unit = di_compile_unit, + .pointer_type = LLVMPointerTypeInContext(context, 0), + .void_type = LLVMVoidTypeInContext(context), + }; + + for (u64 i = 0; i < (u64)IntrinsicIndex::count; i += 1) + { + String name = intrinsic_names[i]; + module->llvm.intrinsic_table[i].n = LLVMLookupIntrinsicID((char*)name.pointer, name.length); + } +} + +enum class AbiSystemVClass +{ + none, + integer, + sse, + sse_up, + x87, + x87_up, + complex_x87, + memory, +}; + +struct AbiSystemVClassifyResult +{ + AbiSystemVClass r[2]; +}; + + +// AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is +// classified recursively so that always two fields are +// considered. The resulting class is calculated according to +// the classes of the fields in the eightbyte: +// +// (a) If both classes are equal, this is the resulting class. +// +// (b) If one of the classes is NO_CLASS, the resulting class is +// the other class. +// +// (c) If one of the classes is MEMORY, the result is the MEMORY +// class. +// +// (d) If one of the classes is INTEGER, the result is the +// INTEGER. +// +// (e) If one of the classes is X87, X87UP, COMPLEX_X87 class, +// MEMORY is used as class. +// +// (f) Otherwise class SSE is used. + +// Accum should never be memory (we should have returned) or +// ComplexX87 (because this cannot be passed in a structure). +fn AbiSystemVClass abi_system_v_merge_class(AbiSystemVClass accumulator, AbiSystemVClass field) +{ + assert(accumulator != AbiSystemVClass::memory && accumulator != AbiSystemVClass::complex_x87); + + if (accumulator == field || field == AbiSystemVClass::none) + { + return accumulator; + } + + if (field == AbiSystemVClass::memory) + { + return AbiSystemVClass::memory; + } + + if (accumulator == AbiSystemVClass::integer || field == AbiSystemVClass::integer) + { + return AbiSystemVClass::integer; + } + + if (field == AbiSystemVClass::x87 || field == AbiSystemVClass::x87_up || field == AbiSystemVClass::complex_x87 || accumulator == AbiSystemVClass::x87 || accumulator == AbiSystemVClass::x87_up) + { + return AbiSystemVClass::memory; + } + + return AbiSystemVClass::sse; +} + +fn AbiSystemVClassifyResult abi_system_v_classify_post_merge(u64 aggregate_size, AbiSystemVClassifyResult classes) +{ + AbiSystemVClassifyResult result = classes; + + if (result.r[1] == AbiSystemVClass::memory) + { + result.r[0] = AbiSystemVClass::memory; + } + + if (result.r[1] == AbiSystemVClass::x87_up) + { + trap(); + } + + if (aggregate_size > 16 && (result.r[0] != AbiSystemVClass::sse || result.r[1] != AbiSystemVClass::sse_up)) + { + result.r[0] = AbiSystemVClass::memory; + } + + if (result.r[1] == AbiSystemVClass::sse_up && result.r[0] != AbiSystemVClass::sse) + { + result.r[0] = AbiSystemVClass::sse; + } + + return result; +} + +fn bool contains_no_user_data(Type* type, u64 start, u64 end) +{ + if (get_byte_size(type) <= start) + { + return true; + } + else + { + switch (type->id) + { + case TypeId::structure: + { + u64 offset = 0; + + for (auto& field: type->structure.fields) + { + if (offset >= end) + { + break; + } + + auto field_start = offset < start ? start - offset : 0; + if (!contains_no_user_data(field.type, field_start, end - offset)) + { + return false; + } + offset += get_byte_size(field.type); + } + + return true; + } break; + case TypeId::array: + { + auto element_type = type->array.element_type; + auto element_count = type->array.element_count; + auto element_size = get_byte_size(element_type); + + for (u64 i = 0; i < element_count; i += 1) + { + auto offset = i * element_size; + if (offset >= end) + { + break; + } + + auto element_start = offset < start ? start - offset : 0; + if (!contains_no_user_data(element_type, element_start, end - offset)) + { + return false; + } + } + trap(); + } break; + default: return false; + } + } +} + +fn Field* get_member_at_offset(Type* struct_type, u32 offset) +{ + assert(struct_type->id == TypeId::structure); + + Field* result = 0; + + if (struct_type->structure.byte_size > offset) + { + u32 offset_it = 0; + auto fields = struct_type->structure.fields; + + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + + if (offset_it > offset) + { + break; + } + + result = field; + offset_it = (u32)align_forward(offset_it + get_byte_size(field->type), get_byte_alignment(field->type)); + } + + assert(result); + } + + return result; +} + +fn Type* abi_system_v_get_integer_type_at_offset(Module* module, Type* type, u32 offset, Type* source_type, u32 source_offset) +{ + switch (type->id) + { + case TypeId::integer: + { + auto bit_count = type->integer.bit_count; + switch (bit_count) + { + case 64: return type; + case 32: case 16: case 8: + { + assert(offset == 0); + auto start = source_offset + get_byte_size(type); + auto end = source_offset + 8; + + if (contains_no_user_data(source_type, start, end)) + { + return type; + } + } break; + default: + { + auto original_byte_count = get_byte_size(type); + assert(original_byte_count != source_offset); + auto byte_count = MIN(original_byte_count - source_offset, 8); + auto bit_count = byte_count * 8; + + auto result_type = integer_type(module, { .bit_count = (u32)bit_count, .is_signed = false }); + return result_type; + } break; + } + } break; + case TypeId::pointer: + { + if (offset == 0) + { + return type; + } + else + { + trap(); + } + } break; + case TypeId::structure: + { + auto* field = get_member_at_offset(type, offset); + if (field) + { + auto field_type = field->type; + switch (field_type->id) + { + case TypeId::integer: + case TypeId::enumerator: + { + field_type = align_integer_type(module, field_type); + } break; + default: break; + } + + return abi_system_v_get_integer_type_at_offset(module, field_type, offset - field->offset, source_type, source_offset); + } + else + { + unreachable(); + } + } break; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + return abi_system_v_get_integer_type_at_offset(module, backing_type, offset, source_type == type ? backing_type : source_type, source_offset); + } break; + default: unreachable(); + } + + auto source_size = get_byte_size(source_type); + auto byte_count = source_size - source_offset; + u32 bit_count = byte_count > 8 ? 64 : byte_count * 8; + auto result = integer_type(module, { .bit_count = bit_count, .is_signed = false }); + return result; +} + +struct AbiSystemVClassify +{ + u64 base_offset; + bool is_variable_argument; + bool is_register_call; +}; + +fn AbiSystemVClassifyResult abi_system_v_classify_type(Type* type, AbiSystemVClassify options) +{ + AbiSystemVClassifyResult result = {}; + auto is_memory = options.base_offset >= 8; + auto current_index = is_memory; + auto not_current_index = !is_memory; + assert(current_index != not_current_index); + result.r[current_index] = AbiSystemVClass::memory; + + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + result.r[current_index] = AbiSystemVClass::none; + break; + case TypeId::bits: + return abi_system_v_classify_type(type->bits.backing_type, options); + case TypeId::enumerator: + return abi_system_v_classify_type(type->enumerator.backing_type, options); + case TypeId::pointer: + result.r[current_index] = AbiSystemVClass::integer; + break; + case TypeId::integer: + { + if (type->integer.bit_count <= 64) + { + result.r[current_index] = AbiSystemVClass::integer; + } + else if (type->integer.bit_count == 128) + { + trap(); + } + else + { + report_error(); + } + } break; + case TypeId::array: + { + auto byte_size = get_byte_size(type); + if (byte_size <= 64) + { + if (options.base_offset % get_byte_alignment(type) == 0) + { + auto element_type = type->array.element_type; + auto element_size = get_byte_size(element_type); + + result.r[current_index] = AbiSystemVClass::none; + + u64 vector_size = 16; + + if (byte_size > 16 && (byte_size != get_byte_size(element_type) || byte_size > vector_size)) + { + unreachable(); + } + else + { + auto offset = options.base_offset; + auto element_count = type->array.element_count; + + for (u64 i = 0; i < element_count; i += 1) + { + auto element_classes = abi_system_v_classify_type(element_type, AbiSystemVClassify{ + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + }); + offset += element_size; + + result.r[0] = abi_system_v_merge_class(result.r[0], element_classes.r[0]); + result.r[1] = abi_system_v_merge_class(result.r[1], element_classes.r[1]); + + if (result.r[0] == AbiSystemVClass::memory || result.r[1] == AbiSystemVClass::memory) + { + break; + } + } + + auto final_result = abi_system_v_classify_post_merge(byte_size, result); + assert(final_result.r[1] != AbiSystemVClass::sse || final_result.r[0] != AbiSystemVClass::sse); + result = final_result; + } + } + } + } break; + case TypeId::structure: + case TypeId::union_type: + { + auto byte_size = type->structure.byte_size; + + if (byte_size <= 64) + { + auto has_variable_array = false; + if (!has_variable_array) + { + result.r[current_index] = AbiSystemVClass::none; + auto is_union = type->id == TypeId::union_type; + + switch (type->id) + { + case TypeId::structure: + { + for (auto& field : type->structure.fields) + { + auto offset = options.base_offset + field.offset; + auto member_type = field.type; + auto member_size = get_byte_size(member_type); + auto member_alignment = get_byte_alignment(member_type); + + u64 native_vector_size = 16; + + auto gt_16 = byte_size > 16 && ((!is_union && byte_size != member_size) || byte_size > native_vector_size); + auto padding = offset % member_alignment != 0; + + if (gt_16 || padding) + { + result.r[0] = AbiSystemVClass::memory; + result = abi_system_v_classify_post_merge(byte_size, result); + return result; + } + + auto member_classes = abi_system_v_classify_type(member_type, { + .base_offset = offset, + .is_variable_argument = options.is_variable_argument, + .is_register_call = options.is_register_call, + }); + + for (u64 i = 0; i < array_length(member_classes.r); i += 1) + { + result.r[i] = abi_system_v_merge_class(result.r[i], member_classes.r[i]); + } + + if (result.r[0] == AbiSystemVClass::memory || result.r[1] == AbiSystemVClass::memory) + { + break; + } + } + + result = abi_system_v_classify_post_merge(byte_size, result); + } break; + case TypeId::union_type: + { + trap(); + } break; + default: unreachable(); + } + } + } + } break; + case TypeId::alias: + return abi_system_v_classify_type(type->alias.type, options); + default: unreachable(); + } + + return result; +} + +fn void resolve_type_in_place_memory(Module* module, Type* type); +fn void resolve_type_in_place_abi(Module* module, Type* type) +{ + if (!type->llvm.abi) + { + LLVMTypeRef result = 0; + + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + result = module->llvm.void_type; + break; + case TypeId::integer: + result = LLVMIntTypeInContext(module->llvm.context, type->integer.bit_count); + break; + case TypeId::pointer: + result = module->llvm.pointer_type; + break; + case TypeId::array: + { + auto* element_type = type->array.element_type; + auto element_count = type->array.element_count; + assert(element_count); + resolve_type_in_place_memory(module, element_type); + auto array_type = LLVMArrayType2(element_type->llvm.memory, element_count); + result = array_type; + } break; + case TypeId::enumerator: + { + auto backing_type = type->enumerator.backing_type; + resolve_type_in_place_abi(module, backing_type); + result = backing_type->llvm.abi; + } break; + case TypeId::structure: + { + LLVMTypeRef llvm_type_buffer[64]; + auto fields = type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + resolve_type_in_place_memory(module, field.type); + llvm_type_buffer[i] = field.type->llvm.memory; + } + + result = LLVMStructTypeInContext(module->llvm.context, llvm_type_buffer, fields.length, 0); + } break; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + resolve_type_in_place_abi(module, backing_type); + result = backing_type->llvm.abi; + } break; + case TypeId::union_type: + { + auto biggest_type = type->union_type.fields[type->union_type.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, 0); + } break; + case TypeId::alias: + { + auto aliased = type->alias.type; + resolve_type_in_place_abi(module, aliased); + result = aliased->llvm.abi; + } break; + default: unreachable(); + } + + assert(result); + type->llvm.abi = result; + } +} + +fn void resolve_type_in_place_memory(Module* module, Type* type) +{ + if (!type->llvm.memory) + { + resolve_type_in_place_abi(module, type); + + LLVMTypeRef result = 0; + + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + case TypeId::pointer: + case TypeId::array: + case TypeId::structure: + result = type->llvm.abi; + break; + case TypeId::integer: + { + auto byte_size = get_byte_size(type); + auto bit_count = byte_size * 8; + result = LLVMIntTypeInContext(module->llvm.context, bit_count); + } break; + case TypeId::enumerator: + { + auto backing_type = type->enumerator.backing_type; + resolve_type_in_place_memory(module, backing_type); + result = backing_type->llvm.memory; + } break; + case TypeId::bits: + { + auto backing_type = type->bits.backing_type; + resolve_type_in_place_memory(module, backing_type); + result = backing_type->llvm.memory; + } break; + case TypeId::union_type: + { + auto biggest_type = type->union_type.fields[type->union_type.biggest_field].type; + resolve_type_in_place_memory(module, biggest_type); + result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, 0); + } break; + case TypeId::alias: + { + auto aliased = type->alias.type; + resolve_type_in_place_memory(module, aliased); + result = aliased->llvm.memory; + } break; + default: unreachable(); + } + + assert(result); + type->llvm.memory = result; + + if (type->id == TypeId::bits) + { + assert(type->llvm.memory == type->llvm.abi); + } + } +} + +fn void resolve_type_in_place_debug(Module* module, Type* type) +{ + if (module->has_debug_info) + { + if (!type->llvm.debug) + { + LLVMMetadataRef result = 0; + + switch (type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + { + result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, 0, (u32)DwarfType::void_type, type->id == TypeId::noreturn ? LLVMDIFlagNoReturn : LLVMDIFlagZero); + } break; + case TypeId::integer: + { + DwarfType dwarf_type = type->integer.bit_count == 1 ? DwarfType::boolean : (type->integer.is_signed ? DwarfType::signed_type : DwarfType::unsigned_type); + LLVMDIFlags flags = {}; + result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, type->integer.bit_count, (u32)dwarf_type, flags); + } break; + case TypeId::pointer: + { + resolve_type_in_place_debug(module, type->pointer.element_type); + if (type->llvm.debug) + { + trap(); + } + else + { + result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length); + } + } break; + case TypeId::array: + { + auto array_element_type = type->array.element_type; + auto array_element_count = type->array.element_count; + assert(array_element_count); + resolve_type_in_place_debug(module, array_element_type); + auto bit_alignment = get_byte_alignment(type) * 8; + auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, array_element_count, bit_alignment, array_element_type->llvm.debug, 0, 0); + result = array_type; + } break; + case TypeId::enumerator: + { + auto backing_type = type->enumerator.backing_type; + resolve_type_in_place_debug(module, backing_type); + + LLVMMetadataRef field_buffer[64]; + for (u64 i = 0; i < type->enumerator.fields.length; i += 1) + { + auto& field = type->enumerator.fields[i]; + auto enum_field = LLVMDIBuilderCreateEnumerator(module->llvm.di_builder, (char*)field.name.pointer, field.name.length, field.value, type_is_signed(backing_type)); + field_buffer[i] = enum_field; + } + + result = LLVMDIBuilderCreateEnumerationType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->enumerator.line, get_bit_size(type), get_byte_alignment(type) * 8, field_buffer, type->enumerator.fields.length, backing_type->llvm.debug); + } break; + case TypeId::structure: + { + LLVMDIFlags flags = {}; + auto forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module->llvm.di_builder, module->llvm.debug_tag, (char*)type->name.pointer, type->name.length, module->scope.llvm, module->llvm.file, type->structure.line, 0, type->structure.byte_size * 8, type->structure.byte_alignment * 8, flags, (char*)type->name.pointer, type->name.length); + module->llvm.debug_tag += 1; + + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, field.offset * 8, flags, field_type->llvm.debug); + llvm_type_buffer[i] = member_type; + } + + auto struct_type = LLVMDIBuilderCreateStructType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->structure.line, type->structure.byte_size * 8, type->structure.byte_alignment * 8, flags, 0, llvm_type_buffer, fields.length, 0, 0, (char*)type->name.pointer, type->name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, struct_type); + result = struct_type; + } break; + case TypeId::bits: + { + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->bits.fields; + auto backing_type = type->bits.backing_type->llvm.debug; + LLVMDIFlags flags = {}; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + u64 bit_offset = 0; + auto member_type = LLVMDIBuilderCreateBitFieldMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_bit_size(field_type), bit_offset, field.offset, flags, backing_type); + llvm_type_buffer[i] = member_type; + } + + auto size = get_byte_size(type) * 8; + auto alignment = get_byte_alignment(type) * 8; + auto struct_type = LLVMDIBuilderCreateStructType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->bits.line, size, alignment, flags, 0, llvm_type_buffer, fields.length, 0, 0, (char*)type->name.pointer, type->name.length); + result = struct_type; + } break; + case TypeId::union_type: + { + LLVMDIFlags flags = {}; + auto forward_declaration = LLVMDIBuilderCreateReplaceableCompositeType(module->llvm.di_builder, module->llvm.debug_tag, (char*)type->name.pointer, type->name.length, module->scope.llvm, module->llvm.file, type->union_type.line, 0, type->union_type.byte_size * 8, type->union_type.byte_alignment * 8, flags, (char*)type->name.pointer, type->name.length); + module->llvm.debug_tag += 1; + + LLVMMetadataRef llvm_type_buffer[64]; + + auto fields = type->union_type.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place_debug(module, field_type); + auto member_type = LLVMDIBuilderCreateMemberType(module->llvm.di_builder, module->scope.llvm, (char*)field.name.pointer, field.name.length, module->llvm.file, field.line, get_byte_size(field_type) * 8, get_byte_alignment(field_type) * 8, 0, flags, field_type->llvm.debug); + llvm_type_buffer[i] = member_type; + } + + auto union_type = LLVMDIBuilderCreateUnionType(module->llvm.di_builder, module->scope.llvm, (char*)type->name.pointer, type->name.length, module->llvm.file, type->union_type.line, type->union_type.byte_size * 8, type->union_type.byte_alignment * 8, flags, llvm_type_buffer, fields.length, 0, (char*)type->name.pointer, type->name.length); + LLVMMetadataReplaceAllUsesWith(forward_declaration, union_type); + result = union_type; + } break; + case TypeId::alias: + { + auto aliased = type->alias.type; + resolve_type_in_place_debug(module, aliased); + auto alignment = get_byte_alignment(aliased); + result = LLVMDIBuilderCreateTypedef(module->llvm.di_builder, aliased->llvm.debug, (char*) type->name.pointer, type->name.length, module->llvm.file, type->alias.line, type->alias.scope->llvm, alignment * 8); + } break; + default: unreachable(); + } + + assert(result); + type->llvm.debug = result; + } + } +} + +fn void resolve_type_in_place(Module* module, Type* type) +{ + resolve_type_in_place_abi(module, type); + resolve_type_in_place_memory(module, type); + resolve_type_in_place_debug(module, type); +} + +fn Type* resolve_type(Module* module, Type* type) +{ + Type* result = 0; + + switch (type->id) + { + case TypeId::unresolved: + { + assert(!module->current_macro_declaration); + auto instantiation = module->current_macro_instantiation; + if (!instantiation) + { + report_error(); + } + + auto declaration = instantiation->declaration; + auto declaration_arguments = declaration->constant_arguments; + auto instantiation_arguments = instantiation->constant_arguments; + assert(declaration_arguments.length == instantiation_arguments.length); + + for (u64 i = 0; i < declaration_arguments.length; i += 1) + { + auto& declaration_argument = declaration_arguments[i]; + auto& instantiation_argument = instantiation_arguments[i]; + if (declaration_argument.id == ConstantArgumentId::type && type == declaration_argument.type) + { + assert(declaration_argument.name.equal(instantiation_argument.name)); + result = instantiation_argument.type; + break; + } + } + + if (!result) + { + report_error(); + } + } break; + case TypeId::void_type: + case TypeId::integer: + { + result = type; + } break; + case TypeId::pointer: + { + auto element_type = resolve_type(module, type->pointer.element_type); + result = get_pointer_type(module, element_type); + } break; + case TypeId::structure: + { + if (type->structure.is_slice) + { + auto pointer_type = type->structure.fields[0].type; + assert(pointer_type->id == TypeId::pointer); + auto element_type = resolve_type(module, pointer_type->pointer.element_type); + auto slice_type = get_slice_type(module, element_type); + result = slice_type; + } + else + { + result = type; + } + } break; + default: trap(); + } + + assert(result); + assert(result->id != TypeId::unresolved); + return result; +} + +fn bool type_is_abi_equal(Module* module, Type* a, Type* b) +{ + resolve_type_in_place(module, a); + resolve_type_in_place(module, b); + + bool result = a == b; + if (!result) + { + result = a->llvm.abi == b->llvm.abi; + } + return result; +} + +fn AbiInformation abi_system_v_get_ignore(Module* module, Type* semantic_type) +{ + resolve_type_in_place(module, semantic_type); + + return { + .semantic_type = semantic_type, + .flags = { + .kind = AbiKind::ignore, + }, + }; +} + +struct DirectOptions +{ + Type* semantic_type; + Type* type; + Type* padding; + u32 offset; + u32 alignment; + bool can_be_flattened = true; +}; + +fn AbiInformation abi_system_v_get_direct(Module* module, DirectOptions direct) +{ + AbiInformation result = { + .semantic_type = direct.semantic_type, + .flags = { + .kind = AbiKind::direct, + }, + }; + resolve_type_in_place(module, direct.semantic_type); + resolve_type_in_place(module, direct.type); + if (unlikely(direct.padding)) + { + resolve_type_in_place(module, direct.padding); + } + + result.set_coerce_to_type(direct.type); + result.set_padding_type(direct.padding); + result.set_direct_offset(direct.offset); + result.set_direct_alignment(direct.alignment); + result.set_can_be_flattened(direct.can_be_flattened); + + return result; +} + +struct ExtendOptions +{ + Type* semantic_type; + Type* type; + bool sign; +}; + +fn AbiInformation abi_system_v_get_extend(ExtendOptions options) +{ + assert(is_integral_or_enumeration_type(options.semantic_type)); + AbiInformation result = { + .semantic_type = options.semantic_type, + .flags = { + .kind = AbiKind::extend, + }, + }; + + result.set_coerce_to_type(options.type ? options.type : options.semantic_type); + result.set_padding_type(0); + result.set_direct_offset(0); + result.set_direct_alignment(0); + result.flags.sign_extension = options.sign; + + return result; +} + +fn Type* get_anonymous_struct_pair(Module* module, Type* low, Type* high) +{ + Type* pair; + for (pair = module->first_pair_struct_type; pair; pair = pair->structure.next) + { + assert(pair->id == TypeId::structure); + assert(pair->structure.fields.length == 2); + + if (pair->structure.fields[0].type == low && + pair->structure.fields[1].type == high) + { + return pair; + } + + if (!pair->structure.next) + { + break; + } + } + + auto high_alignment = get_byte_alignment(high); + auto alignment = MAX(get_byte_alignment(low), high_alignment); + u64 high_offset = align_forward(get_byte_size(low), high_alignment); + + auto fields = arena_allocate(module->arena, 2); + fields[0] = { + .name = string_literal("low"), + .type = low, + .offset = 0, + .line = 0, + }; + fields[1] = { + .name = string_literal("high"), + .type = high, + .offset = high_offset, + .line = 0, + }; + + auto struct_type = type_allocate_init(module, { + .structure = { + .fields = fields, + .byte_size = high_offset + get_byte_size(high), + .byte_alignment = alignment, + }, + .id = TypeId::structure, + .name = string_literal(""), + }); + + if (pair) + { + assert(module->first_pair_struct_type); + pair->structure.next = struct_type; + } + else + { + assert(!module->first_pair_struct_type); + module->first_pair_struct_type = struct_type; + } + + return struct_type; +} + +fn Type* get_by_value_argument_pair(Module* module, Type* low, Type* high) +{ + auto low_size = get_byte_allocation_size(low); + auto high_alignment = get_byte_alignment(high); + auto high_start = align_forward(low_size, high_alignment); + assert(high_start != 0 && high_start <= 8); + if (high_start != 8) + { + trap(); + } + + auto result = get_anonymous_struct_pair(module, low, high); + return result; +} + + +struct IndirectOptions +{ + Type* semantic_type; + Type* padding_type = 0; + u32 alignment; + bool by_value = true; + bool realign = false; +}; + +fn AbiInformation abi_system_v_get_indirect(IndirectOptions indirect) +{ + auto result = AbiInformation{ + .semantic_type = indirect.semantic_type, + .attributes = { + .indirect = { + .alignment = 0, + .address_space = 0, + }, + }, + .flags = { + .kind = AbiKind::indirect, + }, + }; + + result.set_indirect_align(indirect.alignment); + result.set_indirect_by_value(indirect.by_value); + result.set_indirect_realign(indirect.realign); + result.set_sret_after_this(false); + result.set_padding_type(indirect.padding_type); + + return result; +} + +struct NaturalAlignIndirect +{ + Type* semantic_type; + Type* padding_type = 0; + bool by_value = true; + bool realign = false; +}; + +fn AbiInformation abi_system_v_get_natural_align_indirect(NaturalAlignIndirect natural) +{ + auto alignment = get_byte_alignment(natural.semantic_type); + return abi_system_v_get_indirect({ + .semantic_type = natural.semantic_type, + .padding_type = natural.padding_type, + .alignment = alignment, + .by_value = natural.by_value, + .realign = natural.realign, + }); +} + +fn bool is_illegal_vector_type(Type* type) +{ + switch (type->id) + { + case TypeId::vector: trap(); + default: + return false; + } +} + +fn AbiInformation abi_system_v_get_indirect_result(Module* module, Type* type, u32 free_gpr) +{ + if (!type_is_aggregate_type_for_abi(type) && !is_illegal_vector_type(type) && !is_arbitrary_bit_integer(type)) + { + if (is_promotable_integer_type_for_abi(type)) + { + trap(); + } + else + { + return abi_system_v_get_direct(module, { + .semantic_type = type, + .type = type, + }); + } + } + else + { + auto alignment = MAX(get_byte_alignment(type), 8); + auto size = get_byte_size(type); + + if (free_gpr == 0 && alignment == 8 && size <= 8) + { + trap(); + } + else + { + return abi_system_v_get_indirect({ + .semantic_type = type, + .alignment = alignment, + }); + } + } +} + + +struct AbiSystemVClassifyArgumentTypeOptions +{ + u32 available_gpr; + bool is_named_argument; + bool is_reg_call; +}; + +struct AbiSystemVClassifyArgumentTypeResult +{ + AbiInformation abi; + AbiRegisterCountSystemV needed_registers; +}; + +fn AbiSystemVClassifyArgumentTypeResult abi_system_v_classify_argument_type(Module* module, Type* semantic_argument_type, AbiSystemVClassifyArgumentTypeOptions options) +{ + auto classify_result = abi_system_v_classify_type(semantic_argument_type, AbiSystemVClassify{ + .base_offset = 0, + .is_variable_argument = !options.is_named_argument, + .is_register_call = options.is_reg_call, + }); + + auto low_class = classify_result.r[0]; + auto high_class = classify_result.r[1]; + + AbiRegisterCountSystemV needed_registers = {}; + + Type* low_type = 0; + + switch (low_class) + { + case AbiSystemVClass::none: unreachable(); + case AbiSystemVClass::integer: + { + needed_registers.gpr += 1; + low_type = abi_system_v_get_integer_type_at_offset(module, semantic_argument_type, 0, semantic_argument_type, 0); + + if (high_class == AbiSystemVClass::none && low_type->id == TypeId::integer) + { + // TODO: if enumerator + + if (is_integral_or_enumeration_type(semantic_argument_type) && is_promotable_integer_type_for_abi(semantic_argument_type)) + { + return { abi_system_v_get_extend({ + .semantic_type = semantic_argument_type, + .sign = type_is_signed(semantic_argument_type), + }), needed_registers }; + } + } + } break; + case AbiSystemVClass::memory: + { + return { abi_system_v_get_indirect_result(module, semantic_argument_type, options.available_gpr), needed_registers }; + } break; + default: unreachable(); + } + + Type* high_type = 0; + + switch (high_class) + { + case AbiSystemVClass::none: + break; + case AbiSystemVClass::integer: + { + needed_registers.gpr += 1; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_argument_type, 8, semantic_argument_type, 8); + + if (low_class == AbiSystemVClass::none) + { + trap(); + } + } break; + default: unreachable(); + } + + Type* result_type = low_type; + if (high_type) + { + result_type = get_by_value_argument_pair(module, low_type, high_type); + } + + return { + abi_system_v_get_direct(module, DirectOptions{ + .semantic_type = semantic_argument_type, + .type = result_type, + }), + needed_registers, + }; +} + +struct AbiSystemVClassifyArgumentOptions +{ + Type* type; + u16 abi_start; + bool is_reg_call = false; + bool is_named_argument; +}; + +fn AbiInformation abi_system_v_classify_argument(Module* module, AbiRegisterCountSystemV* available_registers, Slice llvm_abi_argument_type_buffer, Slice abi_argument_type_buffer, AbiSystemVClassifyArgumentOptions options) +{ + auto semantic_argument_type = options.type; + if (options.is_reg_call) + { + trap(); + } + + auto result = abi_system_v_classify_argument_type(module, semantic_argument_type, { + .available_gpr = available_registers->gpr, + .is_named_argument = options.is_named_argument, + .is_reg_call = options.is_reg_call, + }); + auto abi = result.abi; + auto needed_registers = result.needed_registers; + + AbiInformation argument_abi; + if (available_registers->gpr >= needed_registers.gpr && available_registers->sse >= needed_registers.sse) + { + available_registers->gpr -= needed_registers.gpr; + available_registers->sse -= needed_registers.sse; + argument_abi = abi; + } + else + { + argument_abi = abi_system_v_get_indirect_result(module, semantic_argument_type, available_registers->gpr); + } + + if (argument_abi.get_padding_type()) + { + trap(); + } + + argument_abi.abi_start = options.abi_start; + + u16 count = 0; + + switch (argument_abi.flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + { + auto coerce_to_type = argument_abi.get_coerce_to_type(); + resolve_type_in_place(module, coerce_to_type); + + auto is_flattened_struct = argument_abi.flags.kind == AbiKind::direct && argument_abi.get_can_be_flattened() && coerce_to_type->id == TypeId::structure; + + count = is_flattened_struct ? coerce_to_type->structure.fields.length : 1; + + if (is_flattened_struct) + { + for (u64 i = 0; i < coerce_to_type->structure.fields.length; i += 1) + { + auto& field = coerce_to_type->structure.fields[i]; + auto field_type = field.type; + llvm_abi_argument_type_buffer[argument_abi.abi_start + i] = field_type->llvm.abi; + abi_argument_type_buffer[argument_abi.abi_start + i] = field_type; + } + } + else + { + llvm_abi_argument_type_buffer[argument_abi.abi_start] = coerce_to_type->llvm.abi; + abi_argument_type_buffer[argument_abi.abi_start] = coerce_to_type; + } + } break; + case AbiKind::indirect: + { + auto indirect_type = get_pointer_type(module, argument_abi.semantic_type); + auto abi_index = argument_abi.abi_start; + abi_argument_type_buffer[abi_index] = indirect_type; + resolve_type_in_place(module, indirect_type); + llvm_abi_argument_type_buffer[abi_index] = indirect_type->llvm.abi; + count = 1; + } break; + default: unreachable(); + } + + assert(count); + argument_abi.abi_count = count; + + return argument_abi; +} + +fn AbiInformation abi_system_v_get_indirect_return_result(Type* type) +{ + if (type_is_aggregate_type_for_abi(type)) + { + return abi_system_v_get_natural_align_indirect({ + .semantic_type = type, + }); + } + else + { + trap(); + } +} + +fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type) +{ + auto type_classes = abi_system_v_classify_type(semantic_return_type, {}); + auto low_class = type_classes.r[0]; + auto high_class = type_classes.r[1]; + assert(high_class != AbiSystemVClass::memory || low_class == AbiSystemVClass::memory); + assert(high_class != AbiSystemVClass::sse_up || low_class == AbiSystemVClass::sse); + + Type* low_type = 0; + + switch (low_class) + { + case AbiSystemVClass::none: + { + if (high_class == AbiSystemVClass::none) + { + return abi_system_v_get_ignore(module, semantic_return_type); + } + + trap(); + } break; + case AbiSystemVClass::integer: + { + low_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0); + + if (high_class == AbiSystemVClass::none && low_type->id == TypeId::integer) + { + if (semantic_return_type->id == TypeId::enumerator) + { + trap(); + } + + if (is_integral_or_enumeration_type(semantic_return_type) && is_promotable_integer_type_for_abi(semantic_return_type)) + { + return abi_system_v_get_extend({ + .semantic_type = semantic_return_type, + .sign = type_is_signed(semantic_return_type), + }); + } + } + } break; + case AbiSystemVClass::memory: + { + return abi_system_v_get_indirect_return_result(semantic_return_type); + } break; + default: unreachable(); + } + + Type* high_type = 0; + + switch (high_class) + { + case AbiSystemVClass::none: + break; + case AbiSystemVClass::integer: + { + u64 high_offset = 8; + high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset); + if (low_class == AbiSystemVClass::none) + { + trap(); + } + } break; + + default: unreachable(); + } + + if (high_type) + { + low_type = get_by_value_argument_pair(module, low_type, high_type); + } + + auto result = abi_system_v_get_direct(module, { + .semantic_type = semantic_return_type, + .type = low_type, + }); + return result; +} + +struct AttributeBuildOptions +{ + AbiInformation return_abi; + Slice argument_abis; + Slice abi_argument_types; + Type* abi_return_type; + FunctionAttributes attributes; + bool call_site; +}; + +struct AllocaOptions +{ + Type* type; + String name = string_literal(""); + u32 alignment; +}; + +fn LLVMValueRef create_alloca(Module* module, AllocaOptions options) +{ + auto abi_type = options.type; + resolve_type_in_place(module, abi_type); + + u32 alignment; + if (options.alignment) + { + alignment = options.alignment; + } + else + { + alignment = get_byte_alignment(abi_type); + } + + auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, 0, alignment, options.name); + return alloca; +} + +struct StoreOptions +{ + LLVMValueRef source; + LLVMValueRef destination; + Type* type; + u32 alignment; +}; + +fn void create_store(Module* module, StoreOptions options) +{ + assert(options.source); + assert(options.destination); + assert(options.type); + + auto resolved_type = resolve_alias(module, options.type); + resolve_type_in_place(module, resolved_type); + + LLVMValueRef source_value; + + LLVMTypeRef memory_type = resolved_type->llvm.memory; + if (resolved_type->llvm.abi == memory_type) + { + source_value = options.source; + } + else + { + source_value = LLVMBuildIntCast2(module->llvm.builder, options.source, memory_type, type_is_signed(resolved_type), ""); + } + + u32 alignment; + if (options.alignment) + { + alignment = options.alignment; + } + else + { + alignment = get_byte_alignment(resolved_type); + } + + auto store = LLVMBuildStore(module->llvm.builder, source_value, options.destination); + LLVMSetAlignment(store, alignment); +} + +struct LoadOptions +{ + Type* type; + LLVMValueRef pointer; + u32 alignment; + TypeKind kind; +}; + +fn LLVMValueRef create_load(Module* module, LoadOptions options) +{ + resolve_type_in_place(module, options.type); + + u32 alignment; + if (options.alignment) + { + alignment = options.alignment; + } + else + { + alignment = get_byte_alignment(options.type); + } + + auto result = LLVMBuildLoad2(module->llvm.builder, options.type->llvm.memory, options.pointer, ""); + LLVMSetAlignment(result, alignment); + + switch (options.kind) + { + case TypeKind::abi: + { + if (options.type->llvm.memory == options.type->llvm.abi) + { + break; + } + else + { + result = LLVMBuildIntCast2(module->llvm.builder, result, options.type->llvm.abi, type_is_signed(options.type), ""); + } + } break; + case TypeKind::memory: break; + } + + return result; +} + +struct GEPOptions +{ + LLVMTypeRef type; + LLVMValueRef pointer; + Slice indices; + bool inbounds = true; +}; + +fn LLVMValueRef create_gep(Module* module, GEPOptions options) +{ + assert(options.indices.length); + auto* gep_function = options.inbounds ? &LLVMBuildInBoundsGEP2 : &LLVMBuildGEP2; + auto gep = gep_function(module->llvm.builder, options.type, options.pointer, options.indices.pointer, (u32)options.indices.length, ""); + return gep; +} + +fn BBLLVMAttributeList build_attribute_list(Module* module, AttributeBuildOptions options) +{ + resolve_type_in_place(module, options.return_abi.semantic_type); + BBLLVMAttributeListOptions attributes = {}; + + attributes.return_ = { + .semantic_type = options.return_abi.semantic_type->llvm.memory, + .abi_type = options.abi_return_type->llvm.abi, + .dereferenceable_bytes = 0, + .alignment = 0, + .no_alias = false, + .non_null = false, + .no_undef = false, + .sign_extend = options.return_abi.flags.kind == AbiKind::extend and options.return_abi.flags.sign_extension, + .zero_extend = options.return_abi.flags.kind == AbiKind::extend and !options.return_abi.flags.sign_extension, + .in_reg = false, + .no_fp_class = 0, // TODO: this is a struct + .struct_return = false, + .writable = false, + .dead_on_unwind = false, + .in_alloca = false, + .dereferenceable = false, + .dereferenceable_or_null = false, + .nest = false, + .by_value = false, + .by_reference = false, + .no_capture = false, + }; + + BBLLVMArgumentAttributes argument_attribute_buffer[128]; + Slice argument_attributes = { .pointer = argument_attribute_buffer, .length = options.abi_argument_types.length }; + attributes.argument_pointer = argument_attributes.pointer; + attributes.argument_count = argument_attributes.length; + + u64 total_abi_count = 0; + if (options.return_abi.flags.kind == AbiKind::indirect) + { + auto abi_index = options.return_abi.flags.sret_after_this; + auto argument_attribute = &argument_attributes[abi_index]; + *argument_attribute = { + .semantic_type = options.return_abi.semantic_type->llvm.memory, + .abi_type = options.abi_argument_types[abi_index]->llvm.abi, + .dereferenceable_bytes = 0, + .alignment = get_byte_alignment(options.return_abi.semantic_type), + .no_alias = true, + .non_null = false, + .no_undef = false, + .sign_extend = false, + .zero_extend = false, + .in_reg = options.return_abi.flags.in_reg, + .no_fp_class = {}, + .struct_return = true, + .writable = true, + .dead_on_unwind = true, + .in_alloca = false, + .dereferenceable = false, + .dereferenceable_or_null = false, + .nest = false, + .by_value = false, + .by_reference = false, + .no_capture = false, + }; + total_abi_count += 1; + } + + for (const auto& abi: options.argument_abis) + { + for (auto abi_index = abi.abi_start; abi_index < abi.abi_start + abi.abi_count; abi_index += 1) + { + auto& attributes = argument_attributes[abi_index]; + resolve_type_in_place(module, abi.semantic_type); + + auto abi_type = options.abi_argument_types[abi_index]; + resolve_type_in_place(module, abi_type); + + attributes = { + .semantic_type = abi.semantic_type->llvm.memory, + .abi_type = abi_type->llvm.abi, + .dereferenceable_bytes = 0, + .alignment = (u32)(abi.flags.kind == AbiKind::indirect ? 8 : 0), + .no_alias = false, + .non_null = false, + .no_undef = false, + .sign_extend = abi.flags.kind == AbiKind::extend and abi.flags.sign_extension, + .zero_extend = abi.flags.kind == AbiKind::extend and !abi.flags.sign_extension, + .in_reg = abi.flags.in_reg, + .no_fp_class = {}, + .struct_return = false, + .writable = false, + .dead_on_unwind = false, + .in_alloca = false, + .dereferenceable = false, + .dereferenceable_or_null = false, + .nest = false, + .by_value = abi.flags.indirect_by_value, + .by_reference = false, + .no_capture = false, + }; + total_abi_count += 1; + } + } + assert(total_abi_count == options.abi_argument_types.length); + + attributes.function = { + .prefer_vector_width = {}, + .stack_protector_buffer_size = {}, + .definition_probe_stack = {}, + .definition_stack_probe_size = {}, + .flags0 = { + .noreturn = options.return_abi.semantic_type == noreturn_type(module), + .cmse_ns_call = false, + .nounwind = true, + .returns_twice = false, + .cold = false, + .hot = false, + .no_duplicate = false, + .convergent = false, + .no_merge = false, + .will_return = false, + .no_caller_saved_registers = false, + .no_cf_check = false, + .no_callback = false, + .alloc_size = false, // TODO + .uniform_work_group_size = false, + .aarch64_pstate_sm_body = false, + .aarch64_pstate_sm_enabled = false, + .aarch64_pstate_sm_compatible = false, + .aarch64_preserves_za = false, + .aarch64_in_za = false, + .aarch64_out_za = false, + .aarch64_inout_za = false, + .aarch64_preserves_zt0 = false, + .aarch64_in_zt0 = false, + .aarch64_out_zt0 = false, + .aarch64_inout_zt0 = false, + .optimize_for_size = false, + .min_size = false, + .no_red_zone = false, + .indirect_tls_seg_refs = false, + .no_implicit_floats = false, + .sample_profile_suffix_elision_policy = false, + .memory_none = false, + .memory_readonly = false, + .memory_inaccessible_or_arg_memory_only = false, + .memory_arg_memory_only = false, + .strict_fp = false, + .no_inline = options.attributes.inline_behavior == InlineBehavior::no_inline, + .always_inline = options.attributes.inline_behavior == InlineBehavior::always_inline, + .guard_no_cf = false, + // TODO: branch protection function attributes + // TODO: cpu features + + // CALL-SITE ATTRIBUTES + .call_no_builtins = false, + + // DEFINITION-SITE ATTRIBUTES + .definition_frame_pointer_kind = module->has_debug_info ? BBLLVMFramePointerKind::all : BBLLVMFramePointerKind::none, + .definition_less_precise_fpmad = false, + .definition_null_pointer_is_valid = false, + .definition_no_trapping_fp_math = false, + .definition_no_infs_fp_math = false, + .definition_no_nans_fp_math = false, + .definition_approx_func_fp_math = false, + .definition_unsafe_fp_math = false, + .definition_use_soft_float = false, + .definition_no_signed_zeroes_fp_math = false, + .definition_stack_realignment = false, + .definition_backchain = false, + .definition_split_stack = false, + .definition_speculative_load_hardening = false, + .definition_zero_call_used_registers = ZeroCallUsedRegsKind::all, + // TODO: denormal builtins + .definition_non_lazy_bind = false, + .definition_cmse_nonsecure_entry = false, + .definition_unwind_table_kind = BBLLVMUWTableKind::None, + }, + .flags1 = { + .definition_disable_tail_calls = false, + .definition_stack_protect_strong = false, + .definition_stack_protect = false, + .definition_stack_protect_req = false, + .definition_aarch64_new_za = false, + .definition_aarch64_new_zt0 = false, + .definition_optimize_none = false, + .definition_naked = !options.call_site and options.attributes.naked, + .definition_inline_hint = !options.call_site and options.attributes.inline_behavior == InlineBehavior::inline_hint, + }, + }; + + auto attribute_list = llvm_attribute_list_build(module->llvm.context, &attributes, options.call_site); + return attribute_list; +} + +fn void check_types(Module* module, Type* expected, Type* source) +{ + assert(expected); + assert(source); + + if (expected != source) + { + auto resolved_expected = resolve_alias(module, expected); + auto resolved_source = resolve_alias(module, source); + + if (resolved_expected != resolved_source) + { + auto is_dst_p_and_source_int = resolved_expected->id == TypeId::pointer && resolved_source->id == TypeId::integer; + if (!is_dst_p_and_source_int) + { + report_error(); + } + } + } +} + +fn void typecheck(Module* module, Type* expected, Type* source) +{ + if (expected) + { + check_types(module, expected, source); + } +} + +fn bool unary_is_boolean(UnaryId id) +{ + switch (id) + { + case UnaryId::exclamation: + return true; + case UnaryId::minus: + case UnaryId::plus: + case UnaryId::ampersand: + case UnaryId::tilde: + case UnaryId::enum_name: + case UnaryId::extend: + case UnaryId::truncate: + case UnaryId::pointer_cast: + case UnaryId::int_from_enum: + case UnaryId::int_from_pointer: + case UnaryId::va_end: + case UnaryId::bitwise_not: + case UnaryId::dereference: + case UnaryId::pointer_from_int: + return false; + } +} + +fn bool binary_is_boolean(BinaryId id) +{ + switch (id) + { + case BinaryId::add: + case BinaryId::sub: + case BinaryId::mul: + case BinaryId::div: + case BinaryId::rem: + case BinaryId::bitwise_and: + case BinaryId::bitwise_or: + case BinaryId::bitwise_xor: + case BinaryId::shift_left: + case BinaryId::shift_right: + return false; + case BinaryId::compare_equal: + case BinaryId::compare_not_equal: + case BinaryId::compare_greater: + case BinaryId::compare_less: + case BinaryId::compare_greater_equal: + case BinaryId::compare_less_equal: + case BinaryId::logical_and: + case BinaryId::logical_or: + case BinaryId::logical_and_shortcircuit: + case BinaryId::logical_or_shortcircuit: + return true; + } +} + +fn bool binary_is_shortcircuiting(BinaryId id) +{ + switch (id) + { + case BinaryId::logical_and_shortcircuit: + case BinaryId::logical_or_shortcircuit: + return true; + default: + return false; + } +} + +fn void analyze_type(Module* module, Value* value, Type* expected_type); + +fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type) +{ + auto left_constant = left->is_constant(); + auto right_constant = right->is_constant(); + + if (!expected_type) + { + if (left_constant && right_constant) + { + if (!left->type && !right->type) + { + auto are_string_literal = left->id == ValueId::string_literal && right->id == ValueId::string_literal; + + if (are_string_literal) + { + expected_type = get_slice_type(module, uint8(module)); + } + else + { + report_error(); + } + } + } + } + + if (is_boolean || !expected_type) + { + if (left_constant) + { + analyze_type(module, right, 0); + analyze_type(module, left, right->type); + } + else + { + analyze_type(module, left, 0); + analyze_type(module, right, left->type); + } + } + else if (!is_boolean && expected_type) + { + analyze_type(module, left, expected_type); + analyze_type(module, right, expected_type); + } + else + { + report_error(); // TODO: this might not be an error necessarily? + } + + assert(left->type); + assert(right->type); +} + +fn Type* get_va_list_type(Module* module) +{ + if (!module->va_list_type) + { + auto u32_type = uint32(module); + auto void_pointer = get_pointer_type(module, uint8(module)); + auto fields = arena_allocate(module->arena, 4); + fields[0] = { .name = string_literal("gp_offset"), .type = u32_type, .offset = 0 }; + fields[1] = { .name = string_literal("fp_offset"), .type = u32_type, .offset = 4 }; + fields[2] = { .name = string_literal("overflow_arg_area"), .type = void_pointer, .offset = 8 }; + fields[3] = { .name = string_literal("reg_save_area"), .type = void_pointer, .offset = 16 }; + + auto va_list_struct = type_allocate_init(module, { + .structure = { + .fields = fields, + .byte_size = 24, + .byte_alignment = 16, + }, + .id = TypeId::structure, + .name = string_literal("va_list"), + }); + + module->va_list_type = get_array_type(module, va_list_struct, 1); + } + + assert(module->va_list_type); + + return module->va_list_type; +} + +fn Global* get_enum_name_array_global(Module* module, Type* enum_type) +{ + assert(enum_type->id == TypeId::enumerator); + + if (!enum_type->enumerator.name_array) + { + auto fields = enum_type->enumerator.fields; + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + LLVMValueRef name_before = 0; + LLVMValueRef name_constant_buffer[64]; + + for (u32 i = 0; i < fields.length; i += 1) + { + auto null_terminate = true; + auto& field = fields[i]; + auto is_constant = true; + String name_parts[] = { + string_literal("string."), + enum_type->name, + string_literal("."), + field.name, + }; + unsigned address_space = 0; + auto name_global = llvm_module_create_global_variable(module->llvm.module, LLVMArrayType2(u8_type->llvm.abi, field.name.length + null_terminate), is_constant, LLVMInternalLinkage, LLVMConstStringInContext2(module->llvm.context, (char*)field.name.pointer, field.name.length, false), arena_join_string(module->arena, array_to_slice(name_parts)), name_before, LLVMNotThreadLocal, address_space, false); + name_before = name_global; + LLVMValueRef constants[] = { + name_global, + LLVMConstInt(u64_type->llvm.abi, field.name.length, false), + }; + auto slice_constant = LLVMConstStructInContext(module->llvm.context, constants, array_length(constants), false); + name_constant_buffer[i] = slice_constant; + } + + auto slice_type = get_slice_type(module, u8_type); + auto array_element_count = fields.length; + auto name_array = LLVMConstArray2(slice_type->llvm.abi, name_constant_buffer, array_element_count); + auto name_array_type = LLVMArrayType2(slice_type->llvm.abi, array_element_count); + auto is_constant = true; + unsigned address_space = 0; + auto name_array_variable = llvm_module_create_global_variable(module->llvm.module, name_array_type, is_constant, LLVMInternalLinkage, name_array, string_literal("name.array.enum"), name_before, LLVMNotThreadLocal, address_space, false); + LLVMSetAlignment(name_array_variable, get_byte_alignment(slice_type)); + LLVMSetUnnamedAddress(name_array_variable, LLVMGlobalUnnamedAddr); + + auto global_type = get_array_type(module, slice_type, array_element_count); + resolve_type_in_place(module, global_type); + + auto storage_type = get_pointer_type(module, global_type); + resolve_type_in_place(module, storage_type); + + auto global_storage = new_value(module); + *global_storage = { + .type = storage_type, + .id = ValueId::global, + .kind = ValueKind::left, + .llvm = name_array_variable, + }; + + String name_parts[] = { + string_literal("name.array.enum."), + enum_type->name, + }; + + auto global = new_global(module); + *global = { + .variable = { + .storage = global_storage, + .initial_value = 0, + .type = global_type, + .scope = &module->scope, + .name = arena_join_string(module->arena, array_to_slice(name_parts)), + .line = 0, + .column = 0, + }, + .linkage = Linkage::internal, + }; + global->emitted = true; + + enum_type->enumerator.name_array = global; + } + + return enum_type->enumerator.name_array; +} + +struct BlockCopy +{ + Block* source; + Block* destination; +}; +fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy); + +fn Value* clone_value(Module* module, Scope* scope, Value* old_value) +{ + assert(old_value); + + Value* result = 0; + if (old_value->id == ValueId::variable_reference) + { + result = reference_identifier(module, scope, old_value->variable_reference->name, old_value->kind); + } + else + { + result = new_value(module); + *result = *old_value; + + switch (old_value->id) + { + case ValueId::variable_reference: + { + unreachable(); + } break; + case ValueId::binary: + { + auto left = clone_value(module, scope, old_value->binary.left); + auto right = clone_value(module, scope, old_value->binary.right); + + result->binary = { + .left = left, + .right = right, + .id = old_value->binary.id, + }; + } break; + case ValueId::unary: + { + auto unary_value = clone_value(module, scope, old_value->unary.value); + result->unary = { + .value = unary_value, + .id = old_value->unary.id, + }; + } break; + case ValueId::unary_type: + { + result->unary_type = old_value->unary_type; + } break; + case ValueId::unreachable: + break; + case ValueId::slice_expression: + { + auto old_start = old_value->slice_expression.start; + auto old_end = old_value->slice_expression.end; + + result->slice_expression = { + .array_like = clone_value(module, scope, old_value->slice_expression.array_like), + .start = old_start ? clone_value(module, scope, old_start) : 0, + .end = old_end ? clone_value(module, scope, old_end) : 0, + }; + } break; + case ValueId::call: + { + auto callable = clone_value(module, scope, old_value->call.callable); + auto old_arguments = old_value->call.arguments; + auto arguments = new_value_array(module, old_arguments.length); + + for (u64 i = 0; i < arguments.length; i += 1) + { + arguments[i] = clone_value(module, scope, old_arguments[i]); + } + + result->call = { + .callable = callable, + .arguments = arguments, + .function_type = old_value->call.function_type, + }; + } break; + default: trap(); + } + } + + assert(result); + + return result; +} + +fn Statement* clone_statement(Module* module, Scope* scope, Statement* old_statement) +{ + auto new_statement = &arena_allocate(module->arena, 1)[0]; + *new_statement = {}; + auto old_id = old_statement->id; + new_statement->id = old_id; // TODO: is this right? + new_statement->line = old_statement->line; + new_statement->column = old_statement->column; + + switch (old_id) + { + case StatementId::return_st: + { + auto old_return_value = old_statement->return_st; + new_statement->return_st = old_return_value ? clone_value(module, scope, old_return_value) : 0; + } break; + case StatementId::if_st: + { + auto condition = clone_value(module, scope, old_statement->if_st.condition); + auto if_statement = clone_statement(module, scope, old_statement->if_st.if_statement); + auto else_statement = old_statement->if_st.else_statement; + else_statement = else_statement ? clone_statement(module, scope, else_statement) : 0; + new_statement->if_st = { + .condition = condition, + .if_statement = if_statement, + .else_statement = else_statement, + }; + } break; + case StatementId::block: + { + auto block = &arena_allocate(module->arena, 1)[0]; + copy_block(module, scope, { + .source = old_statement->block, + .destination = block, + }); + + new_statement->block = block; + } break; + case StatementId::expression: + { + auto value = clone_value(module, scope, old_statement->expression); + new_statement->expression = value; + } break; + case StatementId::local: + { + auto local_old = old_statement->local; + auto local_new = new_local(module, scope); + assert(!local_old->variable.storage); + *local_new = { + .variable = { + .storage = 0, + .initial_value = clone_value(module, scope, local_old->variable.initial_value), + .type = local_old->variable.type ? resolve_type(module, local_old->variable.type) : 0, + .scope = scope, + .name = local_old->variable.name, + .line = local_old->variable.line, + .column = local_old->variable.column, + }, + }; + + new_statement->local = local_new; + } break; + default: trap(); + } + + return new_statement; +} + +fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy) +{ + auto source = copy.source; + auto destination = copy.destination; + + *destination = {}; + auto scope = &destination->scope; + *scope = source->scope; + scope->parent = parent_scope; + assert(!scope->llvm); + + Statement* last_statement = 0; + for (Statement* old_statement = source->first_statement; old_statement; old_statement = old_statement->next) + { + auto statement = clone_statement(module, scope, old_statement); + assert(!statement->next); + if (last_statement) + { + last_statement->next = statement; + last_statement = statement; + } + else + { + last_statement = statement; + destination->first_statement = statement; + } + } +} + +fn void analyze_type(Module* module, Value* value, Type* expected_type) +{ + assert(!value->type); + assert(!value->llvm); + + if (expected_type && expected_type->id == TypeId::unresolved) + { + auto instantiation = module->current_macro_instantiation; + if (!instantiation) + { + report_error(); + } + + auto declaration = instantiation->declaration; + + Type* resolved_type = 0; + + auto instantiation_arguments = instantiation->constant_arguments; + auto declaration_arguments = declaration->constant_arguments; + + assert(instantiation_arguments.length == declaration_arguments.length); + + for (u64 i = 0; i < instantiation_arguments.length; i += 1) + { + auto& instantiation_argument = instantiation_arguments[i]; + auto& declaration_argument = declaration_arguments[i]; + + assert(declaration_argument.id == instantiation_argument.id); + + if (declaration_argument.id == ConstantArgumentId::type && declaration_argument.type == expected_type) + { + resolved_type = instantiation_argument.type; + resolve_type_in_place(module, resolved_type); + break; + } + } + + if (!resolved_type) + { + report_error(); + } + + expected_type = resolved_type; + } + + Type* value_type = 0; + + switch (value->id) + { + case ValueId::constant_integer: + { + if (!expected_type) + { + report_error(); + } + + resolve_type_in_place(module, expected_type); + auto* resolved_type = resolve_alias(module, expected_type); + switch (resolved_type->id) + { + case TypeId::integer: + { + if (value->constant_integer.is_signed) + { + if (resolved_type->integer.is_signed) + { + report_error(); + } + + trap(); + } + else + { + auto max_value = integer_max_value(resolved_type->integer.bit_count, resolved_type->integer.is_signed); + + if (value->constant_integer.value > max_value) + { + report_error(); + } + + value_type = expected_type; + } + } break; + case TypeId::pointer: value_type = uint64(module); break; + default: trap(); + } + + typecheck(module, expected_type, value_type); + } break; + case ValueId::unary: + { + auto unary_id = value->unary.id; + auto unary_value = value->unary.value; + switch (unary_id) + { + case UnaryId::extend: + { + if (!expected_type) + { + report_error(); + } + + auto extended_value = unary_value; + analyze_type(module, extended_value, 0); + auto source = extended_value->type; + assert(source); + + auto source_bit_size = get_bit_size(source); + auto expected_bit_size = get_bit_size(expected_type); + if (source_bit_size > expected_bit_size) + { + report_error(); + } + else if (source_bit_size == expected_bit_size && type_is_signed(source) == type_is_signed(expected_type)) + { + report_error(); + } + + value_type = expected_type; + } break; + case UnaryId::truncate: + { + if (!expected_type) + { + report_error(); + } + + analyze_type(module, unary_value, 0); + auto expected_bit_size = get_bit_size(expected_type); + auto source_bit_size = get_bit_size(unary_value->type); + + if (expected_bit_size >= source_bit_size) + { + report_error(); + } + + value_type = expected_type; + } break; + case UnaryId::dereference: + { + analyze_type(module, unary_value, 0); + if (value->kind == ValueKind::left) + { + report_error(); + } + auto pointer_type = unary_value->type; + assert(pointer_type->id == TypeId::pointer); + auto dereference_type = pointer_type->pointer.element_type; + + typecheck(module, expected_type, dereference_type); + value_type = dereference_type; + } break; + case UnaryId::int_from_enum: + { + analyze_type(module, unary_value, 0); + + auto value_enum_type = unary_value->type; + if (value_enum_type->id != TypeId::enumerator) + { + report_error(); + } + + auto backing_type = value_enum_type->enumerator.backing_type; + typecheck(module, expected_type, backing_type); + + value_type = backing_type; + } break; + case UnaryId::int_from_pointer: + { + analyze_type(module, unary_value, 0); + + auto value_enum_type = unary_value->type; + if (value_enum_type->id != TypeId::pointer) + { + report_error(); + } + + value_type = uint64(module); + typecheck(module, expected_type, value_type); + } break; + case UnaryId::pointer_cast: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::pointer) + { + report_error(); + } + + analyze_type(module, unary_value, 0); + auto value_pointer_type = unary_value->type; + if (value_pointer_type == expected_type) + { + report_error(); + } + + if (value_pointer_type->id != TypeId::pointer) + { + report_error(); + } + + value_type = expected_type; + } break; + case UnaryId::enum_name: + { + auto string_type = get_slice_type(module, uint8(module)); + typecheck(module, expected_type, string_type); + analyze_type(module, unary_value, 0); + auto enum_type = unary_value->type; + if (enum_type->id != TypeId::enumerator) + { + report_error(); + } + + auto enum_to_string = enum_type->enumerator.enum_to_string_function; + if (!enum_to_string) + { + auto current_block = LLVMGetInsertBlock(module->llvm.builder); + auto enum_name_array_global = get_enum_name_array_global(module, enum_type); + LLVMTypeRef argument_types[] = { + enum_type->llvm.abi, + }; + auto llvm_function_type = LLVMFunctionType(string_type->llvm.memory, argument_types, array_length(argument_types), false); + String name_parts[] = { + string_literal("enum_to_string."), + enum_type->name, + }; + auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); + + LLVMValueRef llvm_argument; + LLVMGetParams(llvm_function, &llvm_argument); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto alloca = create_alloca(module, { + .type = string_type, + .name = string_literal("retval"), + }); + + auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); + auto* else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("else_block"), llvm_function); + + auto enum_fields = enum_type->enumerator.fields; + + auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, llvm_argument, else_block, enum_fields.length); + auto backing_type = enum_type->llvm.abi; + assert(backing_type); + auto u64_type = uint64(module)->llvm.abi; + + for (u64 i = 0; i < enum_fields.length; i += 1) + { + auto& field = enum_fields[i]; + auto* case_block = llvm_context_create_basic_block(module->llvm.context, string_literal("case_block"), llvm_function); + auto case_value = LLVMConstInt(backing_type, field.value, false); + + LLVMAddCase(switch_instruction, case_value, case_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, case_block); + + LLVMValueRef indices[] = { + LLVMConstNull(u64_type), + LLVMConstInt(u64_type, i, false), + }; + + auto case_value_result_pointer = create_gep(module, { + .type = enum_name_array_global->variable.type->llvm.memory, + .pointer = enum_name_array_global->variable.storage->llvm, + .indices = array_to_slice(indices), + }); + + auto case_value_result = create_load(module, { + .type = string_type, + .pointer = case_value_result_pointer, + }); + + create_store(module, { + .source = case_value_result, + .destination = alloca, + .type = string_type, + }); + + LLVMBuildBr(module->llvm.builder, return_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + LLVMBuildUnreachable(module->llvm.builder); + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + auto function_result = create_load(module, { + .type = string_type, + .pointer = alloca, + }); + + LLVMBuildRet(module->llvm.builder, function_result); + + if (current_block) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, current_block); + } + + enum_to_string = llvm_function; + enum_type->enumerator.enum_to_string_function = enum_to_string; + } + + assert(enum_to_string); + + value_type = string_type; + } break; + case UnaryId::pointer_from_int: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::pointer) + { + report_error(); + } + + analyze_type(module, unary_value, 0); + auto unary_value_type = unary_value->type; + if (unary_value_type->id != TypeId::integer) + { + report_error(); + } + + // TODO: is this correct? + if (get_bit_size(unary_value_type) != 64) + { + report_error(); + } + + value_type = expected_type; + } break; + default: + { + auto is_boolean = unary_is_boolean(unary_id); + if (is_boolean) + { + analyze_type(module, unary_value, 0); + value_type = uint1(module); + } + else + { + analyze_type(module, unary_value, expected_type); + value_type = unary_value->type; + } + + typecheck(module, expected_type, value_type); + } break; + } + } break; + case ValueId::unary_type: + { + auto unary_type = resolve_type(module, value->unary_type.type); + value->unary_type.type = unary_type; + auto unary_type_id = value->unary_type.id; + + if (expected_type) + { + value_type = expected_type; + } + else + { + value_type = unary_type; + } + + assert(value_type); + if (value_type->id != TypeId::integer) + { + report_error(); + } + + u64 value; + auto max_value = integer_max_value(value_type->integer.bit_count, value_type->integer.is_signed); + switch (unary_type_id) + { + case UnaryTypeId::align_of: + { + value = get_byte_alignment(unary_type); + } break; + case UnaryTypeId::byte_size: + { + + value = get_byte_size(unary_type); + } break; + case UnaryTypeId::integer_max: + { + value = integer_max_value(unary_type->integer.bit_count, unary_type->integer.is_signed); + } break; + } + + if (value > max_value) + { + report_error(); + } + + typecheck(module, expected_type, value_type); + } break; + case ValueId::binary: + { + auto is_boolean = binary_is_boolean(value->binary.id); + analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type); + check_types(module, value->binary.left->type, value->binary.right->type); + + value_type = is_boolean ? uint1(module) : value->binary.left->type; + } break; + case ValueId::variable_reference: + { + switch (value->kind) + { + case ValueKind::left: value_type = value->variable_reference->storage->type; break; + case ValueKind::right: value_type = value->variable_reference->type; break; + } + assert(value_type); + typecheck(module, expected_type, value_type); + } break; + case ValueId::call: + { + auto call = &value->call; + auto callable = call->callable; + analyze_type(module, callable, 0); + Type* function_type = 0; + switch (callable->id) + { + case ValueId::variable_reference: + { + auto variable_type = callable->variable_reference->type; + switch (variable_type->id) + { + case TypeId::function: + function_type = variable_type; break; + case TypeId::pointer: + { + auto* element_type = variable_type->pointer.element_type; + switch (element_type->id) + { + case TypeId::function: function_type = element_type; break; + default: report_error(); + } + } break; + default: report_error(); + } + } break; + default: + report_error(); + } + + assert(function_type); + call->function_type = function_type; + + auto semantic_argument_types = function_type->function.semantic_argument_types; + auto call_arguments = call->arguments; + if (function_type->function.is_variable_arguments) + { + if (call_arguments.length < semantic_argument_types.length) + { + report_error(); + } + } + else + { + if (call_arguments.length != semantic_argument_types.length) + { + report_error(); + } + } + + for (u64 i = 0; i < semantic_argument_types.length; i += 1) + { + auto* argument_type = semantic_argument_types[i]; + auto* call_argument = call_arguments[i]; + analyze_type(module, call_argument, argument_type); + check_types(module, argument_type, call_argument->type); + } + + for (u64 i = semantic_argument_types.length; i < call_arguments.length; i += 1) + { + auto* call_argument = call_arguments[i]; + analyze_type(module, call_argument, 0); + } + + auto semantic_return_type = function_type->function.semantic_return_type; + typecheck(module, expected_type, semantic_return_type); + value_type = semantic_return_type; + } break; + case ValueId::array_initialization: + { + auto values = value->array_initialization.values; + if (expected_type) + { + if (expected_type->id != TypeId::array) + { + report_error(); + } + + if (expected_type->array.element_count == 0) + { + expected_type->array.element_count = values.length; + assert(expected_type->name.equal(string_literal(""))); + expected_type->name = array_name(module, expected_type->array.element_type, expected_type->array.element_count); + } + else + { + if (expected_type->array.element_count != values.length) + { + report_error(); + } + } + + bool is_constant = true; + + auto* element_type = expected_type->array.element_type; + for (auto value : values) + { + analyze_type(module, value, element_type); + is_constant = is_constant && value->is_constant(); + } + + value->array_initialization.is_constant = is_constant; + + if (value->kind == ValueKind::left) // TODO: possible? + { + report_error(); + } + + value_type = expected_type; + } + else + { + if (values.length == 0) + { + report_error(); + } + + Type* expected_type = 0; + bool is_constant = true; + + for (auto value : values) + { + analyze_type(module, value, expected_type); + + is_constant = is_constant && value->is_constant(); + + auto value_type = value->type; + if (expected_type) + { + if (expected_type != value_type) + { + report_error(); + } + } + else + { + assert(value_type); + expected_type = value_type; + } + } + + if (!expected_type) + { + report_error(); + } + + auto element_type = expected_type; + auto element_count = values.length; + + auto array_type = get_array_type(module, element_type, element_count); + value_type = array_type; + + if (value->kind == ValueKind::left) + { + value_type = get_pointer_type(module, array_type); + } + } + } break; + case ValueId::array_expression: + { + analyze_type(module, value->array_expression.index, uint64(module)); + auto array_like = value->array_expression.array_like; + array_like->kind = ValueKind::left; + analyze_type(module, array_like, 0); + assert(array_like->kind == ValueKind::left); + auto array_like_type = array_like->type; + if (array_like_type->id != TypeId::pointer) + { + report_error(); + } + auto pointer_element_type = array_like_type->pointer.element_type; + + Type* element_type = 0; + switch (pointer_element_type->id) + { + case TypeId::array: + { + element_type = pointer_element_type->array.element_type; + } break; + case TypeId::structure: + { + auto slice_type = pointer_element_type; + if (!slice_type->structure.is_slice) + { + report_error(); + } + auto slice_pointer_type = slice_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + element_type = slice_pointer_type->pointer.element_type; + } break; + case TypeId::pointer: + { + element_type = pointer_element_type->pointer.element_type; + } break; + default: report_error(); + } + + assert(element_type); + + value_type = element_type; + if (value->kind == ValueKind::left) + { + value_type = get_pointer_type(module, element_type); + } + + typecheck(module, expected_type, value_type); + } break; + case ValueId::enum_literal: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id != TypeId::enumerator) + { + report_error(); + } + + value_type = expected_type; + } break; + case ValueId::trap: + { + value_type = noreturn_type(module); + } break; + case ValueId::field_access: + { + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + analyze_type(module, aggregate, 0); + + if (aggregate->kind == ValueKind::right) + { + report_error(); + } + + auto aggregate_type = aggregate->type; + if (aggregate_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_element_type = aggregate_type->pointer.element_type; + Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; + auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + { + Field* result_field = 0; + auto fields = resolved_aggregate_type->structure.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + if (field_name.equal(field->name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + auto field_type = result_field->type; + value_type = value->kind == ValueKind::left ? get_pointer_type(module, field_type) : field_type; + } break; + case TypeId::union_type: + { + UnionField* result_field = 0; + auto fields = resolved_aggregate_type->union_type.fields; + for (u64 i = 0; i < fields.length; i += 1) + { + auto* field = &fields[i]; + if (field_name.equal(field->name)) + { + result_field = field; + break; + } + } + + if (!result_field) + { + report_error(); + } + + auto field_type = result_field->type; + value_type = value->kind == ValueKind::left ? get_pointer_type(module, field_type) : field_type; + } break; + case TypeId::bits: + { + if (value->kind == ValueKind::left) + { + report_error(); + } + + auto fields = resolved_aggregate_type->bits.fields; + u64 i; + for (i = 0; i < fields.length; i += 1) + { + auto field = fields[i]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (i == fields.length) + { + report_error(); + } + + assert(value->kind == ValueKind::right); + + auto field = fields[i]; + value_type = field.type; + } break; + case TypeId::array: + { + if (!field_name.equal(string_literal("length"))) + { + report_error(); + } + + if (expected_type) + { + if (expected_type->id != TypeId::integer) + { + report_error(); + } + + value_type = expected_type; + } + else + { + trap(); + } + } break; + case TypeId::pointer: report_error(); // Double indirection is not allowed + default: report_error(); + } + + assert(value_type); + + typecheck(module, expected_type, value_type); + } break; + case ValueId::slice_expression: + { + auto array_like = value->slice_expression.array_like; + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + if (array_like->kind != ValueKind::left) + { + report_error(); + } + + analyze_type(module, array_like, 0); + + auto pointer_type = array_like->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + Type* sliceable_type = pointer_type->pointer.element_type; + + Type* element_type = 0; + + switch (sliceable_type->id) + { + case TypeId::pointer: + { + element_type = sliceable_type->pointer.element_type; + } break; + case TypeId::structure: + { + if (!sliceable_type->structure.is_slice) + { + report_error(); + } + auto slice_pointer_type = sliceable_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + auto slice_element_type = slice_pointer_type->pointer.element_type; + element_type = slice_element_type; + } break; + case TypeId::array: + { + element_type = sliceable_type->array.element_type; + } break; + default: unreachable(); + } + + assert(element_type); + + auto slice_type = get_slice_type(module, element_type); + typecheck(module, expected_type, slice_type); + + auto index_type = uint64(module); + + Value* indices[] = { start, end }; + + for (auto index : indices) + { + if (index) + { + analyze_type(module, index, index_type); + + if (index->type->id != TypeId::integer) + { + report_error(); + } + } + } + + value_type = slice_type; + } break; + case ValueId::string_literal: + { + auto slice_type = get_slice_type(module, uint8(module)); + typecheck(module, expected_type, slice_type); + value_type = slice_type; + } break; + case ValueId::va_start: + { + auto va_list_type = get_va_list_type(module); + typecheck(module, expected_type, va_list_type); + value_type = va_list_type; + } break; + case ValueId::va_arg: + { + analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module))); + value_type = value->va_arg.type; + typecheck(module, expected_type, value_type); + } break; + case ValueId::aggregate_initialization: + { + if (!expected_type) + { + report_error(); + } + + auto resolved_type = resolve_alias(module, expected_type); + value_type = resolved_type; + + assert(!value->aggregate_initialization.is_constant); + bool is_constant = true; + auto values = value->aggregate_initialization.values; + auto names = value->aggregate_initialization.names; + + switch (resolved_type->id) + { + case TypeId::structure: + { + bool is_ordered = true; + auto fields = resolved_type->structure.fields; + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + report_error(); + } + + is_ordered = is_ordered && declaration_index == initialization_index; + + auto field = fields[declaration_index]; + auto declaration_type = field.type; + analyze_type(module, value, declaration_type); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant && is_ordered; + } break; + case TypeId::bits: + { + auto fields = resolved_type->bits.fields; + + + assert(values.length == names.length); + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + report_error(); + } + + auto field = fields[declaration_index]; + auto declaration_type = field.type; + analyze_type(module, value, declaration_type); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant; + } break; + case TypeId::union_type: + { + if (values.length != 1) + { + report_error(); + } + + auto initialization_value = values[0]; + assert(names.length == 1); + auto initialization_name = names[0]; + + u64 i; + auto fields = resolved_type->union_type.fields; + for (i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + if (initialization_name.equal(field.name)) + { + break; + } + } + + if (i == fields.length) + { + report_error(); + } + + auto field = &fields[i]; + analyze_type(module, initialization_value, field->type); + + value_type = expected_type; + } break; + default: report_error(); + } + } break; + case ValueId::zero: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id == TypeId::void_type || expected_type->id == TypeId::noreturn) + { + report_error(); + } + + value_type = expected_type; + } break; + case ValueId::select: + { + auto condition = value->select.condition; + auto true_value = value->select.true_value; + auto false_value = value->select.false_value; + analyze_type(module, condition, 0); + auto is_boolean = false; + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type); + + auto left_type = true_value->type; + auto right_type = false_value->type; + check_types(module, left_type, right_type); + + assert(left_type == right_type); + auto result_type = left_type; + typecheck(module, expected_type, result_type); + + value_type = result_type; + } break; + case ValueId::unreachable: + { + value_type = noreturn_type(module); + } break; + case ValueId::string_to_enum: + { + auto enum_type = value->string_to_enum.type; + auto enum_string_value = value->string_to_enum.string; + if (enum_type->id != TypeId::enumerator) + { + report_error(); + } + + if (!enum_type->enumerator.string_to_enum_function) + { + resolve_type_in_place(module, enum_type); + + auto fields = enum_type->enumerator.fields; + auto array_element_count = fields.length; + + auto insert_block = LLVMGetInsertBlock(module->llvm.builder); + + auto u1_type = uint1(module); + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u1_type); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + auto u64_zero = LLVMConstNull(u64_type->llvm.abi); + + auto enum_alignment = get_byte_alignment(enum_type); + auto enum_size = get_byte_size(enum_type); + auto byte_size = align_forward(enum_size + 1, enum_alignment); + + auto struct_fields = arena_allocate(module->arena, 2); + + struct_fields[0] = { + .name = string_literal("enum_value"), + .type = enum_type, + }; + + struct_fields[1] = { + .name = string_literal("is_valid"), + .type = u1_type, + .offset = enum_size, + }; + + auto struct_type = type_allocate_init(module, { + .structure = { + .fields = struct_fields, + .byte_size = byte_size, + .byte_alignment = enum_alignment, + }, + .id = TypeId::structure, + .name = string_literal("string_to_enum"), + }); + resolve_type_in_place(module, struct_type); + + LLVMTypeRef argument_types[] = { module->llvm.pointer_type, u64_type->llvm.abi }; + auto llvm_function_type = LLVMFunctionType(struct_type->llvm.abi, argument_types, array_length(argument_types), false); + auto slice_struct_type = get_slice_type(module, u8_type); + + String name_parts[] = { + string_literal("string_to_enum."), + enum_type->name, + }; + auto function_name = arena_join_string(module->arena, array_to_slice(name_parts)); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMInternalLinkage, 0, function_name); + LLVMSetFunctionCallConv(llvm_function, LLVMFastCallConv); + + auto name_array_global = get_enum_name_array_global(module, enum_type); + + auto enum_value_type = enum_type->llvm.memory; + + LLVMValueRef value_constant_buffer[64]; + for (u32 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto global_value = LLVMConstInt(enum_value_type, field.value, false); + value_constant_buffer[i] = global_value; + } + + auto value_array = LLVMConstArray2(enum_value_type, value_constant_buffer, array_element_count); + auto value_array_variable_type = LLVMArrayType2(enum_value_type, array_element_count); + auto is_constant = true; + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + unsigned address_space = 0; + auto externally_initialized = false; + auto value_array_variable = llvm_module_create_global_variable(module->llvm.module, value_array_variable_type, is_constant, LLVMInternalLinkage, value_array, string_literal("value.array.enum"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetAlignment(value_array_variable, enum_alignment); + LLVMSetUnnamedAddress(value_array_variable, LLVMGlobalUnnamedAddr); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); + auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), llvm_function); + auto* loop_entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.entry"), llvm_function); + auto* loop_body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.body"), llvm_function); + auto* loop_exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("loop.exit"), llvm_function); + + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + LLVMValueRef arguments[2]; + LLVMGetParams(llvm_function, arguments); + + auto return_value_alloca = create_alloca(module, { + .type = enum_type, + .name = string_literal("retval"), + }); + + auto return_boolean_alloca = create_alloca(module, { + .type = u8_type, + .name = string_literal("retbool"), + }); + + auto index_alloca = create_alloca(module, { + .type = u64_type, + .name = string_literal("index"), + }); + + create_store(module, { + .source = u64_zero, + .destination = index_alloca, + .type = u64_type, + }); + + auto slice_pointer = arguments[0]; + auto slice_length = arguments[1]; + LLVMBuildBr(module->llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_entry_block); + auto index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + auto loop_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, index_load, LLVMConstInt(u64_type->llvm.abi, array_element_count, false), ""); + LLVMBuildCondBr(module->llvm.builder, loop_compare, loop_body_block, loop_exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_body_block); + auto body_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef indices[] = { + u64_zero, + body_index_load, + }; + auto array_element_pointer = create_gep(module, { + .type = name_array_global->variable.type->llvm.memory, + .pointer = name_array_global->variable.storage->llvm, + .indices = array_to_slice(indices), + }); + + auto element_length_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_struct_type->llvm.abi, array_element_pointer, 1, ""); + auto element_length = create_load(module, { + .type = u64_type, + .pointer = element_length_pointer, + }); + + auto length_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, slice_length, element_length, ""); + + auto* length_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.match"), llvm_function); + auto* length_mismatch_block = llvm_context_create_basic_block(module->llvm.context, string_literal("length.mismatch"), llvm_function); + LLVMBuildCondBr(module->llvm.builder, length_comparison, length_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, length_match_block); + + auto s32_type = sint32(module); + resolve_type_in_place(module, s32_type); + + LLVMValueRef memcmp = module->llvm.memcmp; + if (!memcmp) + { + memcmp = LLVMGetNamedFunction(module->llvm.module, "memcmp"); + if (!memcmp) + { + LLVMTypeRef arguments[] = { + module->llvm.pointer_type, + module->llvm.pointer_type, + u64_type->llvm.abi, + }; + auto llvm_function_type = LLVMFunctionType(s32_type->llvm.abi, arguments, array_length(arguments), false); + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, LLVMExternalLinkage, address_space, string_literal("memcmp")); + memcmp = llvm_function; + } + + module->llvm.memcmp = memcmp; + } + + assert(memcmp); + assert(module->llvm.memcmp); + + auto length_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef length_indices[] = { u64_zero, length_index_load }; + auto length_array_element_pointer = create_gep(module, { + .type = name_array_global->variable.type->llvm.memory, + .pointer = name_array_global->variable.storage->llvm, + .indices = array_to_slice(length_indices), + }); + + auto element_pointer_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_struct_type->llvm.abi, length_array_element_pointer, 0, ""); + auto element_pointer = create_load(module, { + .type = get_pointer_type(module, u8_type), + .pointer = element_pointer_pointer, + }); + + LLVMValueRef memcmp_arguments[] = { + slice_pointer, + element_pointer, + slice_length, + }; + auto memcmp_return_result = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(memcmp), memcmp, memcmp_arguments, array_length(memcmp_arguments), ""); + auto content_comparison = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, memcmp_return_result, LLVMConstNull(s32_type->llvm.abi), ""); + auto* content_match_block = llvm_context_create_basic_block(module->llvm.context, string_literal("content.match"), llvm_function); + LLVMBuildCondBr(module->llvm.builder, content_comparison, content_match_block, length_mismatch_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, content_match_block); + + auto content_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + LLVMValueRef value_array_indices[] = { + u64_zero, + content_index_load, + }; + auto value_array_element_pointer = create_gep(module, { + .type = value_array_variable_type, + .pointer = value_array_variable, + .indices = array_to_slice(value_array_indices), + }); + + auto enum_value_load = create_load(module, { + .type = enum_type, + .pointer = value_array_element_pointer, + }); + + create_store(module, { + .source = enum_value_load, + .destination = return_value_alloca, + .type = enum_type, + }); + + create_store(module, { + .source = LLVMConstInt(u8_type->llvm.abi, 1, false), + .destination = return_boolean_alloca, + .type = u8_type, + }); + + LLVMBuildBr(module->llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, length_mismatch_block); + + auto inc_index_load = create_load(module, { + .type = u64_type, + .pointer = index_alloca, + }); + + auto inc = LLVMBuildAdd(module->llvm.builder, inc_index_load, LLVMConstInt(u64_type->llvm.abi, 1, false), ""); + + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = u64_type, + }); + + LLVMBuildBr(module->llvm.builder, loop_entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, loop_exit_block); + + create_store(module, { + .source = LLVMConstNull(enum_type->llvm.memory), + .destination = return_value_alloca, + .type = enum_type, + }); + create_store(module, { + .source = LLVMConstNull(u8_type->llvm.abi), + .destination = return_boolean_alloca, + .type = u8_type, + }); + LLVMBuildBr(module->llvm.builder, return_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + auto value_load = create_load(module, { + .type = enum_type, + .pointer = return_value_alloca, + .kind = TypeKind::memory, + }); + + auto return_value = LLVMBuildInsertValue(module->llvm.builder, LLVMGetPoison(struct_type->llvm.memory), value_load, 0, ""); + auto bool_load = create_load(module, { + .type = u8_type, + .pointer = return_boolean_alloca, + }); + + return_value = LLVMBuildInsertValue(module->llvm.builder, return_value, bool_load, 1, ""); + + LLVMBuildRet(module->llvm.builder, return_value); + + // End of scope + LLVMPositionBuilderAtEnd(module->llvm.builder, insert_block); + + enum_type->enumerator.string_to_enum_function = llvm_function; + enum_type->enumerator.string_to_enum_struct_type = struct_type; + } + + auto struct_type = enum_type->enumerator.string_to_enum_struct_type; + assert(struct_type); + + typecheck(module, expected_type, struct_type); + + auto string_type = get_slice_type(module, uint8(module)); + + analyze_type(module, enum_string_value, string_type); + value_type = struct_type; + } break; + case ValueId::undefined: + { + if (!expected_type) + { + report_error(); + } + + if (expected_type->id == TypeId::void_type || expected_type->id == TypeId::noreturn) + { + report_error(); + } + + value_type = expected_type; + } break; + case ValueId::macro_instantiation: + { + if (module->current_macro_declaration) + { + report_error(); + } + + auto current_function = module->current_function; + if (!current_function) + { + report_error(); + } + + module->current_function = 0; + + auto current_macro_instantiation = module->current_macro_instantiation; + + auto macro_instantiation = &value->macro_instantiation; + module->current_macro_instantiation = macro_instantiation; + + auto declaration = macro_instantiation->declaration; + + auto declaration_arguments = declaration->arguments; + auto instantiation_declaration_arguments = arena_allocate(module->arena, declaration_arguments.length); + macro_instantiation->declaration_arguments = instantiation_declaration_arguments; + + LLVMMetadataRef subprogram = 0; + if (module->has_debug_info) + { + LLVMMetadataRef subroutine_type = 0; + auto is_local_to_unit = true; + auto is_definition = true; + LLVMDIFlags flags = {}; + auto is_optimized = build_mode_is_optimized(module->build_mode); + subprogram = LLVMDIBuilderCreateFunction(module->llvm.di_builder, module->scope.llvm, (char*)declaration->name.pointer, declaration->name.length, (char*)declaration->name.pointer, declaration->name.length, module->llvm.file, macro_instantiation->scope.line, subroutine_type, is_local_to_unit, is_definition, macro_instantiation->scope.line, flags, is_optimized); + } + + macro_instantiation->scope.llvm = subprogram; + + // First copy + for (u64 i = 0; i < declaration_arguments.length; i += 1) + { + auto& instantiation_declaration_argument = instantiation_declaration_arguments[i]; + const auto& declaration_argument = declaration_arguments[i]; + instantiation_declaration_argument = { + .variable = { + .initial_value = 0, + .type = declaration_argument.variable.type, + .scope = ¯o_instantiation->scope, + .name = declaration_argument.variable.name, + .line = declaration_argument.variable.line, + .column = declaration_argument.variable.column, + }, + .index = declaration_argument.index, + }; + } + + auto declaration_constant_arguments = declaration->constant_arguments; + auto instantiation_constant_arguments = macro_instantiation->constant_arguments; + + for (u64 i = 0; i < declaration_constant_arguments.length; i += 1) + { + const auto& declaration_constant_argument = declaration_constant_arguments[i]; + auto& instantiation_constant_argument = instantiation_constant_arguments[i]; + + assert(declaration_constant_argument.id == instantiation_constant_argument.id); + + instantiation_constant_argument.name = declaration_constant_argument.name; + + switch (declaration_constant_argument.id) + { + case ConstantArgumentId::value: + { + trap(); + } break; + case ConstantArgumentId::type: + { + auto declaration_type = declaration_constant_argument.type; + assert(declaration_type->id == TypeId::unresolved); + + auto old_instantiation_type = instantiation_constant_argument.type; + + auto instantiation_type = type_allocate_init(module, { + .alias = { + .type = old_instantiation_type, + .scope = ¯o_instantiation->scope, + .line = macro_instantiation->line, + }, + .id = TypeId::alias, + .name = declaration_constant_argument.name, + }); + instantiation_constant_argument.type = instantiation_type; + } break; + } + } + + value_type = resolve_type(module, declaration->return_type); + assert(value_type->id != TypeId::unresolved); + macro_instantiation->return_type = value_type; + + for (auto& argument : macro_instantiation->declaration_arguments) + { + argument.variable.type = resolve_type(module, argument.variable.type); + } + + auto instantiation_arguments = macro_instantiation->instantiation_arguments; + if (instantiation_arguments.length != instantiation_declaration_arguments.length) + { + report_error(); + } + + if (module->has_debug_info) + { + for (u64 i = 0; i < instantiation_arguments.length; i += 1) + { + auto& instantiation_argument = instantiation_arguments[i]; + const auto& declaration_argument = instantiation_declaration_arguments[i]; + + auto argument_type = declaration_argument.variable.type; + assert(argument_type); + analyze_type(module, instantiation_argument, argument_type); + } + + LLVMMetadataRef type_buffer[64]; + auto type_count = instantiation_arguments.length + 1; + + resolve_type_in_place_debug(module, value_type); + type_buffer[0] = value_type->llvm.debug; + + for (u64 i = 0; i < instantiation_declaration_arguments.length; i += 1) + { + const auto& declaration_argument = instantiation_declaration_arguments[i]; + auto type = declaration_argument.variable.type; + resolve_type_in_place_debug(module, type); + type_buffer[i + 1] = type->llvm.debug; + } + + LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + LLVMDIFlags flags = {}; + auto subroutine_type = LLVMDIBuilderCreateSubroutineType(module->llvm.di_builder, module->llvm.file, type_buffer, type_count, flags); + assert(macro_instantiation->scope.llvm); + llvm_subprogram_replace_type(subprogram, subroutine_type); + } + + assert(!macro_instantiation->block); + macro_instantiation->block = &arena_allocate(module->arena, 1)[0]; + + copy_block(module, ¯o_instantiation->scope, { + .source = declaration->block, + .destination = macro_instantiation->block, + }); + + resolve_type_in_place(module, value_type); + typecheck(module, expected_type, value_type); + + if (!module->has_debug_info) + { + for (u64 i = 0; i < instantiation_arguments.length; i += 1) + { + auto& instantiation_argument = instantiation_arguments[i]; + const auto& declaration_argument = instantiation_declaration_arguments[i]; + + auto argument_type = declaration_argument.variable.type; + assert(argument_type); + analyze_type(module, instantiation_argument, argument_type); + } + } + + // END of scope + module->current_macro_instantiation = current_macro_instantiation; + module->current_function = current_function; + } break; + default: unreachable(); + } + + assert(value_type); + value->type = value_type; +} + +fn LLVMTypeRef get_llvm_type(Type* type, TypeKind type_kind) +{ + switch (type_kind) + { + case TypeKind::abi: + return type->llvm.abi; + case TypeKind::memory: + return type->llvm.memory; + } +} + +fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind); + +fn bool type_is_integer_backing(Type* type) +{ + switch (type->id) + { + case TypeId::enumerator: + case TypeId::integer: + case TypeId::bits: + case TypeId::pointer: + return true; + default: + return false; + } +} + +struct ValueTypePair +{ + LLVMValueRef value; + Type* type; +}; + +fn ValueTypePair enter_struct_pointer_for_coerced_access(Module* module, LLVMValueRef source_value, Type* source_type, u64 destination_size) +{ + unused(module); + assert(source_type->id == TypeId::structure && source_type->structure.fields.length > 0); + auto first_field_type = source_type->structure.fields[0].type; + auto first_field_size = get_byte_size(first_field_type); + auto source_size = get_byte_size(source_type); + + if (!(first_field_size < destination_size && first_field_size < source_size)) + { + trap(); + } + + return { source_value, source_type }; +} + +fn LLVMValueRef coerce_integer_or_pointer_to_integer_or_pointer(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) +{ + unused(module); + unused(source_type); + unused(destination_type); + if (source_type != destination_type) + { + trap(); + } + + return source; +} + +fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* source_type, Type* destination_type) +{ + LLVMValueRef result = 0; + + if (type_is_abi_equal(module, source_type, destination_type)) + { + trap(); + } + else + { + auto destination_size = get_byte_size(destination_type); + if (source_type->id == TypeId::structure) + { + auto src = enter_struct_pointer_for_coerced_access(module, source, source_type, destination_size); + source = src.value; + source_type = src.type; + } + + if (type_is_integer_backing(source_type) && type_is_integer_backing(destination_type)) + { + trap(); + } + else + { + auto source_size = get_byte_size(source_type); + + auto is_source_type_scalable = false; + auto is_destination_type_scalable = false; + + if (!is_source_type_scalable && !is_destination_type_scalable && source_size >= destination_size) + { + result = create_load(module, LoadOptions{ .type = destination_type, .pointer = source }); + } + else + { + trap(); + } + } + } + + assert(result); + + return result; +} + +fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* source_type, LLVMValueRef destination_value, Type* destination_type, u64 destination_size, bool destination_volatile) +{ + unused(destination_volatile); + + auto source_size = get_byte_size(source_type); + + if (!type_is_abi_equal(module, source_type, destination_type)) + { + auto r = enter_struct_pointer_for_coerced_access(module, destination_value, destination_type, source_size); + destination_value = r.value; + destination_type = r.type; + } + + auto is_scalable = false; + + if (is_scalable || source_size <= destination_size) + { + auto destination_alignment = get_byte_alignment(destination_type); + + if (source_type->id == TypeId::integer && destination_type->id == TypeId::pointer && source_size == align_forward(destination_size, destination_alignment)) + { + trap(); + } + else if (source_type->id == TypeId::structure) + { + auto fields = source_type->structure.fields; + for (u32 i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto gep = LLVMBuildStructGEP2(module->llvm.builder, source_type->llvm.abi, destination_value, i, ""); + auto field_value = LLVMBuildExtractValue(module->llvm.builder, source_value, i, ""); + create_store(module, { + .source = field_value, + .destination = gep, + .type = field.type, + .alignment = destination_alignment, + }); + } + } + else + { + create_store(module, StoreOptions{ + .source = source_value, + .destination = destination_value, + .type = destination_type, + .alignment = destination_alignment, + }); + } + } + else if (type_is_integer_backing(source_type)) + { + auto int_type = integer_type(module, { .bit_count = (u32)destination_size * 8, .is_signed = false }); + auto value = coerce_integer_or_pointer_to_integer_or_pointer(module, source_value, source_type, int_type); + create_store(module, { + .source = value, + .destination = destination_value, + .type = int_type, + }); + } + else + { + trap(); + } +} + +struct SliceEmitResult +{ + LLVMValueRef values[2]; +}; + +fn LLVMValueRef emit_slice_result(Module* module, SliceEmitResult slice, LLVMTypeRef slice_type) +{ + auto result = LLVMGetPoison(slice_type); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[0], 0, ""); + result = LLVMBuildInsertValue(module->llvm.builder, result, slice.values[1], 1, ""); + return result; +} + +fn SliceEmitResult emit_slice_expression(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::slice_expression: + { + auto value_type = value->type; + assert(value_type); + assert(type_is_slice(value_type)); + auto slice_pointer_type = value_type->structure.fields[0].type; + assert(slice_pointer_type->id == TypeId::pointer); + auto slice_element_type = slice_pointer_type->pointer.element_type; + + auto index_type = uint64(module); + resolve_type_in_place(module, index_type); + auto llvm_index_type = index_type->llvm.abi; + auto index_zero = LLVMConstInt(llvm_index_type, 0, 0); + + auto array_like = value->slice_expression.array_like; + auto start = value->slice_expression.start; + auto end = value->slice_expression.end; + + assert(array_like->kind == ValueKind::left); + emit_value(module, array_like, TypeKind::memory); + + auto pointer_type = array_like->type; + assert(pointer_type->id == TypeId::pointer); + auto sliceable_type = pointer_type->pointer.element_type; + bool has_start = start; + if (start && start->id == ValueId::constant_integer && start->constant_integer.value == 0) + { + has_start = false; + } + + if (start) + { + emit_value(module, start, TypeKind::memory); + } + + if (end) + { + emit_value(module, end, TypeKind::memory); + } + + switch (sliceable_type->id) + { + case TypeId::pointer: + { + auto element_type = sliceable_type->pointer.element_type; + auto pointer_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + + auto slice_pointer = pointer_load; + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + } + + auto slice_length = end->llvm; + + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_length, start->llvm, ""); + } + return { slice_pointer, slice_length }; + } break; + case TypeId::structure: + { + assert(sliceable_type->structure.is_slice); + auto slice_load = create_load(module, { + .type = sliceable_type, + .pointer = array_like->llvm, + }); + auto old_slice_pointer = LLVMBuildExtractValue(module->llvm.builder, slice_load, 0, ""); + auto slice_pointer = old_slice_pointer; + + if (has_start) + { + LLVMValueRef indices[] = { start->llvm }; + slice_pointer = create_gep(module, { + .type = slice_element_type->llvm.memory, + .pointer = old_slice_pointer, + .indices = array_to_slice(indices), + }); + } + + auto slice_end = end ? end->llvm : LLVMBuildExtractValue(module->llvm.builder, slice_load, 1, ""); + auto slice_length = slice_end; + if (has_start) + { + slice_length = LLVMBuildSub(module->llvm.builder, slice_end, start->llvm, ""); + } + + return { slice_pointer, slice_length }; + } break; + case TypeId::array: + { + assert(sliceable_type->array.element_type == slice_element_type); + LLVMValueRef slice_pointer = array_like->llvm; + if (has_start) + { + LLVMValueRef indices[] = { index_zero, start->llvm }; + slice_pointer = create_gep(module, { + .type = sliceable_type->llvm.memory, + .pointer = slice_pointer, + .indices = array_to_slice(indices), + }); + } + + LLVMValueRef slice_length = 0; + if (has_start) + { + trap(); + } + else if (end) + { + slice_length = end->llvm; + } + else + { + auto element_count = sliceable_type->array.element_count; + slice_length = LLVMConstInt(llvm_index_type, element_count, 0); + } + + assert(slice_length); + return { slice_pointer, slice_length }; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + + +fn SliceEmitResult emit_string_literal(Module* module, Value* value) +{ + auto resolved_value_type = resolve_alias(module, value->type); + switch (value->id) + { + case ValueId::string_literal: + { + bool null_terminate = true; + auto length = value->string_literal.length; + auto constant_string = LLVMConstStringInContext2(module->llvm.context, (char*)value->string_literal.pointer, length, !null_terminate); + auto u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + auto string_type = LLVMArrayType2(u8_type->llvm.abi, length + null_terminate); + auto is_constant = true; + LLVMValueRef before = 0; + LLVMThreadLocalMode tlm = LLVMNotThreadLocal; + bool externally_initialized = false; + unsigned address_space = 0; + auto global = llvm_module_create_global_variable(module->llvm.module, string_type, is_constant, LLVMInternalLinkage, constant_string, string_literal("conststring"), before, tlm, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + auto slice_type = get_slice_type(module, u8_type); + + if (resolved_value_type->id != TypeId::structure) + { + report_error(); + } + + if (!resolved_value_type->structure.is_slice) + { + report_error(); + } + + if (slice_type != resolved_value_type) + { + report_error(); + } + + return { global, LLVMConstInt(slice_type->structure.fields[1].type->llvm.abi, length, false) }; + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) +{ + switch (value->id) + { + case ValueId::call: + { + auto call = &value->call; + + auto raw_function_type = call->function_type; + auto callable = call->callable; + auto call_arguments = call->arguments; + + LLVMValueRef llvm_callable = 0; + + switch (callable->id) + { + case ValueId::variable_reference: + { + auto variable = callable->variable_reference; + auto variable_type = variable->type; + switch (variable_type->id) + { + case TypeId::pointer: + { + auto element_type = variable_type->pointer.element_type; + switch (element_type->id) + { + case TypeId::function: + { + llvm_callable = create_load(module, LoadOptions{ + .type = get_pointer_type(module, raw_function_type), + .pointer = variable->storage->llvm, + }); + } break; + default: report_error(); + } + } break; + case TypeId::function: llvm_callable = variable->storage->llvm; break; + default: report_error(); + } + } break; + default: report_error(); + } + + assert(llvm_callable); + + LLVMValueRef llvm_abi_argument_value_buffer[64]; + LLVMTypeRef llvm_abi_argument_type_buffer[64]; + Type* abi_argument_type_buffer[64]; + AbiInformation argument_abi_buffer[64]; + + u16 abi_argument_count = 0; + + bool uses_in_alloca = false; + if (uses_in_alloca) + { + trap(); + } + + LLVMValueRef llvm_indirect_return_value = 0; + + auto& return_abi = raw_function_type->function.return_abi; + auto return_abi_kind = return_abi.flags.kind; + switch (return_abi_kind) + { + case AbiKind::indirect: + case AbiKind::in_alloca: + case AbiKind::coerce_and_expand: + { + // TODO: handle edge cases: + // - virtual function pointer thunk + // - return alloca already exists + LLVMValueRef pointer = 0; + auto semantic_return_type = return_abi.semantic_type; + if (left_llvm) + { + assert(left_type->pointer.element_type == semantic_return_type); + pointer = left_llvm; + } + else + { + trap(); + } + assert(pointer); + + auto has_sret = return_abi.flags.kind == AbiKind::indirect; + if (has_sret) + { + auto void_ty = void_type(module); + llvm_abi_argument_value_buffer[abi_argument_count] = pointer; + abi_argument_type_buffer[abi_argument_count] = void_ty; + llvm_abi_argument_type_buffer[abi_argument_count] = void_ty->llvm.abi; + abi_argument_count += 1; + llvm_indirect_return_value = pointer; + } + else if (return_abi.flags.kind == AbiKind::in_alloca) + { + trap(); + } + else + { + trap(); + } + } break; + default: break; + } + + auto available_registers = raw_function_type->function.available_registers; + + auto declaration_semantic_argument_count = raw_function_type->function.semantic_argument_types.length; + for (u64 call_argument_index = 0; call_argument_index < call_arguments.length; call_argument_index += 1) + { + auto is_named_argument = call_argument_index < declaration_semantic_argument_count; + auto semantic_call_argument_value = call_arguments[call_argument_index]; + + Type* semantic_argument_type; + AbiInformation argument_abi; + Slice llvm_abi_argument_type_buffer_slice = array_to_slice(llvm_abi_argument_type_buffer); + Slice abi_argument_type_buffer_slice = array_to_slice(abi_argument_type_buffer); + + if (is_named_argument) + { + argument_abi = raw_function_type->function.argument_abis[call_argument_index]; + semantic_argument_type = argument_abi.semantic_type; + } + else + { + semantic_argument_type = semantic_call_argument_value->type; + argument_abi = abi_system_v_classify_argument(module, &available_registers.system_v, llvm_abi_argument_type_buffer_slice, abi_argument_type_buffer_slice, { + .type = resolve_alias(module, semantic_argument_type), + .abi_start = abi_argument_count, + .is_named_argument = false, + }); + } + + if (get_byte_size(semantic_argument_type) > 60 && argument_abi.flags.kind != AbiKind::indirect) + { + trap(); + } + + resolve_type_in_place(module, semantic_argument_type); + + if (is_named_argument) + { + auto llvm_abi_argument_types = llvm_abi_argument_type_buffer_slice(argument_abi.abi_start)(0, argument_abi.abi_count); + auto destination_abi_argument_types = abi_argument_type_buffer_slice(argument_abi.abi_start)(0, argument_abi.abi_count); + auto source_abi_argument_types = raw_function_type->function.abi_argument_types(argument_abi.abi_start)(0, argument_abi.abi_count); + for (u16 i = 0; i < argument_abi.abi_count; i += 1) + { + llvm_abi_argument_types[i] = source_abi_argument_types[i]->llvm.abi; + destination_abi_argument_types[i] = source_abi_argument_types[i]; + } + } + + argument_abi_buffer[call_argument_index] = argument_abi; + + if (argument_abi.padding.type) + { + trap(); + } + + assert(abi_argument_count == argument_abi.abi_start); + auto argument_abi_kind = argument_abi.flags.kind; + switch (argument_abi_kind) + { + case AbiKind::direct: + case AbiKind::extend: + { + auto coerce_to_type = argument_abi.get_coerce_to_type(); + resolve_type_in_place(module, coerce_to_type); + + if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, semantic_argument_type, coerce_to_type) && argument_abi.attributes.direct.offset == 0) + { + emit_value(module, semantic_call_argument_value, TypeKind::memory); + + auto evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + Value* v; + switch (evaluation_kind) + { + case EvaluationKind::scalar: v = semantic_call_argument_value; break; + case EvaluationKind::aggregate: trap(); + case EvaluationKind::complex: trap(); + } + + if (!type_is_abi_equal(module, coerce_to_type, v->type)) + { + trap(); + } + + llvm_abi_argument_value_buffer[abi_argument_count] = v->llvm; + abi_argument_count += 1; + } + else + { + if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && !argument_abi.flags.can_be_flattened) + { + trap(); + } + + auto evaluation_kind = get_evaluation_kind(semantic_argument_type); + Value* src = 0; + switch (evaluation_kind) + { + case EvaluationKind::scalar: trap(); + case EvaluationKind::aggregate: src = semantic_call_argument_value; break; + case EvaluationKind::complex: trap(); + } + assert(src); + + if (argument_abi.attributes.direct.offset != 0) + { + trap(); + } + + if (coerce_to_type->id == TypeId::structure && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) + { + auto source_type_is_scalable = false; + if (source_type_is_scalable) + { + trap(); + } + else + { + if (src->kind == ValueKind::right) + { + if (src->id == ValueId::variable_reference) + { + src->type = 0; + src->kind = ValueKind::left; + analyze_type(module, src, 0); + } + } + + emit_value(module, semantic_call_argument_value, TypeKind::memory); + auto destination_size = get_byte_size(coerce_to_type); + auto source_size = get_byte_size(argument_abi.semantic_type); + auto alignment = get_byte_alignment(argument_abi.semantic_type); + + LLVMValueRef source = src->llvm; + if (source_size < destination_size) + { + trap(); + } + + auto coerce_fields = coerce_to_type->structure.fields; + + // TODO: + assert(argument_abi.attributes.direct.offset == 0); + + switch (semantic_call_argument_value->kind) + { + case ValueKind::left: + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + auto& field = coerce_fields[i]; + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.memory, source, i, ""); + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { + .type = field.type, + .pointer = gep, + .alignment = alignment, + }); + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } break; + case ValueKind::right: + { + if (type_is_abi_equal(module, coerce_to_type, semantic_argument_type)) + { + for (u32 i = 0; i < (u32)coerce_fields.length; i += 1) + { + llvm_abi_argument_value_buffer[abi_argument_count] = LLVMBuildExtractValue(module->llvm.builder, source, i, ""); + abi_argument_count += 1; + } + } + else + { + switch (semantic_call_argument_value->id) + { + case ValueId::aggregate_initialization: + { + auto is_constant = semantic_call_argument_value->aggregate_initialization.is_constant; + + if (is_constant) + { + bool is_constant = true; + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = {}; + u32 address_space = 0; + bool externally_initialized = false; + + auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + + auto alignment = get_byte_alignment(semantic_argument_type); + LLVMSetAlignment(global, alignment); + + for (u32 i = 0; i < coerce_fields.length; i += 1) + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, global, i, ""); + auto& field = coerce_fields[i]; + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + auto load = create_load(module, { .type = field.type, .pointer = gep, .alignment = alignment }); + + llvm_abi_argument_value_buffer[abi_argument_count] = load; + abi_argument_count += 1; + } + } + else + { + trap(); + } + } break; + default: trap(); + } + } + } break; + } + } + } + else + { + assert(argument_abi.abi_count == 1); + auto destination_type = coerce_to_type; + + LLVMValueRef v = 0; + switch (src->id) + { + case ValueId::zero: + { + trap(); + } break; + default: + { + if (src->type->id != TypeId::pointer) + { + assert(src->kind == ValueKind::right); + assert(src->type->id == TypeId::structure); + auto type = src->type; + assert(src->kind == ValueKind::right); + src->type = 0; + src->kind = ValueKind::left; + analyze_type(module, src, get_pointer_type(module, type)); + } + + assert(src->type->id == TypeId::pointer); + assert(src->type->llvm.abi == module->llvm.pointer_type); + emit_value(module, src, TypeKind::memory); + + assert(src->type->id == TypeId::pointer); + auto source_type = src->type->pointer.element_type; + assert(source_type == argument_abi.semantic_type); + auto load = create_coerced_load(module, src->llvm, source_type, destination_type); + + auto is_cmse_ns_call = false; + if (is_cmse_ns_call) + { + trap(); + } + + auto maybe_undef = false; + if (maybe_undef) + { + trap(); + } + + v = load; + } break; + } + assert(v); + + llvm_abi_argument_value_buffer[abi_argument_count] = v; + abi_argument_count += 1; + } + } + } break; + case AbiKind::indirect: + case AbiKind::indirect_aliased: + { + auto evaluation_kind = get_evaluation_kind(semantic_argument_type); + auto do_continue = false; + if (evaluation_kind == EvaluationKind::aggregate) + { + auto same_address_space = true; + assert(argument_abi.abi_start >= raw_function_type->function.abi_argument_types.length || same_address_space); + + // TODO: handmade code, may contain bugs + assert(argument_abi.abi_count == 1); + auto abi_argument_type = abi_argument_type_buffer[argument_abi.abi_start]; + + if (abi_argument_type == semantic_call_argument_value->type) + { + trap(); + } + else if (abi_argument_type->id == TypeId::pointer && abi_argument_type->pointer.element_type == semantic_call_argument_value->type) + { + auto is_constant = semantic_call_argument_value->is_constant(); + + if (is_constant) + { + emit_value(module, semantic_call_argument_value, TypeKind::memory); + + bool is_constant = true; + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = {}; + u32 address_space = 0; + bool externally_initialized = false; + + auto global = llvm_module_create_global_variable(module->llvm.module, semantic_argument_type->llvm.memory, is_constant, linkage_type, semantic_call_argument_value->llvm, string_literal("conststruct"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + + auto alignment = get_byte_alignment(semantic_argument_type); + LLVMSetAlignment(global, alignment); + + llvm_abi_argument_value_buffer[abi_argument_count] = global; + abi_argument_count += 1; + } + else + { + auto pointer_type = get_pointer_type(module, semantic_call_argument_value->type); + + switch (semantic_call_argument_value->id) + { + case ValueId::variable_reference: + { + semantic_call_argument_value->type = 0; + semantic_call_argument_value->kind = ValueKind::left; + analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory); + llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; + abi_argument_count += 1; + } break; + default: + { + assert(abi_argument_type->id == TypeId::pointer); + assert(abi_argument_type->pointer.element_type == semantic_call_argument_value->type); + auto alloca = create_alloca(module, { + .type = semantic_call_argument_value->type, + }); + emit_assignment(module, alloca, pointer_type, semantic_call_argument_value); + llvm_abi_argument_value_buffer[abi_argument_count] = alloca; + abi_argument_count += 1; + } break; + } + } + + do_continue = true; + } + else + { + trap(); + } + } + + if (!do_continue) + { + trap(); + } + + } break; + case AbiKind::ignore: unreachable(); + default: unreachable(); + } + + assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count); + } + + auto declaration_abi_argument_count = raw_function_type->function.abi_argument_types.length; + + if (raw_function_type->function.is_variable_arguments) + { + assert(abi_argument_count >= declaration_abi_argument_count); + } + else + { + assert(abi_argument_count == declaration_abi_argument_count); + } + + assert(raw_function_type->llvm.abi); + Slice argument_abis = { .pointer = argument_abi_buffer, .length = call_arguments.length }; + auto llvm_call = LLVMBuildCall2(module->llvm.builder, raw_function_type->llvm.abi, llvm_callable, llvm_abi_argument_value_buffer, abi_argument_count, ""); + auto attribute_list = build_attribute_list(module, { + .return_abi = return_abi, + .argument_abis = argument_abis, + .abi_argument_types = { .pointer = abi_argument_type_buffer, .length = abi_argument_count }, + .abi_return_type = raw_function_type->function.abi_return_type, + .attributes = {}, + .call_site = true, + }); + LLVMSetInstructionCallConv(llvm_call, llvm_calling_convention(raw_function_type->function.calling_convention)); + llvm_call_base_set_attributes(llvm_call, attribute_list); + switch (return_abi_kind) + { + case AbiKind::ignore: + { + assert(return_abi.semantic_type == noreturn_type(module) || return_abi.semantic_type == void_type(module)); + return llvm_call; + } break; + case AbiKind::direct: + case AbiKind::extend: + { + auto coerce_to_type = return_abi.get_coerce_to_type(); + + if (type_is_abi_equal(module, return_abi.semantic_type, coerce_to_type) && return_abi.attributes.direct.offset == 0) + { + auto evaluation_kind = get_evaluation_kind(coerce_to_type); + + switch (evaluation_kind) + { + case EvaluationKind::scalar: return llvm_call; + case EvaluationKind::aggregate: break; + case EvaluationKind::complex: unreachable(); + } + } + + // TODO: if + auto fixed_vector_type = false; + if (fixed_vector_type) + { + trap(); + } + + LLVMValueRef coerce_alloca = 0; + + if (left_llvm) + { + assert(left_type->pointer.element_type == return_abi.semantic_type); + coerce_alloca = left_llvm; + } + else + { + coerce_alloca = create_alloca(module, { + .type = return_abi.semantic_type, + .name = string_literal("coerce"), + }); + } + + LLVMValueRef destination_pointer = coerce_alloca; + if (return_abi.attributes.direct.offset != 0) + { + trap(); + } + + auto destination_type = return_abi.semantic_type; + + assert(return_abi.semantic_type->id == TypeId::structure); + if (return_abi.semantic_type->structure.fields.length > 0) + { + auto source_value = llvm_call; + auto source_type = raw_function_type->function.abi_return_type; + auto destination_size = get_byte_size(destination_type); + auto left_destination_size = destination_size - return_abi.attributes.direct.offset; + auto is_destination_volatile = false; + create_coerced_store(module, source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile); + } + else + { + trap(); + } + + assert(coerce_alloca); + if (left_llvm) + { + assert(destination_pointer == left_llvm); + return destination_pointer; + } + else + { + switch (value->kind) + { + case ValueKind::right: return create_load(module, { .type = destination_type, .pointer = destination_pointer }); + case ValueKind::left: trap(); + } + } + } break; + case AbiKind::indirect: + { + assert(llvm_indirect_return_value); + return llvm_indirect_return_value; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_va_arg_from_memory(Module* module, LLVMValueRef va_list_pointer, Type* va_list_struct, Type* argument_type) +{ + assert(va_list_struct->id == TypeId::structure); + auto overflow_arg_area_pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct->llvm.abi, va_list_pointer, 2, ""); + auto overflow_arg_area_type = va_list_struct->structure.fields[2].type; + auto overflow_arg_area = create_load(module, { .type = overflow_arg_area_type, .pointer = overflow_arg_area_pointer }); + if (get_byte_alignment(argument_type) > 8) + { + trap(); + } + + auto argument_type_size = get_byte_size(argument_type); + + auto raw_offset = align_forward(argument_type_size, 8); + auto uint32_type = uint32(module)->llvm.abi; + auto offset = LLVMConstInt(uint32_type, raw_offset, false); + LLVMValueRef indices[] = { + offset, + }; + auto new_overflow_arg_area = create_gep(module, { + .type = uint32_type, + .pointer = overflow_arg_area, + .indices = array_to_slice(indices), + .inbounds = false, + }); + create_store(module, { + .source = new_overflow_arg_area, + .destination = overflow_arg_area_pointer, + .type = overflow_arg_area_type, + }); + return overflow_arg_area; +} + + +fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type) +{ + switch (value->id) + { + case ValueId::va_arg: + { + auto raw_va_list_type = get_va_list_type(module); + + auto va_list_value = value->va_arg.va_list; + emit_value(module, va_list_value, TypeKind::memory); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto zero = LLVMConstNull(u64_type->llvm.memory); + LLVMValueRef gep_indices[] = {zero, zero}; + auto va_list_value_llvm = create_gep(module, { + .type = raw_va_list_type->llvm.memory, + .pointer = va_list_value->llvm, + .indices = array_to_slice(gep_indices), + }); + + auto va_arg_type = value->va_arg.type; + auto r = abi_system_v_classify_argument_type(module, va_arg_type, {}); + auto abi = r.abi; + auto needed_registers = r.needed_registers; + assert(abi.flags.kind != AbiKind::ignore); + + assert(raw_va_list_type->id == TypeId::array); + auto va_list_struct = raw_va_list_type->array.element_type; + LLVMValueRef address = 0; + + if (needed_registers.gpr == 0 && needed_registers.sse == 0) + { + address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + } + else + { + auto va_list_struct_llvm = va_list_struct->llvm.memory; + + LLVMValueRef gpr_offset_pointer = 0; + LLVMValueRef gpr_offset = 0; + if (needed_registers.gpr != 0) + { + gpr_offset_pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct_llvm, va_list_value_llvm, 0, ""); + gpr_offset = create_load(module, { + .type = va_list_struct->structure.fields[0].type, + .pointer = gpr_offset_pointer, + .alignment = 16, + }); + } + else + { + trap(); + } + + auto raw_in_regs = 48 - needed_registers.gpr * 8; + auto u32_type = uint32(module); + resolve_type_in_place(module, u32_type); + auto u32_llvm = u32_type->llvm.memory; + LLVMValueRef in_regs = 0; + if (needed_registers.gpr != 0) + { + in_regs = LLVMConstInt(u32_llvm, raw_in_regs, false); + } + else + { + trap(); + } + + if (needed_registers.gpr != 0) + { + in_regs = LLVMBuildICmp(module->llvm.builder, LLVMIntULE, gpr_offset, in_regs, ""); + } + else + { + trap(); + } + + assert(in_regs); + + LLVMValueRef fp_offset_pointer = 0; + if (needed_registers.sse) + { + trap(); + } + LLVMValueRef fp_offset = 0; + if (needed_registers.sse) + { + trap(); + } + + auto raw_fits_in_fp = 176 - needed_registers.sse * 16; + LLVMValueRef fits_in_fp = 0; + if (needed_registers.sse) + { + trap(); + } + + if (needed_registers.sse && needed_registers.gpr) + { + trap(); + } + + auto* in_reg_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_reg"), 0); + auto* in_mem_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.in_mem"), 0); + auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("va_arg.end"), 0); + LLVMBuildCondBr(module->llvm.builder, in_regs, in_reg_block, in_mem_block); + + emit_block(module, in_reg_block); + + auto reg_save_area_type = va_list_struct->structure.fields[3].type; + auto reg_save_area = create_load(module, { + .type = reg_save_area_type, + .pointer = LLVMBuildStructGEP2(module->llvm.builder, va_list_struct_llvm, va_list_value_llvm, 3, ""), + .alignment = 16, + }); + + LLVMValueRef register_address = 0; + + if (needed_registers.gpr && needed_registers.sse) + { + trap(); + } + else if (needed_registers.gpr) + { + auto t = reg_save_area_type->pointer.element_type; + resolve_type_in_place(module, t); + + LLVMValueRef indices[] = { gpr_offset }; + register_address = create_gep(module, { + .type = t->llvm.abi, + .pointer = reg_save_area, + .indices = array_to_slice(indices), + .inbounds = false, + }); + if (get_byte_alignment(va_arg_type) > 8) + { + trap(); + } + } + else if (needed_registers.sse == 1) + { + trap(); + } + else if (needed_registers.sse == 2) + { + trap(); + } + else + { + unreachable(); + } + + if (needed_registers.gpr) + { + auto raw_offset = needed_registers.gpr * 8; + auto new_offset = LLVMBuildAdd(module->llvm.builder, gpr_offset, LLVMConstInt(u32_llvm, raw_offset, false), ""); + create_store(module, StoreOptions{ + .source = new_offset, + .destination = gpr_offset_pointer, + .type = u32_type, + }); + } + + if (needed_registers.sse) + { + trap(); + } + + LLVMBuildBr(module->llvm.builder, end_block); + + emit_block(module, in_mem_block); + auto memory_address = emit_va_arg_from_memory(module, va_list_value_llvm, va_list_struct, va_arg_type); + + emit_block(module, end_block); + + LLVMValueRef values[] = { + register_address, + memory_address, + }; + LLVMBasicBlockRef blocks[] = { + in_reg_block, + in_mem_block, + }; + + auto phi = LLVMBuildPhi(module->llvm.builder, module->llvm.pointer_type, ""); + LLVMAddIncoming(phi, values, blocks, array_length(values)); + + address = phi; + + unused(fp_offset_pointer); + unused(fp_offset); + unused(raw_fits_in_fp); + unused(fits_in_fp); + } + + assert(address); + + auto evaluation_kind = get_evaluation_kind(va_arg_type); + + LLVMValueRef result = 0; + + switch (evaluation_kind) + { + case EvaluationKind::scalar: + { + assert(!left_llvm); + assert(!left_type); + result = create_load(module, { + .type = va_arg_type, + .pointer = address, + }); + } break; + case EvaluationKind::aggregate: + { + if (left_llvm) + { + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + u64 memcpy_size = get_byte_size(va_arg_type); + auto alignment = get_byte_alignment(va_arg_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, address, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + return left_llvm; + } + else + { + trap(); + } + } break; + case EvaluationKind::complex: + { + trap(); + } break; + } + + assert(result); + return result; + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef left_llvm, Type* left_type, TypeKind type_kind) +{ + unused(left_type); + + switch (value->id) + { + case ValueId::field_access: + { + auto aggregate = value->field_access.aggregate; + auto field_name = value->field_access.field_name; + + emit_value(module, aggregate, TypeKind::memory); + + assert(aggregate->kind == ValueKind::left); + auto aggregate_type = aggregate->type; + assert(aggregate_type->id == TypeId::pointer); + auto aggregate_element_type = aggregate_type->pointer.element_type; + + Type* real_aggregate_type = aggregate_element_type->id == TypeId::pointer ? aggregate_element_type->pointer.element_type : aggregate_element_type; + auto resolved_aggregate_type = resolve_alias(module, real_aggregate_type); + resolve_type_in_place(module, resolved_aggregate_type); + unused(left_llvm); + unused(left_type); + unused(type_kind); + LLVMValueRef v; + if (real_aggregate_type != aggregate_element_type) + { + v = create_load(module, { + .type = aggregate_element_type, + .pointer = aggregate->llvm, + }); + } + else + { + v = aggregate->llvm; + } + + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + case TypeId::union_type: + { + struct StructLikeFieldAccess + { + Type* type; + u32 field_index; + LLVMTypeRef struct_type; + }; + + StructLikeFieldAccess field_access; + switch (resolved_aggregate_type->id) + { + case TypeId::structure: + { + u32 field_index; + auto fields = resolved_aggregate_type->structure.fields; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + field_access = { + .type = resolved_aggregate_type->structure.fields[field_index].type, + .field_index = field_index, + .struct_type = resolved_aggregate_type->llvm.memory, + }; + } break; + case TypeId::union_type: + { + auto fields = resolved_aggregate_type->union_type.fields; + u32 field_index; + auto field_count = (u32)fields.length; + for (field_index = 0; field_index < field_count; field_index += 1) + { + auto& field = fields[field_index]; + if (field_name.equal(field.name)) + { + break; + } + } + + if (field_index == field_count) + { + report_error(); + } + + auto field_type = resolved_aggregate_type->union_type.fields[field_index].type; + auto struct_type = LLVMStructTypeInContext(module->llvm.context, &field_type->llvm.memory, 1, false); + assert(struct_type); + + field_access = { + .type = field_type, + .field_index = 0, + .struct_type = struct_type, + }; + } break; + default: unreachable(); + } + + auto gep = LLVMBuildStructGEP2(module->llvm.builder, field_access.struct_type, v, field_access.field_index, ""); + + if (left_llvm) + { + assert(get_evaluation_kind(field_access.type) == EvaluationKind::aggregate); + auto alignment = get_byte_alignment(field_access.type); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, gep, alignment, LLVMConstInt(u64_type->llvm.abi, get_byte_size(field_access.type), false)); + return gep; + } + else + { + switch (value->kind) + { + case ValueKind::left: + { + return gep; + } break; + case ValueKind::right: + { + auto load = create_load(module, { + .type = field_access.type, + .pointer = gep, + }); + return load; + } break; + } + } + } break; + case TypeId::bits: + { + auto fields = resolved_aggregate_type->bits.fields; + u64 i; + for (i = 0; i < fields.length; i += 1) + { + auto& field = fields[i]; + if (field_name.equal(field.name)) + { + break; + } + } + + assert(i < fields.length); + + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + + auto load = create_load(module, { + .type = resolved_aggregate_type, + .pointer = v, + }); + auto shift = LLVMBuildLShr(module->llvm.builder, load, LLVMConstInt(resolved_aggregate_type->llvm.abi, field.offset, false), ""); + auto trunc = LLVMBuildTrunc(module->llvm.builder, shift, field_type->llvm.abi, ""); + if (left_llvm) + { + trap(); + } + + return trunc; + } break; + case TypeId::array: + { + assert(value->field_access.field_name.equal(string_literal("length"))); + auto array_length_type = get_llvm_type(value->type, type_kind); + auto result = LLVMConstInt(array_length_type, resolved_aggregate_type->array.element_count, false); + return result; + } break; + default: unreachable(); + } + + trap(); + + // auto resolved_element_type = resolve_alias(module, element_type); + // auto base_child_type = base_type->pointer.element_type; + // auto pointer_type = resolve_alias(module, base_child_type->id == TypeId::pointer ? base_child_type->pointer.element_type : base_type); + // assert(pointer_type->id == TypeId::pointer); + // auto element_type = pointer_type->pointer.element_type; + // resolve_type_in_place(module, element_type); + // + + // switch (resolved_element_type->id) + // { + // default: unreachable(); + // } + } break; + default: unreachable(); + } +} + +fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right) +{ + assert(!right->llvm); + auto pointer_type = left_type; + auto value_type = right->type; + assert(pointer_type); + assert(value_type); + resolve_type_in_place(module, pointer_type); + resolve_type_in_place(module, value_type); + + auto resolved_pointer_type = resolve_alias(module, pointer_type); + auto resolved_value_type = resolve_alias(module, value_type); + assert(resolved_pointer_type->id == TypeId::pointer); + assert(resolved_pointer_type->pointer.element_type == resolved_value_type); + + auto type_kind = TypeKind::memory; + + auto evaluation_kind = get_evaluation_kind(resolved_value_type); + switch (evaluation_kind) + { + case EvaluationKind::scalar: + { + emit_value(module, right, type_kind); + create_store(module, { + .source = right->llvm, + .destination = left_llvm, + .type = resolved_value_type, + }); + } break; + case EvaluationKind::aggregate: + { + switch (right->id) + { + case ValueId::array_initialization: + { + auto values = right->array_initialization.values; + assert(resolved_value_type->id == TypeId::array); + auto uint64_type = uint64(module); + resolve_type_in_place(module, uint64_type); + + if (right->array_initialization.is_constant) + { + emit_value(module, right, TypeKind::memory); + + bool is_constant = true; + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = {}; + u32 address_space = 0; + bool externally_initialized = false; + + auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); + + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + + auto alignment = get_byte_alignment(resolved_value_type); + LLVMSetAlignment(global, alignment); + + auto element_type = resolved_value_type->array.element_type; + auto element_count = resolved_value_type->array.element_count; + assert(values.length == element_count); + + u64 memcpy_size = get_byte_size(element_type) * element_count; + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(uint64_type->llvm.abi, memcpy_size, false)); + } + else + { + auto u64_zero = LLVMConstNull(uint64_type->llvm.abi); + auto pointer_to_element_type = get_pointer_type(module, resolved_value_type->array.element_type); + + for (u64 i = 0; i < values.length; i += 1) + { + LLVMValueRef indices[] = { + u64_zero, + LLVMConstInt(uint64_type->llvm.abi, i, false), + }; + auto alloca_gep = create_gep(module, { + .type = resolved_value_type->llvm.memory, + .pointer = left_llvm, + .indices = array_to_slice(indices), + }); + + emit_assignment(module, alloca_gep, pointer_to_element_type, values[i]); + } + } + } break; + case ValueId::string_literal: + { + auto string_literal = emit_string_literal(module, right); + auto slice_type = get_slice_type(module, uint8(module)); + + for (u32 i = 0; i < array_length(string_literal.values); i += 1) + { + auto member_pointer = LLVMBuildStructGEP2(module->llvm.builder, slice_type->llvm.abi, left_llvm, i, ""); + auto slice_member_type = slice_type->structure.fields[i].type; + create_store(module, { + .source = string_literal.values[i], + .destination = member_pointer, + .type = slice_member_type, + }); + } + } break; + case ValueId::va_start: + { + assert(resolved_value_type == get_va_list_type(module)); + assert(pointer_type->pointer.element_type == get_va_list_type(module)); + LLVMTypeRef argument_types[] = { + module->llvm.pointer_type, + }; + LLVMValueRef argument_values[] = { + left_llvm, + }; + emit_intrinsic_call(module, IntrinsicIndex::va_start, array_to_slice(argument_types), array_to_slice(argument_values)); + } break; + case ValueId::aggregate_initialization: + { + auto names = right->aggregate_initialization.names; + auto values = right->aggregate_initialization.values; + auto is_constant = right->aggregate_initialization.is_constant; + auto zero = right->aggregate_initialization.zero; + assert(names.length == values.length); + + if (is_constant) + { + emit_value(module, right, TypeKind::memory); + + LLVMLinkage linkage_type = LLVMInternalLinkage; + LLVMValueRef before = 0; + unsigned address_space = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + bool externally_initialized = false; + auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); + LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); + auto alignment = get_byte_alignment(value_type); + LLVMSetAlignment(global, alignment); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + u64 memcpy_size = get_byte_size(value_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + } + else + { + switch (resolved_value_type->id) + { + case TypeId::structure: + { + u64 max_field_index = 0; + u64 field_mask = 0; + auto fields = resolved_value_type->structure.fields; + assert(fields.length <= 64); + unused(field_mask); + + for (u32 initialization_index = 0; initialization_index < (u32)values.length; initialization_index += 1) + { + auto name = names[initialization_index]; + auto value = values[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < (u32)fields.length; declaration_index += 1) + { + auto field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + assert(declaration_index < fields.length); + + field_mask |= 1 << declaration_index; + max_field_index = MAX(max_field_index, declaration_index); + auto& field = fields[declaration_index]; + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.memory, left_llvm, declaration_index, ""); + emit_assignment(module, destination_pointer, get_pointer_type(module, field.type), value); + } + + if (zero) + { + u64 buffer_field_count = sizeof(field_mask) * 8; + auto raw_end_uninitialized_field_count = clz(field_mask); + auto unused_buffer_field_count = buffer_field_count - fields.length; + auto end_uninitialized_field_count = raw_end_uninitialized_field_count - unused_buffer_field_count; + auto initialized_field_count = __builtin_popcount(field_mask); + auto uninitialized_field_count = fields.length - initialized_field_count; + + if (uninitialized_field_count != end_uninitialized_field_count) + { + trap(); + } + + if (end_uninitialized_field_count == 0) + { + report_error(); + } + + auto field_index_offset = fields.length - end_uninitialized_field_count; + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.abi, left_llvm, field_index_offset, ""); + auto start_field = &fields[field_index_offset]; + auto memset_size = get_byte_size(resolved_value_type) - start_field->offset; + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + LLVMBuildMemSet(module->llvm.builder, destination_pointer, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, memset_size, false), 1); + } + } break; + case TypeId::union_type: + { + assert(names.length == 1); + assert(values.length == 1); + auto fields = resolved_value_type->union_type.fields; + auto biggest_field_index = resolved_value_type->union_type.biggest_field; + auto& biggest_field = fields[biggest_field_index]; + auto biggest_field_type = fields[biggest_field_index].type; + auto value = values[0]; + auto field_value_type = value->type; + auto field_type_size = get_byte_size(field_value_type); + + LLVMTypeRef struct_type; + + if (type_is_abi_equal(module, field_value_type, biggest_field_type)) + { + struct_type = resolved_value_type->llvm.memory; + } + else + { + struct_type = LLVMStructTypeInContext(module->llvm.context, &field_value_type->llvm.memory, 1, false); + } + + auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, struct_type, left_llvm, 0, ""); + auto field_pointer_type = get_pointer_type(module, field_value_type); + unused(biggest_field); + emit_assignment(module, destination_pointer, field_pointer_type, value); + + auto union_size = resolved_value_type->union_type.byte_size; + if (field_type_size < union_size) + { + trap(); + } + else if (field_type_size > union_size) + { + unreachable(); + } + } break; + default: unreachable(); + } + } + } break; + case ValueId::call: + { + auto result = emit_call(module, right, left_llvm, left_type); + assert(result == left_llvm); + } break; + case ValueId::va_arg: + { + auto result = emit_va_arg(module, right, left_llvm, left_type); + if (result != left_llvm) + { + trap(); + } + } break; + case ValueId::slice_expression: + { + auto slice = emit_slice_expression(module, right); + auto slice_pointer_type = resolved_value_type->structure.fields[0].type; + create_store(module, { + .source = slice.values[0], + .destination = left_llvm, + .type = slice_pointer_type, + }); + + auto slice_length_destination = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.abi, left_llvm, 1, ""); + create_store(module, { + .source = slice.values[1], + .destination = slice_length_destination, + .type = uint64(module), + }); + } break; + case ValueId::zero: + { + auto u8_type = uint8(module); + auto u64_type = uint64(module); + resolve_type_in_place(module, u8_type); + resolve_type_in_place(module, u64_type); + + auto size = get_byte_size(resolved_value_type); + auto alignment = get_byte_alignment(resolved_value_type); + LLVMBuildMemSet(module->llvm.builder, left_llvm, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, size, false), alignment); + } break; + case ValueId::variable_reference: + { + auto* variable = right->variable_reference; + switch (right->kind) + { + case ValueKind::left: + { + trap(); + } break; + case ValueKind::right: + { + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto memcpy_size = get_byte_size(resolved_value_type); + auto alignment = get_byte_alignment(resolved_value_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, variable->storage->llvm, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + } break; + } + } break; + case ValueId::string_to_enum: + { + emit_value(module, right, TypeKind::memory); + + auto enum_type = right->string_to_enum.type; + auto s2e_struct_type = enum_type->enumerator.string_to_enum_struct_type; + create_store(module, { + .source = right->llvm, + .destination = left_llvm, + .type = s2e_struct_type, + }); + } break; + case ValueId::undefined: + { + // TODO: do something? + } break; + case ValueId::macro_instantiation: + { + emit_macro_instantiation(module, right); + auto size = get_byte_size(resolved_value_type); + auto alignment = get_byte_alignment(resolved_value_type); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, right->macro_instantiation.return_alloca, alignment, LLVMConstInt(u64_type->llvm.abi, size, false)); + } break; + case ValueId::unary: + case ValueId::select: + { + emit_value(module, right, TypeKind::memory); + create_store(module, { + .source = right->llvm, + .destination = left_llvm, + .type = resolved_value_type, + }); + } break; + case ValueId::field_access: + { + auto value = emit_field_access(module, right, left_llvm, left_type, TypeKind::memory); + right->llvm = value; + } break; + default: unreachable(); + } + } break; + default: unreachable(); + } +} + +fn LLVMValueRef emit_binary(Module* module, LLVMValueRef left, Type* left_type, LLVMValueRef right, Type* right_type, BinaryId id, Type* resolved_value_type) +{ + switch (resolved_value_type->id) + { + case TypeId::integer: + { + switch (id) + { + case BinaryId::shift_right: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildAShr(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildLShr(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::div: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSDiv(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildUDiv(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::rem: + if (resolved_value_type->integer.is_signed) + { + return LLVMBuildSRem(module->llvm.builder, left, right, ""); + } + else + { + return LLVMBuildURem(module->llvm.builder, left, right, ""); + } + break; + case BinaryId::compare_equal: + case BinaryId::compare_not_equal: + case BinaryId::compare_greater: + case BinaryId::compare_less: + case BinaryId::compare_greater_equal: + case BinaryId::compare_less_equal: + { + LLVMIntPredicate predicate; + assert(left_type == right_type); + auto left_signed = type_is_signed(left_type); + auto right_signed = type_is_signed(right_type); + assert(left_signed == right_signed); + auto is_signed = left_signed; + + switch (id) + { + case BinaryId::compare_equal: predicate = LLVMIntEQ; break; + case BinaryId::compare_not_equal: predicate = LLVMIntNE; break; + case BinaryId::compare_greater: predicate = is_signed ? LLVMIntSGT : LLVMIntUGT; break; + case BinaryId::compare_less: predicate = is_signed ? LLVMIntSLT : LLVMIntULT; break; + case BinaryId::compare_greater_equal: predicate = is_signed ? LLVMIntSGE : LLVMIntUGE; break; + case BinaryId::compare_less_equal: predicate = is_signed ? LLVMIntSLE : LLVMIntULE; break; + default: unreachable(); + } + return LLVMBuildICmp(module->llvm.builder, predicate, left, right, ""); + } break; + case BinaryId::add: return LLVMBuildAdd(module->llvm.builder, left, right, ""); break; + case BinaryId::sub: return LLVMBuildSub(module->llvm.builder, left, right, ""); break; + case BinaryId::mul: return LLVMBuildMul(module->llvm.builder, left, right, ""); break; + case BinaryId::logical_and: + case BinaryId::bitwise_and: return LLVMBuildAnd(module->llvm.builder, left, right, ""); break; + case BinaryId::logical_or: + case BinaryId::bitwise_or: return LLVMBuildOr(module->llvm.builder, left, right, ""); break; + case BinaryId::bitwise_xor: return LLVMBuildXor(module->llvm.builder, left, right, ""); break; + case BinaryId::shift_left: return LLVMBuildShl(module->llvm.builder, left, right, ""); break; + default: unreachable(); + } + } break; + case TypeId::pointer: + { + auto element_type = resolved_value_type->pointer.element_type; + resolve_type_in_place(module, element_type); + + if (id != BinaryId::add && id != BinaryId::sub) + { + report_error(); + } + + LLVMValueRef index = right; + if (id == BinaryId::sub) + { + index = LLVMBuildNeg(module->llvm.builder, index, ""); + } + + LLVMValueRef indices[] = { index }; + + return create_gep(module, { + .type = element_type->llvm.abi, + .pointer = left, + .indices = array_to_slice(indices), + }); + } break; + default: unreachable(); + } +} + +fn void emit_local_storage(Module* module, Variable* variable) +{ + assert(!variable->storage); + auto value_type = variable->type; + resolve_type_in_place(module, value_type); + auto pointer_type = get_pointer_type(module, value_type); + auto storage = new_value(module); + assert(variable->name.pointer); + assert(variable->name.length); + auto alloca = create_alloca(module, { + .type = value_type, + .name = variable->name, + }); + *storage = Value{ + .type = pointer_type, + .id = ValueId::local, + .llvm = alloca, + }; + variable->storage = storage; +} + +fn LLVMMetadataRef null_expression(Module* module) +{ + return LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0); +} + +fn void end_debug_local(Module* module, Variable* variable, LLVMMetadataRef llvm_local) +{ + auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, variable->line, variable->column, variable->scope->llvm, module->llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); + auto basic_block = LLVMGetInsertBlock(module->llvm.builder); + assert(basic_block); + LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, variable->storage->llvm, llvm_local, null_expression(module), debug_location, basic_block); +} + +fn void emit_local_variable(Module* module, Local* local) +{ + emit_local_storage(module, &local->variable); + assert(local->variable.storage); + + if (module->has_debug_info) + { + auto debug_type = local->variable.type->llvm.debug; + assert(debug_type); + bool always_preserve = true; + LLVMDIFlags flags = {}; + + auto scope = local->variable.scope->llvm; + auto bit_alignment = get_byte_alignment(local->variable.storage->type->pointer.element_type) * 8; + auto local_variable = LLVMDIBuilderCreateAutoVariable(module->llvm.di_builder, scope, (char*)local->variable.name.pointer, local->variable.name.length, module->llvm.file, local->variable.line, debug_type, always_preserve, flags, bit_alignment); + + end_debug_local(module, &local->variable, local_variable); + } +} + +fn void emit_argument(Module* module, Argument* argument) +{ + emit_local_storage(module, &argument->variable); + assert(argument->variable.storage); + + if (module->has_debug_info) + { + auto debug_type = argument->variable.type->llvm.debug; + assert(debug_type); + auto scope = argument->variable.scope->llvm; + auto always_preserve = true; + LLVMDIFlags flags = {}; + auto argument_variable = LLVMDIBuilderCreateParameterVariable(module->llvm.di_builder, scope, (char*)argument->variable.name.pointer, argument->variable.name.length, argument->index, module->llvm.file, argument->variable.line, debug_type, always_preserve, flags); + + end_debug_local(module, &argument->variable, argument_variable); + } +} + +fn void emit_macro_instantiation(Module* module, Value* value) +{ + switch (value->id) + { + case ValueId::macro_instantiation: + { + auto current_function = module->current_function; + if (!current_function) + { + report_error(); + } + module->current_function = 0; + + auto old_macro_instantiation = module->current_macro_instantiation; + assert(!old_macro_instantiation); + auto macro_instantiation = &value->macro_instantiation; + module->current_macro_instantiation = macro_instantiation; + + + for (Value* instantiation_argument: macro_instantiation->instantiation_arguments) + { + emit_value(module, instantiation_argument, TypeKind::abi); + } + + LLVMMetadataRef caller_debug_location = 0; + if (module->has_debug_info) + { + assert(!module->llvm.inlined_at); + caller_debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, macro_instantiation->line, macro_instantiation->column, macro_instantiation->scope.parent->llvm, 0); + } + auto older_inlined_at = module->llvm.inlined_at; + assert(!older_inlined_at); + module->llvm.inlined_at = caller_debug_location; + + auto llvm_function = current_function->variable.storage->llvm; + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("macro.entry"), llvm_function); + + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + LLVMValueRef return_alloca = 0; + auto return_type = macro_instantiation->return_type; + if (return_type->id != TypeId::void_type && return_type->id != TypeId::noreturn) + { + return_alloca = create_alloca(module, { + .type = return_type, + .name = string_literal("macro.return"), + }); + } + assert(!macro_instantiation->return_alloca); + macro_instantiation->return_alloca = return_alloca; + + auto* return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("macro.return_block"), llvm_function); + assert(!macro_instantiation->return_block); + macro_instantiation->return_block = return_block; + + auto declaration_arguments = macro_instantiation->declaration_arguments; + auto instantiation_arguments = macro_instantiation->instantiation_arguments; + assert(declaration_arguments.length == instantiation_arguments.length); + + for (u64 i = 0; i < declaration_arguments.length; i += 1) + { + auto* declaration_argument = &declaration_arguments[i]; + auto* instantiation_argument = instantiation_arguments[i]; + + emit_argument(module, declaration_argument); + + auto type = declaration_argument->variable.type; + auto resolved_type = resolve_alias(module, type); + auto evaluation_kind = get_evaluation_kind(resolved_type); + auto llvm_instantiation_argument = instantiation_argument->llvm; + auto llvm_declaration_argument = declaration_argument->variable.storage->llvm; + switch (evaluation_kind) + { + case EvaluationKind::scalar: + { + create_store(module, { + .source = llvm_instantiation_argument, + .destination = llvm_declaration_argument, + .type = type, + }); + } break; + default: + trap(); + } + } + + analyze_block(module, macro_instantiation->block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, return_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, return_block); + + // END OF SCOPE + if (module->has_debug_info) + { + LLVMSetCurrentDebugLocation2(module->llvm.builder, caller_debug_location); + } + module->llvm.inlined_at = older_inlined_at; + module->current_macro_instantiation = old_macro_instantiation; + module->current_function = current_function; + } break; + default: unreachable(); + } +} + + +fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location); + +fn void analyze_block(Module* module, Block* block) +{ + if (module->has_debug_info) + { + auto lexical_block = LLVMDIBuilderCreateLexicalBlock(module->llvm.di_builder, block->scope.parent->llvm, module->llvm.file, block->scope.line, block->scope.column); + block->scope.llvm = lexical_block; + } + + u32 last_line = 0; + u32 last_column = 0; + LLVMMetadataRef last_debug_location = 0; + + for (auto* statement = block->first_statement; statement; statement = statement->next) + { + analyze_statement(module, &block->scope, statement, &last_line, &last_column, &last_debug_location); + } +} + +fn void emit_value(Module* module, Value* value, TypeKind type_kind) +{ + assert(value->type); + assert(!value->llvm); + auto resolved_value_type = resolve_alias(module, value->type); + resolve_type_in_place(module, resolved_value_type); + + auto must_be_constant = !module->current_function && !module->current_macro_instantiation; + + LLVMValueRef llvm_value = 0; + switch (value->id) + { + case ValueId::constant_integer: + { + auto llvm_integer_type = get_llvm_type(resolved_value_type, type_kind); + llvm_value = LLVMConstInt(llvm_integer_type, value->constant_integer.value, value->constant_integer.is_signed); + } break; + case ValueId::unary: + { + auto unary_value = value->unary.value; + assert(!unary_value->llvm); + auto unary_id = value->unary.id; + auto resolved_unary_type = resolve_alias(module, unary_value->type); + emit_value(module, unary_value, type_kind); + if (unary_id == UnaryId::truncate || unary_id == UnaryId::enum_name) + { + type_kind = TypeKind::abi; + } + auto destination_type = get_llvm_type(resolved_value_type, type_kind); + assert(destination_type); + auto llvm_unary_value = unary_value->llvm; + assert(llvm_unary_value); + + switch (unary_id) + { + case UnaryId::minus: + { + if (value->unary.value->is_constant()) + { + llvm_value = LLVMConstNeg(llvm_unary_value); + } + else + { + llvm_value = LLVMBuildNeg(module->llvm.builder, llvm_unary_value, ""); + } + } break; + case UnaryId::plus: + { + trap(); + } break; + case UnaryId::ampersand: + { + assert(resolved_value_type == resolved_unary_type); + llvm_value = llvm_unary_value; + } break; + case UnaryId::exclamation: + { + if (resolved_value_type == resolved_unary_type) + { + llvm_value = LLVMBuildNot(module->llvm.builder, llvm_unary_value, ""); + } + else + { + switch (resolved_unary_type->id) + { + case TypeId::pointer: + { + llvm_value = LLVMBuildICmp(module->llvm.builder, LLVMIntEQ, llvm_unary_value, LLVMConstNull(resolved_unary_type->llvm.abi), ""); + } break; + default: report_error(); + } + } + } break; + case UnaryId::tilde: + { + trap(); + } break; + case UnaryId::enum_name: + { + assert(type_kind == TypeKind::abi); + auto enum_type = resolved_unary_type; + assert(enum_type->id == TypeId::enumerator); + auto enum_to_string = enum_type->enumerator.enum_to_string_function; + assert(enum_to_string); + auto call = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(enum_to_string), enum_to_string, &llvm_unary_value, 1, ""); + LLVMSetInstructionCallConv(call, LLVMFastCallConv); + llvm_value = call; + } break; + case UnaryId::extend: + { + assert(resolved_unary_type->id == TypeId::integer); + if (resolved_unary_type->integer.is_signed) + { + llvm_value = LLVMBuildSExt(module->llvm.builder, llvm_unary_value, destination_type, ""); + } + else + { + llvm_value = LLVMBuildZExt(module->llvm.builder, llvm_unary_value, destination_type, ""); + } + } break; + case UnaryId::truncate: + { + if (type_kind != TypeKind::abi) + { + assert(resolved_value_type->llvm.abi == resolved_value_type->llvm.memory); + } + + llvm_value = LLVMBuildTrunc(module->llvm.builder, llvm_unary_value, destination_type, ""); + } break; + case UnaryId::pointer_cast: + case UnaryId::int_from_enum: + { + llvm_value = llvm_unary_value; + } break; + case UnaryId::int_from_pointer: + { + llvm_value = LLVMBuildPtrToInt(module->llvm.builder, llvm_unary_value, resolved_value_type->llvm.abi, ""); + } break; + case UnaryId::va_end: + { + LLVMTypeRef argument_types[] = { module->llvm.pointer_type }; + LLVMValueRef argument_values[] = { llvm_unary_value }; + llvm_value = emit_intrinsic_call(module, IntrinsicIndex::va_end, array_to_slice(argument_types), array_to_slice(argument_values)); + } break; + case UnaryId::bitwise_not: + { + llvm_value = LLVMBuildNot(module->llvm.builder, llvm_unary_value, ""); + } break; + case UnaryId::dereference: + { + switch (value->kind) + { + case ValueKind::right: + { + auto pointer_type = unary_value->type; + assert(pointer_type->id == TypeId::pointer); + auto child_type = resolve_alias(module, pointer_type->pointer.element_type); + assert(child_type == resolved_value_type); + auto load = create_load(module, LoadOptions{ + .type = child_type, + .pointer = unary_value->llvm, + .kind = type_kind, + }); + llvm_value = load; + } break; + case ValueKind::left: + trap(); + } + } break; + case UnaryId::pointer_from_int: + { + llvm_value = LLVMBuildIntToPtr(module->llvm.builder, llvm_unary_value, resolved_value_type->llvm.abi, ""); + } break; + } + } break; + case ValueId::unary_type: + { + auto unary_type = value->unary_type.type; + auto unary_type_id = value->unary_type.id; + + resolve_type_in_place(module, unary_type); + + switch (unary_type_id) + { + case UnaryTypeId::align_of: + { + assert(resolved_value_type->id == TypeId::integer); + auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, get_byte_alignment(unary_type), false); + llvm_value = constant_integer; + } break; + case UnaryTypeId::byte_size: + { + assert(resolved_value_type->id == TypeId::integer); + auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, get_byte_size(unary_type), false); + llvm_value = constant_integer; + } break; + case UnaryTypeId::integer_max: + { + assert(unary_type->id == TypeId::integer); + auto is_signed = unary_type->integer.is_signed; + auto max_value = integer_max_value(resolved_value_type->integer.bit_count, is_signed); + auto constant_integer = LLVMConstInt(resolved_value_type->llvm.abi, max_value, is_signed); + llvm_value = constant_integer; + } break; + } + } break; + case ValueId::binary: + { + auto binary_id = value->binary.id; + bool is_shorcircuiting = binary_is_shortcircuiting(binary_id); + Value* values[2] = { value->binary.left, value->binary.right }; + + if (is_shorcircuiting) + { + enum class ShortcircuitingOperation + { + boolean_and, + boolean_or, + }; + + ShortcircuitingOperation shorcircuiting_op; + switch (binary_id) + { + case BinaryId::logical_and_shortcircuit: + shorcircuiting_op = ShortcircuitingOperation::boolean_and; + break; + case BinaryId::logical_or_shortcircuit: + shorcircuiting_op = ShortcircuitingOperation::boolean_or; + break; + default: + unreachable(); + } + + auto* left = value->binary.left; + if (left->llvm) + { + assert(false); // TODO: check if this if is really necessary + } + else + { + emit_value(module, left, TypeKind::abi); + } + + auto left_llvm = left->llvm; + + LLVMValueRef left_condition = 0; + + switch (left->type->id) + { + case TypeId::integer: + { + switch (left->type->integer.bit_count) + { + case 1: + left_condition = left_llvm; + break; + default: trap(); + } + } break; + default: trap(); + } + + assert(left_condition); + + auto llvm_function = module->current_function->variable.storage->llvm; + assert(llvm_function); + + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + + auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function); + auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function); + + LLVMBasicBlockRef true_block; + LLVMBasicBlockRef false_block; + + switch (shorcircuiting_op) + { + case ShortcircuitingOperation::boolean_and: + true_block = right_block; + false_block = end_block; + break; + case ShortcircuitingOperation::boolean_or: + true_block = end_block; + false_block = right_block; + break; + } + + LLVMBuildCondBr(module->llvm.builder, left_condition, true_block, false_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, right_block); + + auto* right = value->binary.right; + if (right->llvm) + { + assert(false); // TODO: check if this if is really necessary + } + else + { + emit_value(module, right, TypeKind::abi); + } + + auto right_llvm = right->llvm; + + LLVMValueRef right_condition = 0; + + switch (right->type->id) + { + case TypeId::integer: + { + switch (right->type->integer.bit_count) + { + case 1: + right_condition = right_llvm; + break; + default: trap(); + } + } break; + default: trap(); + } + + assert(right_condition); + + LLVMBuildBr(module->llvm.builder, end_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, end_block); + + auto boolean_type = uint1(module); + resolve_type_in_place(module, boolean_type); + auto boolean = boolean_type->llvm.abi; + + + LLVMValueRef incoming_left = 0; + + switch (shorcircuiting_op) + { + case ShortcircuitingOperation::boolean_and: + incoming_left = LLVMConstNull(boolean); + break; + case ShortcircuitingOperation::boolean_or: + incoming_left = LLVMConstInt(boolean, 1, false); + break; + } + + assert(incoming_left); + + LLVMValueRef incoming_values[] = { + incoming_left, + right_condition, + }; + + LLVMBasicBlockRef blocks[] = { + current_basic_block, + right_block, + }; + static_assert(array_length(incoming_values) == array_length(blocks)); + + auto phi = LLVMBuildPhi(module->llvm.builder, boolean, ""); + LLVMAddIncoming(phi, incoming_values, blocks, array_length(blocks)); + + llvm_value = phi; + + switch (type_kind) + { + case TypeKind::abi: + break; + case TypeKind::memory: + trap(); + } + } + else + { + LLVMValueRef llvm_values[2]; + for (u64 i = 0; i < array_length(values); i += 1) + { + auto* binary_value = values[i]; + if (binary_value->llvm) + { + assert(false); // TODO: check if this if is really necessary + } + else + { + emit_value(module, binary_value, TypeKind::abi); + } + + llvm_values[i] = binary_value->llvm; + } + + llvm_value = emit_binary(module, llvm_values[0], values[0]->type, llvm_values[1], values[1]->type, value->binary.id, resolved_value_type); + } + } break; + case ValueId::variable_reference: + { + auto* variable = value->variable_reference; + + auto resolved_variable_value_type = resolve_alias(module, variable->type); + auto resolved_variable_pointer_type = resolve_alias(module, variable->storage->type); + + switch (value->kind) + { + case ValueKind::left: + { + if (resolved_variable_pointer_type == resolved_value_type) + { + llvm_value = variable->storage->llvm; + } + else + { + trap(); + } + } break; + case ValueKind::right: + { + if (resolved_variable_value_type != resolved_value_type) + { + report_error(); + } + + if (must_be_constant) + { + if (variable->scope->kind != ScopeKind::global) + { + report_error(); + } + + llvm_value = variable->initial_value->llvm; + assert(llvm_value); + } + else + { + assert(get_byte_size(resolved_value_type) <= 16); + + auto evaluation_kind = get_evaluation_kind(resolved_value_type); + switch (evaluation_kind) + { + case EvaluationKind::scalar: + case EvaluationKind::aggregate: + { + llvm_value = create_load(module, { + .type = resolved_value_type, + .pointer = variable->storage->llvm, + }); + } break; + case EvaluationKind::complex: + trap(); + } + } + } break; + } + } break; + case ValueId::call: + { + auto call = emit_call(module, value, 0, 0); + llvm_value = call; + } break; + case ValueId::array_initialization: + { + auto values = value->array_initialization.values; + auto element_count = values.length; + + if (value->array_initialization.is_constant) + { + assert(value->kind == ValueKind::right); + auto element_type = resolved_value_type->array.element_type; + LLVMValueRef value_buffer[64]; + + resolve_type_in_place(module, element_type); + + for (u64 i = 0; i < element_count; i += 1) + { + auto* v = values[i]; + emit_value(module, v, TypeKind::memory); + value_buffer[i] = v->llvm; + } + + auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, element_count); + llvm_value = constant_array; + } + else + { + switch (value->kind) + { + case ValueKind::right: + { + trap(); + } break; + case ValueKind::left: + { + assert(resolved_value_type->id == TypeId::pointer); + auto array_type = resolved_value_type->pointer.element_type; + assert(array_type->id == TypeId::array); + auto alloca = create_alloca(module, { + .type = array_type, + }); + + auto pointer_to_element_type = get_pointer_type(module, array_type->array.element_type); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto llvm_u64_type = u64_type->llvm.abi; + auto u64_zero = LLVMConstNull(llvm_u64_type); + + LLVMTypeRef llvm_array_type = array_type->llvm.memory; + + for (u64 i = 0; i < values.length; i += 1) + { + LLVMValueRef indices[] = { + u64_zero, + LLVMConstInt(llvm_u64_type, i, false), + }; + auto alloca_gep = create_gep(module, { + .type = llvm_array_type, + .pointer = alloca, + .indices = array_to_slice(indices), + }); + auto value = values[i]; + emit_assignment(module, alloca_gep, pointer_to_element_type, value); + } + + llvm_value = alloca; + } break; + } + } + } break; + case ValueId::array_expression: + { + auto* array_like = value->array_expression.array_like; + auto* index = value->array_expression.index; + + switch (array_like->kind) + { + case ValueKind::left: + { + emit_value(module, array_like, TypeKind::memory); + emit_value(module, index, TypeKind::memory); + + auto array_like_type = array_like->type; + assert(array_like_type->id == TypeId::pointer); + auto pointer_element_type = array_like_type->pointer.element_type; + + switch (pointer_element_type->id) + { + case TypeId::array: + { + auto array_type = pointer_element_type; + + auto uint64_type = uint64(module); + resolve_type_in_place(module, uint64_type); + auto zero_index = LLVMConstNull(uint64_type->llvm.abi); + LLVMValueRef indices[] = { zero_index, index->llvm }; + auto gep = create_gep(module, { + .type = array_type->llvm.memory, + .pointer = array_like->llvm, + .indices = array_to_slice(indices), + }); + auto element_type = array_type->array.element_type; + + switch (value->kind) + { + case ValueKind::left: + llvm_value = gep; + break; + case ValueKind::right: + llvm_value = create_load(module, LoadOptions{ + .type = element_type, + .pointer = gep, + }); + break; + } + } break; + case TypeId::structure: + { + auto slice_type = pointer_element_type; + assert(slice_type->structure.is_slice); + auto slice_pointer_type = slice_type->structure.fields[0].type; + auto slice_element_type = slice_pointer_type->pointer.element_type; + resolve_type_in_place(module, slice_element_type); + + auto pointer_load = create_load(module, { + .type = slice_pointer_type, + .pointer = array_like->llvm, + }); + LLVMValueRef indices[1] = { + index->llvm, + }; + auto gep = create_gep(module, { + .type = slice_element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + + switch (value->kind) + { + case ValueKind::left: + llvm_value = gep; + break; + case ValueKind::right: + llvm_value = create_load(module, LoadOptions{ + .type = slice_element_type, + .pointer = gep, + }); + break; + } + } break; + case TypeId::pointer: + { + auto element_type = pointer_element_type->pointer.element_type; + // TODO: consider not emitting the and doing straight GEP? + auto pointer_load = create_load(module, { + .type = pointer_element_type, + .pointer = array_like->llvm, + }); + LLVMValueRef indices[] = { index->llvm }; + auto gep = create_gep(module, { + .type = element_type->llvm.memory, + .pointer = pointer_load, + .indices = array_to_slice(indices), + }); + + llvm_value = gep; + + if (value->kind == ValueKind::right) + { + llvm_value = create_load(module, { + .type = element_type, + .pointer = gep, + }); + } + } break; + default: unreachable(); + } + } break; + case ValueKind::right: + { + trap(); + } break; + } + } break; + case ValueId::enum_literal: + { + assert(resolved_value_type->id == TypeId::enumerator); + auto enum_name = value->enum_literal; + bool found = false; + u64 i; + for (i = 0; i < resolved_value_type->enumerator.fields.length; i += 1) + { + auto& field = resolved_value_type->enumerator.fields[i]; + if (enum_name.equal(field.name)) + { + found = true; + break; + } + } + + if (!found) + { + report_error(); + } + + auto& field = resolved_value_type->enumerator.fields[i]; + auto llvm_type = get_llvm_type(resolved_value_type, type_kind); + llvm_value = LLVMConstInt(llvm_type, field.value, type_is_signed(resolved_value_type)); + } break; + case ValueId::trap: + { + auto call = emit_intrinsic_call(module, IntrinsicIndex::trap, {}, {}); + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + llvm_value = call; + } break; + case ValueId::field_access: + { + llvm_value = emit_field_access(module, value, 0, 0, type_kind); + } break; + case ValueId::slice_expression: + { + auto slice = emit_slice_expression(module, value); + llvm_value = emit_slice_result(module, slice, resolved_value_type->llvm.abi); + } break; + case ValueId::va_arg: + { + llvm_value = emit_va_arg(module, value, 0, 0); + } break; + case ValueId::aggregate_initialization: + { + auto names = value->aggregate_initialization.names; + auto values = value->aggregate_initialization.values; + assert(names.length == values.length); + auto is_constant = value->aggregate_initialization.is_constant; + auto zero = value->aggregate_initialization.zero; + + switch (resolved_value_type->id) + { + case TypeId::structure: + { + auto fields = resolved_value_type->structure.fields; + + if (is_constant) + { + LLVMValueRef constant_buffer[64]; + u32 constant_count = (u32)values.length; + + for (u64 i = 0; i < values.length; i += 1) + { + auto* value = values[i]; + emit_value(module, value, TypeKind::memory); + auto llvm_value = value->llvm; + assert(llvm_value); + assert(LLVMIsAConstant(llvm_value)); + constant_buffer[i] = llvm_value; + } + + + if (zero) + { + if (values.length == fields.length) + { + unreachable(); + } + + for (u64 i = values.length; i < fields.length; i += 1) + { + auto& field = fields[i]; + auto field_type = field.type; + resolve_type_in_place(module, field_type); + constant_buffer[i] = LLVMConstNull(field_type->llvm.memory); + constant_count += 1; + } + } + + assert(constant_count == fields.length); + + llvm_value = LLVMConstNamedStruct(get_llvm_type(resolved_value_type, type_kind), constant_buffer, constant_count); + } + else + { + trap(); + } + } break; + case TypeId::union_type: + { + trap(); + } break; + case TypeId::bits: + { + auto fields = resolved_value_type->bits.fields; + Type* backing_type = resolved_value_type->bits.backing_type; + resolve_type_in_place(module, backing_type); + auto abi_type = get_llvm_type(backing_type, type_kind); + + if (is_constant) + { + u64 bits_value = 0; + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + const auto& field = fields[declaration_index]; + u64 field_value; + switch (value->id) + { + case ValueId::constant_integer: + { + field_value = value->constant_integer.value; + } break; + default: unreachable(); + } + + bits_value |= field_value << field.offset; + } + + llvm_value = LLVMConstInt(abi_type, bits_value, false); + } + else + { + llvm_value = LLVMConstNull(abi_type); + + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + unreachable(); + } + + const auto& field = fields[declaration_index]; + + emit_value(module, value, TypeKind::memory); + + auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); + auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); + auto or_value = LLVMBuildOr(module->llvm.builder, llvm_value, shl, ""); + llvm_value = or_value; + } + } + } break; + default: unreachable(); + } + } break; + case ValueId::zero: + { + llvm_value = LLVMConstNull(get_llvm_type(resolved_value_type, type_kind)); + } break; + case ValueId::select: + { + auto condition = value->select.condition; + auto true_value = value->select.true_value; + auto false_value = value->select.false_value; + emit_value(module, condition, TypeKind::abi); + LLVMValueRef llvm_condition = condition->llvm; + auto condition_type = condition->type; + + switch (condition_type->id) + { + case TypeId::integer: + { + if (condition_type->integer.bit_count != 1) + { + trap(); + } + } break; + default: trap(); + } + + emit_value(module, true_value, type_kind); + emit_value(module, false_value, type_kind); + + llvm_value = LLVMBuildSelect(module->llvm.builder, llvm_condition, true_value->llvm, false_value->llvm, ""); + } break; + case ValueId::unreachable: + { + llvm_value = LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } break; + case ValueId::string_to_enum: + { + auto enum_type = value->string_to_enum.type; + auto string_value = value->string_to_enum.string; + emit_value(module, string_value, TypeKind::memory); + auto llvm_string_value = string_value->llvm; + + auto s2e = enum_type->enumerator.string_to_enum_function; + auto first_field = LLVMBuildExtractValue(module->llvm.builder, llvm_string_value, 0, ""); + auto second_field = LLVMBuildExtractValue(module->llvm.builder, llvm_string_value, 1, ""); + LLVMValueRef fields[] = { + first_field, + second_field, + }; + auto call = LLVMBuildCall2(module->llvm.builder, LLVMGlobalGetValueType(s2e), s2e, fields, array_length(fields), ""); + LLVMSetInstructionCallConv(call, LLVMFastCallConv); + llvm_value = call; + } break; + case ValueId::string_literal: + { + assert(type_is_slice(resolved_value_type)); + auto string_literal = emit_string_literal(module, value); + llvm_value = emit_slice_result(module, string_literal, resolved_value_type->llvm.abi); + } break; + case ValueId::macro_instantiation: + { + emit_macro_instantiation(module, value); + + auto macro_instantiation = &value->macro_instantiation; + auto return_type = macro_instantiation->return_type; + auto return_alloca = macro_instantiation->return_alloca; + + // TODO: more professional + switch (return_type->id) + { + case TypeId::void_type: + case TypeId::noreturn: + { + return; + } + default: + { + llvm_value = create_load(module, { + .type = return_type, + .pointer = return_alloca, + .kind = type_kind, + }); + } break; + } + } break; + case ValueId::undefined: + { + llvm_value = LLVMGetPoison(get_llvm_type(resolved_value_type, type_kind)); + } break; + default: unreachable(); + } + + assert(llvm_value); + value->llvm = llvm_value; +} + +fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind) +{ + analyze_type(module, value, expected_type); + emit_value(module, value, type_kind); +} + +fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location) +{ + Global* parent_function_global; + if (module->current_function) + { + parent_function_global = module->current_function; + } + else if (module->current_macro_instantiation) + { + parent_function_global = module->current_macro_instantiation->instantiation_function; + } + else + { + report_error(); + } + + auto* llvm_function = parent_function_global->variable.storage->llvm; + assert(llvm_function); + + if (module->has_debug_info) + { + if (statement->line != *last_line || statement->column != *last_column) + { + auto new_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, statement->line, statement->column, scope->llvm, module->llvm.inlined_at); + *last_debug_location = new_location; + LLVMSetCurrentDebugLocation2(module->llvm.builder, new_location); + *last_line = statement->line; + *last_column = statement->column; + } + } + + switch (statement->id) + { + case StatementId::return_st: + { + auto return_value = statement->return_st; + + if (module->current_function) + { + assert(!module->current_macro_instantiation); + auto& function_type = parent_function_global->variable.storage->type->pointer.element_type->function; + auto& return_abi = function_type.return_abi; + + switch (return_abi.semantic_type->id) + { + case TypeId::void_type: + { + if (return_value) + { + report_error(); + } + } break; + case TypeId::noreturn: + { + report_error(); + } break; + default: + { + if (module->has_debug_info) + { + LLVMSetCurrentDebugLocation2(module->llvm.builder, *last_debug_location); + } + + auto return_alloca = module->current_function->variable.storage->function.llvm.return_alloca; + if (!return_alloca) + { + report_error(); + } + + if (!return_value) + { + report_error(); + } + + analyze_type(module, return_value, return_abi.semantic_type); + auto pointer_type = get_pointer_type(module, return_abi.semantic_type); + emit_assignment(module, return_alloca, pointer_type, return_value); + } break; + } + + auto return_block = module->current_function->variable.storage->function.llvm.return_block; + LLVMBuildBr(module->llvm.builder, return_block); + LLVMClearInsertionPosition(module->llvm.builder); + } + else if (module->current_macro_instantiation) + { + auto macro_instantiation = module->current_macro_instantiation; + auto return_type = macro_instantiation->return_type; + assert(return_type); + analyze_type(module, return_value, return_type); + emit_assignment(module, macro_instantiation->return_alloca, get_pointer_type(module, return_type), return_value); + LLVMBuildBr(module->llvm.builder, macro_instantiation->return_block); + LLVMClearInsertionPosition(module->llvm.builder); + } + else + { + report_error(); + } + } break; + case StatementId::local: + { + auto local = statement->local; + auto expected_type = local->variable.type; + assert(!local->variable.storage); + analyze_type(module, local->variable.initial_value, expected_type); + local->variable.type = expected_type ? expected_type : local->variable.initial_value->type; + assert(local->variable.type); + if (expected_type) + { + assert(expected_type == local->variable.type); + } + emit_local_variable(module, local); + emit_assignment(module, local->variable.storage->llvm, local->variable.storage->type, local->variable.initial_value); + } break; + case StatementId::if_st: + { + auto* taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.taken"), llvm_function); + auto* not_taken_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.not_taken"), llvm_function); + auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.exit"), llvm_function); + + auto condition = statement->if_st.condition; + analyze_value(module, condition, 0, TypeKind::abi); + auto condition_type = condition->type; + + LLVMValueRef llvm_condition = 0; + assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer); + + llvm_condition = condition->llvm; + + if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1)) + { + llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); + } + + assert(llvm_condition); + + LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); + + analyze_statement(module, scope, statement->if_st.if_statement, last_line, last_column, last_debug_location); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, not_taken_block); + auto else_statement = statement->if_st.else_statement; + if (else_statement) + { + analyze_statement(module, scope, else_statement, last_line, last_column, last_debug_location); + } + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, exit_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + } break; + case StatementId::block: + { + analyze_block(module, statement->block); + } break; + case StatementId::expression: + { + analyze_value(module, statement->expression, 0, TypeKind::memory); + } break; + case StatementId::while_st: + { + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.entry"), llvm_function); + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.body"), llvm_function); + auto continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.continue"), llvm_function); + auto exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("while.exit"), llvm_function); + + auto previous_continue_block = module->llvm.continue_block; + auto previous_exit_block = module->llvm.exit_block; + module->llvm.continue_block = continue_block; + module->llvm.exit_block = exit_block; + + auto condition = statement->while_st.condition; + auto block = statement->while_st.block; + + if (condition->is_constant()) + { + switch (condition->id) + { + case ValueId::constant_integer: + { + if (condition->constant_integer.value == 0) + { + report_error(); + } + } break; + default: unreachable(); + } + + LLVMBuildBr(module->llvm.builder, body_block); + } + else + { + analyze_value(module, condition, 0, TypeKind::abi); + + auto boolean = uint1(module); + + LLVMValueRef llvm_condition = condition->llvm; + auto condition_type = condition->type; + if (condition_type != boolean) + { + switch (condition_type->id) + { + case TypeId::integer: + { + llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); + } break; + default: unreachable(); + } + } + + LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + + analyze_block(module, block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + LLVMBuildBr(module->llvm.builder, entry_block); + + if (llvm_value_use_empty((LLVMValueRef)body_block)) + { + trap(); + } + + if (llvm_value_use_empty((LLVMValueRef)exit_block)) + { + trap(); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + + module->llvm.continue_block = previous_continue_block; + module->llvm.exit_block = previous_exit_block; + } break; + case StatementId::assignment: + { + auto left = statement->assignment.left; + auto right = statement->assignment.right; + auto id = statement->assignment.id; + analyze_value(module, left, 0, TypeKind::memory); + + auto left_type = left->type; + if (left_type->id != TypeId::pointer) + { + report_error(); + } + auto element_type = left_type->pointer.element_type; + auto left_llvm = left->llvm; + + switch (id) + { + case StatementAssignmentId::assign: + { + analyze_type(module, right, element_type); + emit_assignment(module, left_llvm, left_type, right); + } break; + case StatementAssignmentId::assign_add: + case StatementAssignmentId::assign_sub: + case StatementAssignmentId::assign_mul: + case StatementAssignmentId::assign_div: + case StatementAssignmentId::assign_rem: + case StatementAssignmentId::assign_shift_left: + case StatementAssignmentId::assign_shift_right: + case StatementAssignmentId::assign_and: + case StatementAssignmentId::assign_or: + case StatementAssignmentId::assign_xor: + { + auto evaluation_kind = get_evaluation_kind(element_type); + assert(evaluation_kind == EvaluationKind::scalar); + auto load = create_load(module, { + .type = element_type, + .pointer = left_llvm, + .kind = TypeKind::abi, + }); + analyze_value(module, right, element_type, TypeKind::abi); + auto a = load; + auto b = right->llvm; + + BinaryId binary_id; + switch (id) + { + case StatementAssignmentId::assign: unreachable(); + case StatementAssignmentId::assign_add: binary_id = BinaryId::add; break; + case StatementAssignmentId::assign_sub: binary_id = BinaryId::sub; break; + case StatementAssignmentId::assign_mul: binary_id = BinaryId::mul; break; + case StatementAssignmentId::assign_div: binary_id = BinaryId::div; break; + case StatementAssignmentId::assign_rem: binary_id = BinaryId::rem; break; + case StatementAssignmentId::assign_shift_left: binary_id = BinaryId::shift_left; break; + case StatementAssignmentId::assign_shift_right: binary_id = BinaryId::shift_right; break; + case StatementAssignmentId::assign_and: binary_id = BinaryId::bitwise_and; break; + case StatementAssignmentId::assign_or: binary_id = BinaryId::bitwise_or; break; + case StatementAssignmentId::assign_xor: binary_id = BinaryId::bitwise_xor; break; + } + + auto op = emit_binary(module, a, element_type, b, right->type, binary_id, element_type); + + create_store(module, { + .source = op, + .destination = left_llvm, + .type = element_type, + }); + } break; + } + } break; + case StatementId::switch_st: + { + auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.exit"), llvm_function); + + auto discriminant = statement->switch_st.discriminant; + auto clauses = statement->switch_st.clauses; + analyze_value(module, discriminant, 0, TypeKind::abi); + + auto discriminant_type = discriminant->type; + + switch (discriminant_type->id) + { + case TypeId::enumerator: + { + u32 invalid_clause_index = ~(u32)0; + u32 else_clause_index = invalid_clause_index; + u32 discriminant_case_count = 0; + + for (u64 i = 0; i < clauses.length; i += 1) + { + auto& clause = clauses[i]; + clause.basic_block = llvm_context_create_basic_block(module->llvm.context, clause.values.length == 0 ? string_literal("switch.else_case_block") : string_literal("switch.case_block"), llvm_function); + discriminant_case_count += clause.values.length; + + if (clause.values.length == 0) + { + if (else_clause_index != invalid_clause_index) + { + report_error(); + } + + else_clause_index = i; + } + else + { + for (auto value: clause.values) + { + analyze_value(module, value, discriminant_type, TypeKind::abi); + if (!value->is_constant()) + { + report_error(); + } + } + } + } + + LLVMBasicBlockRef else_block; + if (else_clause_index != invalid_clause_index) + { + else_block = clauses[else_clause_index].basic_block; + } + else + { + else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.else_case_block"), llvm_function); + } + + auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, discriminant->llvm, else_block, discriminant_case_count); + bool all_blocks_terminated = true; + + for (auto& clause : clauses) + { + for (auto value : clause.values) + { + LLVMAddCase(switch_instruction, value->llvm, clause.basic_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, clause.basic_block); + + analyze_block(module, clause.block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + all_blocks_terminated = false; + LLVMBuildBr(module->llvm.builder, exit_block); + LLVMClearInsertionPosition(module->llvm.builder); + } + } + + if (else_clause_index == invalid_clause_index) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + + if (all_blocks_terminated) + { + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } + } break; + default: trap(); + } + } break; + case StatementId::for_each: + { + if (module->has_debug_info) + { + auto lexical_block = LLVMDIBuilderCreateLexicalBlock(module->llvm.di_builder, statement->for_each.scope.parent->llvm, module->llvm.file, statement->for_each.scope.line, statement->for_each.scope.column); + statement->for_each.scope.llvm = lexical_block; + } + + auto index_type = uint64(module); + resolve_type_in_place(module, index_type); + auto index_zero = LLVMConstNull(index_type->llvm.abi); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.entry"), llvm_function); + auto* body_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.body"), llvm_function); + auto* continue_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.continue"), llvm_function); + auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("for_each.exit"), llvm_function); + + auto previous_continue_block = module->llvm.continue_block; + auto previous_exit_block = module->llvm.exit_block; + + module->llvm.continue_block = continue_block; + module->llvm.exit_block = exit_block; + + auto left_values = statement->for_each.left_values; + auto right_values = statement->for_each.right_values; + + switch (statement->for_each.kind) + { + case ForEachKind::slice: + { + assert(left_values.length == right_values.length); + + Local* local = statement->for_each.first_local; + + for (u64 i = 0; i < right_values.length; i += 1, local = local->next) + { + auto kind = left_values[i]; + auto right = right_values[i]; + assert(right->kind == ValueKind::left); + analyze_type(module, right, 0); + + auto pointer_type = right->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = pointer_type->pointer.element_type; + + Type* child_type = 0; + + switch (aggregate_type->id) + { + case TypeId::array: + { + child_type = aggregate_type->array.element_type; + } break; + case TypeId::structure: + { + if (!aggregate_type->structure.is_slice) + { + report_error(); + } + child_type = aggregate_type->structure.fields[0].type->pointer.element_type; + } break; + default: trap(); + } + + assert(child_type); + assert(!local->variable.type); + + Type* local_type = 0; + + switch (kind) + { + case ValueKind::left: local_type = get_pointer_type(module, child_type); break; + case ValueKind::right: local_type = child_type; break; + } + + assert(local_type); + + local->variable.type = local_type; + + emit_local_variable(module, local); + emit_value(module, right, TypeKind::memory); + } + + assert(!local); + + LLVMValueRef length_value = 0; + + // TODO: make it right + for (auto value : right_values) + { + auto pointer_type = value->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = pointer_type->pointer.element_type; + switch (aggregate_type->id) + { + case TypeId::array: + { + length_value = LLVMConstInt(index_type->llvm.abi, aggregate_type->array.element_count, false); + } break; + case TypeId::structure: + { + assert(aggregate_type->structure.is_slice); + + auto gep = LLVMBuildStructGEP2(module->llvm.builder, aggregate_type->llvm.abi, value->llvm, 1, "slice.length.pointer"); + auto load = create_load(module, { + .type = index_type, + .pointer = gep, + }); + length_value = load; + } break; + default: unreachable(); + } + + break; + } + + assert(length_value); + + auto index_alloca = create_alloca(module, { .type = index_type, .name = string_literal("for_each.index") }); + create_store(module, { .source = index_zero, .destination = index_alloca, .type = index_type }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto header_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + auto index_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, header_index_load, length_value, ""); + LLVMBuildCondBr(module->llvm.builder, index_compare, body_block, exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + auto body_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + + local = statement->for_each.first_local; + + for (u64 i = 0; i < right_values.length; i += 1, local = local->next) + { + auto kind = left_values[i]; + auto right = right_values[i]; + + auto aggregate_type = right->type->pointer.element_type; + + LLVMValueRef element_pointer_value = 0; + + switch (aggregate_type->id) + { + case TypeId::array: + { + LLVMValueRef indices[] = { + index_zero, + body_index_load, + }; + element_pointer_value = create_gep(module, { + .type = right->type->pointer.element_type->llvm.memory, + .pointer = right->llvm, + .indices = array_to_slice(indices), + }); + } break; + case TypeId::structure: + { + assert(aggregate_type->structure.is_slice); + + auto load = create_load(module, { + .type = aggregate_type, + .pointer = right->llvm, + }); + auto extract_pointer = LLVMBuildExtractValue(module->llvm.builder, load, 0, ""); + + LLVMValueRef indices[] = { + body_index_load, + }; + auto gep = create_gep(module, { + .type = aggregate_type->structure.fields[0].type->pointer.element_type->llvm.memory, + .pointer = extract_pointer, + .indices = array_to_slice(indices), + }); + element_pointer_value = gep; + } break; + default: unreachable(); + } + + assert(element_pointer_value); + + auto local_type = local->variable.type; + + switch (kind) + { + case ValueKind::right: + { + auto evaluation_kind = get_evaluation_kind(local_type); + if (evaluation_kind == EvaluationKind::scalar || (aggregate_type->id == TypeId::structure && aggregate_type->structure.is_slice)) + { + auto load = create_load(module, { + .type = local_type, + .pointer = element_pointer_value, + }); + + create_store(module, { + .source = load, + .destination = local->variable.storage->llvm, + .type = local_type, + }); + } + else + { + trap(); + } + } break; + case ValueKind::left: + { + create_store(module, { + .source = element_pointer_value, + .destination = local->variable.storage->llvm, + .type = local_type, + }); + } break; + } + } + + analyze_statement(module, &statement->for_each.scope, statement->for_each.predicate, last_line, last_column, last_debug_location); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + auto continue_index_load = create_load(module, { .type = index_type, .pointer = index_alloca }); + auto inc = LLVMBuildAdd(module->llvm.builder, continue_index_load, LLVMConstInt(index_type->llvm.abi, 1, false), ""); + create_store(module, { .source = inc, .destination = index_alloca, .type = index_type }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + } break; + case ForEachKind::range: + { + Local* local = statement->for_each.first_local; + // Assert there is only one + assert(local); + assert(!local->next); + assert(!local->variable.type); + + assert(left_values.length == 1); + + if (right_values.length == 2) + { + auto start = right_values[0]; + auto end = right_values[1]; + + Type* local_type = 0; + + switch (start->id) + { + case ValueId::constant_integer: + { + switch (end->id) + { + case ValueId::constant_integer: + { + auto start_signed = start->constant_integer.is_signed; + auto end_signed = end->constant_integer.is_signed; + auto is_signed = !(!start_signed && !end_signed); + local_type = integer_type(module, { .bit_count = 64, .is_signed = is_signed }); + } break; + default: + { + analyze_type(module, end, 0); + auto end_type = end->type; + assert(end_type); + start->type = end_type; + local_type = end_type; + } break; + } + } break; + default: trap(); + } + + assert(local_type); + + for (auto right: right_values) + { + if (!right->type) + { + analyze_type(module, right, local_type); + } + } + + local->variable.type = local_type; + emit_local_variable(module, local); + emit_value(module, start, TypeKind::memory); + + auto index_alloca = local->variable.storage->llvm; + + create_store(module, { + .source = start->llvm, + .destination = index_alloca, + .type = local_type, + }); + + LLVMBuildBr(module->llvm.builder, entry_block); + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + + auto header_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + }); + emit_value(module, end, TypeKind::abi); + auto length_value = end->llvm; + auto index_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, header_index_load, length_value, ""); + LLVMBuildCondBr(module->llvm.builder, index_compare, body_block, exit_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, body_block); + analyze_statement(module, &statement->for_each.scope, statement->for_each.predicate, last_line, last_column, last_debug_location); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + LLVMBuildBr(module->llvm.builder, continue_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, continue_block); + + auto continue_index_load = create_load(module, { + .type = local_type, + .pointer = index_alloca, + }); + + auto inc = LLVMBuildAdd(module->llvm.builder, continue_index_load, LLVMConstInt(local_type->llvm.abi, 1, false), ""); + create_store(module, { + .source = inc, + .destination = index_alloca, + .type = local_type, + }); + + LLVMBuildBr(module->llvm.builder, entry_block); + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + } + else + { + // TODO: case for reverse range + trap(); + } + } break; + } + + // END OF SCOPE + module->llvm.continue_block = previous_continue_block; + module->llvm.exit_block = previous_exit_block; + } break; + case StatementId::break_st: + { + auto exit_block = module->llvm.exit_block; + if (!exit_block) + { + report_error(); + } + + LLVMBuildBr(module->llvm.builder, exit_block); + LLVMClearInsertionPosition(module->llvm.builder); + } break; + case StatementId::continue_st: + { + auto continue_block = module->llvm.continue_block; + if (!continue_block) + { + report_error(); + } + + LLVMBuildBr(module->llvm.builder, continue_block); + LLVMClearInsertionPosition(module->llvm.builder); + } break; + default: unreachable(); + } +} + +fn void emit_debug_argument(Module* module, Argument* argument, LLVMBasicBlockRef basic_block) +{ + assert(module->has_debug_info); + resolve_type_in_place(module, argument->variable.type); + bool always_preserve = true; + LLVMDIFlags flags = {}; + LLVMMetadataRef scope = argument->variable.scope->llvm; + auto parameter_variable = LLVMDIBuilderCreateParameterVariable(module->llvm.di_builder, scope, (char*)argument->variable.name.pointer, argument->variable.name.length, argument->index, module->llvm.file, argument->variable.line, argument->variable.type->llvm.debug, always_preserve, flags); + auto inlined_at = module->llvm.inlined_at; + auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, argument->variable.line, argument->variable.column, scope, inlined_at); + LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, argument->variable.storage->llvm, parameter_variable, LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0), debug_location, basic_block); +} + +struct ObjectGenerate +{ + String path; + BBLLVMOptimizationLevel optimization_level; + bool run_optimization_passes; + bool has_debug_info; +}; + +fn BBLLVMCodeGenerationPipelineResult generate_object(LLVMModuleRef module, LLVMTargetMachineRef target_machine, ObjectGenerate options) +{ + llvm_module_set_target(module, target_machine); + + if (options.run_optimization_passes) + { + // BBLLVM + bool prefer_speed = options.optimization_level == BBLLVMOptimizationLevel::O2 || options.optimization_level == BBLLVMOptimizationLevel::O3; + BBLLVMOptimizationPipelineOptions optimization_options = { + .optimization_level = (u64)options.optimization_level, + .debug_info = options.has_debug_info, + .loop_unrolling = prefer_speed, + .loop_interleaving = prefer_speed, + .loop_vectorization = prefer_speed, + .slp_vectorization = prefer_speed, + .merge_functions = prefer_speed, + .call_graph_profile = false, + .unified_lto = false, + .assignment_tracking = options.has_debug_info, + .verify_module = true, + }; + llvm_module_run_optimization_pipeline(module, target_machine, optimization_options); + } + + BBLLVMCodeGenerationPipelineOptions code_generation_options = { + .output_file_path = options.path, + .code_generation_file_type = (u64)BBLLVMCodeGenerationFileType::object_file, + .optimize_when_possible = options.optimization_level > BBLLVMOptimizationLevel::O0, + .verify_module = true, + }; + auto result = llvm_module_run_code_generation_pipeline(module, target_machine, &code_generation_options); + return result; +} + +struct ArgBuilder +{ + const char* args[128]; + u32 argument_count = 0; + + void add(const char* arg) + { + assert(argument_count < array_length(args)); + args[argument_count] = arg; + argument_count += 1; + } + + Slice flush() + { + assert(argument_count < array_length(args)); + args[argument_count] = 0; + return { args, argument_count }; + } +}; + +void link(Module* module) +{ + Arena* arena = module->arena; + ArgBuilder builder; + builder.add("ld.lld"); + builder.add("--error-limit=0"); + builder.add("-o"); + assert(module->executable.pointer[module->executable.length] == 0); + builder.add((char*)module->executable.pointer); + for (String object: module->objects) + { + assert(object.pointer[object.length] == 0); + builder.add((char*)object.pointer); + } + + for (String library: module->libraries) + { + assert(library.pointer[library.length] == 0); + builder.add((char*)library.pointer); + } + + String candidate_library_paths[] = { + string_literal("/usr/lib"), + string_literal("/usr/lib/x86_64-linux-gnu"), + }; + + u64 index; + String scrt1_object_path = {}; + for (index = 0; index < array_length(candidate_library_paths); index += 1) + { + auto directory_path = candidate_library_paths[index]; + String parts[] = { + directory_path, + string_literal("/Scrt1.o"), + }; + scrt1_object_path = arena_join_string(arena, array_to_slice(parts)); + auto file = os_open(scrt1_object_path, { .read = 1}, {}); + if (file >= 0) + { + os_close(file); + break; + } + } + + if (index == array_length(candidate_library_paths)) + { + report_error(); + } + + { + String parts[] = { + string_literal("-L"), + candidate_library_paths[index], + }; + + builder.add((char*)arena_join_string(arena, array_to_slice(parts)).pointer); + } + + auto link_libcpp = false; + if (link_libcpp) + { + builder.add("-lstdc++"); + } + + auto link_libc = true; + auto dynamic_linker = true; + + if (dynamic_linker) + { + builder.add("-dynamic-linker"); + auto dynamic_linker_path = "/usr/lib64/ld-linux-x86-64.so.2"; + builder.add(dynamic_linker_path); + } + + if (link_libc) + { + assert(scrt1_object_path.pointer); + builder.add((char*)scrt1_object_path.pointer); + builder.add("-lc"); + } + + auto args = builder.flush(); + auto result = lld_elf_link(args.pointer, args.length, true, false); + if (!result.success) + { + print(string_literal("Command failed:\n")); + for (auto arg : args) + { + auto a = c_string_to_slice(arg); + print(a); + print(string_literal(" ")); + } + print(string_literal("\n")); + assert(result.stdout_string.length == 0); + assert(result.stderr_string.length != 0); + print(result.stderr_string); + print(string_literal("\n")); + exit(1); + } +} + +void emit(Module* module) +{ + assert(!module->current_function); + assert(!module->current_macro_instantiation); + assert(!module->current_macro_declaration); + llvm_initialize(module); + + for (auto* global = module->first_global; global; global = global->next) + { + assert(!module->current_function); + assert(!module->current_macro_instantiation); + assert(!module->current_macro_declaration); + + if (global->emitted) + { + continue; + } + + switch (global->variable.storage->id) + { + case ValueId::function: + case ValueId::external_function: + { + auto function_type = &global->variable.storage->type->pointer.element_type->function; + auto semantic_argument_count = function_type->semantic_argument_types.length; + function_type->argument_abis = arena_allocate(module->arena, semantic_argument_count); + auto resolved_calling_convention = resolve_calling_convention(function_type->calling_convention); + auto is_reg_call = resolved_calling_convention == ResolvedCallingConvention::system_v && false; // TODO: regcall calling convention + + LLVMTypeRef llvm_abi_argument_type_buffer[64]; + + switch (resolved_calling_convention) + { + case ResolvedCallingConvention::system_v: + { + function_type->available_registers = { + .system_v = { + .gpr = (u32)(is_reg_call ? 11 : 6), + .sse = (u32)(is_reg_call ? 16 : 8), + }, + }; + auto semantic_return_type = function_type->semantic_return_type; + function_type->return_abi = abi_system_classify_return_type(module, resolve_alias(module, semantic_return_type)); + auto return_abi_kind = function_type->return_abi.flags.kind; + + Type* abi_argument_type_buffer[64]; + u16 abi_argument_type_count = 0; + + Type* abi_return_type; + switch (return_abi_kind) + { + case AbiKind::direct: + case AbiKind::extend: + { + abi_return_type = function_type->return_abi.coerce_to_type; + } break; + case AbiKind::ignore: + case AbiKind::indirect: + { + abi_return_type = void_type(module); + } break; + default: unreachable(); // TODO + } + assert(abi_return_type); + function_type->abi_return_type = abi_return_type; + resolve_type_in_place(module, abi_return_type); + + if (function_type->return_abi.flags.kind == AbiKind::indirect) + { + assert(!function_type->return_abi.flags.sret_after_this); + function_type->available_registers.system_v.gpr -= 1; + auto indirect_type = get_pointer_type(module, function_type->return_abi.semantic_type); + resolve_type_in_place(module, indirect_type); + + auto abi_index = abi_argument_type_count; + abi_argument_type_buffer[abi_index] = indirect_type; + llvm_abi_argument_type_buffer[abi_index] = indirect_type->llvm.abi; + abi_argument_type_count += 1; + } + + for (u64 i = 0; i < semantic_argument_count; i += 1) + { + auto& abi = function_type->argument_abis[i]; + auto semantic_argument_type = resolve_alias(module, function_type->semantic_argument_types[i]); + auto is_named_argument = i < semantic_argument_count; + assert(is_named_argument); + + abi = abi_system_v_classify_argument(module, &function_type->available_registers.system_v, array_to_slice(llvm_abi_argument_type_buffer), array_to_slice(abi_argument_type_buffer), { + .type = semantic_argument_type, + .abi_start = abi_argument_type_count, + .is_named_argument = is_named_argument, + }); + + abi_argument_type_count += abi.abi_count; + } + + auto abi_argument_types = new_type_array(module, abi_argument_type_count); + memcpy(abi_argument_types.pointer, abi_argument_type_buffer, sizeof(abi_argument_type_buffer[0]) * abi_argument_type_count); + function_type->abi_argument_types = abi_argument_types; + } break; + case ResolvedCallingConvention::win64: + { + report_error(); + } break; + case ResolvedCallingConvention::count: unreachable(); + } + + auto llvm_function_type = LLVMFunctionType(function_type->abi_return_type->llvm.abi, llvm_abi_argument_type_buffer, (u32)function_type->abi_argument_types.length, function_type->is_variable_arguments); + + LLVMMetadataRef subroutine_type = 0; + if (module->has_debug_info) + { + LLVMMetadataRef debug_argument_type_buffer[64]; + Slice debug_argument_types = { .pointer = debug_argument_type_buffer, .length = function_type->argument_abis.length + 1 + function_type->is_variable_arguments }; + debug_argument_types[0] = function_type->return_abi.semantic_type->llvm.debug; + assert(debug_argument_types[0]); + + auto debug_argument_type_slice = debug_argument_types(1)(0, function_type->argument_abis.length); + + for (u64 i = 0; i < function_type->argument_abis.length; i += 1) + { + auto& argument_abi = function_type->argument_abis[i]; + auto* debug_argument_type = &debug_argument_type_slice[i]; + *debug_argument_type = argument_abi.semantic_type->llvm.debug; + assert(*debug_argument_type); + } + + if (function_type->is_variable_arguments) + { + auto void_ty = void_type(module); + assert(void_ty->llvm.debug); + debug_argument_types[function_type->argument_abis.length + 1] = void_ty->llvm.debug; + } + + LLVMDIFlags flags = {}; + subroutine_type = LLVMDIBuilderCreateSubroutineType(module->llvm.di_builder, module->llvm.file, debug_argument_types.pointer, (u32)debug_argument_types.length, flags); + } + + global->variable.storage->type->pointer.element_type->llvm.abi = llvm_function_type; + global->variable.storage->type->pointer.element_type->llvm.debug = subroutine_type; + + LLVMLinkage llvm_linkage_type; + switch (global->linkage) + { + case Linkage::internal: llvm_linkage_type = LLVMInternalLinkage; break; + case Linkage::external: llvm_linkage_type = LLVMExternalLinkage; break; + } + unsigned address_space = 0; + auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, llvm_linkage_type, address_space, global->variable.name); + global->variable.storage->llvm = llvm_function; + + LLVMCallConv cc; + switch (function_type->calling_convention) + { + case CallingConvention::c: cc = LLVMCCallConv; break; + case CallingConvention::count: unreachable(); + } + LLVMSetFunctionCallConv(llvm_function, cc); + + auto attribute_list = build_attribute_list(module, { + .return_abi = function_type->return_abi, + .argument_abis = function_type->argument_abis, + .abi_argument_types = function_type->abi_argument_types, + .abi_return_type = function_type->abi_return_type, + .attributes = global->variable.storage->function.attributes, + .call_site = false, + }); + llvm_function_set_attributes(llvm_function, attribute_list); + + LLVMMetadataRef subprogram = 0; + auto is_definition = global->variable.storage->id == ValueId::function; + + if (module->has_debug_info) + { + auto is_local_to_unit = global->linkage == Linkage::internal; + auto line = global->variable.line; + auto scope_line = line + 1; + LLVMDIFlags flags = {}; + auto is_optimized = build_mode_is_optimized(module->build_mode); + subprogram = LLVMDIBuilderCreateFunction(module->llvm.di_builder, module->scope.llvm, (char*)global->variable.name.pointer, global->variable.name.length, (char*)global->variable.name.pointer, global->variable.name.length, module->llvm.file, line, subroutine_type, is_local_to_unit, is_definition, scope_line, flags, is_optimized); + LLVMSetSubprogram(llvm_function, subprogram); + } + + if (is_definition) + { + global->variable.storage->function.scope.llvm = subprogram; + + module->current_function = global; + + LLVMValueRef llvm_abi_argument_buffer[64]; + Slice llvm_abi_arguments = { .pointer = llvm_abi_argument_buffer, .length = function_type->abi_argument_types.length }; + LLVMGetParams(llvm_function, llvm_abi_argument_buffer); + + auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function); + auto return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), 0); + global->variable.storage->function.llvm.return_block = return_block; + + LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block); + LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + + auto return_abi_kind = function_type->return_abi.flags.kind; + switch (return_abi_kind) + { + case AbiKind::indirect: + { + auto indirect_argument_index = function_type->return_abi.flags.sret_after_this; + if (function_type->return_abi.flags.sret_after_this) + { + trap(); + } + + global->variable.storage->function.llvm.return_alloca = llvm_abi_arguments[indirect_argument_index]; + + if (!function_type->return_abi.flags.indirect_by_value) + { + trap(); + } + } break; + case AbiKind::in_alloca: + { + trap(); + } break; + default: + { + auto alloca = create_alloca(module, { + .type = function_type->return_abi.semantic_type, + .name = string_literal("retval"), + }); + global->variable.storage->function.llvm.return_alloca = alloca; + } break; + case AbiKind::ignore: break; + } + + auto arguments = global->variable.storage->function.arguments; + auto argument_abis = function_type->argument_abis; + assert(arguments.length == argument_abis.length); + for (u64 i = 0; i < semantic_argument_count; i += 1) + { + auto* argument = &arguments[i]; + auto& argument_abi = argument_abis[i]; + auto argument_abi_arguments = llvm_abi_arguments(argument_abi.abi_start)(0, argument_abi.abi_count); + + LLVMValueRef semantic_argument_storage = 0; + switch (argument_abi.flags.kind) + { + case AbiKind::direct: + case AbiKind::extend: + { + auto first_argument = argument_abi_arguments[0]; + auto coerce_to_type = argument_abi.get_coerce_to_type(); + if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, coerce_to_type, argument_abi.semantic_type) && argument_abi.attributes.direct.offset == 0) + { + assert(argument_abi.abi_count == 1); + + auto is_promoted = false; + auto v = first_argument; + if (coerce_to_type->llvm.abi != LLVMTypeOf(v)) + { + trap(); + } + + if (is_promoted) + { + trap(); + } + + // TODO: this we can get rid of because we handle all of this inside `create_alloca`, load, stores, etc + if (is_arbitrary_bit_integer(argument_abi.semantic_type)) + { + auto bit_count = (u32)get_bit_size(argument_abi.semantic_type); + auto abi_bit_count = align_bit_count(bit_count); + bool is_signed = type_is_signed(argument_abi.semantic_type); + auto destination_type = integer_type(module, { .bit_count = abi_bit_count, .is_signed = is_signed }); + auto alloca = create_alloca(module, { + .type = destination_type, + .name = argument->variable.name, + }); + + LLVMValueRef result; + if (bit_count < abi_bit_count) + { + if (is_signed) + { + result = LLVMBuildSExt(module->llvm.builder, first_argument, destination_type->llvm.memory, ""); + } + else + { + result = LLVMBuildZExt(module->llvm.builder, first_argument, destination_type->llvm.memory, ""); + } + } + else + { + trap(); + } + + create_store(module, { + .source = result, + .destination = alloca, + .type = destination_type, + }); + + semantic_argument_storage = alloca; + } + else + { + auto alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = argument->variable.name, + }); + create_store(module, { + .source = first_argument, + .destination = alloca, + .type = argument_abi.semantic_type, + }); + + semantic_argument_storage = alloca; + } + } + else + { + auto is_fixed_vector_type = false; + if (is_fixed_vector_type) + { + trap(); + } + + if (coerce_to_type->id == TypeId::structure && coerce_to_type->structure.fields.length > 1 && argument_abi.flags.kind == AbiKind::direct && !argument_abi.flags.can_be_flattened) + { + auto contains_homogeneous_scalable_vector_types = false; + if (contains_homogeneous_scalable_vector_types) + { + trap(); + } + } + + auto alloca = create_alloca(module, { .type = argument_abi.semantic_type, .name = argument->variable.name }); + LLVMValueRef pointer; + Type* pointer_type; + if (argument_abi.attributes.direct.offset > 0) + { + trap(); + } + else + { + pointer = alloca; + pointer_type = argument_abi.semantic_type; + } + + if (coerce_to_type->id == TypeId::structure && coerce_to_type->structure.fields.length > 1 && argument_abi.flags.kind == AbiKind::direct && argument_abi.flags.can_be_flattened) + { + auto struct_size = get_byte_size(coerce_to_type); + auto pointer_element_size = get_byte_size(pointer_type); + auto is_scalable = false; + + if (is_scalable) + { + trap(); + } + else + { + auto source_size = struct_size; + auto destination_size = pointer_element_size; + auto address_alignment = get_byte_alignment(argument_abi.semantic_type); + + LLVMValueRef address; + if (source_size <= destination_size) + { + address = alloca; + } + else + { + address = create_alloca(module, { .type = coerce_to_type, .name = string_literal("coerce"), .alignment = address_alignment }); + } + + assert(coerce_to_type->structure.fields.length == argument_abi.abi_count); + + resolve_type_in_place(module, coerce_to_type); + + for (u64 i = 0; i < coerce_to_type->structure.fields.length; i += 1) + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, coerce_to_type->llvm.abi, address, i, ""); + create_store(module, { + .source = argument_abi_arguments[i], + .destination = gep, + .type = coerce_to_type->structure.fields[i].type, + }); + } + + if (source_size > destination_size) + { + unused(pointer); + trap(); + } + } + } + else + { + assert(argument_abi.abi_count == 1); + auto abi_argument_type = function_type->abi_argument_types[argument_abi.abi_start]; + auto destination_size = get_byte_size(pointer_type) - argument_abi.attributes.direct.offset; + auto is_volatile = false; + create_coerced_store(module, argument_abi_arguments[0], abi_argument_type, pointer, pointer_type, destination_size, is_volatile); + } + + semantic_argument_storage = alloca; + } + } break; + case AbiKind::indirect: + { + assert(argument_abi.abi_count == 1); + auto evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); + switch (evaluation_kind) + { + default: + { + if (argument_abi.flags.indirect_realign || argument_abi.flags.kind == AbiKind::indirect_aliased) + { + trap(); + } + + auto use_indirect_debug_address = !argument_abi.flags.indirect_by_value; + if (use_indirect_debug_address) + { + trap(); + } + + auto llvm_argument = argument_abi_arguments[0]; + semantic_argument_storage = llvm_argument; + } break; + case EvaluationKind::scalar: trap(); + } + } break; + default: unreachable(); + } + + assert(semantic_argument_storage); + + auto storage = new_value(module); + auto value_type = argument->variable.type; + *storage = { + .type = get_pointer_type(module, value_type), + .id = ValueId::argument, + .llvm = semantic_argument_storage, + }; + argument->variable.storage = storage; + + if (module->has_debug_info) + { + emit_debug_argument(module, argument, entry_block); + } + } + + analyze_block(module, global->variable.storage->function.block); + + auto* current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + if (current_basic_block) + { + assert(!LLVMGetBasicBlockTerminator(current_basic_block)); + + if (llvm_basic_block_is_empty(current_basic_block) || llvm_value_use_empty((LLVMValueRef)current_basic_block)) + { + LLVMReplaceAllUsesWith((LLVMValueRef)return_block, (LLVMValueRef)current_basic_block); + llvm_basic_block_delete(return_block); + } + else + { + emit_block(module, return_block); + } + } + else + { + bool is_reachable = false; + + if (llvm_value_has_one_use((LLVMValueRef)return_block)) + { + auto user = llvm_basic_block_user_begin(return_block); + is_reachable = LLVMIsABranchInst(user) && !LLVMIsConditional(user) && LLVMGetSuccessor(user, 0) == return_block; + if (is_reachable) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, LLVMGetInstructionParent(user)); + LLVMInstructionEraseFromParent(user); + llvm_basic_block_delete(return_block); + } + } + + if (!is_reachable) + { + emit_block(module, return_block); + } + } + + if (module->has_debug_info) + { + LLVMSetCurrentDebugLocation2(module->llvm.builder, 0); + auto subprogram = LLVMGetSubprogram(llvm_function); + LLVMDIBuilderFinalizeSubprogram(module->llvm.di_builder, subprogram); + } + + if (function_type->return_abi.semantic_type == noreturn_type(module) || global->variable.storage->function.attributes.naked) + { + LLVMBuildUnreachable(module->llvm.builder); + } + else if (function_type->return_abi.semantic_type == void_type(module)) + { + LLVMBuildRetVoid(module->llvm.builder); + } + else + { + LLVMValueRef return_value = 0; + + switch (return_abi_kind) + { + case AbiKind::direct: + case AbiKind::extend: + { + auto return_alloca = global->variable.storage->function.llvm.return_alloca; + auto coerce_to_type = function_type->return_abi.get_coerce_to_type(); + auto return_semantic_type = function_type->return_abi.semantic_type; + if (type_is_abi_equal(module, coerce_to_type, return_semantic_type) && function_type->return_abi.attributes.direct.offset == 0) + { + auto store = llvm_find_return_value_dominating_store(module->llvm.builder, return_alloca, return_semantic_type->llvm.abi); + if (store) + { + return_value = LLVMGetOperand(store, 0); + auto alloca = LLVMGetOperand(store, 1); + assert(alloca == return_alloca); + LLVMInstructionEraseFromParent(store); + assert(llvm_value_use_empty(alloca)); + LLVMInstructionEraseFromParent(alloca); + } + else + { + return_value = create_load(module, LoadOptions{ + .type = return_semantic_type, + .pointer = return_alloca, + }); + } + } + else + { + LLVMValueRef source = 0; + if (function_type->return_abi.attributes.direct.offset == 0) + { + source = return_alloca; + } + else + { + trap(); + } + assert(source); + + auto source_type = function_type->return_abi.semantic_type; + auto destination_type = coerce_to_type; + auto result = create_coerced_load(module, source, source_type, destination_type); + return_value = result; + } + } break; + case AbiKind::indirect: + { + auto evaluation_kind = get_evaluation_kind(function_type->return_abi.semantic_type); + switch (evaluation_kind) + { + case EvaluationKind::scalar: trap(); + case EvaluationKind::aggregate: break; + case EvaluationKind::complex: trap(); + } + } break; + default: unreachable(); + } + + LLVMBuildRet(module->llvm.builder, return_value); + } + + // END OF SCOPE + module->current_function = 0; + } + } break; + case ValueId::global: + { + assert(!module->current_function); + analyze_value(module, global->variable.initial_value, global->variable.type, TypeKind::memory); + + auto initial_value_type = global->variable.initial_value->type; + + if (!global->variable.type) + { + global->variable.type = initial_value_type; + } + + auto global_type = global->variable.type; + + if (global_type != initial_value_type) + { + report_error(); + } + + resolve_type_in_place(module, global_type); + + bool is_constant = false; + LLVMLinkage linkage; + switch (global->linkage) + { + case Linkage::internal: linkage = LLVMInternalLinkage; break; + case Linkage::external: linkage = LLVMExternalLinkage; break; + } + + LLVMValueRef before = 0; + LLVMThreadLocalMode thread_local_mode = LLVMNotThreadLocal; + unsigned address_space = 0; + bool externally_initialized = false; + + auto global_llvm = llvm_module_create_global_variable(module->llvm.module, global_type->llvm.memory, is_constant, linkage, global->variable.initial_value->llvm, global->variable.name, before, thread_local_mode, address_space, externally_initialized); + auto alignment = get_byte_alignment(global_type); + LLVMSetAlignment(global_llvm, alignment); + global->variable.storage->llvm = global_llvm; + global->variable.storage->type = get_pointer_type(module, global_type); + + if (module->has_debug_info) + { + auto name = global->variable.name; + auto linkage_name = name; + auto local_to_unit = global->linkage == Linkage::internal; + auto global_debug = LLVMDIBuilderCreateGlobalVariableExpression(module->llvm.di_builder, module->scope.llvm, (char*)name.pointer, name.length, (char*)linkage_name.pointer, linkage_name.length, module->llvm.file, global->variable.line, global_type->llvm.debug, local_to_unit, null_expression(module), 0, alignment * 8); + LLVMGlobalSetMetadata(global_llvm, 0, global_debug); + } + } break; + default: report_error(); + } + } + + if (module->has_debug_info) + { + LLVMDIBuilderFinalize(module->llvm.di_builder); + } + + String verification_error_message = {}; + if (!llvm_module_verify(module->llvm.module, &verification_error_message)) + { + dump_module(module); + print(string_literal("\n==========================\nLLVM VERIFICATION ERROR\n==========================\n")); + print(verification_error_message); + bb_fail(); + } + + if (!module->silent) + { + dump_module(module); + } + + BBLLVMCodeGenerationOptimizationLevel code_generation_optimization_level; + switch (module->build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::none; + break; + case BuildMode::soft_optimize: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::less; + break; + case BuildMode::optimize_for_speed: + case BuildMode::optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::normal; + break; + case BuildMode::aggressively_optimize_for_speed: + case BuildMode::aggressively_optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::aggressive; + break; + case BuildMode::count: + unreachable(); + } + BBLLVMTargetMachineCreate target_machine_options = { + .target_triple = llvm_default_target_triple(), + .cpu_model = llvm_host_cpu_name(), + .cpu_features = llvm_host_cpu_features(), + .relocation_model = BBLLVMRelocationModel::default_relocation, + .code_model = BBLLVMCodeModel::none, + .optimization_level = code_generation_optimization_level, + }; + String error_message = {}; + auto target_machine = llvm_create_target_machine(&target_machine_options, &error_message); + assert(target_machine); + BBLLVMOptimizationLevel optimization_level; + switch (module->build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + optimization_level = BBLLVMOptimizationLevel::O0; + break; + case BuildMode::soft_optimize: + optimization_level = BBLLVMOptimizationLevel::O1; + break; + case BuildMode::optimize_for_speed: + optimization_level = BBLLVMOptimizationLevel::O2; + break; + case BuildMode::optimize_for_size: + optimization_level = BBLLVMOptimizationLevel::Os; + break; + case BuildMode::aggressively_optimize_for_speed: + optimization_level = BBLLVMOptimizationLevel::O3; + break; + case BuildMode::aggressively_optimize_for_size: + optimization_level = BBLLVMOptimizationLevel::Oz; + break; + case BuildMode::count: + unreachable(); + } + auto object_generation_result = generate_object(module->llvm.module, target_machine, { + .path = module->objects[0], + .optimization_level = optimization_level, + .run_optimization_passes = module->build_mode != BuildMode::debug_none, + .has_debug_info = module->has_debug_info, + }); + if (object_generation_result != BBLLVMCodeGenerationPipelineResult::success) + { + report_error(); + } + + link(module); +} diff --git a/src/entry_point.cpp b/src/entry_point.cpp new file mode 100644 index 0000000..a6e60bf --- /dev/null +++ b/src/entry_point.cpp @@ -0,0 +1,13 @@ +#include +void entry_point(Slice arguments, Slice environment); +int main(int argc, const char* argv[], char* const envp[]) +{ + auto* envp_end = envp; + while (*envp_end) + { + envp_end += 1; + } + + entry_point({argv, (u64)argc}, {envp, (u64)(envp_end - envp)}); + return 0; +} diff --git a/src/entry_point.hpp b/src/entry_point.hpp new file mode 100644 index 0000000..8ef2889 --- /dev/null +++ b/src/entry_point.hpp @@ -0,0 +1,2 @@ +#include + diff --git a/src/lib.cpp b/src/lib.cpp new file mode 100644 index 0000000..ff76892 --- /dev/null +++ b/src/lib.cpp @@ -0,0 +1,212 @@ +#include +using uid_t = u32; +using gid_t = u32; +using off_t = s64; +using ino_t = u64; +using dev_t = u64; + +struct timespec +{ + s64 seconds; + s64 nanoseconds; +}; + +struct Stat +{ + dev_t dev; + ino_t ino; + u64 nlink; + + u32 mode; + uid_t uid; + gid_t gid; + u32 _0; + dev_t rdev; + off_t size; + s64 blksize; + s64 blocks; + + timespec atim; + timespec mtim; + timespec ctim; + s64 _1[3]; +}; + +extern "C" s32 fstat(s32, Stat*); +extern "C" s32 fork(); +extern "C" s32 dup2(s32, s32); +extern "C" s32 execve(const char* path_name, const char* const argv[], char* const envp[]); +extern "C" s32 waitpid(s32 pid, int* wstatus, int options); + +u64 os_file_size(s32 fd) +{ + Stat stat; + auto result = fstat(fd, &stat); + assert(result == 0); + return (u64)stat.size; +} + +fn u8 EXITSTATUS(u32 s) +{ + return (u8)((s & 0xff00) >> 8); +} + +fn u32 TERMSIG(u32 s) +{ + return s & 0x7f; +} + +fn u32 STOPSIG(u32 s) +{ + return EXITSTATUS(s); +} + +fn bool IFEXITED(u32 s) +{ + return TERMSIG(s) == 0; +} + +fn bool IFSTOPPED(u32 s) +{ + return u16(((s & 0xffff) * 0x10001) >> 8) > 0x7f00; +} + +fn bool IFSIGNALED(u32 s) +{ + return (s & 0xffff) - 1 < 0xff; +} +Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options) +{ + unused(arena); + assert(arguments.pointer[arguments.length] == 0); + assert(environment.pointer[environment.length] == 0); + + Execution execution = {}; + + s32 null_file_descriptor = -1; + if (options.null_file_descriptor >= 0) + { + null_file_descriptor = options.null_file_descriptor; + } + else if (options.policies[0] == ExecuteStandardStreamPolicy::ignore || options.policies[1] == ExecuteStandardStreamPolicy::ignore) + { + trap(); + } + + int pipes[standard_stream_count][2]; + + for (int i = 0; i < 2; i += 1) + { + if (options.policies[i] == ExecuteStandardStreamPolicy::pipe) + { + trap(); + } + } + + auto pid = fork(); + + switch (pid) + { + case -1: + { + trap(); + } break; + case 0: // Child process + { + for (u64 i = 0; i < standard_stream_count; i += 1) + { + auto fd = (s32)i + 1; + switch (options.policies[i]) + { + case ExecuteStandardStreamPolicy::inherit: + { + } break; + case ExecuteStandardStreamPolicy::pipe: + { + close(pipes[i][0]); + dup2(pipes[i][1], fd); + close(pipes[i][1]); + } break; + case ExecuteStandardStreamPolicy::ignore: + { + dup2(null_file_descriptor, fd); + close(null_file_descriptor); + } break; + } + } + + auto result = execve(arguments[0], arguments.pointer, environment.pointer); + + if (result != -1) + { + unreachable(); + } + + trap(); + } break; + default: + { + for (u64 i = 0; i < standard_stream_count; i += 1) + { + if (options.policies[i] == ExecuteStandardStreamPolicy::pipe) + { + close(pipes[i][1]); + } + } + + if (options.policies[0] == ExecuteStandardStreamPolicy::pipe || options.policies[1] == ExecuteStandardStreamPolicy::pipe) + { + trap(); + } + + for (u64 i = 0; i < standard_stream_count; i += 1) + { + if (options.policies[i] == ExecuteStandardStreamPolicy::pipe) + { + trap(); + } + } + + int status = 0; + auto waitpid_result = waitpid(pid, &status, 0); + + if (waitpid_result == pid) + { + if (IFEXITED(status)) + { + execution.termination_kind = TerminationKind::exit; + execution.termination_code = EXITSTATUS(status); + } + else if (IFSIGNALED(status)) + { + execution.termination_kind = TerminationKind::signal; + execution.termination_code = TERMSIG(status); + } + else if (IFSTOPPED(status)) + { + execution.termination_kind = TerminationKind::stop; + execution.termination_code = STOPSIG(status); + } + else + { + execution.termination_kind = TerminationKind::unknown; + } + + if (options.null_file_descriptor < 0 && null_file_descriptor >= 0) + { + close(null_file_descriptor); + } + } + else if (waitpid_result == -1) + { + trap(); + } + else + { + trap(); + } + } break; + } + + return execution; +} diff --git a/src/lib.hpp b/src/lib.hpp new file mode 100644 index 0000000..424974f --- /dev/null +++ b/src/lib.hpp @@ -0,0 +1,718 @@ +#pragma once + +#define global_variable static + +#define EXPORT extern "C" +#define fn static +#define unused(x) (void)(x) +#define breakpoint() __builtin_debugtrap() +#define string_literal_length(s) (sizeof(s) - 1) +#define string_literal(s) ((String){ .pointer = (u8*)(s), .length = string_literal_length(s), }) +#define split_string_literal(s) (char*)(s), string_literal_length(s) +#define offsetof(S, f) __builtin_offsetof(S, f) + +#define array_length(arr) sizeof(arr) / sizeof((arr)[0]) +#define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) } +#define array_to_bytes(arr) { .pointer = (u8*)(arr), .length = sizeof(arr) } +#define backing_type(E) __underlying_type(E) + +#define unreachable_raw() __builtin_unreachable() +#define trap() __builtin_trap() +#if BB_DEBUG +#define unreachable() trap() +#else +#define unreachable() unreachable_raw() +#endif +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#define expect(x, b) __builtin_expect(!!(x), b) +#define likely(x) expect(x, 1) +#define unlikely(x) expect(x, 0) + +#define assert(x) (unlikely(!(x)) ? unreachable() : unused(0)) + +#define clz(x) __builtin_clzg(x) +#define ctz(x) __builtin_ctzg(x) + +#define case_to_name(E,n) case E::n: return string_literal(#n) + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long u64; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long s64; + +typedef float f32; +typedef double f64; + +fn u64 align_forward(u64 value, u64 alignment) +{ + assert(alignment != 0); + auto mask = alignment - 1; + auto result = (value + mask) & ~mask; + return result; +} + +constexpr u64 kb = 1024; +constexpr u64 mb = 1024 * 1024; +constexpr u64 gb = 1024 * 1024 * 1024; + +extern "C" [[noreturn]] void exit(s32 status) noexcept(true); +extern "C" void *memcpy (void* __restrict destination, const void *__restrict source, u64 byte_count) noexcept(true); +extern "C" s32 memcmp (const void* a, const void *b, u64 __n) noexcept(true); +extern "C" char* realpath(const char* __restrict path, char* resolved_path) noexcept(true); + +struct RawSlice +{ + void* pointer; + u64 length; +}; + +fn bool raw_slice_equal(RawSlice a, RawSlice b, u64 size_of_T) +{ + bool result = a.length == b.length; + if (result) + { + if (a.pointer != b.pointer) + { + result = memcmp(a.pointer, b.pointer, a.length * size_of_T) == 0; + } + } + + return result; +} + +fn RawSlice raw_slice_slice(RawSlice s, u64 start, u64 end, u64 size_of_T) +{ + return {(u8*)s.pointer + (size_of_T * start), end - start}; +} + +template +struct Slice +{ + T* pointer; + u64 length; + + T* begin() + { + return pointer; + } + + T* end() { + return pointer + length; + } + + T& operator[](u64 index) + { + assert(index < length); + return pointer[index]; + } + + bool equal(Slice other) + { + return raw_slice_equal(*(RawSlice*)this, *(RawSlice*)&other, sizeof(T)); + } + + Slice operator()(u64 start, u64 end) + { + return {pointer + start, end - start}; + } + + Slice operator()(u64 start) + { + return {pointer + start, length - start}; + } +}; + +using String = Slice; +fn const char* cstr(String string) +{ + assert(string.pointer[string.length] == 0); + return (const char*) string.pointer; +} + +fn String c_string_to_slice(const char* cstr) +{ + const auto* end = cstr; + while (*end) + { + end += 1; + } + + return { (u8*)cstr, u64(end - cstr) }; +} + +constexpr auto string_no_match = ~(u64)0; + +fn u64 string_first_character(String string, u8 ch) +{ + u64 result = string_no_match; + + for (u64 i = 0; i < string.length; i += 1) + { + if (string[i] == ch) + { + result = i; + break; + } + } + + return result; +} + +fn u64 string_last_character(String string, u8 ch) +{ + u64 result = string_no_match; + u64 i = string.length; + + while (i > 0) + { + i -= 1; + + if (string[i] == ch) + { + result = i; + break; + } + } + + return result; +} + +struct ProtectionFlags +{ + u8 read:1; + u8 write:1; + u8 execute:1; +}; + +struct MapFlags +{ + u8 priv:1; + u8 anonymous:1; + u8 no_reserve:1; + u8 populate:1; +}; + +struct PROT +{ + u32 read:1; + u32 write:1; + u32 execute:1; + u32 sem:1; + u32 _:28; +}; +static_assert(sizeof(PROT) == sizeof(u32)); + +struct MAP +{ + enum class Type : u32 + { + shared = 0, + priv = 1, + shared_validate = 2, + }; + + Type type:4; + u32 fixed:1; + u32 anonymous:1; + u32 bit32:1; + u32 _0: 1; + u32 grows_down:1; + u32 _1: 2; + u32 deny_write:1; + u32 executable:1; + u32 locked:1; + u32 no_reserve:1; + u32 populate:1; + u32 non_block:1; + u32 stack:1; + u32 huge_tlb:1; + u32 sync:1; + u32 fixed_no_replace:1; + u32 _2:5; + u32 uninitialized:1; + u32 _3:5; +}; +static_assert(sizeof(MAP) == sizeof(u32)); + +struct OPEN +{ + enum class AccessMode : u32 + { + read_only = 0, + write_only = 1, + read_write = 2, + }; + + AccessMode access_mode:2; + u32 _0:4; + u32 creat:1; + u32 excl:1; + u32 no_ctty:1; + u32 trunc:1; + u32 append:1; + u32 non_block:1; + u32 d_sync:1; + u32 a_sync:1; + u32 direct:1; + u32 _1:1; + u32 directory:1; + u32 no_follow:1; + u32 no_a_time:1; + u32 cloexec:1; + u32 sync:1; + u32 path:1; + u32 tmp_file:1; + u32 _2:9; +}; +static_assert(sizeof(OPEN) == sizeof(u32)); + +extern "C" s32* __errno_location() noexcept(true); +extern "C" void* mmap(void*, u64, PROT, MAP, s32, s64); +extern "C" s32 mprotect(void*, u64, PROT); +extern "C" s64 ptrace(s32, s32, u64, u64); +extern "C" s32 open(const char*, OPEN, ...); +extern "C" s32 close(s32); +extern "C" s64 write(s32, const void*, u64); +extern "C" s64 read(s32, void*, u64); +extern "C" s32 mkdir(const char*, u64); + +enum class Error : u32 +{ + success = 0, + perm = 1, +}; + +fn Error errno() +{ + return (Error)*__errno_location(); +} + +fn void* os_reserve(void* base, u64 size, ProtectionFlags protection, MapFlags map) +{ + auto protection_flags = PROT + { + .read = protection.read, + .write = protection.write, + .execute = protection.execute, + .sem = 0, + ._ = 0, + }; + + auto map_flags = MAP + { + .type = map.priv ? MAP::Type::priv : MAP::Type::shared, + .fixed = 0, + .anonymous = map.anonymous, + .bit32 = 0, + ._0 = 0, + .grows_down = 0, + ._1 = 0, + .deny_write = 0, + .executable = 0, + .locked = 0, + .no_reserve = map.no_reserve, + .populate = map.populate, + .non_block = 0, + .stack = 0, + .huge_tlb = 0, + .sync = 0, + .fixed_no_replace = 0, + ._2 = 0, + .uninitialized = 0, + ._3 = 0, + }; + + auto* address = mmap(base, size, protection_flags, map_flags, -1, 0); + assert((u64)address != ~(u64)0); + + return address; +} + +fn void os_commit(void* address, u64 size, ProtectionFlags protection) +{ + auto protection_flags = PROT + { + .read = protection.read, + .write = protection.write, + .execute = protection.execute, + .sem = 0, + ._ = 0, + }; + auto result = mprotect(address, size, protection_flags); + assert(!result); +} + +struct OpenFlags +{ + u32 truncate:1; + u32 execute:1; + u32 write:1; + u32 read:1; + u32 create:1; + u32 directory:1; +}; + +struct Permissions +{ + u32 read:1; + u32 write:1; + u32 execute:1; +}; + +fn s32 os_open(String path, OpenFlags flags, Permissions permissions) +{ + OPEN::AccessMode access_mode; + if (flags.read && flags.write) + { + access_mode = OPEN::AccessMode::read_write; + } + else if (flags.read) + { + access_mode = OPEN::AccessMode::read_only; + } + else if (flags.write) + { + access_mode = OPEN::AccessMode::read_only; + } + else + { + unreachable(); + } + + auto o = OPEN { + .access_mode = access_mode, + .creat = flags.create, + .trunc = flags.truncate, + .directory = flags.directory, + }; + + // TODO: + auto mode = permissions.execute ? 0755 : 0644; + + auto fd = open(cstr(path), o, mode); + return fd; +} + +fn bool is_file_valid(s32 fd) +{ + return fd >= 0; +} + +fn void os_close(s32 fd) +{ + assert(is_file_valid(fd)); + + auto result = close(fd); + assert(result == 0); +} + +u64 os_file_size(s32 fd); + +fn u64 os_read_partially(s32 fd, u8* buffer, u64 byte_count) +{ + auto result = read(fd, buffer, byte_count); + assert(result > 0); + return (u64)result; +} + +fn void os_read(s32 fd, String buffer, u64 byte_count) +{ + assert(byte_count <= buffer.length); + u64 it_byte_count = 0; + while (it_byte_count < byte_count) + { + auto read_byte_count = os_read_partially(fd, buffer.pointer + it_byte_count, byte_count - it_byte_count); + it_byte_count += read_byte_count; + } + assert(it_byte_count == byte_count); +} + +fn u64 os_write_partially(s32 fd, u8* buffer, u64 byte_count) +{ + auto result = write(fd, buffer, byte_count); + assert(result > 0); + return (u64)result; +} + +fn void os_write(s32 fd, String content) +{ + u64 it_byte_count = 0; + while (it_byte_count < content.length) + { + auto written_byte_count = os_write_partially(fd, content.pointer + it_byte_count, content.length - it_byte_count); + it_byte_count += written_byte_count; + } + assert(it_byte_count == content.length); +} + +fn String path_absolute_stack(String buffer, String relative_path) +{ + const char* absolute_path = realpath(cstr(relative_path), (char*)buffer.pointer); + if (absolute_path) + { + auto slice = c_string_to_slice(absolute_path); + assert(slice.length < buffer.length); + return slice; + } + return {}; +} + +fn bool os_is_debugger_present() +{ + bool result = false; + if (ptrace(0, 0, 0, 0) == -1) + { + auto errno_error = errno(); + result = errno_error == Error::perm; + } + + return result; +} + +fn void make_directory(const char* path) +{ + auto result = mkdir(path, 0755); + unused(result); +} + +fn void print(String string) +{ + os_write(1, string); +} + +struct ArenaInitialization +{ + u64 reserved_size; + u64 granularity; + u64 initial_size; +}; + +struct Arena +{ + u64 reserved_size; + u64 position; + u64 os_position; + u64 granularity; + u8 reserved[32]; +}; + +constexpr u64 arena_minimum_position = sizeof(Arena); + +fn Arena* arena_initialize(ArenaInitialization i) +{ + ProtectionFlags protection_flags = { + .read = 1, + .write = 1, + }; + MapFlags map_flags = { + .priv = 1, + .anonymous = 1, + .no_reserve = 1, + }; + + auto* arena = (Arena*)os_reserve(0, i.reserved_size, protection_flags, map_flags); + os_commit(arena, i.initial_size, { .read = 1, .write = 1 }); + + *arena = { + .reserved_size = i.reserved_size, + .position = arena_minimum_position, + .os_position = i.initial_size, + .granularity = i.granularity, + }; + + return arena; +} + +fn inline Arena* arena_initialize_default(u64 initial_size) +{ + ArenaInitialization i = { + .reserved_size = 4 * gb, + .granularity = 4 * kb, + .initial_size = initial_size, + }; + return arena_initialize(i); +} + +fn void* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment) +{ + void* result = 0; + + if (size) + { + auto aligned_offset = align_forward(arena->position, alignment); + auto aligned_size_after = aligned_offset + size; + + if (aligned_size_after > arena->os_position) + { + unreachable(); + } + + result = (u8*)arena + aligned_offset; + arena->position = aligned_size_after; + assert(arena->position <= arena->os_position); + } + + return result; +} + +template +fn Slice arena_allocate(Arena* arena, u64 count) +{ + return { (T*)arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)), count }; +} + +fn String arena_join_string(Arena* arena, Slice pieces) +{ + u64 size = 0; + for (auto piece : pieces) + { + size += piece.length; + } + + auto* pointer = (u8*)arena_allocate_bytes(arena, size + 1, 1); + u64 i = 0; + for (auto piece : pieces) + { + memcpy(pointer + i, piece.pointer, piece.length); + i += piece.length; + } + + assert(i == size); + pointer[i] = 0; + + return { pointer, size }; +} + +fn String arena_duplicate_string(Arena* arena, String string) +{ + auto memory = (u8*)arena_allocate_bytes(arena, string.length + 1, 1); + memcpy(memory, string.pointer, string.length); + memory[string.length] = 0; + return { memory, string.length}; +} + +fn void arena_restore(Arena* arena, u64 position) +{ + assert(position <= arena->position); + arena->position = position; +} + +fn void arena_reset(Arena* arena) +{ + arena->position = arena_minimum_position; +} + +fn String path_absolute(Arena* arena, String relative_path) +{ + u8 buffer[4096]; + auto stack = path_absolute_stack(array_to_slice(buffer), relative_path); + auto result = arena_duplicate_string(arena, stack); + return result; +} + +fn String file_read(Arena* arena, String file_path) +{ + auto fd = os_open(file_path, { .read = 1 }, { .read = 1 }); + String result = {}; + + if (is_file_valid(fd)) + { + auto file_size = os_file_size(fd); + result = arena_allocate(arena, file_size); + os_read(fd, result, file_size); + os_close(fd); + } + + return result; +} + +#define bb_fail() os_is_debugger_present() ? trap() : exit(1) +#define bb_fail_with_message(message) (print(message), bb_fail()) + +fn u64 next_power_of_two(u64 n) +{ + n -= 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n += 1; + return n; +} + +fn u8 format_integer_decimal(String buffer, u64 v) +{ + u8 byte_count = 0; + auto value = v; + + if (value != 0) + { + u8 reverse_buffer[64]; + u8 reverse_index = 0; + + while (value != 0) + { + auto digit_value = (u8)(value % 10); + auto ascii_character = digit_value + '0'; + value /= 10; + reverse_buffer[reverse_index] = ascii_character; + reverse_index += 1; + } + + while (reverse_index != 0) + { + reverse_index -= 1; + buffer[byte_count] = reverse_buffer[reverse_index]; + byte_count += 1; + } + } + else + { + buffer[0] = '0'; + byte_count = 1; + } + + return byte_count; +} + +enum class ExecuteStandardStreamPolicy : u8 +{ + inherit, + pipe, + ignore, +}; + +global_variable constexpr u64 standard_stream_count = 2; + +struct ExecuteOptions +{ + ExecuteStandardStreamPolicy policies[standard_stream_count]; // INDICES: stdout = 0, stderr = 1 + s32 null_file_descriptor = -1; +}; + +enum class TerminationKind : u8 +{ + unknown, + exit, + signal, + stop, +}; + +struct Execution +{ + String stdout; + String stderr; + TerminationKind termination_kind; + u32 termination_code; +}; + +Execution os_execute(Arena* arena, Slice arguments, Slice environment, ExecuteOptions options); diff --git a/src/lib.zig b/src/lib.zig deleted file mode 100644 index 8957823..0000000 --- a/src/lib.zig +++ /dev/null @@ -1,3159 +0,0 @@ -pub const builtin = @import("builtin"); -pub const is_test = builtin.is_test; -pub const optimization_mode = builtin.mode; -pub const VariableArguments = extern struct { - gp_offset: c_uint, - fp_offset: c_uint, - overflow_arg_area: *anyopaque, - reg_save_area: *anyopaque, -}; -extern "c" fn IsDebuggerPresent() bool; -extern "c" fn __errno_location() *c_int; - -const CSlice = extern struct { - pointer: ?*anyopaque, - length: usize, -}; - -extern fn dump_stack_trace(return_address: usize) void; -extern fn enable_signal_handlers() void; - -pub const KB = 1024; -pub const MB = 1024 * 1024; -pub const GB = 1024 * 1024 * 1024; - -pub fn assert(ok: bool) void { - if (!ok) { - @branchHint(.unlikely); - unreachable; - } -} - -fn errno() Error { - return @enumFromInt(__errno_location().*); -} - -fn align_forward(T: type, value: T, alignment: T) T { - assert(alignment != 0); - const mask = alignment - 1; - const result = (value + mask) & ~mask; - return result; -} - -pub fn align_forward_u64(value: u64, alignment: u64) u64 { - return align_forward(u64, value, alignment); -} - -pub fn align_forward_u32(value: u32, alignment: u32) u32 { - return align_forward(u32, value, alignment); -} - -pub fn is_power_of_two(value: anytype) bool { - return (value & (value - 1)) == 0; -} - -pub fn next_power_of_two(n: u64) u64 { - var result = n; - result -= 1; - result |= result >> 1; - result |= result >> 2; - result |= result >> 4; - result |= result >> 8; - result |= result >> 16; - result |= result >> 32; - result += 1; - return result; -} - -const ValueFromFlag = enum { - sub, - cmov, -}; -const value_from_flag_kind = ValueFromFlag.sub; - -pub fn value_from_flag(value: anytype, flag: anytype) @TypeOf(value) { - const flag_int: @TypeOf(value) = switch (@TypeOf(flag)) { - comptime_int => b: { - assert(flag == 1 or flag == 0); - break :b flag; - }, - bool => @intFromBool(flag), - u1 => flag, - else => @compileError("Unhandled type: " ++ @typeName(@TypeOf(flag))), - }; - - const result = switch (value_from_flag_kind) { - .cmov => { - @compileError("TODO"); - }, - .sub => value & (@as(@TypeOf(value), 0) -% flag_int), - }; - return result; -} - -pub const Error = enum(c_int) { - SUCCESS = 0, - PERM = 1, -}; - -pub const u64_max = ~@as(u64, 0); - -pub const file = struct { - pub const WriteOptions = packed struct { - executable: u1 = 0, - }; - pub fn write(path: [:0]const u8, content: []const u8, options: WriteOptions) void { - const fd = os.File.open(path, .{ - .write = 1, - .truncate = 1, - .create = 1, - .execute = options.executable, - }, .{ - .read = 1, - .write = 1, - .execute = options.executable, - }); - defer fd.close(); - - if (fd.is_valid()) { - fd.write(content); - } - } - - pub fn read(arena: *Arena, path: [:0]const u8) []u8 { - const fd = os.File.open(path, .{ - .read = 1, - }, .{ - .read = 1, - }); - var result: []u8 = undefined; - const ptr = @as(*[2]u64, @ptrCast(&result)); - ptr[0] = 0; - ptr[1] = 0; - - if (fd.is_valid()) { - const file_size = fd.get_size(); - const file_buffer = arena.allocate_bytes(file_size, 1); - result = file_buffer[0..file_size]; - fd.read(result, file_size); - fd.close(); - } - - return result; - } -}; - -pub const os = struct { - const system = switch (builtin.os.tag) { - .windows => windows, - .linux => linux, - else => @compileError("TODO"), - }; - pub const posix = switch (builtin.os.tag) { - .windows => @compileError("TODO"), - .linux => linux, - else => @compileError("TODO"), - }; - - pub const File = struct { - fd: Descriptor, - - const Descriptor = system.FileDescriptor; - - pub fn is_valid(fd: File) bool { - return system.fd_is_valid(fd.fd); - } - - pub const OpenFlags = packed struct { - truncate: u1 = 0, - execute: u1 = 0, - write: u1 = 0, - read: u1 = 0, - create: u1 = 0, - directory: u1 = 0, - }; - - pub const Permissions = packed struct { - read: u1 = 1, - write: u1 = 1, - execute: u1 = 0, - }; - - pub fn open(path: [*:0]const u8, flags: OpenFlags, permissions: Permissions) File { - switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - const o = posix.O{ - .ACCMODE = if (flags.read & flags.write != 0) .RDWR else if (flags.read != 0) .RDONLY else if (flags.write != 0) .WRONLY else unreachable, - .TRUNC = flags.truncate, - .CREAT = flags.create, - .DIRECTORY = flags.directory, - }; - const mode: posix.mode_t = if (permissions.execute != 0) 0o755 else 0o644; - - const fd = posix.open(path, o, mode); - return File{ - .fd = fd, - }; - }, - } - } - - pub fn close(fd: File) void { - switch (builtin.os.tag) { - .windows => { - @compileError("TODO"); - }, - else => { - const result = posix.close(fd.fd); - assert(result == 0); - }, - } - } - - pub fn get_size(fd: File) u64 { - switch (builtin.os.tag) { - .windows => { - @compileError("TODO"); - }, - else => { - var stat: posix.Stat = undefined; - const result = posix.fstat(fd.fd, &stat); - assert(result == 0); - return @intCast(stat.size); - }, - } - } - - pub fn write_partially(fd: File, content: []const u8) usize { - switch (builtin.os.tag) { - .windows => { - @compileError("TODO"); - }, - else => { - const syscall_result = posix.write(fd.fd, content.ptr, content.len); - if (syscall_result <= 0) { - abort(); - } else { - return @intCast(syscall_result); - } - }, - } - } - - pub fn write(fd: File, content: []const u8) void { - var it = content; - while (it.len != 0) { - const written_bytes = fd.write_partially(it); - it.ptr += written_bytes; - it.len -= written_bytes; - } - } - - pub fn read_partially(fd: File, buffer: [*]u8, byte_count: usize) usize { - switch (builtin.os.tag) { - .windows => { - @compileError("TODO"); - }, - else => { - const syscall_result = posix.read(fd.fd, buffer, byte_count); - if (syscall_result <= 0) { - abort(); - } else { - return @intCast(syscall_result); - } - }, - } - } - - pub fn read(fd: File, buffer: []u8, byte_count: usize) void { - assert(byte_count <= buffer.len); - var it_byte_count: usize = 0; - while (it_byte_count < byte_count) { - const read_bytes = fd.read_partially(buffer.ptr + it_byte_count, byte_count - it_byte_count); - it_byte_count += read_bytes; - } - } - }; - - pub fn is_being_debugged() bool { - var result = false; - switch (builtin.os.tag) { - .linux => { - if (linux.ptrace(0, 0, 0, 0) == -1) { - result = errno() == Error.PERM; - } - }, - .windows => IsDebuggerPresent(), - .macos => {}, - else => @compileError("TODO"), - } - - return result; - } - - pub fn get_cpu_count() usize { - switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - var cpu_set: posix.cpu_set_t = undefined; - const result = posix.sched_getaffinity(0, @sizeOf(posix.cpu_set_t), &cpu_set); - assert(result == 0); - const cpu_count = posix.CPU_COUNT(cpu_set); - return cpu_count; - }, - } - } - - pub fn make_directory(path: [*:0]const u8) void { - switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - const result = posix.mkdir(path, 0o755); - _ = result; - }, - } - } - - const linux = struct { - pub const stdin = 0; - pub const stdout = 1; - pub const stderr = 2; - pub const CPU_SETSIZE = 128; - pub const cpu_set_t = [CPU_SETSIZE / @sizeOf(usize)]usize; - pub const cpu_count_t = @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = log2_int(@as(u64, CPU_SETSIZE * 8)), - }, - }); - - pub fn CPU_COUNT(set: cpu_set_t) cpu_count_t { - var sum: cpu_count_t = 0; - for (set) |x| { - sum += @popCount(x); - } - return sum; - } - - const FileDescriptor = c_int; - - fn fd_is_valid(fd: FileDescriptor) bool { - return fd >= 0; - } - - pub const uid_t = u32; - pub const gid_t = u32; - pub const off_t = i64; - pub const ino_t = u64; - pub const dev_t = u64; - - pub const timespec = extern struct { - seconds: isize, - nanoseconds: isize, - }; - - // The `stat` definition used by the Linux kernel. - pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: usize, - - mode: u32, - uid: uid_t, - gid: gid_t, - __pad0: u32, - rdev: dev_t, - size: off_t, - blksize: isize, - blocks: i64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - __unused: [3]isize, - }; - - const PROT = packed struct(u32) { - read: u1, - write: u1, - exec: u1, - sem: u1 = 0, - _: u28 = 0, - }; - const MAP = packed struct(u32) { - const Type = enum(u4) { - shared = 0x1, - private = 0x2, - shared_validate = 0x3, - }; - - type: Type = .private, - FIXED: u1 = 0, - ANONYMOUS: u1 = 0, - @"32BIT": u1 = 0, - _7: u1 = 0, - GROWSDOWN: u1 = 0, - _9: u2 = 0, - DENYWRITE: u1 = 0, - EXECUTABLE: u1 = 0, - LOCKED: u1 = 0, - NORESERVE: u1 = 0, - POPULATE: u1 = 0, - NONBLOCK: u1 = 0, - STACK: u1 = 0, - HUGETLB: u1 = 0, - SYNC: u1 = 0, - FIXED_NOREPLACE: u1 = 0, - _21: u5 = 0, - UNINITIALIZED: u1 = 0, - _: u5 = 0, - }; - - pub const ACCMODE = enum(u2) { - RDONLY = 0, - WRONLY = 1, - RDWR = 2, - }; - - const O = packed struct(u32) { - ACCMODE: ACCMODE, - _2: u4 = 0, - CREAT: u1 = 0, - EXCL: u1 = 0, - NOCTTY: u1 = 0, - TRUNC: u1 = 0, - APPEND: u1 = 0, - NONBLOCK: u1 = 0, - DSYNC: u1 = 0, - ASYNC: u1 = 0, - DIRECT: u1 = 0, - _15: u1 = 0, - DIRECTORY: u1 = 0, - NOFOLLOW: u1 = 0, - NOATIME: u1 = 0, - CLOEXEC: u1 = 0, - SYNC: u1 = 0, - PATH: u1 = 0, - TMPFILE: u1 = 0, - _: u9 = 0, - }; - - extern "c" fn ptrace(c_int, c_int, usize, usize) c_long; - extern "c" fn mmap(usize, usize, PROT, MAP, c_int, isize) *align(4096) anyopaque; - extern "c" fn mprotect(*anyopaque, usize, PROT) c_int; - extern "c" fn open(path: [*:0]const u8, oflag: O, ...) c_int; - extern "c" fn close(fd: system.FileDescriptor) c_int; - extern "c" fn fstat(fd: system.FileDescriptor, stat: *Stat) c_int; - extern "c" fn read(fd: system.FileDescriptor, pointer: [*]u8, byte_count: usize) isize; - extern "c" fn write(fd: system.FileDescriptor, pointer: [*]const u8, byte_count: usize) isize; - extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; - extern "c" fn mkdir(path: [*:0]const u8, mode: mode_t) c_int; - extern "c" fn pipe(pipe: *[2]i32) File.Descriptor; - extern "c" fn fork() Process.Descriptor; - extern "c" fn dup2(old: File.Descriptor, new: File.Descriptor) c_int; - extern "c" fn execve(path_name: [*:0]const u8, arguments: [*:null]const ?[*:0]const u8, environment: [*:null]const ?[*:0]const u8) c_int; - extern "c" fn waitpid(pid: Process.Descriptor, status: ?*u32, options: u32) Process.Descriptor; - - const mode_t = usize; - - fn protection_flags(protection: ProtectionFlags) PROT { - const result = PROT{ - .read = protection.read, - .write = protection.write, - .exec = protection.execute, - }; - return result; - } - - fn map_flags(map: MapFlags) MAP { - const result = MAP{ - .type = if (map.private != 0) .private else .shared, - .ANONYMOUS = map.anonymous, - .NORESERVE = map.no_reserve, - .POPULATE = map.populate, - }; - - return result; - } - - pub const W = struct { - pub const NOHANG = 1; - pub const UNTRACED = 2; - pub const STOPPED = 2; - pub const EXITED = 4; - pub const CONTINUED = 8; - pub const NOWAIT = 0x1000000; - - pub fn EXITSTATUS(s: u32) u8 { - return @as(u8, @intCast((s & 0xff00) >> 8)); - } - pub fn TERMSIG(s: u32) u32 { - return s & 0x7f; - } - pub fn STOPSIG(s: u32) u32 { - return EXITSTATUS(s); - } - pub fn IFEXITED(s: u32) bool { - return TERMSIG(s) == 0; - } - pub fn IFSTOPPED(s: u32) bool { - return @as(u16, @truncate(((s & 0xffff) *% 0x10001) >> 8)) > 0x7f00; - } - pub fn IFSIGNALED(s: u32) bool { - return (s & 0xffff) -% 1 < 0xff; - } - }; - }; - - const windows = struct { - const HANDLE = ?*anyopaque; - }; - - const ProtectionFlags = packed struct { - read: u1, - write: u1, - execute: u1, - }; - - const MapFlags = packed struct { - private: u1, - anonymous: u1, - no_reserve: u1, - populate: u1, - }; - - pub fn reserve(base: u64, size: u64, protection: ProtectionFlags, map: MapFlags) *align(4096) anyopaque { - switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - const protection_flags = linux.protection_flags(protection); - const map_flags = linux.map_flags(map); - const address = linux.mmap(base, size, protection_flags, map_flags, -1, 0); - if (@intFromPtr(address) == u64_max) { - @branchHint(.unlikely); - unreachable; - } - return address; - }, - } - } - - pub fn commit(address: *anyopaque, size: u64, protection: ProtectionFlags) void { - switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - const protection_flags = linux.protection_flags(protection); - const result = linux.mprotect(address, size, protection_flags); - if (result != 0) { - unreachable; - } - }, - } - } - - pub fn abort() noreturn { - if (os.is_being_debugged()) { - @trap(); - } else { - dump_stack_trace(@returnAddress()); - - libc.exit(1); - } - } - - pub fn get_stdout() File { - return switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - return File{ - .fd = posix.stdout, - }; - }, - }; - } - - pub fn get_stderr() File { - return switch (builtin.os.tag) { - .windows => @compileError("TODO"), - else => { - return File{ - .fd = posix.stderr, - }; - }, - }; - } - - pub fn absolute_path_stack(noalias buffer: []u8, noalias relative_path: [*:0]const u8) [:0]const u8 { - const real_path = libc.realpath(relative_path, buffer.ptr) orelse unreachable; - const result = cstring.to_slice(real_path); - assert(result.len < buffer.len); - return result; - } - - pub fn absolute_path(arena: *Arena, noalias relative_path: [*:0]const u8) [:0]const u8 { - // TODO: pick a more correct number - var buffer: [4096]u8 = undefined; - return arena.duplicate_string(absolute_path_stack(&buffer, relative_path)); - } - - const ChildProcessStream = struct { - const Policy = enum { - inherit, - pipe, - ignore, - }; - }; - - pub const RunChildProcessOptions = struct { - stdout: ChildProcessStream.Policy, - stderr: ChildProcessStream.Policy, - null_file_descriptor: ?File.Descriptor, - }; - - pub const RunChildProcessResult = struct { - stdout: []const u8, - stderr: []const u8, - kind: Kind, - code: u32, - - pub fn is_successful(result: RunChildProcessResult) bool { - return result.kind == .exit and result.code == 0; - } - - pub const Kind = enum { - exit, - signal, - stop, - unknown, - }; - }; - - pub fn run_child_process(arena: *Arena, arguments: [*:null]const ?[*:0]const u8, environment_variables: [*:null]const ?[*:0]const u8, options: RunChildProcessOptions) RunChildProcessResult { - const null_fd: File.Descriptor = if (options.null_file_descriptor) |nullfd| nullfd else if (options.stdout == .ignore or options.stderr == .ignore) posix.open("/dev/null", .{ .ACCMODE = .WRONLY }) else undefined; - - var stdout_pipe: [2]i32 = undefined; - var stderr_pipe: [2]i32 = undefined; - - if (options.stdout == .pipe) { - if (posix.pipe(&stdout_pipe) == -1) { - @trap(); - } - } - - if (options.stderr == .pipe) { - if (posix.pipe(&stderr_pipe) == -1) { - @trap(); - } - } - - const pid = posix.fork(); - - switch (pid) { - -1 => @trap(), - // Child process - 0 => { - switch (options.stdout) { - .pipe => { - _ = posix.close(stdout_pipe[0]); - _ = posix.dup2(stdout_pipe[1], posix.stdout); - _ = posix.close(stdout_pipe[1]); - }, - .ignore => { - _ = posix.dup2(null_fd, posix.stdout); - _ = posix.close(null_fd); - }, - .inherit => {}, - } - - switch (options.stderr) { - .pipe => { - _ = posix.close(stderr_pipe[0]); - _ = posix.dup2(stderr_pipe[1], posix.stderr); - _ = posix.close(stderr_pipe[1]); - }, - .ignore => { - _ = posix.dup2(null_fd, posix.stderr); - _ = posix.close(null_fd); - }, - .inherit => {}, - } - - const result = posix.execve(arguments[0].?, arguments, environment_variables); - if (result != 1) { - unreachable; - } - - @trap(); - }, - // Parent (~current) process - else => { - if (options.stdout == .pipe) { - _ = posix.close(stdout_pipe[1]); - } - - if (options.stderr == .pipe) { - _ = posix.close(stderr_pipe[1]); - } - - var stdout_buffer_slice: []u8 = &.{}; - var stderr_buffer_slice: []u8 = &.{}; - if (options.stdout == .pipe or options.stderr == .pipe) { - const allocation_size = 1024 * 1024; - const allocation = arena.allocate_bytes(2 * allocation_size, 1); - - var offset: u64 = 0; - if (options.stdout == .pipe) { - const stdout_buffer_offset = offset; - offset += allocation_size; - stdout_buffer_slice = allocation[stdout_buffer_offset..][0..allocation_size]; - } - - if (options.stderr == .pipe) { - const stderr_buffer_offset = offset; - offset += allocation_size; - stderr_buffer_slice = allocation[stderr_buffer_offset..][0..allocation_size]; - } - } - - if (options.stdout == .pipe) { - const byte_count = posix.read(stdout_pipe[0], stdout_buffer_slice.ptr, stdout_buffer_slice.len); - assert(byte_count >= 0); - stdout_buffer_slice = stdout_buffer_slice[0..@intCast(byte_count)]; - _ = posix.close(stdout_pipe[0]); - } - - if (options.stderr == .pipe) { - const byte_count = posix.read(stderr_pipe[0], stderr_buffer_slice.ptr, stderr_buffer_slice.len); - assert(byte_count >= 0); - stderr_buffer_slice = stderr_buffer_slice[0..@intCast(byte_count)]; - _ = posix.close(stderr_pipe[0]); - } - - var status: u32 = 0; - const waitpid_result = posix.waitpid(pid, &status, 0); - - if (waitpid_result == pid) { - const termination: struct { - code: u32, - kind: RunChildProcessResult.Kind, - } = if (linux.W.IFEXITED(status)) .{ - .code = linux.W.EXITSTATUS(status), - .kind = .exit, - } else if (linux.W.IFSIGNALED(status)) .{ - .code = linux.W.TERMSIG(status), - .kind = .signal, - } else if (linux.W.IFSTOPPED(status)) .{ - .code = linux.W.STOPSIG(status), - .kind = .stop, - } else .{ - .code = 0, - .kind = .unknown, - }; - - if (options.null_file_descriptor == null and system.fd_is_valid(null_fd)) { - _ = posix.close(null_fd); - } - - return .{ - .stdout = stdout_buffer_slice, - .stderr = stderr_buffer_slice, - .kind = termination.kind, - .code = termination.code, - }; - } else if (waitpid_result == -1) { - @trap(); - } else { - @trap(); - } - }, - } - } - - const Process = struct { - descriptor: Descriptor, - - const Descriptor = system.FileDescriptor; - }; -}; - -pub const libc = struct { - pub extern "c" fn exit(c_int) noreturn; - pub extern "c" fn memcmp(a: [*]const u8, b: [*]const u8, byte_count: usize) c_int; - pub extern "c" fn realpath(noalias path: [*:0]const u8, noalias resolved_path: [*]u8) ?[*:0]const u8; -}; - -pub const string = struct { - pub fn to_enum(comptime E: type, str: []const u8) ?E { - inline for (@typeInfo(E).@"enum".fields) |e| { - if (equal(e.name, str)) { - return @field(E, e.name); - } - } else return null; - } - - pub fn equal(a: []const u8, b: []const u8) bool { - var result = a.len == b.len; - if (result) { - result = libc.memcmp(a.ptr, b.ptr, a.len) == 0; - } - return result; - } - - pub fn first_character(str: []const u8, ch: u8) ?usize { - for (str, 0..) |existing_ch, i| { - if (ch == existing_ch) { - return i; - } - } - - return null; - } - - pub fn last_character(str: []const u8, ch: u8) ?usize { - var i = str.len; - while (i > 0) { - i -= 1; - - if (str[i] == ch) { - return i; - } - } - - return null; - } -}; - -pub const cstring = struct { - pub fn length(pointer: [*:0]const u8) usize { - var it = pointer; - while (it[0] != 0) { - it += 1; - } - - const result = it - pointer; - return result; - } - - pub fn to_slice(pointer: [*:0]const u8) [:0]const u8 { - return pointer[0..length(pointer) :0]; - } -}; - -pub const Arena = struct { - reserved_size: u64, - position: u64, - os_position: u64, - granularity: u64, - reserved: [32]u8 = [1]u8{0} ** 32, - - const minimum_position = @sizeOf(Arena); - - const Initialization = struct { - reserved_size: u64, - granularity: u64, - initial_size: u64, - }; - - pub fn initialize(initialization: Initialization) *Arena { - const protection_flags = os.ProtectionFlags{ - .read = 1, - .write = 1, - .execute = 0, - }; - const map_flags = os.MapFlags{ - .private = 1, - .anonymous = 1, - .no_reserve = 1, - .populate = 0, - }; - const arena: *Arena = @ptrCast(os.reserve(0, initialization.reserved_size, protection_flags, map_flags)); - os.commit(arena, initialization.initial_size, .{ - .read = 1, - .write = 1, - .execute = 0, - }); - - arena.* = .{ - .reserved_size = initialization.reserved_size, - .os_position = initialization.initial_size, - .position = minimum_position, - .granularity = initialization.granularity, - }; - - return arena; - } - - const default_size = 4 * GB; - const minimum_granularity = 4 * KB; - - pub fn initialize_default(initial_size: u64) *Arena { - const arena = initialize(.{ - .reserved_size = default_size, - .granularity = minimum_granularity, - .initial_size = initial_size, - }); - return arena; - } - - pub fn allocate_bytes(arena: *Arena, size: u64, alignment: u64) [*]u8 { - const aligned_offset = align_forward_u64(arena.position, alignment); - const aligned_size_after = aligned_offset + size; - - if (aligned_size_after > arena.os_position) { - const target_committed_size = align_forward_u64(aligned_size_after, arena.granularity); - const size_to_commit = target_committed_size - arena.os_position; - const commit_pointer = @as(*anyopaque, @ptrFromInt(@intFromPtr(arena) + arena.os_position)); - os.commit(commit_pointer, size_to_commit, .{ - .read = 1, - .write = 1, - .execute = 0, - }); - arena.os_position = target_committed_size; - } - - const result = @as([*]u8, @ptrFromInt(@intFromPtr(arena) + aligned_offset)); - arena.position = aligned_size_after; - assert(arena.position <= arena.os_position); - - return result; - } - - pub fn allocate(arena: *Arena, T: type, count: usize) []T { - const result = arena.allocate_bytes(@sizeOf(T) * count, @alignOf(T)); - const t_ptr: [*]T = @alignCast(@ptrCast(result)); - const t_len = count; - return t_ptr[0..t_len]; - } - - pub fn allocate_one(arena: *Arena, T: type) *T { - const result = arena.allocate(T, 1); - return &result[0]; - } - - pub fn join_string(arena: *Arena, pieces: []const []const u8) [:0]u8 { - var size: u64 = 0; - for (pieces) |piece| { - size += piece.len; - } - - const pointer = arena.allocate_bytes(size + 1, 1); - var i: u64 = 0; - for (pieces) |piece| { - @memcpy(pointer + i, piece); - i += piece.len; - } - - assert(i == size); - pointer[i] = 0; - - return pointer[0..size :0]; - } - - pub fn duplicate_string(arena: *Arena, str: []const u8) [:0]u8 { - const memory = arena.allocate_bytes(str.len + 1, 1); - @memcpy(memory, str); - memory[str.len] = 0; - return memory[0..str.len :0]; - } - - pub fn restore(arena: *Arena, position: u64) void { - assert(position <= arena.position); - const do_memset = false; - if (do_memset) { - @memset(@as([*]u8, @ptrCast(arena))[position..][0 .. arena.position - position], 0); - } - arena.position = position; - } - - pub fn reset(arena: *Arena) void { - arena.restore(minimum_position); - } -}; - -pub const string_format = struct { - pub fn integer_hexadecimal(buffer: []u8, v: u64) u8 { - var byte_count: u8 = 0; - var value = v; - - if (value) { - var reverse_buffer: [16]u8 = undefined; - var reverse_index: u8 = 0; - - while (value) : (reverse_index += 1) { - const digit_value: u8 = value % 16; - const ascii_character = if (digit_value >= 10) (digit_value + 'a' - 10) else (digit_value + '0'); - value /= 16; - reverse_buffer[reverse_index] = ascii_character; - } - - while (reverse_index != 0) : (byte_count += 1) { - reverse_index -= 1; - buffer[byte_count] = reverse_buffer[reverse_index]; - } - } else { - buffer[0] = '0'; - byte_count += 1; - } - - return byte_count; - } - - pub fn integer_decimal(buffer: []u8, v: u64) u8 { - var byte_count: u8 = 0; - var value = v; - - if (value != 0) { - var reverse_buffer: [64]u8 = undefined; - var reverse_index: u8 = 0; - - while (value != 0) : (reverse_index += 1) { - const digit_value: u8 = @intCast(value % 10); - const ascii_character = digit_value + '0'; - value /= 10; - reverse_buffer[reverse_index] = ascii_character; - } - - while (reverse_index != 0) : (byte_count += 1) { - reverse_index -= 1; - buffer[byte_count] = reverse_buffer[reverse_index]; - } - } else { - buffer[0] = '0'; - byte_count += 1; - } - - return byte_count; - } - const special_exponent = 0x7fffffff; - - /// Any buffer used for `format` must be at least this large. This is asserted. A runtime check will - /// additionally be performed if more bytes are required. - pub const min_buffer_size = 53; - - /// Returns the minimum buffer size needed to print every float of a specific type and format. - pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int { - comptime assert(@typeInfo(T) == .float); - return switch (mode) { - .scientific => 53, - // Based on minimum subnormal values. - .decimal => switch (@bitSizeOf(T)) { - 16 => @max(15, min_buffer_size), - 32 => 55, - 64 => 347, - 80 => 4996, - 128 => 5011, - else => unreachable, - }, - }; - } - - pub const FormatError = error{ - BufferTooSmall, - }; - - pub const Format = enum { - scientific, - decimal, - }; - - pub const FormatOptions = struct { - mode: Format = .scientific, - precision: ?usize = null, - }; - - /// Format a floating-point value and write it to buffer. Returns a slice to the buffer containing - /// the string representation. - /// - /// Full precision is the default. Any full precision float can be reparsed with std.fmt.parseFloat - /// unambiguously. - /// - /// Scientific mode is recommended generally as the output is more compact and any type can be - /// written in full precision using a buffer of only `min_buffer_size`. - /// - /// When printing full precision decimals, use `bufferSize` to get the required space. It is - /// recommended to bound decimal output with a fixed precision to reduce the required buffer size. - pub fn formatFloat(buf: []u8, v_: anytype, options: FormatOptions) FormatError![]const u8 { - const v = switch (@TypeOf(v_)) { - // comptime_float internally is a f128; this preserves precision. - comptime_float => @as(f128, v_), - else => v_, - }; - - const T = @TypeOf(v); - comptime assert(@typeInfo(T) == .float); - const I = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); - - const DT = if (@bitSizeOf(T) <= 64) u64 else u128; - const tables = switch (DT) { - u64 => if (@import("builtin").mode == .ReleaseSmall) &Backend64_TablesSmall else &Backend64_TablesFull, - u128 => &Backend128_Tables, - else => unreachable, - }; - - const has_explicit_leading_bit = float_mantissa_bits(T) - float_fractional_bits(T) != 0; - const d = binaryToDecimal(DT, @as(I, @bitCast(v)), float_mantissa_bits(T), float_exponent_bits(T), has_explicit_leading_bit, tables); - - return switch (options.mode) { - .scientific => formatScientific(DT, buf, d, options.precision), - .decimal => formatDecimal(DT, buf, d, options.precision), - }; - } - - pub fn FloatDecimal(comptime T: type) type { - comptime assert(T == u64 or T == u128); - return struct { - mantissa: T, - exponent: i32, - sign: bool, - }; - } - - fn copySpecialStr(buf: []u8, f: anytype) []const u8 { - if (f.sign) { - buf[0] = '-'; - } - const offset: usize = @intFromBool(f.sign); - if (f.mantissa != 0) { - @memcpy(buf[offset..][0..3], "nan"); - return buf[0 .. 3 + offset]; - } - @memcpy(buf[offset..][0..3], "inf"); - return buf[0 .. 3 + offset]; - } - - fn writeDecimal(buf: []u8, value: anytype, count: usize) void { - var i: usize = 0; - - while (i + 2 < count) : (i += 2) { - const c: u8 = @intCast(value.* % 100); - value.* /= 100; - const d = digits2(c); - buf[count - i - 1] = d[1]; - buf[count - i - 2] = d[0]; - } - - while (i < count) : (i += 1) { - const c: u8 = @intCast(value.* % 10); - value.* /= 10; - buf[count - i - 1] = '0' + c; - } - } - - fn isPowerOf10(n_: u128) bool { - var n = n_; - while (n != 0) : (n /= 10) { - if (n % 10 != 0) return false; - } - return true; - } - - const RoundMode = enum { - /// 1234.56 = precision 2 - decimal, - /// 1.23456e3 = precision 5 - scientific, - }; - - fn round(comptime T: type, f: FloatDecimal(T), mode: RoundMode, precision: usize) FloatDecimal(T) { - var round_digit: usize = 0; - var output = f.mantissa; - var exp = f.exponent; - const olength = decimalLength(output); - - switch (mode) { - .decimal => { - if (f.exponent > 0) { - round_digit = (olength - 1) + precision + @as(usize, @intCast(f.exponent)); - } else { - const min_exp_required = @as(usize, @intCast(-f.exponent)); - if (precision + olength > min_exp_required) { - round_digit = precision + olength - min_exp_required; - } - } - }, - .scientific => { - round_digit = 1 + precision; - }, - } - - if (round_digit < olength) { - var nlength = olength; - for (round_digit + 1..olength) |_| { - output /= 10; - exp += 1; - nlength -= 1; - } - - if (output % 10 >= 5) { - output /= 10; - output += 1; - exp += 1; - - // e.g. 9999 -> 10000 - if (isPowerOf10(output)) { - output /= 10; - exp += 1; - } - } - } - - return .{ - .mantissa = output, - .exponent = exp, - .sign = f.sign, - }; - } - - /// Write a FloatDecimal to a buffer in scientific form. - /// - /// The buffer provided must be greater than `min_buffer_size` in length. If no precision is - /// specified, this function will never return an error. If a precision is specified, up to - /// `8 + precision` bytes will be written to the buffer. An error will be returned if the content - /// will not fit. - /// - /// It is recommended to bound decimal formatting with an exact precision. - pub fn formatScientific(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) FormatError![]const u8 { - assert(buf.len >= min_buffer_size); - var f = f_; - - if (f.exponent == special_exponent) { - return copySpecialStr(buf, f); - } - - if (precision) |prec| { - f = round(T, f, .scientific, prec); - } - - var output = f.mantissa; - const olength = decimalLength(output); - - if (precision) |prec| { - // fixed bound: sign(1) + leading_digit(1) + point(1) + exp_sign(1) + exp_max(4) - const req_bytes = 8 + prec; - if (buf.len < req_bytes) { - return error.BufferTooSmall; - } - } - - // Step 5: Print the scientific representation - var index: usize = 0; - if (f.sign) { - buf[index] = '-'; - index += 1; - } - - // 1.12345 - writeDecimal(buf[index + 2 ..], &output, olength - 1); - buf[index] = '0' + @as(u8, @intCast(output % 10)); - buf[index + 1] = '.'; - index += 2; - const dp_index = index; - if (olength > 1) index += olength - 1 else index -= 1; - - if (precision) |prec| { - index += @intFromBool(olength == 1); - if (prec > olength - 1) { - const len = prec - (olength - 1); - @memset(buf[index..][0..len], '0'); - index += len; - } else { - index = dp_index + prec - @intFromBool(prec == 0); - } - } - - // e100 - buf[index] = 'e'; - index += 1; - var exp = f.exponent + @as(i32, @intCast(olength)) - 1; - if (exp < 0) { - buf[index] = '-'; - index += 1; - exp = -exp; - } - var uexp: u32 = @intCast(exp); - const elength = decimalLength(uexp); - writeDecimal(buf[index..], &uexp, elength); - index += elength; - - return buf[0..index]; - } - - /// Write a FloatDecimal to a buffer in decimal form. - /// - /// The buffer provided must be greater than `min_buffer_size` bytes in length. If no precision is - /// specified, this may still return an error. If precision is specified, `2 + precision` bytes will - /// always be written. - pub fn formatDecimal(comptime T: type, buf: []u8, f_: FloatDecimal(T), precision: ?usize) FormatError![]const u8 { - assert(buf.len >= min_buffer_size); - var f = f_; - - if (f.exponent == special_exponent) { - return copySpecialStr(buf, f); - } - - if (precision) |prec| { - f = round(T, f, .decimal, prec); - } - - var output = f.mantissa; - const olength = decimalLength(output); - - // fixed bound: leading_digit(1) + point(1) - const req_bytes = if (f.exponent >= 0) - @as(usize, 2) + @abs(f.exponent) + olength + (precision orelse 0) - else - @as(usize, 2) + @max(@abs(f.exponent) + olength, precision orelse 0); - if (buf.len < req_bytes) { - return error.BufferTooSmall; - } - - // Step 5: Print the decimal representation - var index: usize = 0; - if (f.sign) { - buf[index] = '-'; - index += 1; - } - - const dp_offset = f.exponent + cast_i32(olength); - if (dp_offset <= 0) { - // 0.000001234 - buf[index] = '0'; - buf[index + 1] = '.'; - index += 2; - const dp_index = index; - - const dp_poffset: u32 = @intCast(-dp_offset); - @memset(buf[index..][0..dp_poffset], '0'); - index += dp_poffset; - writeDecimal(buf[index..], &output, olength); - index += olength; - - if (precision) |prec| { - const dp_written = index - dp_index; - if (prec > dp_written) { - @memset(buf[index..][0 .. prec - dp_written], '0'); - } - index = dp_index + prec - @intFromBool(prec == 0); - } - } else { - // 123456000 - const dp_uoffset: usize = @intCast(dp_offset); - if (dp_uoffset >= olength) { - writeDecimal(buf[index..], &output, olength); - index += olength; - @memset(buf[index..][0 .. dp_uoffset - olength], '0'); - index += dp_uoffset - olength; - - if (precision) |prec| { - if (prec != 0) { - buf[index] = '.'; - index += 1; - @memset(buf[index..][0..prec], '0'); - index += prec; - } - } - } else { - // 12345.6789 - writeDecimal(buf[index + dp_uoffset + 1 ..], &output, olength - dp_uoffset); - buf[index + dp_uoffset] = '.'; - const dp_index = index + dp_uoffset + 1; - writeDecimal(buf[index..], &output, dp_uoffset); - index += olength + 1; - - if (precision) |prec| { - const dp_written = olength - dp_uoffset; - if (prec > dp_written) { - @memset(buf[index..][0 .. prec - dp_written], '0'); - } - index = dp_index + prec - @intFromBool(prec == 0); - } - } - } - - return buf[0..index]; - } - - fn cast_i32(v: anytype) i32 { - return @intCast(v); - } - - /// Convert a binary float representation to decimal. - pub fn binaryToDecimal(comptime T: type, bits: T, mantissa_bits: Log2Int(T), exponent_bits: u5, explicit_leading_bit: bool, comptime tables: anytype) FloatDecimal(T) { - if (T != tables.T) { - @compileError("table type does not match backend type: " ++ @typeName(tables.T) ++ " != " ++ @typeName(T)); - } - - const bias = (@as(u32, 1) << (exponent_bits - 1)) - 1; - const ieee_sign = ((bits >> (mantissa_bits + exponent_bits)) & 1) != 0; - const ieee_mantissa = bits & ((@as(T, 1) << mantissa_bits) - 1); - const ieee_exponent: u32 = @intCast((bits >> mantissa_bits) & ((@as(T, 1) << exponent_bits) - 1)); - - if (ieee_exponent == 0 and ieee_mantissa == 0) { - return .{ - .mantissa = 0, - .exponent = 0, - .sign = ieee_sign, - }; - } - if (ieee_exponent == ((@as(u32, 1) << exponent_bits) - 1)) { - return .{ - .mantissa = if (explicit_leading_bit) ieee_mantissa & ((@as(T, 1) << (mantissa_bits - 1)) - 1) else ieee_mantissa, - .exponent = special_exponent, - .sign = ieee_sign, - }; - } - - var e2: i32 = undefined; - var m2: T = undefined; - if (explicit_leading_bit) { - if (ieee_exponent == 0) { - e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; - } else { - e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) + 1 - 2; - } - m2 = ieee_mantissa; - } else { - if (ieee_exponent == 0) { - e2 = 1 - cast_i32(bias) - cast_i32(mantissa_bits) - 2; - m2 = ieee_mantissa; - } else { - e2 = cast_i32(ieee_exponent) - cast_i32(bias) - cast_i32(mantissa_bits) - 2; - m2 = (@as(T, 1) << mantissa_bits) | ieee_mantissa; - } - } - const even = (m2 & 1) == 0; - const accept_bounds = even; - - // Step 2: Determine the interval of legal decimal representations. - const mv = 4 * m2; - const mm_shift: u1 = @intFromBool((ieee_mantissa != if (explicit_leading_bit) (@as(T, 1) << (mantissa_bits - 1)) else 0) or (ieee_exponent == 0)); - - // Step 3: Convert to a decimal power base using 128-bit arithmetic. - var vr: T = undefined; - var vp: T = undefined; - var vm: T = undefined; - var e10: i32 = undefined; - var vm_is_trailing_zeros = false; - var vr_is_trailing_zeros = false; - if (e2 >= 0) { - const q: u32 = log10Pow2(@intCast(e2)) - @intFromBool(e2 > 3); - e10 = cast_i32(q); - const k: i32 = @intCast(tables.POW5_INV_BITCOUNT + pow5Bits(q) - 1); - const i: u32 = @intCast(-e2 + cast_i32(q) + k); - - const pow5 = tables.computeInvPow5(q); - vr = tables.mulShift(4 * m2, &pow5, i); - vp = tables.mulShift(4 * m2 + 2, &pow5, i); - vm = tables.mulShift(4 * m2 - 1 - mm_shift, &pow5, i); - - if (q <= tables.bound1) { - if (mv % 5 == 0) { - vr_is_trailing_zeros = multipleOfPowerOf5(mv, if (tables.adjust_q) q -% 1 else q); - } else if (accept_bounds) { - vm_is_trailing_zeros = multipleOfPowerOf5(mv - 1 - mm_shift, q); - } else { - vp -= @intFromBool(multipleOfPowerOf5(mv + 2, q)); - } - } - } else { - const q: u32 = log10Pow5(@intCast(-e2)) - @intFromBool(-e2 > 1); - e10 = cast_i32(q) + e2; - const i: i32 = -e2 - cast_i32(q); - const k: i32 = cast_i32(pow5Bits(@intCast(i))) - tables.POW5_BITCOUNT; - const j: u32 = @intCast(cast_i32(q) - k); - - const pow5 = tables.computePow5(@intCast(i)); - vr = tables.mulShift(4 * m2, &pow5, j); - vp = tables.mulShift(4 * m2 + 2, &pow5, j); - vm = tables.mulShift(4 * m2 - 1 - mm_shift, &pow5, j); - - if (q <= 1) { - vr_is_trailing_zeros = true; - if (accept_bounds) { - vm_is_trailing_zeros = mm_shift == 1; - } else { - vp -= 1; - } - } else if (q < tables.bound2) { - vr_is_trailing_zeros = multipleOfPowerOf2(mv, if (tables.adjust_q) q - 1 else q); - } - } - - // Step 4: Find the shortest decimal representation in the interval of legal representations. - var removed: u32 = 0; - var last_removed_digit: u8 = 0; - - while (vp / 10 > vm / 10) { - vm_is_trailing_zeros = vm_is_trailing_zeros and vm % 10 == 0; - vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; - last_removed_digit = @intCast(vr % 10); - vr /= 10; - vp /= 10; - vm /= 10; - removed += 1; - } - - if (vm_is_trailing_zeros) { - while (vm % 10 == 0) { - vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; - last_removed_digit = @intCast(vr % 10); - vr /= 10; - vp /= 10; - vm /= 10; - removed += 1; - } - } - - if (vr_is_trailing_zeros and (last_removed_digit == 5) and (vr % 2 == 0)) { - last_removed_digit = 4; - } - - return .{ - .mantissa = vr + @intFromBool((vr == vm and (!accept_bounds or !vm_is_trailing_zeros)) or last_removed_digit >= 5), - .exponent = e10 + cast_i32(removed), - .sign = ieee_sign, - }; - } - - fn decimalLength(v: anytype) u32 { - switch (@TypeOf(v)) { - u32, u64 => { - assert(v < 100000000000000000); - if (v >= 10000000000000000) return 17; - if (v >= 1000000000000000) return 16; - if (v >= 100000000000000) return 15; - if (v >= 10000000000000) return 14; - if (v >= 1000000000000) return 13; - if (v >= 100000000000) return 12; - if (v >= 10000000000) return 11; - if (v >= 1000000000) return 10; - if (v >= 100000000) return 9; - if (v >= 10000000) return 8; - if (v >= 1000000) return 7; - if (v >= 100000) return 6; - if (v >= 10000) return 5; - if (v >= 1000) return 4; - if (v >= 100) return 3; - if (v >= 10) return 2; - return 1; - }, - u128 => { - const LARGEST_POW10 = (@as(u128, 5421010862427522170) << 64) | 687399551400673280; - var p10 = LARGEST_POW10; - var i: u32 = 39; - while (i > 0) : (i -= 1) { - if (v >= p10) return i; - p10 /= 10; - } - return 1; - }, - else => unreachable, - } - } - - // floor(log_10(2^e)) - fn log10Pow2(e: u32) u32 { - assert(e <= 1 << 15); - return @intCast((@as(u64, @intCast(e)) * 169464822037455) >> 49); - } - - // floor(log_10(5^e)) - fn log10Pow5(e: u32) u32 { - assert(e <= 1 << 15); - return @intCast((@as(u64, @intCast(e)) * 196742565691928) >> 48); - } - - // if (e == 0) 1 else ceil(log_2(5^e)) - fn pow5Bits(e: u32) u32 { - assert(e <= 1 << 15); - return @intCast(((@as(u64, @intCast(e)) * 163391164108059) >> 46) + 1); - } - - fn pow5Factor(value_: anytype) u32 { - var count: u32 = 0; - var value = value_; - while (value > 0) : ({ - count += 1; - value /= 5; - }) { - if (value % 5 != 0) return count; - } - return 0; - } - - fn multipleOfPowerOf5(value: anytype, p: u32) bool { - const T = @TypeOf(value); - assert(@typeInfo(T) == .int); - return pow5Factor(value) >= p; - } - - fn multipleOfPowerOf2(value: anytype, p: u32) bool { - const T = @TypeOf(value); - assert(@typeInfo(T) == .int); - return (value & ((@as(T, 1) << @as(Log2Int(T), @intCast(p))) - 1)) == 0; - } - - fn mulShift128(m: u128, mul: *const [4]u64, j: u32) u128 { - assert(j > 128); - const a: [2]u64 = .{ @truncate(m), @truncate(m >> 64) }; - const r = mul_128_256_shift(&a, mul, j, 0); - return (@as(u128, r[1]) << 64) | r[0]; - } - - fn mul_128_256_shift(a: *const [2]u64, b: *const [4]u64, shift: u32, corr: u32) [4]u64 { - assert(shift > 0); - assert(shift < 256); - - const b00 = @as(u128, a[0]) * b[0]; - const b01 = @as(u128, a[0]) * b[1]; - const b02 = @as(u128, a[0]) * b[2]; - const b03 = @as(u128, a[0]) * b[3]; - const b10 = @as(u128, a[1]) * b[0]; - const b11 = @as(u128, a[1]) * b[1]; - const b12 = @as(u128, a[1]) * b[2]; - const b13 = @as(u128, a[1]) * b[3]; - - const s0 = b00; - const s1 = b01 +% b10; - const c1: u128 = @intFromBool(s1 < b01); - const s2 = b02 +% b11; - const c2: u128 = @intFromBool(s2 < b02); - const s3 = b03 +% b12; - const c3: u128 = @intFromBool(s3 < b03); - - const p0 = s0 +% (s1 << 64); - const d0: u128 = @intFromBool(p0 < b00); - const q1 = s2 +% (s1 >> 64) +% (s3 << 64); - const d1: u128 = @intFromBool(q1 < s2); - const p1 = q1 +% (c1 << 64) +% d0; - const d2: u128 = @intFromBool(p1 < q1); - const p2 = b13 +% (s3 >> 64) +% c2 +% (c3 << 64) +% d1 +% d2; - - var r0: u128 = undefined; - var r1: u128 = undefined; - if (shift < 128) { - const cshift: u7 = @intCast(shift); - const sshift: u7 = @intCast(128 - shift); - r0 = corr +% ((p0 >> cshift) | (p1 << sshift)); - r1 = ((p1 >> cshift) | (p2 << sshift)) +% @intFromBool(r0 < corr); - } else if (shift == 128) { - r0 = corr +% p1; - r1 = p2 +% @intFromBool(r0 < corr); - } else { - const ashift: u7 = @intCast(shift - 128); - const sshift: u7 = @intCast(256 - shift); - r0 = corr +% ((p1 >> ashift) | (p2 << sshift)); - r1 = (p2 >> ashift) +% @intFromBool(r0 < corr); - } - - return .{ @truncate(r0), @truncate(r0 >> 64), @truncate(r1), @truncate(r1 >> 64) }; - } - - pub const Backend128_Tables = struct { - const T = u128; - const mulShift = mulShift128; - const POW5_INV_BITCOUNT = FLOAT128_POW5_INV_BITCOUNT; - const POW5_BITCOUNT = FLOAT128_POW5_BITCOUNT; - - const bound1 = 55; - const bound2 = 127; - const adjust_q = true; - - fn computePow5(i: u32) [4]u64 { - const base = i / FLOAT128_POW5_TABLE_SIZE; - const base2 = base * FLOAT128_POW5_TABLE_SIZE; - const mul = &FLOAT128_POW5_SPLIT[base]; - if (i == base2) { - return mul.*; - } else { - const offset = i - base2; - const m = &FLOAT128_POW5_TABLE[offset]; - const delta = pow5Bits(i) - pow5Bits(base2); - - const shift: u6 = @intCast(2 * (i % 32)); - const corr: u32 = @intCast((FLOAT128_POW5_ERRORS[i / 32] >> shift) & 3); - return mul_128_256_shift(m, mul, delta, corr); - } - } - - fn computeInvPow5(i: u32) [4]u64 { - const base = (i + FLOAT128_POW5_TABLE_SIZE - 1) / FLOAT128_POW5_TABLE_SIZE; - const base2 = base * FLOAT128_POW5_TABLE_SIZE; - const mul = &FLOAT128_POW5_INV_SPLIT[base]; // 1 / 5^base2 - if (i == base2) { - return .{ mul[0] + 1, mul[1], mul[2], mul[3] }; - } else { - const offset = base2 - i; - const m = &FLOAT128_POW5_TABLE[offset]; // 5^offset - const delta = pow5Bits(base2) - pow5Bits(i); - - const shift: u6 = @intCast(2 * (i % 32)); - const corr: u32 = @intCast(((FLOAT128_POW5_INV_ERRORS[i / 32] >> shift) & 3) + 1); - return mul_128_256_shift(m, mul, delta, corr); - } - } - }; - - fn mulShift64(m: u64, mul: *const [2]u64, j: u32) u64 { - assert(j > 64); - const b0 = @as(u128, m) * mul[0]; - const b2 = @as(u128, m) * mul[1]; - - if (j < 128) { - const shift: u6 = @intCast(j - 64); - return @intCast(((b0 >> 64) + b2) >> shift); - } else { - return 0; - } - } - - pub const Backend64_TablesFull = struct { - const T = u64; - const mulShift = mulShift64; - const POW5_INV_BITCOUNT = FLOAT64_POW5_INV_BITCOUNT; - const POW5_BITCOUNT = FLOAT64_POW5_BITCOUNT; - - const bound1 = 21; - const bound2 = 63; - const adjust_q = false; - - fn computePow5(i: u32) [2]u64 { - return FLOAT64_POW5_SPLIT[i]; - } - - fn computeInvPow5(i: u32) [2]u64 { - return FLOAT64_POW5_INV_SPLIT[i]; - } - }; - - pub const Backend64_TablesSmall = struct { - const T = u64; - const mulShift = mulShift64; - const POW5_INV_BITCOUNT = FLOAT64_POW5_INV_BITCOUNT; - const POW5_BITCOUNT = FLOAT64_POW5_BITCOUNT; - - const bound1 = 21; - const bound2 = 63; - const adjust_q = false; - - fn computePow5(i: u32) [2]u64 { - const base = i / FLOAT64_POW5_TABLE_SIZE; - const base2 = base * FLOAT64_POW5_TABLE_SIZE; - const mul = &FLOAT64_POW5_SPLIT2[base]; - if (i == base2) { - return .{ mul[0], mul[1] }; - } else { - const offset = i - base2; - const m = FLOAT64_POW5_TABLE[offset]; - const b0 = @as(u128, m) * mul[0]; - const b2 = @as(u128, m) * mul[1]; - const delta: u7 = @intCast(pow5Bits(i) - pow5Bits(base2)); - const shift: u5 = @intCast((i % 16) << 1); - const shifted_sum = ((b0 >> delta) + (b2 << (64 - delta))) + 1 + ((FLOAT64_POW5_OFFSETS[i / 16] >> shift) & 3); - return .{ @truncate(shifted_sum), @truncate(shifted_sum >> 64) }; - } - } - - fn computeInvPow5(i: u32) [2]u64 { - const base = (i + FLOAT64_POW5_TABLE_SIZE - 1) / FLOAT64_POW5_TABLE_SIZE; - const base2 = base * FLOAT64_POW5_TABLE_SIZE; - const mul = &FLOAT64_POW5_INV_SPLIT2[base]; // 1 / 5^base2 - if (i == base2) { - return .{ mul[0], mul[1] }; - } else { - const offset = base2 - i; - const m = FLOAT64_POW5_TABLE[offset]; // 5^offset - const b0 = @as(u128, m) * (mul[0] - 1); - const b2 = @as(u128, m) * mul[1]; // 1/5^base2 * 5^offset = 1/5^(base2-offset) = 1/5^i - const delta: u7 = @intCast(pow5Bits(base2) - pow5Bits(i)); - const shift: u5 = @intCast((i % 16) << 1); - const shifted_sum = ((b0 >> delta) + (b2 << (64 - delta))) + 1 + ((FLOAT64_POW5_INV_OFFSETS[i / 16] >> shift) & 3); - return .{ @truncate(shifted_sum), @truncate(shifted_sum >> 64) }; - } - } - }; - - const FLOAT64_POW5_INV_BITCOUNT = 125; - const FLOAT64_POW5_BITCOUNT = 125; - - // zig fmt: off - // - // f64 small tables: 816 bytes - - const FLOAT64_POW5_TABLE_SIZE: comptime_int = FLOAT64_POW5_TABLE.len; - - const FLOAT64_POW5_TABLE: [26]u64 = .{ - 1, 5, - 25, 125, - 625, 3125, - 15625, 78125, - 390625, 1953125, - 9765625, 48828125, - 244140625, 1220703125, - 6103515625, 30517578125, - 152587890625, 762939453125, - 3814697265625, 19073486328125, - 95367431640625, 476837158203125, - 2384185791015625, 11920928955078125, - 59604644775390625, 298023223876953125, - }; - - const FLOAT64_POW5_SPLIT2: [13][2]u64 = .{ - .{ 0, 1152921504606846976 }, - .{ 0, 1490116119384765625 }, - .{ 1032610780636961552, 1925929944387235853 }, - .{ 7910200175544436838, 1244603055572228341 }, - .{ 16941905809032713930, 1608611746708759036 }, - .{ 13024893955298202172, 2079081953128979843 }, - .{ 6607496772837067824, 1343575221513417750 }, - .{ 17332926989895652603, 1736530273035216783 }, - .{ 13037379183483547984, 2244412773384604712 }, - .{ 1605989338741628675, 1450417759929778918 }, - .{ 9630225068416591280, 1874621017369538693 }, - .{ 665883850346957067, 1211445438634777304 }, - .{ 14931890668723713708, 1565756531257009982 } - }; - - const FLOAT64_POW5_OFFSETS: [21]u32 = .{ - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40000000, 0x59695995, 0x55545555, 0x56555515, - 0x41150504, 0x40555410, 0x44555145, 0x44504540, - 0x45555550, 0x40004000, 0x96440440, 0x55565565, - 0x54454045, 0x40154151, 0x55559155, 0x51405555, - 0x00000105, - }; - - const FLOAT64_POW5_INV_SPLIT2: [15][2]u64 = .{ - .{ 1, 2305843009213693952 }, - .{ 5955668970331000884, 1784059615882449851 }, - .{ 8982663654677661702, 1380349269358112757 }, - .{ 7286864317269821294, 2135987035920910082 }, - .{ 7005857020398200553, 1652639921975621497 }, - .{ 17965325103354776697, 1278668206209430417 }, - .{ 8928596168509315048, 1978643211784836272 }, - .{ 10075671573058298858, 1530901034580419511 }, - .{ 597001226353042382, 1184477304306571148 }, - .{ 1527430471115325346, 1832889850782397517 }, - .{ 12533209867169019542, 1418129833677084982 }, - .{ 5577825024675947042, 2194449627517475473 }, - .{ 11006974540203867551, 1697873161311732311 }, - .{ 10313493231639821582, 1313665730009899186 }, - .{ 12701016819766672773, 2032799256770390445 } - }; - - const FLOAT64_POW5_INV_OFFSETS: [19]u32 = .{ - 0x54544554, 0x04055545, 0x10041000, 0x00400414, - 0x40010000, 0x41155555, 0x00000454, 0x00010044, - 0x40000000, 0x44000041, 0x50454450, 0x55550054, - 0x51655554, 0x40004000, 0x01000001, 0x00010500, - 0x51515411, 0x05555554, 0x00000000, - }; - - - // zig fmt: off - - // f64 full tables: 10688 bytes - - const FLOAT64_POW5_SPLIT: [326][2]u64 = .{ - .{ 0, 1152921504606846976 }, .{ 0, 1441151880758558720 }, - .{ 0, 1801439850948198400 }, .{ 0, 2251799813685248000 }, - .{ 0, 1407374883553280000 }, .{ 0, 1759218604441600000 }, - .{ 0, 2199023255552000000 }, .{ 0, 1374389534720000000 }, - .{ 0, 1717986918400000000 }, .{ 0, 2147483648000000000 }, - .{ 0, 1342177280000000000 }, .{ 0, 1677721600000000000 }, - .{ 0, 2097152000000000000 }, .{ 0, 1310720000000000000 }, - .{ 0, 1638400000000000000 }, .{ 0, 2048000000000000000 }, - .{ 0, 1280000000000000000 }, .{ 0, 1600000000000000000 }, - .{ 0, 2000000000000000000 }, .{ 0, 1250000000000000000 }, - .{ 0, 1562500000000000000 }, .{ 0, 1953125000000000000 }, - .{ 0, 1220703125000000000 }, .{ 0, 1525878906250000000 }, - .{ 0, 1907348632812500000 }, .{ 0, 1192092895507812500 }, - .{ 0, 1490116119384765625 }, .{ 4611686018427387904, 1862645149230957031 }, - .{ 9799832789158199296, 1164153218269348144 }, .{ 12249790986447749120, 1455191522836685180 }, - .{ 15312238733059686400, 1818989403545856475 }, .{ 14528612397897220096, 2273736754432320594 }, - .{ 13692068767113150464, 1421085471520200371 }, .{ 12503399940464050176, 1776356839400250464 }, - .{ 15629249925580062720, 2220446049250313080 }, .{ 9768281203487539200, 1387778780781445675 }, - .{ 7598665485932036096, 1734723475976807094 }, .{ 274959820560269312, 2168404344971008868 }, - .{ 9395221924704944128, 1355252715606880542 }, .{ 2520655369026404352, 1694065894508600678 }, - .{ 12374191248137781248, 2117582368135750847 }, .{ 14651398557727195136, 1323488980084844279 }, - .{ 13702562178731606016, 1654361225106055349 }, .{ 3293144668132343808, 2067951531382569187 }, - .{ 18199116482078572544, 1292469707114105741 }, .{ 8913837547316051968, 1615587133892632177 }, - .{ 15753982952572452864, 2019483917365790221 }, .{ 12152082354571476992, 1262177448353618888 }, - .{ 15190102943214346240, 1577721810442023610 }, .{ 9764256642163156992, 1972152263052529513 }, - .{ 17631875447420442880, 1232595164407830945 }, .{ 8204786253993389888, 1540743955509788682 }, - .{ 1032610780636961552, 1925929944387235853 }, .{ 2951224747111794922, 1203706215242022408 }, - .{ 3689030933889743652, 1504632769052528010 }, .{ 13834660704216955373, 1880790961315660012 }, - .{ 17870034976990372916, 1175494350822287507 }, .{ 17725857702810578241, 1469367938527859384 }, - .{ 3710578054803671186, 1836709923159824231 }, .{ 26536550077201078, 2295887403949780289 }, - .{ 11545800389866720434, 1434929627468612680 }, .{ 14432250487333400542, 1793662034335765850 }, - .{ 8816941072311974870, 2242077542919707313 }, .{ 17039803216263454053, 1401298464324817070 }, - .{ 12076381983474541759, 1751623080406021338 }, .{ 5872105442488401391, 2189528850507526673 }, - .{ 15199280947623720629, 1368455531567204170 }, .{ 9775729147674874978, 1710569414459005213 }, - .{ 16831347453020981627, 2138211768073756516 }, .{ 1296220121283337709, 1336382355046097823 }, - .{ 15455333206886335848, 1670477943807622278 }, .{ 10095794471753144002, 2088097429759527848 }, - .{ 6309871544845715001, 1305060893599704905 }, .{ 12499025449484531656, 1631326116999631131 }, - .{ 11012095793428276666, 2039157646249538914 }, .{ 11494245889320060820, 1274473528905961821 }, - .{ 532749306367912313, 1593091911132452277 }, .{ 5277622651387278295, 1991364888915565346 }, - .{ 7910200175544436838, 1244603055572228341 }, .{ 14499436237857933952, 1555753819465285426 }, - .{ 8900923260467641632, 1944692274331606783 }, .{ 12480606065433357876, 1215432671457254239 }, - .{ 10989071563364309441, 1519290839321567799 }, .{ 9124653435777998898, 1899113549151959749 }, - .{ 8008751406574943263, 1186945968219974843 }, .{ 5399253239791291175, 1483682460274968554 }, - .{ 15972438586593889776, 1854603075343710692 }, .{ 759402079766405302, 1159126922089819183 }, - .{ 14784310654990170340, 1448908652612273978 }, .{ 9257016281882937117, 1811135815765342473 }, - .{ 16182956370781059300, 2263919769706678091 }, .{ 7808504722524468110, 1414949856066673807 }, - .{ 5148944884728197234, 1768687320083342259 }, .{ 1824495087482858639, 2210859150104177824 }, - .{ 1140309429676786649, 1381786968815111140 }, .{ 1425386787095983311, 1727233711018888925 }, - .{ 6393419502297367043, 2159042138773611156 }, .{ 13219259225790630210, 1349401336733506972 }, - .{ 16524074032238287762, 1686751670916883715 }, .{ 16043406521870471799, 2108439588646104644 }, - .{ 803757039314269066, 1317774742903815403 }, .{ 14839754354425000045, 1647218428629769253 }, - .{ 4714634887749086344, 2059023035787211567 }, .{ 9864175832484260821, 1286889397367007229 }, - .{ 16941905809032713930, 1608611746708759036 }, .{ 2730638187581340797, 2010764683385948796 }, - .{ 10930020904093113806, 1256727927116217997 }, .{ 18274212148543780162, 1570909908895272496 }, - .{ 4396021111970173586, 1963637386119090621 }, .{ 5053356204195052443, 1227273366324431638 }, - .{ 15540067292098591362, 1534091707905539547 }, .{ 14813398096695851299, 1917614634881924434 }, - .{ 13870059828862294966, 1198509146801202771 }, .{ 12725888767650480803, 1498136433501503464 }, - .{ 15907360959563101004, 1872670541876879330 }, .{ 14553786618154326031, 1170419088673049581 }, - .{ 4357175217410743827, 1463023860841311977 }, .{ 10058155040190817688, 1828779826051639971 }, - .{ 7961007781811134206, 2285974782564549964 }, .{ 14199001900486734687, 1428734239102843727 }, - .{ 13137066357181030455, 1785917798878554659 }, .{ 11809646928048900164, 2232397248598193324 }, - .{ 16604401366885338411, 1395248280373870827 }, .{ 16143815690179285109, 1744060350467338534 }, - .{ 10956397575869330579, 2180075438084173168 }, .{ 6847748484918331612, 1362547148802608230 }, - .{ 17783057643002690323, 1703183936003260287 }, .{ 17617136035325974999, 2128979920004075359 }, - .{ 17928239049719816230, 1330612450002547099 }, .{ 17798612793722382384, 1663265562503183874 }, - .{ 13024893955298202172, 2079081953128979843 }, .{ 5834715712847682405, 1299426220705612402 }, - .{ 16516766677914378815, 1624282775882015502 }, .{ 11422586310538197711, 2030353469852519378 }, - .{ 11750802462513761473, 1268970918657824611 }, .{ 10076817059714813937, 1586213648322280764 }, - .{ 12596021324643517422, 1982767060402850955 }, .{ 5566670318688504437, 1239229412751781847 }, - .{ 2346651879933242642, 1549036765939727309 }, .{ 7545000868343941206, 1936295957424659136 }, - .{ 4715625542714963254, 1210184973390411960 }, .{ 5894531928393704067, 1512731216738014950 }, - .{ 16591536947346905892, 1890914020922518687 }, .{ 17287239619732898039, 1181821263076574179 }, - .{ 16997363506238734644, 1477276578845717724 }, .{ 2799960309088866689, 1846595723557147156 }, - .{ 10973347230035317489, 1154122327223216972 }, .{ 13716684037544146861, 1442652909029021215 }, - .{ 12534169028502795672, 1803316136286276519 }, .{ 11056025267201106687, 2254145170357845649 }, - .{ 18439230838069161439, 1408840731473653530 }, .{ 13825666510731675991, 1761050914342066913 }, - .{ 3447025083132431277, 2201313642927583642 }, .{ 6766076695385157452, 1375821026829739776 }, - .{ 8457595869231446815, 1719776283537174720 }, .{ 10571994836539308519, 2149720354421468400 }, - .{ 6607496772837067824, 1343575221513417750 }, .{ 17482743002901110588, 1679469026891772187 }, - .{ 17241742735199000331, 2099336283614715234 }, .{ 15387775227926763111, 1312085177259197021 }, - .{ 5399660979626290177, 1640106471573996277 }, .{ 11361262242960250625, 2050133089467495346 }, - .{ 11712474920277544544, 1281333180917184591 }, .{ 10028907631919542777, 1601666476146480739 }, - .{ 7924448521472040567, 2002083095183100924 }, .{ 14176152362774801162, 1251301934489438077 }, - .{ 3885132398186337741, 1564127418111797597 }, .{ 9468101516160310080, 1955159272639746996 }, - .{ 15140935484454969608, 1221974545399841872 }, .{ 479425281859160394, 1527468181749802341 }, - .{ 5210967620751338397, 1909335227187252926 }, .{ 17091912818251750210, 1193334516992033078 }, - .{ 12141518985959911954, 1491668146240041348 }, .{ 15176898732449889943, 1864585182800051685 }, - .{ 11791404716994875166, 1165365739250032303 }, .{ 10127569877816206054, 1456707174062540379 }, - .{ 8047776328842869663, 1820883967578175474 }, .{ 836348374198811271, 2276104959472719343 }, - .{ 7440246761515338900, 1422565599670449589 }, .{ 13911994470321561530, 1778206999588061986 }, - .{ 8166621051047176104, 2222758749485077483 }, .{ 2798295147690791113, 1389224218428173427 }, - .{ 17332926989895652603, 1736530273035216783 }, .{ 17054472718942177850, 2170662841294020979 }, - .{ 8353202440125167204, 1356664275808763112 }, .{ 10441503050156459005, 1695830344760953890 }, - .{ 3828506775840797949, 2119787930951192363 }, .{ 86973725686804766, 1324867456844495227 }, - .{ 13943775212390669669, 1656084321055619033 }, .{ 3594660960206173375, 2070105401319523792 }, - .{ 2246663100128858359, 1293815875824702370 }, .{ 12031700912015848757, 1617269844780877962 }, - .{ 5816254103165035138, 2021587305976097453 }, .{ 5941001823691840913, 1263492066235060908 }, - .{ 7426252279614801142, 1579365082793826135 }, .{ 4671129331091113523, 1974206353492282669 }, - .{ 5225298841145639904, 1233878970932676668 }, .{ 6531623551432049880, 1542348713665845835 }, - .{ 3552843420862674446, 1927935892082307294 }, .{ 16055585193321335241, 1204959932551442058 }, - .{ 10846109454796893243, 1506199915689302573 }, .{ 18169322836923504458, 1882749894611628216 }, - .{ 11355826773077190286, 1176718684132267635 }, .{ 9583097447919099954, 1470898355165334544 }, - .{ 11978871809898874942, 1838622943956668180 }, .{ 14973589762373593678, 2298278679945835225 }, - .{ 2440964573842414192, 1436424174966147016 }, .{ 3051205717303017741, 1795530218707683770 }, - .{ 13037379183483547984, 2244412773384604712 }, .{ 8148361989677217490, 1402757983365377945 }, - .{ 14797138505523909766, 1753447479206722431 }, .{ 13884737113477499304, 2191809349008403039 }, - .{ 15595489723564518921, 1369880843130251899 }, .{ 14882676136028260747, 1712351053912814874 }, - .{ 9379973133180550126, 2140438817391018593 }, .{ 17391698254306313589, 1337774260869386620 }, - .{ 3292878744173340370, 1672217826086733276 }, .{ 4116098430216675462, 2090272282608416595 }, - .{ 266718509671728212, 1306420176630260372 }, .{ 333398137089660265, 1633025220787825465 }, - .{ 5028433689789463235, 2041281525984781831 }, .{ 10060300083759496378, 1275800953740488644 }, - .{ 12575375104699370472, 1594751192175610805 }, .{ 1884160825592049379, 1993438990219513507 }, - .{ 17318501580490888525, 1245899368887195941 }, .{ 7813068920331446945, 1557374211108994927 }, - .{ 5154650131986920777, 1946717763886243659 }, .{ 915813323278131534, 1216698602428902287 }, - .{ 14979824709379828129, 1520873253036127858 }, .{ 9501408849870009354, 1901091566295159823 }, - .{ 12855909558809837702, 1188182228934474889 }, .{ 2234828893230133415, 1485227786168093612 }, - .{ 2793536116537666769, 1856534732710117015 }, .{ 8663489100477123587, 1160334207943823134 }, - .{ 1605989338741628675, 1450417759929778918 }, .{ 11230858710281811652, 1813022199912223647 }, - .{ 9426887369424876662, 2266277749890279559 }, .{ 12809333633531629769, 1416423593681424724 }, - .{ 16011667041914537212, 1770529492101780905 }, .{ 6179525747111007803, 2213161865127226132 }, - .{ 13085575628799155685, 1383226165704516332 }, .{ 16356969535998944606, 1729032707130645415 }, - .{ 15834525901571292854, 2161290883913306769 }, .{ 2979049660840976177, 1350806802445816731 }, - .{ 17558870131333383934, 1688508503057270913 }, .{ 8113529608884566205, 2110635628821588642 }, - .{ 9682642023980241782, 1319147268013492901 }, .{ 16714988548402690132, 1648934085016866126 }, - .{ 11670363648648586857, 2061167606271082658 }, .{ 11905663298832754689, 1288229753919426661 }, - .{ 1047021068258779650, 1610287192399283327 }, .{ 15143834390605638274, 2012858990499104158 }, - .{ 4853210475701136017, 1258036869061940099 }, .{ 1454827076199032118, 1572546086327425124 }, - .{ 1818533845248790147, 1965682607909281405 }, .{ 3442426662494187794, 1228551629943300878 }, - .{ 13526405364972510550, 1535689537429126097 }, .{ 3072948650933474476, 1919611921786407622 }, - .{ 15755650962115585259, 1199757451116504763 }, .{ 15082877684217093670, 1499696813895630954 }, - .{ 9630225068416591280, 1874621017369538693 }, .{ 8324733676974063502, 1171638135855961683 }, - .{ 5794231077790191473, 1464547669819952104 }, .{ 7242788847237739342, 1830684587274940130 }, - .{ 18276858095901949986, 2288355734093675162 }, .{ 16034722328366106645, 1430222333808546976 }, - .{ 1596658836748081690, 1787777917260683721 }, .{ 6607509564362490017, 2234722396575854651 }, - .{ 1823850468512862308, 1396701497859909157 }, .{ 6891499104068465790, 1745876872324886446 }, - .{ 17837745916940358045, 2182346090406108057 }, .{ 4231062170446641922, 1363966306503817536 }, - .{ 5288827713058302403, 1704957883129771920 }, .{ 6611034641322878003, 2131197353912214900 }, - .{ 13355268687681574560, 1331998346195134312 }, .{ 16694085859601968200, 1664997932743917890 }, - .{ 11644235287647684442, 2081247415929897363 }, .{ 4971804045566108824, 1300779634956185852 }, - .{ 6214755056957636030, 1625974543695232315 }, .{ 3156757802769657134, 2032468179619040394 }, - .{ 6584659645158423613, 1270292612261900246 }, .{ 17454196593302805324, 1587865765327375307 }, - .{ 17206059723201118751, 1984832206659219134 }, .{ 6142101308573311315, 1240520129162011959 }, - .{ 3065940617289251240, 1550650161452514949 }, .{ 8444111790038951954, 1938312701815643686 }, - .{ 665883850346957067, 1211445438634777304 }, .{ 832354812933696334, 1514306798293471630 }, - .{ 10263815553021896226, 1892883497866839537 }, .{ 17944099766707154901, 1183052186166774710 }, - .{ 13206752671529167818, 1478815232708468388 }, .{ 16508440839411459773, 1848519040885585485 }, - .{ 12623618533845856310, 1155324400553490928 }, .{ 15779523167307320387, 1444155500691863660 }, - .{ 1277659885424598868, 1805194375864829576 }, .{ 1597074856780748586, 2256492969831036970 }, - .{ 5609857803915355770, 1410308106144398106 }, .{ 16235694291748970521, 1762885132680497632 }, - .{ 1847873790976661535, 2203606415850622041 }, .{ 12684136165428883219, 1377254009906638775 }, - .{ 11243484188358716120, 1721567512383298469 }, .{ 219297180166231438, 2151959390479123087 }, - .{ 7054589765244976505, 1344974619049451929 }, .{ 13429923224983608535, 1681218273811814911 }, - .{ 12175718012802122765, 2101522842264768639 }, .{ 14527352785642408584, 1313451776415480399 }, - .{ 13547504963625622826, 1641814720519350499 }, .{ 12322695186104640628, 2052268400649188124 }, - .{ 16925056528170176201, 1282667750405742577 }, .{ 7321262604930556539, 1603334688007178222 }, - .{ 18374950293017971482, 2004168360008972777 }, .{ 4566814905495150320, 1252605225005607986 }, - .{ 14931890668723713708, 1565756531257009982 }, .{ 9441491299049866327, 1957195664071262478 }, - .{ 1289246043478778550, 1223247290044539049 }, .{ 6223243572775861092, 1529059112555673811 }, - .{ 3167368447542438461, 1911323890694592264 }, .{ 1979605279714024038, 1194577431684120165 }, - .{ 7086192618069917952, 1493221789605150206 }, .{ 18081112809442173248, 1866527237006437757 }, - .{ 13606538515115052232, 1166579523129023598 }, .{ 7784801107039039482, 1458224403911279498 }, - .{ 507629346944023544, 1822780504889099373 }, .{ 5246222702107417334, 2278475631111374216 }, - .{ 3278889188817135834, 1424047269444608885 }, .{ 8710297504448807696, 1780059086805761106 } - }; - - const FLOAT64_POW5_INV_SPLIT: [342][2]u64 = .{ - .{ 1, 2305843009213693952 }, .{ 11068046444225730970, 1844674407370955161 }, - .{ 5165088340638674453, 1475739525896764129 }, .{ 7821419487252849886, 1180591620717411303 }, - .{ 8824922364862649494, 1888946593147858085 }, .{ 7059937891890119595, 1511157274518286468 }, - .{ 13026647942995916322, 1208925819614629174 }, .{ 9774590264567735146, 1934281311383406679 }, - .{ 11509021026396098440, 1547425049106725343 }, .{ 16585914450600699399, 1237940039285380274 }, - .{ 15469416676735388068, 1980704062856608439 }, .{ 16064882156130220778, 1584563250285286751 }, - .{ 9162556910162266299, 1267650600228229401 }, .{ 7281393426775805432, 2028240960365167042 }, - .{ 16893161185646375315, 1622592768292133633 }, .{ 2446482504291369283, 1298074214633706907 }, - .{ 7603720821608101175, 2076918743413931051 }, .{ 2393627842544570617, 1661534994731144841 }, - .{ 16672297533003297786, 1329227995784915872 }, .{ 11918280793837635165, 2126764793255865396 }, - .{ 5845275820328197809, 1701411834604692317 }, .{ 15744267100488289217, 1361129467683753853 }, - .{ 3054734472329800808, 2177807148294006166 }, .{ 17201182836831481939, 1742245718635204932 }, - .{ 6382248639981364905, 1393796574908163946 }, .{ 2832900194486363201, 2230074519853062314 }, - .{ 5955668970331000884, 1784059615882449851 }, .{ 1075186361522890384, 1427247692705959881 }, - .{ 12788344622662355584, 2283596308329535809 }, .{ 13920024512871794791, 1826877046663628647 }, - .{ 3757321980813615186, 1461501637330902918 }, .{ 10384555214134712795, 1169201309864722334 }, - .{ 5547241898389809503, 1870722095783555735 }, .{ 4437793518711847602, 1496577676626844588 }, - .{ 10928932444453298728, 1197262141301475670 }, .{ 17486291911125277965, 1915619426082361072 }, - .{ 6610335899416401726, 1532495540865888858 }, .{ 12666966349016942027, 1225996432692711086 }, - .{ 12888448528943286597, 1961594292308337738 }, .{ 17689456452638449924, 1569275433846670190 }, - .{ 14151565162110759939, 1255420347077336152 }, .{ 7885109000409574610, 2008672555323737844 }, - .{ 9997436015069570011, 1606938044258990275 }, .{ 7997948812055656009, 1285550435407192220 }, - .{ 12796718099289049614, 2056880696651507552 }, .{ 2858676849947419045, 1645504557321206042 }, - .{ 13354987924183666206, 1316403645856964833 }, .{ 17678631863951955605, 2106245833371143733 }, - .{ 3074859046935833515, 1684996666696914987 }, .{ 13527933681774397782, 1347997333357531989 }, - .{ 10576647446613305481, 2156795733372051183 }, .{ 15840015586774465031, 1725436586697640946 }, - .{ 8982663654677661702, 1380349269358112757 }, .{ 18061610662226169046, 2208558830972980411 }, - .{ 10759939715039024913, 1766847064778384329 }, .{ 12297300586773130254, 1413477651822707463 }, - .{ 15986332124095098083, 2261564242916331941 }, .{ 9099716884534168143, 1809251394333065553 }, - .{ 14658471137111155161, 1447401115466452442 }, .{ 4348079280205103483, 1157920892373161954 }, - .{ 14335624477811986218, 1852673427797059126 }, .{ 7779150767507678651, 1482138742237647301 }, - .{ 2533971799264232598, 1185710993790117841 }, .{ 15122401323048503126, 1897137590064188545 }, - .{ 12097921058438802501, 1517710072051350836 }, .{ 5988988032009131678, 1214168057641080669 }, - .{ 16961078480698431330, 1942668892225729070 }, .{ 13568862784558745064, 1554135113780583256 }, - .{ 7165741412905085728, 1243308091024466605 }, .{ 11465186260648137165, 1989292945639146568 }, - .{ 16550846638002330379, 1591434356511317254 }, .{ 16930026125143774626, 1273147485209053803 }, - .{ 4951948911778577463, 2037035976334486086 }, .{ 272210314680951647, 1629628781067588869 }, - .{ 3907117066486671641, 1303703024854071095 }, .{ 6251387306378674625, 2085924839766513752 }, - .{ 16069156289328670670, 1668739871813211001 }, .{ 9165976216721026213, 1334991897450568801 }, - .{ 7286864317269821294, 2135987035920910082 }, .{ 16897537898041588005, 1708789628736728065 }, - .{ 13518030318433270404, 1367031702989382452 }, .{ 6871453250525591353, 2187250724783011924 }, - .{ 9186511415162383406, 1749800579826409539 }, .{ 11038557946871817048, 1399840463861127631 }, - .{ 10282995085511086630, 2239744742177804210 }, .{ 8226396068408869304, 1791795793742243368 }, - .{ 13959814484210916090, 1433436634993794694 }, .{ 11267656730511734774, 2293498615990071511 }, - .{ 5324776569667477496, 1834798892792057209 }, .{ 7949170070475892320, 1467839114233645767 }, - .{ 17427382500606444826, 1174271291386916613 }, .{ 5747719112518849781, 1878834066219066582 }, - .{ 15666221734240810795, 1503067252975253265 }, .{ 12532977387392648636, 1202453802380202612 }, - .{ 5295368560860596524, 1923926083808324180 }, .{ 4236294848688477220, 1539140867046659344 }, - .{ 7078384693692692099, 1231312693637327475 }, .{ 11325415509908307358, 1970100309819723960 }, - .{ 9060332407926645887, 1576080247855779168 }, .{ 14626963555825137356, 1260864198284623334 }, - .{ 12335095245094488799, 2017382717255397335 }, .{ 9868076196075591040, 1613906173804317868 }, - .{ 15273158586344293478, 1291124939043454294 }, .{ 13369007293925138595, 2065799902469526871 }, - .{ 7005857020398200553, 1652639921975621497 }, .{ 16672732060544291412, 1322111937580497197 }, - .{ 11918976037903224966, 2115379100128795516 }, .{ 5845832015580669650, 1692303280103036413 }, - .{ 12055363241948356366, 1353842624082429130 }, .{ 841837113407818570, 2166148198531886609 }, - .{ 4362818505468165179, 1732918558825509287 }, .{ 14558301248600263113, 1386334847060407429 }, - .{ 12225235553534690011, 2218135755296651887 }, .{ 2401490813343931363, 1774508604237321510 }, - .{ 1921192650675145090, 1419606883389857208 }, .{ 17831303500047873437, 2271371013423771532 }, - .{ 6886345170554478103, 1817096810739017226 }, .{ 1819727321701672159, 1453677448591213781 }, - .{ 16213177116328979020, 1162941958872971024 }, .{ 14873036941900635463, 1860707134196753639 }, - .{ 15587778368262418694, 1488565707357402911 }, .{ 8780873879868024632, 1190852565885922329 }, - .{ 2981351763563108441, 1905364105417475727 }, .{ 13453127855076217722, 1524291284333980581 }, - .{ 7073153469319063855, 1219433027467184465 }, .{ 11317045550910502167, 1951092843947495144 }, - .{ 12742985255470312057, 1560874275157996115 }, .{ 10194388204376249646, 1248699420126396892 }, - .{ 1553625868034358140, 1997919072202235028 }, .{ 8621598323911307159, 1598335257761788022 }, - .{ 17965325103354776697, 1278668206209430417 }, .{ 13987124906400001422, 2045869129935088668 }, - .{ 121653480894270168, 1636695303948070935 }, .{ 97322784715416134, 1309356243158456748 }, - .{ 14913111714512307107, 2094969989053530796 }, .{ 8241140556867935363, 1675975991242824637 }, - .{ 17660958889720079260, 1340780792994259709 }, .{ 17189487779326395846, 2145249268790815535 }, - .{ 13751590223461116677, 1716199415032652428 }, .{ 18379969808252713988, 1372959532026121942 }, - .{ 14650556434236701088, 2196735251241795108 }, .{ 652398703163629901, 1757388200993436087 }, - .{ 11589965406756634890, 1405910560794748869 }, .{ 7475898206584884855, 2249456897271598191 }, - .{ 2291369750525997561, 1799565517817278553 }, .{ 9211793429904618695, 1439652414253822842 }, - .{ 18428218302589300235, 2303443862806116547 }, .{ 7363877012587619542, 1842755090244893238 }, - .{ 13269799239553916280, 1474204072195914590 }, .{ 10615839391643133024, 1179363257756731672 }, - .{ 2227947767661371545, 1886981212410770676 }, .{ 16539753473096738529, 1509584969928616540 }, - .{ 13231802778477390823, 1207667975942893232 }, .{ 6413489186596184024, 1932268761508629172 }, - .{ 16198837793502678189, 1545815009206903337 }, .{ 5580372605318321905, 1236652007365522670 }, - .{ 8928596168509315048, 1978643211784836272 }, .{ 18210923379033183008, 1582914569427869017 }, - .{ 7190041073742725760, 1266331655542295214 }, .{ 436019273762630246, 2026130648867672343 }, - .{ 7727513048493924843, 1620904519094137874 }, .{ 9871359253537050198, 1296723615275310299 }, - .{ 4726128361433549347, 2074757784440496479 }, .{ 7470251503888749801, 1659806227552397183 }, - .{ 13354898832594820487, 1327844982041917746 }, .{ 13989140502667892133, 2124551971267068394 }, - .{ 14880661216876224029, 1699641577013654715 }, .{ 11904528973500979224, 1359713261610923772 }, - .{ 4289851098633925465, 2175541218577478036 }, .{ 18189276137874781665, 1740432974861982428 }, - .{ 3483374466074094362, 1392346379889585943 }, .{ 1884050330976640656, 2227754207823337509 }, - .{ 5196589079523222848, 1782203366258670007 }, .{ 15225317707844309248, 1425762693006936005 }, - .{ 5913764258841343181, 2281220308811097609 }, .{ 8420360221814984868, 1824976247048878087 }, - .{ 17804334621677718864, 1459980997639102469 }, .{ 17932816512084085415, 1167984798111281975 }, - .{ 10245762345624985047, 1868775676978051161 }, .{ 4507261061758077715, 1495020541582440929 }, - .{ 7295157664148372495, 1196016433265952743 }, .{ 7982903447895485668, 1913626293225524389 }, - .{ 10075671573058298858, 1530901034580419511 }, .{ 4371188443704728763, 1224720827664335609 }, - .{ 14372599139411386667, 1959553324262936974 }, .{ 15187428126271019657, 1567642659410349579 }, - .{ 15839291315758726049, 1254114127528279663 }, .{ 3206773216762499739, 2006582604045247462 }, - .{ 13633465017635730761, 1605266083236197969 }, .{ 14596120828850494932, 1284212866588958375 }, - .{ 4907049252451240275, 2054740586542333401 }, .{ 236290587219081897, 1643792469233866721 }, - .{ 14946427728742906810, 1315033975387093376 }, .{ 16535586736504830250, 2104054360619349402 }, - .{ 5849771759720043554, 1683243488495479522 }, .{ 15747863852001765813, 1346594790796383617 }, - .{ 10439186904235184007, 2154551665274213788 }, .{ 15730047152871967852, 1723641332219371030 }, - .{ 12584037722297574282, 1378913065775496824 }, .{ 9066413911450387881, 2206260905240794919 }, - .{ 10942479943902220628, 1765008724192635935 }, .{ 8753983955121776503, 1412006979354108748 }, - .{ 10317025513452932081, 2259211166966573997 }, .{ 874922781278525018, 1807368933573259198 }, - .{ 8078635854506640661, 1445895146858607358 }, .{ 13841606313089133175, 1156716117486885886 }, - .{ 14767872471458792434, 1850745787979017418 }, .{ 746251532941302978, 1480596630383213935 }, - .{ 597001226353042382, 1184477304306571148 }, .{ 15712597221132509104, 1895163686890513836 }, - .{ 8880728962164096960, 1516130949512411069 }, .{ 10793931984473187891, 1212904759609928855 }, - .{ 17270291175157100626, 1940647615375886168 }, .{ 2748186495899949531, 1552518092300708935 }, - .{ 2198549196719959625, 1242014473840567148 }, .{ 18275073973719576693, 1987223158144907436 }, - .{ 10930710364233751031, 1589778526515925949 }, .{ 12433917106128911148, 1271822821212740759 }, - .{ 8826220925580526867, 2034916513940385215 }, .{ 7060976740464421494, 1627933211152308172 }, - .{ 16716827836597268165, 1302346568921846537 }, .{ 11989529279587987770, 2083754510274954460 }, - .{ 9591623423670390216, 1667003608219963568 }, .{ 15051996368420132820, 1333602886575970854 }, - .{ 13015147745246481542, 2133764618521553367 }, .{ 3033420566713364587, 1707011694817242694 }, - .{ 6116085268112601993, 1365609355853794155 }, .{ 9785736428980163188, 2184974969366070648 }, - .{ 15207286772667951197, 1747979975492856518 }, .{ 1097782973908629988, 1398383980394285215 }, - .{ 1756452758253807981, 2237414368630856344 }, .{ 5094511021344956708, 1789931494904685075 }, - .{ 4075608817075965366, 1431945195923748060 }, .{ 6520974107321544586, 2291112313477996896 }, - .{ 1527430471115325346, 1832889850782397517 }, .{ 12289990821117991246, 1466311880625918013 }, - .{ 17210690286378213644, 1173049504500734410 }, .{ 9090360384495590213, 1876879207201175057 }, - .{ 18340334751822203140, 1501503365760940045 }, .{ 14672267801457762512, 1201202692608752036 }, - .{ 16096930852848599373, 1921924308174003258 }, .{ 1809498238053148529, 1537539446539202607 }, - .{ 12515645034668249793, 1230031557231362085 }, .{ 1578287981759648052, 1968050491570179337 }, - .{ 12330676829633449412, 1574440393256143469 }, .{ 13553890278448669853, 1259552314604914775 }, - .{ 3239480371808320148, 2015283703367863641 }, .{ 17348979556414297411, 1612226962694290912 }, - .{ 6500486015647617283, 1289781570155432730 }, .{ 10400777625036187652, 2063650512248692368 }, - .{ 15699319729512770768, 1650920409798953894 }, .{ 16248804598352126938, 1320736327839163115 }, - .{ 7551343283653851484, 2113178124542660985 }, .{ 6041074626923081187, 1690542499634128788 }, - .{ 12211557331022285596, 1352433999707303030 }, .{ 1091747655926105338, 2163894399531684849 }, - .{ 4562746939482794594, 1731115519625347879 }, .{ 7339546366328145998, 1384892415700278303 }, - .{ 8053925371383123274, 2215827865120445285 }, .{ 6443140297106498619, 1772662292096356228 }, - .{ 12533209867169019542, 1418129833677084982 }, .{ 5295740528502789974, 2269007733883335972 }, - .{ 15304638867027962949, 1815206187106668777 }, .{ 4865013464138549713, 1452164949685335022 }, - .{ 14960057215536570740, 1161731959748268017 }, .{ 9178696285890871890, 1858771135597228828 }, - .{ 14721654658196518159, 1487016908477783062 }, .{ 4398626097073393881, 1189613526782226450 }, - .{ 7037801755317430209, 1903381642851562320 }, .{ 5630241404253944167, 1522705314281249856 }, - .{ 814844308661245011, 1218164251424999885 }, .{ 1303750893857992017, 1949062802279999816 }, - .{ 15800395974054034906, 1559250241823999852 }, .{ 5261619149759407279, 1247400193459199882 }, - .{ 12107939454356961969, 1995840309534719811 }, .{ 5997002748743659252, 1596672247627775849 }, - .{ 8486951013736837725, 1277337798102220679 }, .{ 2511075177753209390, 2043740476963553087 }, - .{ 13076906586428298482, 1634992381570842469 }, .{ 14150874083884549109, 1307993905256673975 }, - .{ 4194654460505726958, 2092790248410678361 }, .{ 18113118827372222859, 1674232198728542688 }, - .{ 3422448617672047318, 1339385758982834151 }, .{ 16543964232501006678, 2143017214372534641 }, - .{ 9545822571258895019, 1714413771498027713 }, .{ 15015355686490936662, 1371531017198422170 }, - .{ 5577825024675947042, 2194449627517475473 }, .{ 11840957649224578280, 1755559702013980378 }, - .{ 16851463748863483271, 1404447761611184302 }, .{ 12204946739213931940, 2247116418577894884 }, - .{ 13453306206113055875, 1797693134862315907 }, .{ 3383947335406624054, 1438154507889852726 }, - .{ 16482362180876329456, 2301047212623764361 }, .{ 9496540929959153242, 1840837770099011489 }, - .{ 11286581558709232917, 1472670216079209191 }, .{ 5339916432225476010, 1178136172863367353 }, - .{ 4854517476818851293, 1885017876581387765 }, .{ 3883613981455081034, 1508014301265110212 }, - .{ 14174937629389795797, 1206411441012088169 }, .{ 11611853762797942306, 1930258305619341071 }, - .{ 5600134195496443521, 1544206644495472857 }, .{ 15548153800622885787, 1235365315596378285 }, - .{ 6430302007287065643, 1976584504954205257 }, .{ 16212288050055383484, 1581267603963364205 }, - .{ 12969830440044306787, 1265014083170691364 }, .{ 9683682259845159889, 2024022533073106183 }, - .{ 15125643437359948558, 1619218026458484946 }, .{ 8411165935146048523, 1295374421166787957 }, - .{ 17147214310975587960, 2072599073866860731 }, .{ 10028422634038560045, 1658079259093488585 }, - .{ 8022738107230848036, 1326463407274790868 }, .{ 9147032156827446534, 2122341451639665389 }, - .{ 11006974540203867551, 1697873161311732311 }, .{ 5116230817421183718, 1358298529049385849 }, - .{ 15564666937357714594, 2173277646479017358 }, .{ 1383687105660440706, 1738622117183213887 }, - .{ 12174996128754083534, 1390897693746571109 }, .{ 8411947361780802685, 2225436309994513775 }, - .{ 6729557889424642148, 1780349047995611020 }, .{ 5383646311539713719, 1424279238396488816 }, - .{ 1235136468979721303, 2278846781434382106 }, .{ 15745504434151418335, 1823077425147505684 }, - .{ 16285752362063044992, 1458461940118004547 }, .{ 5649904260166615347, 1166769552094403638 }, - .{ 5350498001524674232, 1866831283351045821 }, .{ 591049586477829062, 1493465026680836657 }, - .{ 11540886113407994219, 1194772021344669325 }, .{ 18673707743239135, 1911635234151470921 }, - .{ 14772334225162232601, 1529308187321176736 }, .{ 8128518565387875758, 1223446549856941389 }, - .{ 1937583260394870242, 1957514479771106223 }, .{ 8928764237799716840, 1566011583816884978 }, - .{ 14521709019723594119, 1252809267053507982 }, .{ 8477339172590109297, 2004494827285612772 }, - .{ 17849917782297818407, 1603595861828490217 }, .{ 6901236596354434079, 1282876689462792174 }, - .{ 18420676183650915173, 2052602703140467478 }, .{ 3668494502695001169, 1642082162512373983 }, - .{ 10313493231639821582, 1313665730009899186 }, .{ 9122891541139893884, 2101865168015838698 }, - .{ 14677010862395735754, 1681492134412670958 }, .{ 673562245690857633, 1345193707530136767 } - }; - - // zig fmt: off - // - // f128 small tables: 9072 bytes - - const FLOAT128_POW5_INV_BITCOUNT = 249; - const FLOAT128_POW5_BITCOUNT = 249; - const FLOAT128_POW5_TABLE_SIZE: comptime_int = FLOAT128_POW5_TABLE.len; - - const FLOAT128_POW5_TABLE: [56][2]u64 = .{ - .{ 1, 0 }, - .{ 5, 0 }, - .{ 25, 0 }, - .{ 125, 0 }, - .{ 625, 0 }, - .{ 3125, 0 }, - .{ 15625, 0 }, - .{ 78125, 0 }, - .{ 390625, 0 }, - .{ 1953125, 0 }, - .{ 9765625, 0 }, - .{ 48828125, 0 }, - .{ 244140625, 0 }, - .{ 1220703125, 0 }, - .{ 6103515625, 0 }, - .{ 30517578125, 0 }, - .{ 152587890625, 0 }, - .{ 762939453125, 0 }, - .{ 3814697265625, 0 }, - .{ 19073486328125, 0 }, - .{ 95367431640625, 0 }, - .{ 476837158203125, 0 }, - .{ 2384185791015625, 0 }, - .{ 11920928955078125, 0 }, - .{ 59604644775390625, 0 }, - .{ 298023223876953125, 0 }, - .{ 1490116119384765625, 0 }, - .{ 7450580596923828125, 0 }, - .{ 359414837200037393, 2 }, - .{ 1797074186000186965, 10 }, - .{ 8985370930000934825, 50 }, - .{ 8033366502585570893, 252 }, - .{ 3273344365508751233, 1262 }, - .{ 16366721827543756165, 6310 }, - .{ 8046632842880574361, 31554 }, - .{ 3339676066983768573, 157772 }, - .{ 16698380334918842865, 788860 }, - .{ 9704925379756007861, 3944304 }, - .{ 11631138751360936073, 19721522 }, - .{ 2815461535676025517, 98607613 }, - .{ 14077307678380127585, 493038065 }, - .{ 15046306170771983077, 2465190328 }, - .{ 1444554559021708921, 12325951644 }, - .{ 7222772795108544605, 61629758220 }, - .{ 17667119901833171409, 308148791101 }, - .{ 14548623214327650581, 1540743955509 }, - .{ 17402883850509598057, 7703719777548 }, - .{ 13227442957709783821, 38518598887744 }, - .{ 10796982567420264257, 192592994438723 }, - .{ 17091424689682218053, 962964972193617 }, - .{ 11670147153572883801, 4814824860968089 }, - .{ 3010503546735764157, 24074124304840448 }, - .{ 15052517733678820785, 120370621524202240 }, - .{ 1475612373555897461, 601853107621011204 }, - .{ 7378061867779487305, 3009265538105056020 }, - .{ 18443565265187884909, 15046327690525280101 }, - }; - - const FLOAT128_POW5_SPLIT: [89][4]u64 = .{ - .{ 0, 0, 0, 72057594037927936 }, - .{ 0, 5206161169240293376, 4575641699882439235, 73468396926392969 }, - .{ 3360510775605221349, 6983200512169538081, 4325643253124434363, 74906821675075173 }, - .{ 11917660854915489451, 9652941469841108803, 946308467778435600, 76373409087490117 }, - .{ 1994853395185689235, 16102657350889591545, 6847013871814915412, 77868710555449746 }, - .{ 958415760277438274, 15059347134713823592, 7329070255463483331, 79393288266368765 }, - .{ 2065144883315240188, 7145278325844925976, 14718454754511147343, 80947715414629833 }, - .{ 8980391188862868935, 13709057401304208685, 8230434828742694591, 82532576417087045 }, - .{ 432148644612782575, 7960151582448466064, 12056089168559840552, 84148467132788711 }, - .{ 484109300864744403, 15010663910730448582, 16824949663447227068, 85795995087002057 }, - .{ 14793711725276144220, 16494403799991899904, 10145107106505865967, 87475779699624060 }, - .{ 15427548291869817042, 12330588654550505203, 13980791795114552342, 89188452518064298 }, - .{ 9979404135116626552, 13477446383271537499, 14459862802511591337, 90934657454687378 }, - .{ 12385121150303452775, 9097130814231585614, 6523855782339765207, 92715051028904201 }, - .{ 1822931022538209743, 16062974719797586441, 3619180286173516788, 94530302614003091 }, - .{ 12318611738248470829, 13330752208259324507, 10986694768744162601, 96381094688813589 }, - .{ 13684493829640282333, 7674802078297225834, 15208116197624593182, 98268123094297527 }, - .{ 5408877057066295332, 6470124174091971006, 15112713923117703147, 100192097295163851 }, - .{ 11407083166564425062, 18189998238742408185, 4337638702446708282, 102153740646605557 }, - .{ 4112405898036935485, 924624216579956435, 14251108172073737125, 104153790666259019 }, - .{ 16996739107011444789, 10015944118339042475, 2395188869672266257, 106192999311487969 }, - .{ 4588314690421337879, 5339991768263654604, 15441007590670620066, 108272133262096356 }, - .{ 2286159977890359825, 14329706763185060248, 5980012964059367667, 110391974208576409 }, - .{ 9654767503237031099, 11293544302844823188, 11739932712678287805, 112553319146000238 }, - .{ 11362964448496095896, 7990659682315657680, 251480263940996374, 114756980673665505 }, - .{ 1423410421096377129, 14274395557581462179, 16553482793602208894, 117003787300607788 }, - .{ 2070444190619093137, 11517140404712147401, 11657844572835578076, 119294583757094535 }, - .{ 7648316884775828921, 15264332483297977688, 247182277434709002, 121630231312217685 }, - .{ 17410896758132241352, 10923914482914417070, 13976383996795783649, 124011608097704390 }, - .{ 9542674537907272703, 3079432708831728956, 14235189590642919676, 126439609438067572 }, - .{ 10364666969937261816, 8464573184892924210, 12758646866025101190, 128915148187220428 }, - .{ 14720354822146013883, 11480204489231511423, 7449876034836187038, 131439155071681461 }, - .{ 1692907053653558553, 17835392458598425233, 1754856712536736598, 134012579040499057 }, - .{ 5620591334531458755, 11361776175667106627, 13350215315297937856, 136636387622027174 }, - .{ 17455759733928092601, 10362573084069962561, 11246018728801810510, 139311567287686283 }, - .{ 2465404073814044982, 17694822665274381860, 1509954037718722697, 142039123822846312 }, - .{ 2152236053329638369, 11202280800589637091, 16388426812920420176, 72410041352485523 }, - .{ 17319024055671609028, 10944982848661280484, 2457150158022562661, 73827744744583080 }, - .{ 17511219308535248024, 5122059497846768077, 2089605804219668451, 75273205100637900 }, - .{ 10082673333144031533, 14429008783411894887, 12842832230171903890, 76746965869337783 }, - .{ 16196653406315961184, 10260180891682904501, 10537411930446752461, 78249581139456266 }, - .{ 15084422041749743389, 234835370106753111, 16662517110286225617, 79781615848172976 }, - .{ 8199644021067702606, 3787318116274991885, 7438130039325743106, 81343645993472659 }, - .{ 12039493937039359765, 9773822153580393709, 5945428874398357806, 82936258850702722 }, - .{ 984543865091303961, 7975107621689454830, 6556665988501773347, 84560053193370726 }, - .{ 9633317878125234244, 16099592426808915028, 9706674539190598200, 86215639518264828 }, - .{ 6860695058870476186, 4471839111886709592, 7828342285492709568, 87903640274981819 }, - .{ 14583324717644598331, 4496120889473451238, 5290040788305728466, 89624690099949049 }, - .{ 18093669366515003715, 12879506572606942994, 18005739787089675377, 91379436055028227 }, - .{ 17997493966862379937, 14646222655265145582, 10265023312844161858, 93168537870790806 }, - .{ 12283848109039722318, 11290258077250314935, 9878160025624946825, 94992668194556404 }, - .{ 8087752761883078164, 5262596608437575693, 11093553063763274413, 96852512843287537 }, - .{ 15027787746776840781, 12250273651168257752, 9290470558712181914, 98748771061435726 }, - .{ 15003915578366724489, 2937334162439764327, 5404085603526796602, 100682155783835929 }, - .{ 5225610465224746757, 14932114897406142027, 2774647558180708010, 102653393903748137 }, - .{ 17112957703385190360, 12069082008339002412, 3901112447086388439, 104663226546146909 }, - .{ 4062324464323300238, 3992768146772240329, 15757196565593695724, 106712409346361594 }, - .{ 5525364615810306701, 11855206026704935156, 11344868740897365300, 108801712734172003 }, - .{ 9274143661888462646, 4478365862348432381, 18010077872551661771, 110931922223466333 }, - .{ 12604141221930060148, 8930937759942591500, 9382183116147201338, 113103838707570263 }, - .{ 14513929377491886653, 1410646149696279084, 587092196850797612, 115318278760358235 }, - .{ 2226851524999454362, 7717102471110805679, 7187441550995571734, 117576074943260147 }, - .{ 5527526061344932763, 2347100676188369132, 16976241418824030445, 119878076118278875 }, - .{ 6088479778147221611, 17669593130014777580, 10991124207197663546, 122225147767136307 }, - .{ 11107734086759692041, 3391795220306863431, 17233960908859089158, 124618172316667879 }, - .{ 7913172514655155198, 17726879005381242552, 641069866244011540, 127058049470587962 }, - .{ 12596991768458713949, 15714785522479904446, 6035972567136116512, 129545696547750811 }, - .{ 16901996933781815980, 4275085211437148707, 14091642539965169063, 132082048827034281 }, - .{ 7524574627987869240, 15661204384239316051, 2444526454225712267, 134668059898975949 }, - .{ 8199251625090479942, 6803282222165044067, 16064817666437851504, 137304702024293857 }, - .{ 4453256673338111920, 15269922543084434181, 3139961729834750852, 139992966499426682 }, - .{ 15841763546372731299, 3013174075437671812, 4383755396295695606, 142733864029230733 }, - .{ 9771896230907310329, 4900659362437687569, 12386126719044266361, 72764212553486967 }, - .{ 9420455527449565190, 1859606122611023693, 6555040298902684281, 74188850200884818 }, - .{ 5146105983135678095, 2287300449992174951, 4325371679080264751, 75641380576797959 }, - .{ 11019359372592553360, 8422686425957443718, 7175176077944048210, 77122349788024458 }, - .{ 11005742969399620716, 4132174559240043701, 9372258443096612118, 78632314633490790 }, - .{ 8887589641394725840, 8029899502466543662, 14582206497241572853, 80171842813591127 }, - .{ 360247523705545899, 12568341805293354211, 14653258284762517866, 81741513143625247 }, - .{ 12314272731984275834, 4740745023227177044, 6141631472368337539, 83341915771415304 }, - .{ 441052047733984759, 7940090120939869826, 11750200619921094248, 84973652399183278 }, - .{ 3436657868127012749, 9187006432149937667, 16389726097323041290, 86637336509772529 }, - .{ 13490220260784534044, 15339072891382896702, 8846102360835316895, 88333593597298497 }, - .{ 4125672032094859833, 158347675704003277, 10592598512749774447, 90063061402315272 }, - .{ 12189928252974395775, 2386931199439295891, 7009030566469913276, 91826390151586454 }, - .{ 9256479608339282969, 2844900158963599229, 11148388908923225596, 93624242802550437 }, - .{ 11584393507658707408, 2863659090805147914, 9873421561981063551, 95457295292572042 }, - .{ 13984297296943171390, 1931468383973130608, 12905719743235082319, 97326236793074198 }, - .{ 5837045222254987499, 10213498696735864176, 14893951506257020749, 99231769968645227 }, - }; - - // Unfortunately, the results are sometimes off by one or two. We use an additional - // lookup table to store those cases and adjust the result. - const FLOAT128_POW5_ERRORS: [156]u64 = .{ - 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x9555596400000000, - 0x65a6569525565555, 0x4415551445449655, 0x5105015504144541, 0x65a69969a6965964, - 0x5054955969959656, 0x5105154515554145, 0x4055511051591555, 0x5500514455550115, - 0x0041140014145515, 0x1005440545511051, 0x0014405450411004, 0x0414440010500000, - 0x0044000440010040, 0x5551155000004001, 0x4554555454544114, 0x5150045544005441, - 0x0001111400054501, 0x6550955555554554, 0x1504159645559559, 0x4105055141454545, - 0x1411541410405454, 0x0415555044545555, 0x0014154115405550, 0x1540055040411445, - 0x0000000500000000, 0x5644000000000000, 0x1155555591596555, 0x0410440054569565, - 0x5145100010010005, 0x0555041405500150, 0x4141450455140450, 0x0000000144000140, - 0x5114004001105410, 0x4444100404005504, 0x0414014410001015, 0x5145055155555015, - 0x0141041444445540, 0x0000100451541414, 0x4105041104155550, 0x0500501150451145, - 0x1001050000004114, 0x5551504400141045, 0x5110545410151454, 0x0100001400004040, - 0x5040010111040000, 0x0140000150541100, 0x4400140400104110, 0x5011014405545004, - 0x0000000044155440, 0x0000000010000000, 0x1100401444440001, 0x0040401010055111, - 0x5155155551405454, 0x0444440015514411, 0x0054505054014101, 0x0451015441115511, - 0x1541411401140551, 0x4155104514445110, 0x4141145450145515, 0x5451445055155050, - 0x4400515554110054, 0x5111145104501151, 0x565a655455500501, 0x5565555555525955, - 0x0550511500405695, 0x4415504051054544, 0x6555595965555554, 0x0100915915555655, - 0x5540001510001001, 0x5450051414000544, 0x1405010555555551, 0x5555515555644155, - 0x5555055595496555, 0x5451045004415000, 0x5450510144040144, 0x5554155555556455, - 0x5051555495415555, 0x5555554555555545, 0x0000000010005455, 0x4000005000040000, - 0x5565555555555954, 0x5554559555555505, 0x9645545495552555, 0x4000400055955564, - 0x0040000000000001, 0x4004100100000000, 0x5540040440000411, 0x4565555955545644, - 0x1140659549651556, 0x0100000410010000, 0x5555515400004001, 0x5955545555155255, - 0x5151055545505556, 0x5051454510554515, 0x0501500050415554, 0x5044154005441005, - 0x1455445450550455, 0x0010144055144545, 0x0000401100000004, 0x1050145050000010, - 0x0415004554011540, 0x1000510100151150, 0x0100040400001144, 0x0000000000000000, - 0x0550004400000100, 0x0151145041451151, 0x0000400400005450, 0x0000100044010004, - 0x0100054100050040, 0x0504400005410010, 0x4011410445500105, 0x0000404000144411, - 0x0101504404500000, 0x0000005044400400, 0x0000000014000100, 0x0404440414000000, - 0x5554100410000140, 0x4555455544505555, 0x5454105055455455, 0x0115454155454015, - 0x4404110000045100, 0x4400001100101501, 0x6596955956966a94, 0x0040655955665965, - 0x5554144400100155, 0xa549495401011041, 0x5596555565955555, 0x5569965959549555, - 0x969565a655555456, 0x0000001000000000, 0x0000000040000140, 0x0000040100000000, - 0x1415454400000000, 0x5410415411454114, 0x0400040104000154, 0x0504045000000411, - 0x0000001000000010, 0x5554000000001040, 0x5549155551556595, 0x1455541055515555, - 0x0510555454554541, 0x9555555555540455, 0x6455456555556465, 0x4524565555654514, - 0x5554655255559545, 0x9555455441155556, 0x0000000051515555, 0x0010005040000550, - 0x5044044040000000, 0x1045040440010500, 0x0000400000040000, 0x0000000000000000, - }; - - const FLOAT128_POW5_INV_SPLIT: [89][4]u64 = .{ - .{ 0, 0, 0, 144115188075855872 }, - .{ 1573859546583440065, 2691002611772552616, 6763753280790178510, 141347765182270746 }, - .{ 12960290449513840412, 12345512957918226762, 18057899791198622765, 138633484706040742 }, - .{ 7615871757716765416, 9507132263365501332, 4879801712092008245, 135971326161092377 }, - .{ 7869961150745287587, 5804035291554591636, 8883897266325833928, 133360288657597085 }, - .{ 2942118023529634767, 15128191429820565086, 10638459445243230718, 130799390525667397 }, - .{ 14188759758411913794, 5362791266439207815, 8068821289119264054, 128287668946279217 }, - .{ 7183196927902545212, 1952291723540117099, 12075928209936341512, 125824179589281448 }, - .{ 5672588001402349748, 17892323620748423487, 9874578446960390364, 123407996258356868 }, - .{ 4442590541217566325, 4558254706293456445, 10343828952663182727, 121038210542800766 }, - .{ 3005560928406962566, 2082271027139057888, 13961184524927245081, 118713931475986426 }, - .{ 13299058168408384786, 17834349496131278595, 9029906103900731664, 116434285200389047 }, - .{ 5414878118283973035, 13079825470227392078, 17897304791683760280, 114198414639042157 }, - .{ 14609755883382484834, 14991702445765844156, 3269802549772755411, 112005479173303009 }, - .{ 15967774957605076027, 2511532636717499923, 16221038267832563171, 109854654326805788 }, - .{ 9269330061621627145, 3332501053426257392, 16223281189403734630, 107745131455483836 }, - .{ 16739559299223642282, 1873986623300664530, 6546709159471442872, 105676117443544318 }, - .{ 17116435360051202055, 1359075105581853924, 2038341371621886470, 103646834405281051 }, - .{ 17144715798009627550, 3201623802661132408, 9757551605154622431, 101656519392613377 }, - .{ 17580479792687825857, 6546633380567327312, 15099972427870912398, 99704424108241124 }, - .{ 9726477118325522902, 14578369026754005435, 11728055595254428803, 97789814624307808 }, - .{ 134593949518343635, 5715151379816901985, 1660163707976377376, 95911971106466306 }, - .{ 5515914027713859358, 7124354893273815720, 5548463282858794077, 94070187543243255 }, - .{ 6188403395862945512, 5681264392632320838, 15417410852121406654, 92263771480600430 }, - .{ 15908890877468271457, 10398888261125597540, 4817794962769172309, 90492043761593298 }, - .{ 1413077535082201005, 12675058125384151580, 7731426132303759597, 88754338271028867 }, - .{ 1486733163972670293, 11369385300195092554, 11610016711694864110, 87050001685026843 }, - .{ 8788596583757589684, 3978580923851924802, 9255162428306775812, 85378393225389919 }, - .{ 7203518319660962120, 15044736224407683725, 2488132019818199792, 83738884418690858 }, - .{ 4004175967662388707, 18236988667757575407, 15613100370957482671, 82130858859985791 }, - .{ 18371903370586036463, 53497579022921640, 16465963977267203307, 80553711981064899 }, - .{ 10170778323887491315, 1999668801648976001, 10209763593579456445, 79006850823153334 }, - .{ 17108131712433974546, 16825784443029944237, 2078700786753338945, 77489693813976938 }, - .{ 17221789422665858532, 12145427517550446164, 5391414622238668005, 76001670549108934 }, - .{ 4859588996898795878, 1715798948121313204, 3950858167455137171, 74542221577515387 }, - .{ 13513469241795711526, 631367850494860526, 10517278915021816160, 73110798191218799 }, - .{ 11757513142672073111, 2581974932255022228, 17498959383193606459, 143413724438001539 }, - .{ 14524355192525042817, 5640643347559376447, 1309659274756813016, 140659771648132296 }, - .{ 2765095348461978538, 11021111021896007722, 3224303603779962366, 137958702611185230 }, - .{ 12373410389187981037, 13679193545685856195, 11644609038462631561, 135309501808182158 }, - .{ 12813176257562780151, 3754199046160268020, 9954691079802960722, 132711173221007413 }, - .{ 17557452279667723458, 3237799193992485824, 17893947919029030695, 130162739957935629 }, - .{ 14634200999559435155, 4123869946105211004, 6955301747350769239, 127663243886350468 }, - .{ 2185352760627740240, 2864813346878886844, 13049218671329690184, 125211745272516185 }, - .{ 6143438674322183002, 10464733336980678750, 6982925169933978309, 122807322428266620 }, - .{ 1099509117817174576, 10202656147550524081, 754997032816608484, 120449071364478757 }, - .{ 2410631293559367023, 17407273750261453804, 15307291918933463037, 118136105451200587 }, - .{ 12224968375134586697, 1664436604907828062, 11506086230137787358, 115867555084305488 }, - .{ 3495926216898000888, 18392536965197424288, 10992889188570643156, 113642567358547782 }, - .{ 8744506286256259680, 3966568369496879937, 18342264969761820037, 111460305746896569 }, - .{ 7689600520560455039, 5254331190877624630, 9628558080573245556, 109319949786027263 }, - .{ 11862637625618819436, 3456120362318976488, 14690471063106001082, 107220694767852583 }, - .{ 5697330450030126444, 12424082405392918899, 358204170751754904, 105161751436977040 }, - .{ 11257457505097373622, 15373192700214208870, 671619062372033814, 103142345693961148 }, - .{ 16850355018477166700, 1913910419361963966, 4550257919755970531, 101161718304283822 }, - .{ 9670835567561997011, 10584031339132130638, 3060560222974851757, 99219124612893520 }, - .{ 7698686577353054710, 11689292838639130817, 11806331021588878241, 97313834264240819 }, - .{ 12233569599615692137, 3347791226108469959, 10333904326094451110, 95445130927687169 }, - .{ 13049400362825383933, 17142621313007799680, 3790542585289224168, 93612312028186576 }, - .{ 12430457242474442072, 5625077542189557960, 14765055286236672238, 91814688482138969 }, - .{ 4759444137752473128, 2230562561567025078, 4954443037339580076, 90051584438315940 }, - .{ 7246913525170274758, 8910297835195760709, 4015904029508858381, 88322337023761438 }, - .{ 12854430245836432067, 8135139748065431455, 11548083631386317976, 86626296094571907 }, - .{ 4848827254502687803, 4789491250196085625, 3988192420450664125, 84962823991462151 }, - .{ 7435538409611286684, 904061756819742353, 14598026519493048444, 83331295300025028 }, - .{ 11042616160352530997, 8948390828345326218, 10052651191118271927, 81731096615594853 }, - .{ 11059348291563778943, 11696515766184685544, 3783210511290897367, 80161626312626082 }, - .{ 7020010856491885826, 5025093219346041680, 8960210401638911765, 78622294318500592 }, - .{ 17732844474490699984, 7820866704994446502, 6088373186798844243, 77112521891678506 }, - .{ 688278527545590501, 3045610706602776618, 8684243536999567610, 75631741404109150 }, - .{ 2734573255120657297, 3903146411440697663, 9470794821691856713, 74179396127820347 }, - .{ 15996457521023071259, 4776627823451271680, 12394856457265744744, 72754940025605801 }, - .{ 13492065758834518331, 7390517611012222399, 1630485387832860230, 142715675091463768 }, - .{ 13665021627282055864, 9897834675523659302, 17907668136755296849, 139975126841173266 }, - .{ 9603773719399446181, 10771916301484339398, 10672699855989487527, 137287204938390542 }, - .{ 3630218541553511265, 8139010004241080614, 2876479648932814543, 134650898807055963 }, - .{ 8318835909686377084, 9525369258927993371, 2796120270400437057, 132065217277054270 }, - .{ 11190003059043290163, 12424345635599592110, 12539346395388933763, 129529188211565064 }, - .{ 8701968833973242276, 820569587086330727, 2315591597351480110, 127041858141569228 }, - .{ 5115113890115690487, 16906305245394587826, 9899749468931071388, 124602291907373862 }, - .{ 15543535488939245974, 10945189844466391399, 3553863472349432246, 122209572307020975 }, - .{ 7709257252608325038, 1191832167690640880, 15077137020234258537, 119862799751447719 }, - .{ 7541333244210021737, 9790054727902174575, 5160944773155322014, 117561091926268545 }, - .{ 12297384708782857832, 1281328873123467374, 4827925254630475769, 115303583460052092 }, - .{ 13243237906232367265, 15873887428139547641, 3607993172301799599, 113089425598968120 }, - .{ 11384616453739611114, 15184114243769211033, 13148448124803481057, 110917785887682141 }, - .{ 17727970963596660683, 1196965221832671990, 14537830463956404138, 108787847856377790 }, - .{ 17241367586707330931, 8880584684128262874, 11173506540726547818, 106698810713789254 }, - .{ 7184427196661305643, 14332510582433188173, 14230167953789677901, 104649889046128358 }, - }; - - const FLOAT128_POW5_INV_ERRORS: [154]u64 = .{ - 0x1144155514145504, 0x0000541555401141, 0x0000000000000000, 0x0154454000000000, - 0x4114105515544440, 0x0001001111500415, 0x4041411410011000, 0x5550114515155014, - 0x1404100041554551, 0x0515000450404410, 0x5054544401140004, 0x5155501005555105, - 0x1144141000105515, 0x0541500000500000, 0x1104105540444140, 0x4000015055514110, - 0x0054010450004005, 0x4155515404100005, 0x5155145045155555, 0x1511555515440558, - 0x5558544555515555, 0x0000000000000010, 0x5004000000000050, 0x1415510100000010, - 0x4545555444514500, 0x5155151555555551, 0x1441540144044554, 0x5150104045544400, - 0x5450545401444040, 0x5554455045501400, 0x4655155555555145, 0x1000010055455055, - 0x1000004000055004, 0x4455405104000005, 0x4500114504150545, 0x0000000014000000, - 0x5450000000000000, 0x5514551511445555, 0x4111501040555451, 0x4515445500054444, - 0x5101500104100441, 0x1545115155545055, 0x0000000000000000, 0x1554000000100000, - 0x5555545595551555, 0x5555051851455955, 0x5555555555555559, 0x0000400011001555, - 0x0000004400040000, 0x5455511555554554, 0x5614555544115445, 0x6455156145555155, - 0x5455855455415455, 0x5515555144555545, 0x0114400000145155, 0x0000051000450511, - 0x4455154554445100, 0x4554150141544455, 0x65955555559a5965, 0x5555555854559559, - 0x9569654559616595, 0x1040044040005565, 0x1010010500011044, 0x1554015545154540, - 0x4440555401545441, 0x1014441450550105, 0x4545400410504145, 0x5015111541040151, - 0x5145051154000410, 0x1040001044545044, 0x4001400000151410, 0x0540000044040000, - 0x0510555454411544, 0x0400054054141550, 0x1001041145001100, 0x0000000140000000, - 0x0000000014100000, 0x1544005454000140, 0x4050055505445145, 0x0011511104504155, - 0x5505544415045055, 0x1155154445515554, 0x0000000000004555, 0x0000000000000000, - 0x5101010510400004, 0x1514045044440400, 0x5515519555515555, 0x4554545441555545, - 0x1551055955551515, 0x0150000011505515, 0x0044005040400000, 0x0004001004010050, - 0x0000051004450414, 0x0114001101001144, 0x0401000001000001, 0x4500010001000401, - 0x0004100000005000, 0x0105000441101100, 0x0455455550454540, 0x5404050144105505, - 0x4101510540555455, 0x1055541411451555, 0x5451445110115505, 0x1154110010101545, - 0x1145140450054055, 0x5555565415551554, 0x1550559555555555, 0x5555541545045141, - 0x4555455450500100, 0x5510454545554555, 0x1510140115045455, 0x1001050040111510, - 0x5555454555555504, 0x9954155545515554, 0x6596656555555555, 0x0140410051555559, - 0x0011104010001544, 0x965669659a680501, 0x5655a55955556955, 0x4015111014404514, - 0x1414155554505145, 0x0540040011051404, 0x1010000000015005, 0x0010054050004410, - 0x5041104014000100, 0x4440010500100001, 0x1155510504545554, 0x0450151545115541, - 0x4000100400110440, 0x1004440010514440, 0x0000115050450000, 0x0545404455541500, - 0x1051051555505101, 0x5505144554544144, 0x4550545555515550, 0x0015400450045445, - 0x4514155400554415, 0x4555055051050151, 0x1511441450001014, 0x4544554510404414, - 0x4115115545545450, 0x5500541555551555, 0x5550010544155015, 0x0144414045545500, - 0x4154050001050150, 0x5550511111000145, 0x1114504055000151, 0x5104041101451040, - 0x0010501401051441, 0x0010501450504401, 0x4554585440044444, 0x5155555951450455, - 0x0040000400105555, 0x0000000000000001, - }; - - pub fn float_mantissa_bits(comptime T: type) comptime_int { - comptime assert(@typeInfo(T) == .float); - - return switch (@typeInfo(T).float.bits) { - 16 => 10, - 32 => 23, - 64 => 52, - 80 => 64, - 128 => 112, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; - } - - /// Returns the number of fractional bits in the mantissa of floating point type T. - pub fn float_fractional_bits(comptime T: type) comptime_int { - comptime assert(@typeInfo(T) == .float); - - // standard IEEE floats have an implicit 0.m or 1.m integer part - // f80 is special and has an explicitly stored bit in the MSB - // this function corresponds to `MANT_DIG - 1' from C - return switch (@typeInfo(T).float.bits) { - 16 => 10, - 32 => 23, - 64 => 52, - 80 => 63, - 128 => 112, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; - } - - pub fn float_exponent_bits(comptime T: type) comptime_int { - comptime assert(@typeInfo(T) == .float); - - return switch (@typeInfo(T).float.bits) { - 16 => 5, - 32 => 8, - 64 => 11, - 80 => 15, - 128 => 15, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; - } - pub fn digits2(value: u8) [2]u8 { - if (builtin.mode == .ReleaseSmall) { - return .{ @intCast('0' + value / 10), @intCast('0' + value % 10) }; - } else { - return "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"[value * 2 ..][0..2].*; - } - } - - pub fn Log2Int(comptime T: type) type { - // comptime ceil log2 - if (T == comptime_int) return comptime_int; - const bits: u16 = @typeInfo(T).int.bits; - const log2_bits = 16 - @clz(bits - 1); - return @Type(.{ - .int = .{ - .signedness = .unsigned, - .bits = log2_bits, - }, - }); - } - - // zig fmt: on -}; - -pub fn and_bool_branchless(a: bool, b: bool) bool { - return (@intFromBool(a) & @intFromBool(b)) != 0; -} - -pub fn or_bool_branchless(a: bool, b: bool) bool { - return (@intFromBool(a) | @intFromBool(b)) != 0; -} - -fn is_character_between_ranges(ch: u8, start: u8, end: u8) bool { - return and_bool_branchless(ch >= start, ch <= end); -} - -fn is_decimal_digit(ch: u8) bool { - return is_character_between_ranges(ch, '0', '9'); -} - -fn is_hex_digit_alpha_lower(ch: u8) bool { - return is_character_between_ranges(ch, 'a', 'f'); -} - -fn is_hex_digit_alpha_upper(ch: u8) bool { - return is_character_between_ranges(ch, 'A', 'F'); -} - -fn is_hex_digit_alpha(ch: u8) bool { - return or_bool_branchless(is_hex_digit_alpha_lower(ch), is_hex_digit_alpha_upper(ch)); -} - -pub fn is_hex_digit(ch: u8) bool { - return or_bool_branchless(is_decimal_digit(ch), is_hex_digit_alpha(ch)); -} - -inline fn log2_int(v: anytype) u8 { - assert(v != 0); - return @bitSizeOf(@TypeOf(v)) - 1 - @clz(v); -} - -fn hex_digit_count(v: u64) u8 { - return if (v != 0) log2_int(v) / log2_int(@as(u64, 16)) + 1 else 1; -} - -pub const left_brace = '{'; -pub const right_brace = '}'; - -const IntegerFormatOptions = struct { - kind: Kind, - width: u32, - - const Kind = enum { - hexadecimal, - decimal, - octal, - binary, - }; -}; - -fn integer_format_options(pointer: [*:0]const u8, index: *usize) IntegerFormatOptions { - var result = IntegerFormatOptions{ - .kind = .decimal, - .width = 0, - }; - - if (pointer[index.*] == ':') { - index.* += 1; - - while (pointer[index.*] != right_brace) { - switch (pointer[index.*]) { - 'x' => { - result.kind = .hexadecimal; - index.* += 1; - }, - 'd' => { - result.kind = .decimal; - index.* += 1; - }, - 'o' => { - result.kind = .octal; - index.* += 1; - }, - 'b' => { - result.kind = .binary; - index.* += 1; - }, - 'w' => { - index.* += 1; - - if (pointer[index.*] != '=') { - os.abort(); - } - - index.* += 1; - - const start = index.*; - - while (is_decimal_digit(pointer[index.*])) { - index.* += 1; - } - const end = index.*; - - result.width = @intCast(parse.integer_decimal(pointer[start..end])); - }, - else => unreachable, - } - } - } - - return result; -} - -pub fn format_va(buffer: []u8, format_string: [*:0]const u8, variable_arguments: *VariableArguments) usize { - var read_byte_count: usize = 0; - var written_byte_count: usize = 0; - - while (format_string[read_byte_count] != 0) { - while (true) { - const ch = format_string[read_byte_count]; - - switch (ch) { - else => { - buffer[written_byte_count] = ch; - written_byte_count += 1; - read_byte_count += 1; - }, - 0 => break, - left_brace => { - read_byte_count += 1; - - const next_ch = format_string[read_byte_count]; - - switch (next_ch) { - left_brace => os.abort(), - 'c' => { - read_byte_count += 1; - assert(format_string[read_byte_count] == 's'); - read_byte_count += 1; - assert(format_string[read_byte_count] == 't'); - read_byte_count += 1; - assert(format_string[read_byte_count] == 'r'); - read_byte_count += 1; - - const c_string = @cVaArg(variable_arguments, [*:0]const u8); - const str = cstring.to_slice(c_string); - @memcpy(buffer[written_byte_count..][0..str.len], str); - written_byte_count += str.len; - }, - 'f' => { - os.abort(); - }, - 's' => { - os.abort(); - }, - 'u' => { - read_byte_count += 1; - - const bit_count_start = read_byte_count; - while (is_decimal_digit(format_string[read_byte_count])) { - read_byte_count += 1; - } - - const bit_count_end = read_byte_count; - - const bit_count_string = format_string[bit_count_start..bit_count_end]; - const bit_count = parse.integer_decimal(bit_count_string); - - const original_value: u64 = switch (bit_count) { - 8, 16, 32 => @cVaArg(variable_arguments, u32), - 64 => @cVaArg(variable_arguments, u64), - else => unreachable, - }; - - const options = integer_format_options(format_string, &read_byte_count); - - switch (options.kind) { - .hexadecimal => { - const digit_count = hex_digit_count(original_value); - _ = digit_count; - os.abort(); - }, - .decimal => { - const decimal_buffer = buffer[written_byte_count..]; - const decimal_written_byte_count = string_format.integer_decimal(decimal_buffer, original_value); - written_byte_count += decimal_written_byte_count; - }, - .octal, .binary => os.abort(), - } - }, - else => os.abort(), - } - - if (format_string[read_byte_count] != right_brace) { - os.abort(); - } - - read_byte_count += 1; - - break; - }, - } - } - } - - _ = &written_byte_count; - _ = &variable_arguments; - return written_byte_count; -} - -pub fn format(buffer_pointer: [*]u8, buffer_length: usize, format_string: [*:0]const u8, ...) callconv(.C) usize { - var variable_arguments = @cVaStart(); - const byte_count = format_va(buffer_pointer[0..buffer_length], format_string, &variable_arguments); - @cVaEnd(&variable_arguments); - return byte_count; -} - -pub const GlobalState = struct { - arena: *Arena, - thread_count: usize, - - pub var initialized = false; - - pub fn initialize() void { - assert(!initialized); - defer initialized = true; - const thread_count = os.get_cpu_count(); - global = .{ - .arena = Arena.initialize_default(2 * MB), - .thread_count = thread_count, - }; - } -}; -pub var global: GlobalState = undefined; - -pub const parse = struct { - pub fn integer_hexadecimal(str: []const u8) u64 { - var value: u64 = 0; - - for (str) |ch| { - assert(is_hex_digit(ch)); - value = accumulate_hexadecimal(value, ch); - } - - return value; - } - - pub fn accumulate_hexadecimal(accumulator: u64, ch: u8) u64 { - const value_to_add: u64 = if (is_decimal_digit(ch)) ch - '0' else if (is_hex_digit_alpha_upper(ch)) - ch - 'A' + 10 - else if (is_hex_digit_alpha_lower(ch)) - ch - 'a' + 10 - else - unreachable; - return (accumulator * 16) + value_to_add; - } - - pub fn integer_decimal(str: []const u8) u64 { - var value: u64 = 0; - - for (str) |ch| { - assert(is_decimal_digit(ch)); - value = accumulate_decimal(value, ch); - } - - return value; - } - - pub fn accumulate_decimal(accumulator: u64, ch: u8) u64 { - return (accumulator * 10) + (ch - '0'); - } - - pub fn accumulate_octal(accumulator: u64, ch: u8) u64 { - return (accumulator * 8) + (ch - '0'); - } - - pub fn accumulate_binary(accumulator: u64, ch: u8) u64 { - return (accumulator * 2) + (ch - '0'); - } -}; - -test "parse integer decimal" { - const std = @import("std"); - const expect = std.testing.expect; - try expect(parse.integer_decimal("0") == 0); - try expect(parse.integer_decimal("5") == 5); - try expect(parse.integer_decimal("10") == 10); - try expect(parse.integer_decimal("901283") == 901283); - try expect(parse.integer_decimal("189234819023129038") == 189234819023129038); -} - -test "parse integer hexadecimal" { - const std = @import("std"); - const expect = std.testing.expect; - try expect(parse.integer_hexadecimal("0") == 0); - try expect(parse.integer_hexadecimal("5") == 5); - try expect(parse.integer_hexadecimal("10") == 0x10); - try expect(parse.integer_hexadecimal("901283") == 0x901283); - try expect(parse.integer_hexadecimal("1892348190231290") == 0x1892348190231290); - try expect(parse.integer_hexadecimal("1b92a48c90d3e2f0") == 0x1b92a48c90d3e2f0); -} - -fn vprint(format_string: [*:0]const u8, args: *VariableArguments) void { - var buffer: [16 * 1024]u8 = undefined; - const byte_count = format_va(&buffer, format_string, args); - print_string(buffer[0..byte_count]); -} - -pub fn print(format_string: [*:0]const u8, ...) callconv(.C) void { - var args = @cVaStart(); - vprint(format_string, &args); - @cVaEnd(&args); -} - -pub const print_string = print_string_stderr; - -pub fn print_string_stdout(str: []const u8) void { - os.get_stdout().write(str); -} - -pub fn print_string_stderr(str: []const u8) void { - os.get_stderr().write(str); -} - -pub const RawVirtualBuffer = struct { - pointer: [*]align(4096) u8, - length: u32, - capacity: u32, - - pub fn initialize() RawVirtualBuffer { - const pointer = os.reserve(0, 4 * 1024 * 1024, .{ - .read = 1, - .write = 1, - .execute = 0, - }, .{ - .private = 1, - .anonymous = 1, - .no_reserve = 1, - .populate = 0, - }); - return .{ - .pointer = @ptrCast(pointer), - .length = 0, - .capacity = 0, - }; - } - - pub fn ensure_capacity(vb: *RawVirtualBuffer, capacity: u64) void { - const commit_offset: u64 = vb.capacity; - const unaligned_existing_capacity = commit_offset - vb.length; - if (unaligned_existing_capacity < capacity) { - const aligned_wanted_capacity = align_forward_u64(commit_offset + capacity, 0x1000); - if (aligned_wanted_capacity > @as(u64, ~@as(u32, 0)) + 1) { - @trap(); - } - - const commit_size = aligned_wanted_capacity - commit_offset; - const commit_pointer = vb.pointer + commit_offset; - vb.capacity = @intCast(aligned_wanted_capacity); - - os.commit(commit_pointer, commit_size, .{ - .read = 1, - .write = 1, - .execute = 0, - }); - } - } - - pub fn add_bytes(vb: *RawVirtualBuffer, byte_count: u64) []u8 { - vb.ensure_capacity(byte_count); - const current_length = vb.length; - vb.length += @intCast(byte_count); - const slice = vb.pointer[current_length..][0..byte_count]; - return slice; - } - - pub fn append_bytes(vb: *RawVirtualBuffer, bytes: []const u8) []u8 { - const slice = vb.add_bytes(bytes.len); - @memcpy(slice, bytes); - return slice; - } -}; - -pub fn VirtualBuffer(comptime T: type) type { - return struct { - backing: RawVirtualBuffer, - - const VB = @This(); - - pub fn initialize() VB { - return .{ - .backing = .initialize(), - }; - } - - pub fn get_slice(vb: VB) []align(4096) T { - return @as([*]align(4096) T, @ptrCast(vb.backing.pointer))[0..@divExact(vb.backing.length, @sizeOf(T))]; - } - - fn byte_to_slice(v: *const T) []const u8 { - const byte_pointer: [*]const u8 = @ptrCast(v); - const byte_length = @sizeOf(T); - return byte_pointer[0..byte_length]; - } - - pub fn append(vb: *VB, v: T) *T { - const result = vb.backing.append_bytes(byte_to_slice(&v)); - const pointer: *T = @alignCast(@ptrCast(&result[0])); - return pointer; - } - - pub fn add(vb: *VB) *T { - const result = vb.backing.add_bytes(@sizeOf(T)); - const pointer: *T = @alignCast(@ptrCast(&result[0])); - return pointer; - } - }; -} - -pub const panic_struct = struct { - const abort = os.abort; - pub fn call(_: []const u8, _: ?usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn sentinelMismatch(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn unwrapError(_: anyerror) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn outOfBounds(_: usize, _: usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn startGreaterThanEnd(_: usize, _: usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn inactiveUnionField(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn reachedUnreachable() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn unwrapNull() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn castToNull() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn incorrectAlignment() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn invalidErrorCode() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn castTruncatedData() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn negativeToUnsigned() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn integerOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shlOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shrOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn divideByZero() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn exactDivisionRemainder() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn integerPartOutOfBounds() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn corruptSwitch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shiftRhsTooBig() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn invalidEnumValue() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn forLenMismatch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn memcpyLenMismatch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn memcpyAlias() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn noreturnReturned() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn sliceCastLenRemainder(_: usize) noreturn { - @branchHint(.cold); - abort(); - } -}; - -pub export fn main(argc: c_int, argv: [*:null]const ?[*:0]const u8, environment: [*:null]const ?[*:0]const u8) callconv(.C) c_int { - enable_signal_handlers(); - const arguments: []const [*:0]const u8 = @ptrCast(argv[0..@intCast(argc)]); - @import("root").entry_point(arguments, environment); - return 0; -} diff --git a/src/llvm.cpp b/src/llvm.cpp index 8df04f8..c6243b8 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -1,13 +1,4 @@ -#include - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -#define EXPORT extern "C" -#define fn static -#define array_length(arr) (sizeof(arr) / sizeof((arr)[0])) +#include #include "llvm/Config/llvm-config.h" @@ -35,104 +26,120 @@ typedef uint64_t u64; #include "lld/Common/CommonLinkerContext.h" - -using namespace llvm; - -struct BBLLVMString +fn llvm::StringRef string_ref(String string) { - const char* pointer; - size_t length; - - inline StringRef string_ref() const - { - return { pointer, length }; - } -}; - -EXPORT Module* llvm_context_create_module(LLVMContext& context, BBLLVMString name) -{ - return new Module(name.string_ref(), context); + return llvm::StringRef((char*)string.pointer, string.length); } -EXPORT unsigned llvm_integer_type_get_bit_count(const IntegerType& integer_type) +EXPORT LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name) +{ + auto module = new llvm::Module(string_ref(name), *llvm::unwrap(context)); + return wrap(module); +} + +EXPORT unsigned llvm_integer_type_get_bit_count(const llvm::IntegerType& integer_type) { auto result = integer_type.getBitWidth(); return result; } -EXPORT GlobalVariable* llvm_module_create_global_variable(Module& module, Type* type, bool is_constant, GlobalValue::LinkageTypes linkage_type, Constant* initial_value, BBLLVMString name, GlobalVariable* before, GlobalValue::ThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized) +EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized) { - auto* global_variable = new GlobalVariable(module, type, is_constant, linkage_type, initial_value, name.string_ref(), before, thread_local_mode, address_space, externally_initialized); - return global_variable; + llvm::GlobalValue::LinkageTypes linkage; + switch (linkage_type) + { + case LLVMExternalLinkage: linkage = llvm::GlobalValue::ExternalLinkage; break; + case LLVMInternalLinkage: linkage = llvm::GlobalValue::InternalLinkage; break; + default: trap(); + } + + llvm::GlobalValue::ThreadLocalMode tlm; + switch (thread_local_mode) + { + case LLVMNotThreadLocal: tlm = llvm::GlobalValue::NotThreadLocal; break; + default: trap(); + } + auto* global = new llvm::GlobalVariable(*llvm::unwrap(module), llvm::unwrap(type), is_constant, linkage, llvm::unwrap(initial_value), string_ref(name), before ? llvm::unwrap(before) : 0, tlm, address_space, externally_initialized); + return wrap(global); } -EXPORT void llvm_global_variable_add_debug_info(GlobalVariable& global_variable, DIGlobalVariableExpression* debug_global_variable) +EXPORT void llvm_global_variable_add_debug_info(llvm::GlobalVariable& global, llvm::DIGlobalVariableExpression* debug_global_variable) { - global_variable.addDebugInfo(debug_global_variable); + global.addDebugInfo(debug_global_variable); } -EXPORT void llvm_global_variable_delete(GlobalVariable* global_variable) +EXPORT void llvm_global_variable_delete(llvm::GlobalVariable* global) { - delete global_variable; + delete global; } -EXPORT void llvm_subprogram_replace_type(DISubprogram& subprogram, DISubroutineType* subroutine_type) +EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type) { - subprogram.replaceType(subroutine_type); + auto sp = llvm::unwrap(subprogram); + sp->replaceType(llvm::unwrap(subroutine_type)); } -EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, BBLLVMString name) +EXPORT LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name) { - auto* function = Function::Create(function_type, linkage_type, address_space, name.string_ref(), module); - return function; + llvm::GlobalValue::LinkageTypes llvm_linkage_type; + switch (linkage_type) + { + case LLVMExternalLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalLinkage; break; + case LLVMAvailableExternallyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AvailableExternallyLinkage; break; + case LLVMLinkOnceAnyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceAnyLinkage; break; + case LLVMLinkOnceODRLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::LinkOnceODRLinkage; break; + case LLVMWeakAnyLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakAnyLinkage; break; + case LLVMWeakODRLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::WeakODRLinkage; break; + case LLVMAppendingLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::AppendingLinkage; break; + case LLVMInternalLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::InternalLinkage; break; + case LLVMPrivateLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::PrivateLinkage; break; + case LLVMExternalWeakLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::ExternalWeakLinkage; break; + case LLVMCommonLinkage: + llvm_linkage_type = llvm::GlobalValue::LinkageTypes::CommonLinkage; break; + default: + trap(); + } + auto* function = llvm::Function::Create(llvm::unwrap(function_type), llvm_linkage_type, address_space, string_ref(name), llvm::unwrap(module)); + return wrap(function); } -EXPORT StructType* llvm_context_create_forward_declared_struct_type(LLVMContext& context, BBLLVMString name) +EXPORT LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function) { - auto* struct_type = StructType::create(context, name.string_ref()); - return struct_type; + auto* basic_block = llvm::BasicBlock::Create(*llvm::unwrap(context), string_ref(name), parent_function ? llvm::unwrap(parent_function) : 0); + return wrap(basic_block); } -EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, BBLLVMString name, bool is_packed) +EXPORT bool llvm_value_has_one_use(LLVMValueRef value) { - auto types = ArrayRef(type_pointer, type_count); - auto* struct_type = StructType::create(context, types, name.string_ref(), is_packed); - return struct_type; -} - -EXPORT StructType* llvm_context_get_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, bool is_packed) -{ - auto types = ArrayRef(type_pointer, type_count); - auto* struct_type = StructType::get(context, types, is_packed); - return struct_type; -} - -EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, BBLLVMString name, Function* parent) -{ - auto* basic_block = BasicBlock::Create(context, name.string_ref(), parent); - return basic_block; -} - -EXPORT bool llvm_value_has_one_use(Value& value) -{ - auto result = value.hasOneUse(); + auto v = llvm::unwrap(value); + auto result = v->hasOneUse(); return result; } -EXPORT Value* llvm_basic_block_user_begin(BasicBlock* basic_block) +EXPORT LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block) { - Value* value = *basic_block->user_begin(); - return value; + llvm::Value* value = *llvm::unwrap(basic_block)->user_begin(); + return wrap(value); } -EXPORT void llvm_basic_block_delete(BasicBlock* basic_block) +EXPORT void llvm_basic_block_delete(LLVMBasicBlockRef basic_block) { - delete basic_block; + delete llvm::unwrap(basic_block); } -EXPORT BranchInst* llvm_value_to_branch(Value* value) +EXPORT llvm::BranchInst* llvm_value_to_branch(llvm::Value* value) { - auto* result = dyn_cast(value); + auto* result = dyn_cast(value); return result; } @@ -140,7 +147,7 @@ EXPORT BranchInst* llvm_value_to_branch(Value* value) // for something immediately preceding the IP. Sometimes this can // happen with how we generate implicit-returns; it can also happen // with noreturn cleanups. -fn StoreInst* get_store_if_valid(User* user, Value* return_alloca, Type* element_type) +fn llvm::StoreInst* get_store_if_valid(llvm::User* user, llvm::Value* return_alloca, llvm::Type* element_type) { auto *SI = dyn_cast(user); if (!SI || SI->getPointerOperand() != return_alloca || @@ -158,62 +165,65 @@ fn StoreInst* get_store_if_valid(User* user, Value* return_alloca, Type* element // copy of static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) { // in clang/lib/CodeGen/CGCall.cpp:3526 in LLVM 19 -EXPORT StoreInst* llvm_find_return_value_dominating_store(IRBuilder<>& builder, Value* return_alloca, Type* element_type) +EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et) { - // Check if a User is a store which pointerOperand is the ReturnValue. - // We are looking for stores to the ReturnValue, not for stores of the - // ReturnValue to some other location. - if (!return_alloca->hasOneUse()) { - llvm::BasicBlock *IP = builder.GetInsertBlock(); - if (IP->empty()) return nullptr; + auto builder = llvm::unwrap(b); + auto return_alloca = llvm::unwrap(ra); + auto element_type = llvm::unwrap(et); + // Check if a User is a store which pointerOperand is the ReturnValue. + // We are looking for stores to the ReturnValue, not for stores of the + // ReturnValue to some other location. + if (!return_alloca->hasOneUse()) { + llvm::BasicBlock *IP = builder->GetInsertBlock(); + if (IP->empty()) return nullptr; - // Look at directly preceding instruction, skipping bitcasts and lifetime - // markers. - for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) { - if (isa(&I)) - continue; - if (auto *II = dyn_cast(&I)) - if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) - continue; + // Look at directly preceding instruction, skipping bitcasts and lifetime + // markers. + for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) { + if (isa(&I)) + continue; + if (auto *II = dyn_cast(&I)) + if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end) + continue; - return get_store_if_valid(&I, return_alloca, element_type); + return wrap(get_store_if_valid(&I, return_alloca, element_type)); + } + return nullptr; } - return nullptr; - } - llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type); - if (!store) return nullptr; + llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type); + if (!store) return nullptr; - // Now do a first-and-dirty dominance check: just walk up the - // single-predecessors chain from the current insertion point. - llvm::BasicBlock *StoreBB = store->getParent(); - llvm::BasicBlock *IP = builder.GetInsertBlock(); - llvm::SmallPtrSet SeenBBs; - while (IP != StoreBB) { - if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor())) - return nullptr; - } + // Now do a first-and-dirty dominance check: just walk up the + // single-predecessors chain from the current insertion point. + llvm::BasicBlock *StoreBB = store->getParent(); + llvm::BasicBlock *IP = builder->GetInsertBlock(); + llvm::SmallPtrSet SeenBBs; + while (IP != StoreBB) { + if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor())) + return nullptr; + } - // Okay, the store's basic block dominates the insertion point; we - // can do our thing. - return store; + // Okay, the store's basic block dominates the insertion point; we + // can do our thing. + return wrap(store); } -EXPORT bool llvm_value_use_empty(Value& value) +EXPORT bool llvm_value_use_empty(LLVMValueRef value) { - return value.use_empty(); + return llvm::unwrap(value)->use_empty(); } -EXPORT bool llvm_basic_block_is_empty(BasicBlock& basic_block) +EXPORT bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block) { - return basic_block.empty(); + return llvm::unwrap(basic_block)->empty(); } -EXPORT AllocaInst* llvm_builder_create_alloca(IRBuilder<>& builder, Type* type, unsigned address_space, BBLLVMString name) +EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef type, unsigned address_space, u32 alignment, String name) { - const DataLayout &data_layout = builder.GetInsertBlock()->getDataLayout(); - Align alignment = data_layout.getABITypeAlign(type); - return builder.Insert(new AllocaInst(type, address_space, 0, alignment), name.string_ref()); + auto& builder = *llvm::unwrap(b); + auto llvm_alignment = llvm::Align(alignment); + return wrap(builder.Insert(new llvm::AllocaInst(llvm::unwrap(type), address_space, 0, llvm_alignment), string_ref(name))); } enum class BBLLVMAttributeFramePointerKind : u8 @@ -279,445 +289,312 @@ enum class BBLLVMFPClassTest : unsigned AllFlags = Nan | Inf | Finite, }; -enum class BBLLVMUWTableKind +fn llvm::AttributeSet build_argument_attributes(LLVMContextRef context, BBLLVMArgumentAttributes* attributes) { - None = 0, ///< No unwind table requested - Sync = 1, ///< "Synchronous" unwind tables - Async = 2, ///< "Asynchronous" unwind tables (instr precise) - Default = 2, -}; + llvm::AttrBuilder builder(*llvm::unwrap(context)); -struct BBLLVMArgumentAttributes -{ - Type* semantic_type; - Type* abi_type; - u64 dereferenceable_bytes; - u32 alignment; - u32 no_alias:1; - u32 non_null:1; - u32 no_undef:1; - u32 sign_extend:1; - u32 zero_extend:1; - u32 in_reg:1; - u32 no_fp_class:10; - u32 struct_return:1; - u32 writable:1; - u32 dead_on_unwind:1; - u32 in_alloca:1; - u32 dereferenceable:1; - u32 dereferenceable_or_null:1; - u32 nest:1; - u32 by_value:1; - u32 by_reference:1; - u32 no_capture:1; - u32 _:6; -}; - -static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(Type*) + 2 * sizeof(u64)); - -fn AttributeSet build_argument_attributes(LLVMContext& context, const BBLLVMArgumentAttributes& attributes) -{ - AttrBuilder builder(context); - - if (attributes.alignment) + if (attributes->alignment) { - builder.addAlignmentAttr(attributes.alignment); + builder.addAlignmentAttr(attributes->alignment); } - if (attributes.no_alias) + if (attributes->no_alias) { - builder.addAttribute(Attribute::NoAlias); + builder.addAttribute(llvm::Attribute::NoAlias); } - if (attributes.non_null) + if (attributes->non_null) { - builder.addAttribute(Attribute::NonNull); + builder.addAttribute(llvm::Attribute::NonNull); } - if (attributes.no_undef) + if (attributes->no_undef) { - builder.addAttribute(Attribute::NoUndef); + builder.addAttribute(llvm::Attribute::NoUndef); } - if (attributes.sign_extend) + if (attributes->sign_extend) { - builder.addAttribute(Attribute::SExt); + builder.addAttribute(llvm::Attribute::SExt); } - if (attributes.zero_extend) + if (attributes->zero_extend) { - builder.addAttribute(Attribute::ZExt); + builder.addAttribute(llvm::Attribute::ZExt); } - if (attributes.in_reg) + if (attributes->in_reg) { - builder.addAttribute(Attribute::InReg); + builder.addAttribute(llvm::Attribute::InReg); } - if (attributes.no_fp_class) + if (attributes->no_fp_class) { __builtin_trap(); // TODO } - if (attributes.struct_return) + if (attributes->struct_return) { - builder.addStructRetAttr(attributes.semantic_type); + builder.addStructRetAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.writable) + if (attributes->writable) { - builder.addAttribute(Attribute::Writable); + builder.addAttribute(llvm::Attribute::Writable); } - if (attributes.dead_on_unwind) + if (attributes->dead_on_unwind) { - builder.addAttribute(Attribute::DeadOnUnwind); + builder.addAttribute(llvm::Attribute::DeadOnUnwind); } - if (attributes.in_alloca) + if (attributes->in_alloca) { __builtin_trap(); // TODO } - if (attributes.dereferenceable) + if (attributes->dereferenceable) { - builder.addDereferenceableAttr(attributes.dereferenceable_bytes); + builder.addDereferenceableAttr(attributes->dereferenceable_bytes); } - if (attributes.dereferenceable_or_null) + if (attributes->dereferenceable_or_null) { - builder.addDereferenceableOrNullAttr(attributes.dereferenceable_bytes); + builder.addDereferenceableOrNullAttr(attributes->dereferenceable_bytes); } - if (attributes.nest) + if (attributes->nest) { - builder.addAttribute(Attribute::Nest); + builder.addAttribute(llvm::Attribute::Nest); } - if (attributes.by_value) + if (attributes->by_value) { - builder.addByValAttr(attributes.semantic_type); + builder.addByValAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.by_reference) + if (attributes->by_reference) { - builder.addByRefAttr(attributes.semantic_type); + builder.addByRefAttr(llvm::unwrap(attributes->semantic_type)); } - if (attributes.no_capture) + if (attributes->no_capture) { - builder.addAttribute(Attribute::NoCapture); + builder.addAttribute(llvm::Attribute::NoCapture); } - auto attribute_set = AttributeSet::get(context, builder); + auto attribute_set = llvm::AttributeSet::get(*llvm::unwrap(context), builder); return attribute_set; } -struct BBLLVMFunctionAttributesFlags0 +inline fn BBLLVMAttributeList llvm_attribute_list_to_abi(llvm::AttributeList list) { - u64 noreturn:1; - u64 cmse_ns_call:1; - u64 nounwind:1; - u64 returns_twice:1; - u64 cold:1; - u64 hot:1; - u64 no_duplicate:1; - u64 convergent:1; - u64 no_merge:1; - u64 will_return:1; - u64 no_caller_saved_registers:1; - u64 no_cf_check:1; - u64 no_callback:1; - u64 alloc_size:1; - u64 uniform_work_group_size:1; - u64 aarch64_pstate_sm_body:1; - u64 aarch64_pstate_sm_enabled:1; - u64 aarch64_pstate_sm_compatible:1; - u64 aarch64_preserves_za:1; - u64 aarch64_in_za:1; - u64 aarch64_out_za:1; - u64 aarch64_inout_za:1; - u64 aarch64_preserves_zt0:1; - u64 aarch64_in_zt0:1; - u64 aarch64_out_zt0:1; - u64 aarch64_inout_zt0:1; - u64 optimize_for_size:1; - u64 min_size:1; - u64 no_red_zone:1; - u64 indirect_tls_seg_refs:1; - u64 no_implicit_floats:1; - u64 sample_profile_suffix_elision_policy:1; - u64 memory_none:1; - u64 memory_readonly:1; - u64 memory_inaccessible_or_arg_memory_only:1; - u64 memory_arg_memory_only:1; - u64 strict_fp:1; - u64 no_inline:1; - u64 always_inline:1; - u64 guard_no_cf:1; + static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); + static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); - // TODO: branch protection function attributes - // TODO: cpu features + return list.getRawPointer(); +} - // Call-site begin - u64 call_no_builtins:1; - - u64 definition_frame_pointer_kind:2; - u64 definition_less_precise_fpmad:1; - u64 definition_null_pointer_is_valid:1; - u64 definition_no_trapping_fp_math:1; - u64 definition_no_infs_fp_math:1; - u64 definition_no_nans_fp_math:1; - u64 definition_approx_func_fp_math:1; - u64 definition_unsafe_fp_math:1; - u64 definition_use_soft_float:1; - u64 definition_no_signed_zeroes_fp_math:1; - u64 definition_stack_realignment:1; - u64 definition_backchain:1; - u64 definition_split_stack:1; - u64 definition_speculative_load_hardening:1; - u64 definition_zero_call_used_registers:4; - // TODO: denormal builtins - u64 definition_non_lazy_bind:1; - u64 definition_cmse_nonsecure_entry:1; - u64 definition_unwind_table_kind:2; -}; - -static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64)); - -struct BBLLVMFunctionAttributesFlags1 +inline fn llvm::AttributeList abi_attribute_list_to_llvm(BBLLVMAttributeList list) { - u64 definition_disable_tail_calls:1; - u64 definition_stack_protect_strong:1; - u64 definition_stack_protect:1; - u64 definition_stack_protect_req:1; - u64 definition_aarch64_new_za:1; - u64 definition_aarch64_new_zt0:1; - u64 definition_optimize_none:1; - u64 definition_naked:1; - u64 definition_inline_hint:1; - u64 _:55; -}; + static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t)); + static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t)); + auto attribute_list = *(llvm::AttributeList*)&list; + return attribute_list; +} -static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64)); - -struct BBLLVMFunctionAttributes +EXPORT BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site) { - BBLLVMString prefer_vector_width; - BBLLVMString stack_protector_buffer_size; - BBLLVMString definition_probe_stack; - BBLLVMString definition_stack_probe_size; + llvm::AttrBuilder function_attribute_builder(*llvm::unwrap(context)); - BBLLVMFunctionAttributesFlags0 flags0; - BBLLVMFunctionAttributesFlags1 flags1; -}; - -static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64)); - -struct BBLLVMAttributeList -{ - BBLLVMFunctionAttributes function; - BBLLVMArgumentAttributes return_; - const BBLLVMArgumentAttributes* argument_pointer; - u64 argument_count; -}; - -static_assert(sizeof(BBLLVMAttributeList) == sizeof(BBLLVMFunctionAttributes) + sizeof(BBLLVMArgumentAttributes) + sizeof(void*) + sizeof(u64)); - -typedef void* BBLLVMAttributeListHandle; - -EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, const BBLLVMAttributeList& attributes, bool call_site) -{ - AttrBuilder function_attribute_builder(context); - - if (attributes.function.prefer_vector_width.length) + if (attributes->function.prefer_vector_width.length) { - function_attribute_builder.addAttribute("prefer-vector-width", attributes.function.prefer_vector_width.string_ref()); + function_attribute_builder.addAttribute("prefer-vector-width", string_ref(attributes->function.prefer_vector_width)); } - if (attributes.function.stack_protector_buffer_size.length) + if (attributes->function.stack_protector_buffer_size.length) { - function_attribute_builder.addAttribute("stack-protector-buffer-size", attributes.function.stack_protector_buffer_size.string_ref()); + function_attribute_builder.addAttribute("stack-protector-buffer-size", string_ref(attributes->function.stack_protector_buffer_size)); } - if (attributes.function.flags0.noreturn) + if (attributes->function.flags0.noreturn) { - function_attribute_builder.addAttribute(Attribute::NoReturn); + function_attribute_builder.addAttribute(llvm::Attribute::NoReturn); } - if (attributes.function.flags0.cmse_ns_call) + if (attributes->function.flags0.cmse_ns_call) { function_attribute_builder.addAttribute("cmse_nonsecure_call"); } - if (attributes.function.flags0.nounwind) + if (attributes->function.flags0.nounwind) { - function_attribute_builder.addAttribute(Attribute::NoUnwind); + function_attribute_builder.addAttribute(llvm::Attribute::NoUnwind); } - if (attributes.function.flags0.returns_twice) + if (attributes->function.flags0.returns_twice) { - function_attribute_builder.addAttribute(Attribute::ReturnsTwice); + function_attribute_builder.addAttribute(llvm::Attribute::ReturnsTwice); } - if (attributes.function.flags0.cold) + if (attributes->function.flags0.cold) { - function_attribute_builder.addAttribute(Attribute::Cold); + function_attribute_builder.addAttribute(llvm::Attribute::Cold); } - if (attributes.function.flags0.hot) + if (attributes->function.flags0.hot) { - function_attribute_builder.addAttribute(Attribute::Hot); + function_attribute_builder.addAttribute(llvm::Attribute::Hot); } - if (attributes.function.flags0.no_duplicate) + if (attributes->function.flags0.no_duplicate) { - function_attribute_builder.addAttribute(Attribute::NoDuplicate); + function_attribute_builder.addAttribute(llvm::Attribute::NoDuplicate); } - if (attributes.function.flags0.convergent) + if (attributes->function.flags0.convergent) { - function_attribute_builder.addAttribute(Attribute::Convergent); + function_attribute_builder.addAttribute(llvm::Attribute::Convergent); } - if (attributes.function.flags0.no_merge) + if (attributes->function.flags0.no_merge) { - function_attribute_builder.addAttribute(Attribute::NoMerge); + function_attribute_builder.addAttribute(llvm::Attribute::NoMerge); } - if (attributes.function.flags0.will_return) + if (attributes->function.flags0.will_return) { - function_attribute_builder.addAttribute(Attribute::WillReturn); + function_attribute_builder.addAttribute(llvm::Attribute::WillReturn); } - if (attributes.function.flags0.no_caller_saved_registers) + if (attributes->function.flags0.no_caller_saved_registers) { function_attribute_builder.addAttribute("no-caller-saved-registers"); } - if (attributes.function.flags0.no_cf_check) + if (attributes->function.flags0.no_cf_check) { - function_attribute_builder.addAttribute(Attribute::NoCfCheck); + function_attribute_builder.addAttribute(llvm::Attribute::NoCfCheck); } - if (attributes.function.flags0.no_callback) + if (attributes->function.flags0.no_callback) { - function_attribute_builder.addAttribute(Attribute::NoCallback); + function_attribute_builder.addAttribute(llvm::Attribute::NoCallback); } - if (attributes.function.flags0.alloc_size) + if (attributes->function.flags0.alloc_size) { __builtin_trap(); // TODO } - if (attributes.function.flags0.uniform_work_group_size) + if (attributes->function.flags0.uniform_work_group_size) { __builtin_trap(); // TODO } - if (attributes.function.flags0.aarch64_pstate_sm_body) + if (attributes->function.flags0.aarch64_pstate_sm_body) { function_attribute_builder.addAttribute("aarch64_pstate_sm_body"); } - if (attributes.function.flags0.aarch64_pstate_sm_enabled) + if (attributes->function.flags0.aarch64_pstate_sm_enabled) { function_attribute_builder.addAttribute("aarch64_pstate_sm_enabled"); } - if (attributes.function.flags0.aarch64_pstate_sm_compatible) + if (attributes->function.flags0.aarch64_pstate_sm_compatible) { function_attribute_builder.addAttribute("aarch64_pstate_sm_compatible"); } - if (attributes.function.flags0.aarch64_preserves_za) + if (attributes->function.flags0.aarch64_preserves_za) { function_attribute_builder.addAttribute("aarch64_preserves_za"); } - if (attributes.function.flags0.aarch64_in_za) + if (attributes->function.flags0.aarch64_in_za) { function_attribute_builder.addAttribute("aarch64_in_za"); } - if (attributes.function.flags0.aarch64_out_za) + if (attributes->function.flags0.aarch64_out_za) { function_attribute_builder.addAttribute("aarch64_out_za"); } - if (attributes.function.flags0.aarch64_inout_za) + if (attributes->function.flags0.aarch64_inout_za) { function_attribute_builder.addAttribute("aarch64_inout_za"); } - if (attributes.function.flags0.aarch64_preserves_zt0) + if (attributes->function.flags0.aarch64_preserves_zt0) { function_attribute_builder.addAttribute("aarch64_preserves_zt0"); } - if (attributes.function.flags0.aarch64_in_zt0) + if (attributes->function.flags0.aarch64_in_zt0) { function_attribute_builder.addAttribute("aarch64_in_zt0"); } - if (attributes.function.flags0.aarch64_out_zt0) + if (attributes->function.flags0.aarch64_out_zt0) { function_attribute_builder.addAttribute("aarch64_out_zt0"); } - if (attributes.function.flags0.aarch64_inout_zt0) + if (attributes->function.flags0.aarch64_inout_zt0) { function_attribute_builder.addAttribute("aarch64_inout_zt0"); } - if (attributes.function.flags0.optimize_for_size) + if (attributes->function.flags0.optimize_for_size) { - function_attribute_builder.addAttribute(Attribute::OptimizeForSize); + function_attribute_builder.addAttribute(llvm::Attribute::OptimizeForSize); } - if (attributes.function.flags0.min_size) + if (attributes->function.flags0.min_size) { - function_attribute_builder.addAttribute(Attribute::MinSize); + function_attribute_builder.addAttribute(llvm::Attribute::MinSize); } - if (attributes.function.flags0.no_red_zone) + if (attributes->function.flags0.no_red_zone) { - function_attribute_builder.addAttribute(Attribute::NoRedZone); + function_attribute_builder.addAttribute(llvm::Attribute::NoRedZone); } - if (attributes.function.flags0.indirect_tls_seg_refs) + if (attributes->function.flags0.indirect_tls_seg_refs) { function_attribute_builder.addAttribute("indirect-tls-seg-refs"); } - if (attributes.function.flags0.no_implicit_floats) + if (attributes->function.flags0.no_implicit_floats) { - function_attribute_builder.addAttribute(Attribute::NoImplicitFloat); + function_attribute_builder.addAttribute(llvm::Attribute::NoImplicitFloat); } - if (attributes.function.flags0.sample_profile_suffix_elision_policy) + if (attributes->function.flags0.sample_profile_suffix_elision_policy) { function_attribute_builder.addAttribute("sample-profile-suffix-elision-policy", "selected"); } - if (attributes.function.flags0.memory_none) + if (attributes->function.flags0.memory_none) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::none()); } - if (attributes.function.flags0.memory_readonly) + if (attributes->function.flags0.memory_readonly) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::readOnly()); } - if (attributes.function.flags0.memory_inaccessible_or_arg_memory_only) + if (attributes->function.flags0.memory_inaccessible_or_arg_memory_only) { function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly()); } - if (attributes.function.flags0.memory_arg_memory_only) + if (attributes->function.flags0.memory_arg_memory_only) { - Attribute attribute = function_attribute_builder.getAttribute(Attribute::Memory); + llvm::Attribute attribute = function_attribute_builder.getAttribute(llvm::Attribute::Memory); function_attribute_builder.addMemoryAttr(attribute.getMemoryEffects() | llvm::MemoryEffects::argMemOnly()); } @@ -727,25 +604,25 @@ EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, if (call_site) { - if (attributes.function.flags0.call_no_builtins) + if (attributes->function.flags0.call_no_builtins) { - function_attribute_builder.addAttribute(Attribute::NoBuiltin); + function_attribute_builder.addAttribute(llvm::Attribute::NoBuiltin); } } else { - if (attributes.function.definition_probe_stack.length) + if (attributes->function.definition_probe_stack.length) { - function_attribute_builder.addAttribute("probe-stack", attributes.function.definition_probe_stack.string_ref()); + function_attribute_builder.addAttribute("probe-stack", string_ref(attributes->function.definition_probe_stack)); } - if (attributes.function.definition_stack_probe_size.length) + if (attributes->function.definition_stack_probe_size.length) { - function_attribute_builder.addAttribute("stack-probe-size", attributes.function.definition_stack_probe_size.string_ref()); + function_attribute_builder.addAttribute("stack-probe-size", string_ref(attributes->function.definition_stack_probe_size)); } - StringRef frame_pointer_kind_name; - switch ((BBLLVMAttributeFramePointerKind) attributes.function.flags0.definition_frame_pointer_kind) + llvm::StringRef frame_pointer_kind_name; + switch ((BBLLVMAttributeFramePointerKind) attributes->function.flags0.definition_frame_pointer_kind) { case BBLLVMAttributeFramePointerKind::None: frame_pointer_kind_name = "none"; break; case BBLLVMAttributeFramePointerKind::Reserved: frame_pointer_kind_name = "reserved"; break; @@ -754,184 +631,184 @@ EXPORT BBLLVMAttributeListHandle llvm_attribute_list_build(LLVMContext& context, } function_attribute_builder.addAttribute("frame-pointer", frame_pointer_kind_name); - if (attributes.function.flags0.definition_less_precise_fpmad) + if (attributes->function.flags0.definition_less_precise_fpmad) { function_attribute_builder.addAttribute("less-precise-fp-mad", "true"); } - if (attributes.function.flags0.definition_null_pointer_is_valid) + if (attributes->function.flags0.definition_null_pointer_is_valid) { - function_attribute_builder.addAttribute(Attribute::NullPointerIsValid); + function_attribute_builder.addAttribute(llvm::Attribute::NullPointerIsValid); } - if (attributes.function.flags0.definition_no_trapping_fp_math) + if (attributes->function.flags0.definition_no_trapping_fp_math) { function_attribute_builder.addAttribute("no-trapping-math", "true"); } - if (attributes.function.flags0.definition_no_infs_fp_math) + if (attributes->function.flags0.definition_no_infs_fp_math) { function_attribute_builder.addAttribute("no-infs-fp-math", "true"); } - if (attributes.function.flags0.definition_no_nans_fp_math) + if (attributes->function.flags0.definition_no_nans_fp_math) { function_attribute_builder.addAttribute("no-nans-fp-math", "true"); } - if (attributes.function.flags0.definition_approx_func_fp_math) + if (attributes->function.flags0.definition_approx_func_fp_math) { function_attribute_builder.addAttribute("approx-func-fp-math", "true"); } - if (attributes.function.flags0.definition_unsafe_fp_math) + if (attributes->function.flags0.definition_unsafe_fp_math) { function_attribute_builder.addAttribute("unsafe-fp-math", "true"); } - if (attributes.function.flags0.definition_use_soft_float) + if (attributes->function.flags0.definition_use_soft_float) { function_attribute_builder.addAttribute("use-soft-float", "true"); } - if (attributes.function.flags0.definition_no_signed_zeroes_fp_math) + if (attributes->function.flags0.definition_no_signed_zeroes_fp_math) { function_attribute_builder.addAttribute("no-signed-zeros-fp-math", "true"); } - if (attributes.function.flags0.definition_stack_realignment) + if (attributes->function.flags0.definition_stack_realignment) { function_attribute_builder.addAttribute("stackrealign"); } - if (attributes.function.flags0.definition_backchain) + if (attributes->function.flags0.definition_backchain) { function_attribute_builder.addAttribute("backchain"); } - if (attributes.function.flags0.definition_split_stack) + if (attributes->function.flags0.definition_split_stack) { function_attribute_builder.addAttribute("split-stack"); } - if (attributes.function.flags0.definition_speculative_load_hardening) + if (attributes->function.flags0.definition_speculative_load_hardening) { function_attribute_builder.addAttribute("split-stack"); } - if (attributes.function.flags0.definition_zero_call_used_registers) + if (attributes->function.flags0.definition_zero_call_used_registers != ZeroCallUsedRegsKind::all) { __builtin_trap(); // TODO } // TODO: denormal builtins - if (attributes.function.flags0.definition_non_lazy_bind) + if (attributes->function.flags0.definition_non_lazy_bind) { - function_attribute_builder.addAttribute(Attribute::NonLazyBind); + function_attribute_builder.addAttribute(llvm::Attribute::NonLazyBind); } - if (attributes.function.flags0.definition_cmse_nonsecure_entry) + if (attributes->function.flags0.definition_cmse_nonsecure_entry) { function_attribute_builder.addAttribute("cmse_nonsecure_entry"); } - UWTableKind unwind_table_kind; - switch ((BBLLVMUWTableKind)attributes.function.flags0.definition_unwind_table_kind) + llvm::UWTableKind unwind_table_kind; + switch ((BBLLVMUWTableKind)attributes->function.flags0.definition_unwind_table_kind) { - case BBLLVMUWTableKind::None: unwind_table_kind = UWTableKind::None; break; - case BBLLVMUWTableKind::Sync: unwind_table_kind = UWTableKind::Sync; break; - case BBLLVMUWTableKind::Async: unwind_table_kind = UWTableKind::Async; break; + case BBLLVMUWTableKind::None: unwind_table_kind = llvm::UWTableKind::None; break; + case BBLLVMUWTableKind::Sync: unwind_table_kind = llvm::UWTableKind::Sync; break; + case BBLLVMUWTableKind::Async: unwind_table_kind = llvm::UWTableKind::Async; break; } function_attribute_builder.addUWTableAttr(unwind_table_kind); - if (attributes.function.flags1.definition_disable_tail_calls) + if (attributes->function.flags1.definition_disable_tail_calls) { function_attribute_builder.addAttribute("disable-tail-calls", "true"); } - if (attributes.function.flags1.definition_stack_protect_strong) + if (attributes->function.flags1.definition_stack_protect_strong) { - function_attribute_builder.addAttribute(Attribute::StackProtectStrong); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtectStrong); } - if (attributes.function.flags1.definition_stack_protect) + if (attributes->function.flags1.definition_stack_protect) { - function_attribute_builder.addAttribute(Attribute::StackProtect); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtect); } - if (attributes.function.flags1.definition_stack_protect_req) + if (attributes->function.flags1.definition_stack_protect_req) { - function_attribute_builder.addAttribute(Attribute::StackProtectReq); + function_attribute_builder.addAttribute(llvm::Attribute::StackProtectReq); } - if (attributes.function.flags1.definition_aarch64_new_za) + if (attributes->function.flags1.definition_aarch64_new_za) { function_attribute_builder.addAttribute("aarch64_new_za"); } - if (attributes.function.flags1.definition_aarch64_new_zt0) + if (attributes->function.flags1.definition_aarch64_new_zt0) { function_attribute_builder.addAttribute("aarch64_new_zt0"); } - if (attributes.function.flags1.definition_optimize_none) + if (attributes->function.flags1.definition_optimize_none) { - function_attribute_builder.addAttribute(Attribute::OptimizeNone); + function_attribute_builder.addAttribute(llvm::Attribute::OptimizeNone); } - if (attributes.function.flags1.definition_naked) + if (attributes->function.flags1.definition_naked) { - function_attribute_builder.addAttribute(Attribute::Naked); + function_attribute_builder.addAttribute(llvm::Attribute::Naked); } - if (attributes.function.flags1.definition_inline_hint) + if (attributes->function.flags1.definition_inline_hint) { - function_attribute_builder.addAttribute(Attribute::InlineHint); + function_attribute_builder.addAttribute(llvm::Attribute::InlineHint); } } - auto function_attributes = AttributeSet::get(context, function_attribute_builder); + auto function_attributes = llvm::AttributeSet::get(*llvm::unwrap(context), function_attribute_builder); - auto return_attributes = build_argument_attributes(context, attributes.return_); + auto return_attributes = build_argument_attributes(context, &attributes->return_); - AttributeSet argument_attribute_buffer[128]; - assert(attributes.argument_count < array_length(argument_attribute_buffer)); + llvm::AttributeSet argument_attribute_buffer[128]; + assert(attributes->argument_count < array_length(argument_attribute_buffer)); - for (u64 i = 0; i < attributes.argument_count; i += 1) + for (u64 i = 0; i < attributes->argument_count; i += 1) { - auto attribute_set = build_argument_attributes(context, attributes.argument_pointer[i]); + auto attribute_set = build_argument_attributes(context, &attributes->argument_pointer[i]); argument_attribute_buffer[i] = attribute_set; } - ArrayRef argument_attributes = ArrayRef(argument_attribute_buffer, attributes.argument_count); + llvm::ArrayRef argument_attributes = llvm::ArrayRef(argument_attribute_buffer, attributes->argument_count); - auto attribute_list = AttributeList::get(context, function_attributes, return_attributes, argument_attributes); + auto attribute_list = llvm::AttributeList::get(*llvm::unwrap(context), function_attributes, return_attributes, argument_attributes); - static_assert(sizeof(AttributeList) == sizeof(uintptr_t)); - - return *(BBLLVMAttributeListHandle*)&attribute_list; + return llvm_attribute_list_to_abi(attribute_list); } -EXPORT bool llvm_instruction_is_call_base(Instruction* instruction) +EXPORT bool llvm_instruction_is_call_base(llvm::Instruction* instruction) { - return isa(instruction); + return isa(instruction); } -EXPORT void llvm_function_set_attributes(Function& function, BBLLVMAttributeListHandle attribute_list_handle) +EXPORT void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle) { - auto attribute_list = *(AttributeList*)&attribute_list_handle; - function.setAttributes(attribute_list); + auto* function = llvm::unwrap(f); + auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle); + function->setAttributes(attribute_list); } -EXPORT void llvm_call_base_set_attributes(CallBase& call, BBLLVMAttributeListHandle attribute_list_handle) +EXPORT void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle) { - auto attribute_list = *(AttributeList*)&attribute_list_handle; - call.setAttributes(attribute_list); + auto call = llvm::unwrap(call_value); + auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle); + call->setAttributes(attribute_list); } -fn BBLLVMString stream_to_string(raw_string_ostream& stream) +fn String stream_to_string(llvm::raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based stream.flush(); @@ -939,86 +816,87 @@ fn BBLLVMString stream_to_string(raw_string_ostream& stream) auto string = stream.str(); auto length = string.length(); - char* result = 0; + u8* result = 0; if (length) { - result = new char[length]; + result = new u8[length]; memcpy(result, string.c_str(), length); } - return { result, length }; + return String{ result, length }; } -EXPORT BBLLVMString llvm_function_to_string(Function& function) +EXPORT String llvm_function_to_string(llvm::Function& function) { std::string buffer; - raw_string_ostream os(buffer); + llvm::raw_string_ostream os(buffer); function.print(os); os.flush(); auto result = stream_to_string(os); return result; } -EXPORT bool llvm_function_verify(Function& function, BBLLVMString* error_message) +EXPORT bool llvm_function_verify(LLVMValueRef function_value, String* error_message) { std::string message_buffer; - raw_string_ostream message_stream(message_buffer); + llvm::raw_string_ostream message_stream(message_buffer); + auto& function = *llvm::unwrap(function_value); bool result = verifyFunction(function, &message_stream); - auto size = message_stream.str().size(); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT bool llvm_module_verify(const Module& module, BBLLVMString* error_message) +EXPORT bool llvm_module_verify(LLVMModuleRef m, String* error_message) { std::string message_buffer; - raw_string_ostream message_stream(message_buffer); + llvm::raw_string_ostream message_stream(message_buffer); - bool result = verifyModule(module, &message_stream); + const auto& module = *llvm::unwrap(m); + bool result = llvm::verifyModule(module, &message_stream); *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT BBLLVMString llvm_module_to_string(Module* module) +EXPORT String llvm_module_to_string(LLVMModuleRef module) { std::string buffer; - raw_string_ostream stream(buffer); - module->print(stream, 0); + llvm::raw_string_ostream stream(buffer); + llvm::unwrap(module)->print(stream, 0); return stream_to_string(stream); } -EXPORT BBLLVMString llvm_default_target_triple() +EXPORT String llvm_default_target_triple() { auto triple = llvm::sys::getDefaultTargetTriple(); auto length = triple.length(); - char* pointer = 0; + u8* pointer = 0; if (length) { - pointer = new char[length]; + pointer = new u8[length]; memcpy(pointer, triple.c_str(), length); } return { pointer, length }; } -EXPORT BBLLVMString llvm_host_cpu_name() +EXPORT String llvm_host_cpu_name() { auto cpu = llvm::sys::getHostCPUName(); - return { cpu.data(), cpu.size() }; + return { (u8*)cpu.data(), cpu.size() }; } -EXPORT BBLLVMString llvm_host_cpu_features() +EXPORT String llvm_host_cpu_features() { - SubtargetFeatures Features; + llvm::SubtargetFeatures Features; #if LLVM_VERSION_MAJOR >= 19 - auto host_cpu_features = sys::getHostCPUFeatures(); + auto host_cpu_features = llvm::sys::getHostCPUFeatures(); #else StringMap host_cpu_features; if (!sys::getHostCPUFeatures(host_cpu_features)) { @@ -1034,593 +912,311 @@ EXPORT BBLLVMString llvm_host_cpu_features() auto feature_string = Features.getString(); auto length = feature_string.length(); - char* result = 0; + u8* result = 0; if (length) { - result = new char[length]; + result = new u8[length]; memcpy(result, feature_string.c_str(), length); } return { result, length }; } -enum class BBLLVMEmitDwarfUnwindType : u8 -{ - always = 0, - no_compact_unwind = 1, - normal = 2, -}; - -enum class BBLLVMDwarfDirectory : u8 -{ - disable = 0, - enable = 1, - normal = 2, -}; - -enum class BBLLVMDebugCompressionType : u8 -{ - none = 0, - zlib = 1, - zstd = 2, -}; - -#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) - -struct BBLLVMMCTargetOptions -{ - BBLLVMString abi_name; - BBLLVMString assembly_language; - BBLLVMString split_dwarf_file; - BBLLVMString as_secure_log_file; - const char* argv0; - BBLLVMString* argv_pointer; - u64 argv_count; - BBLLVMString* integrated_assembler_search_path_pointer; - u64 integrated_assembler_search_path_count; - u32 relax_all:1; - u32 no_exec_stack:1; - u32 fatal_warnings:1; - u32 no_warn:1; - u32 no_deprecated_warn:1; - u32 no_type_check:1; - u32 save_temp_labels:1; - u32 incremental_linker_compatible:1; - u32 fdpic:1; - u32 show_mc_encoding:1; - u32 show_mc_inst:1; - u32 asm_verbose:1; - u32 preserve_asm_comments:1; - u32 dwarf64:1; - u32 crel:1; - u32 x86_relax_relocations:1; - u32 x86_sse2_avx:1; - u32 emit_dwarf_unwind:2; - u32 use_dwarf_directory:2; - u32 debug_compression_type:2; - u32 emit_compact_unwind_non_canonical:1; - u32 ppc_use_full_register_names:1; - u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMMCTargetOptions) == 112); -static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); - -enum class BBLLVMCodeModel : u8 -{ - none = 0, - tiny = 1, - small = 2, - kernel = 3, - medium = 4, - large = 5, -}; - -enum class BBLLVMRelocationModel : u8 -{ - default_relocation = 0, - static_relocation = 1, - pic = 2, - dynamic_no_pic = 3, - ropi = 4, - rwpi = 5, - ropi_rwpi = 6, -}; - -enum class BBLLVMCodeGenerationOptimizationLevel : u8 -{ - none = 0, // -O0 - less = 1, // -O1 - normal = 2, // -O2, -Os - aggressive = 3 // -O3 -}; - -enum class BBLLVMGlobalISelAbortMode : u8 -{ - disable = 0, - enable = 1, - disable_with_diag = 2, -}; - -enum class BBLLVMSwiftAsyncFramePointerMode : u8 -{ - deployment_based = 0, - always = 1, - never = 2, -}; - -enum class BBLLVMBasicBlockSection : u8 -{ - all = 0, - list = 1, - preset = 2, - none = 3, -}; - -enum class BBLLVMFloatAbi : u8 -{ - normal = 0, - soft = 1, - hard = 2, -}; - -enum class BBLLVMFPOpFusion : u8 -{ - fast = 0, - standard = 1, - strict = 2, -}; - -enum class BBLLVMThreadModel : u8 -{ - posix = 0, - single = 1, -}; - -enum class BBLLVMEAbi : u8 -{ - unknown = 0, - normal = 1, - eabi4 = 2, - eabi5 = 3, - gnu = 4, -}; - -enum class BBLLVMDebuggerKind : u8 -{ - normal = 0, - gdb = 1, - lldb = 2, - sce = 3, - dbx = 4, -}; - -enum class BBLLVMExceptionHandling : u8 -{ - none = 0, - dwarf_cfi = 1, - setjmp_longjmp = 2, - arm = 3, - win_eh = 4, - wasm = 5, - aix = 6, - zos = 7, -}; - -#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) - -struct BBLLVMTargetOptions -{ - u64 unsafe_fp_math:1; - u64 no_infs_fp_math:1; - u64 no_nans_fp_math:1; - u64 no_trapping_fp_math:1; - u64 no_signed_zeroes_fp_math:1; - u64 approx_func_fp_math:1; - u64 enable_aix_extended_altivec_abi:1; - u64 honor_sign_dependent_rounding_fp_math:1; - u64 no_zeroes_in_bss:1; - u64 guaranteed_tail_call_optimization:1; - u64 stack_symbol_ordering:1; - u64 enable_fast_isel:1; - u64 enable_global_isel:1; - u64 global_isel_abort_mode:2; - u64 swift_async_frame_pointer:2; - u64 use_init_array:1; - u64 disable_integrated_assembler:1; - u64 function_sections:1; - u64 data_sections:1; - u64 ignore_xcoff_visibility:1; - u64 xcoff_traceback_table:1; - u64 unique_section_names:1; - u64 unique_basic_block_section_names:1; - u64 separate_named_sections:1; - u64 trap_unreachable:1; - u64 no_trap_after_noreturn:1; - u64 tls_size:8; - u64 emulated_tls:1; - u64 enable_tls_descriptors:1; - u64 enable_ipra:1; - u64 emit_stack_size_section:1; - u64 enable_machine_outliner:1; - u64 enable_machine_function_splitter:1; - u64 supports_default_outlining:1; - u64 emit_address_significance_table:1; - u64 bb_address_map:1; - u64 bb_sections:3; - u64 emit_call_site_information:1; - u64 supports_debug_entry_values:1; - u64 enable_debug_entry_values:1; - u64 value_tracking_variable_locations:1; - u64 force_dwarf_frame_section:1; - u64 xray_function_index:1; - u64 debug_strict_dwarf:1; - u64 hotpatch:1; - u64 ppc_gen_scalar_mass_entries:1; - u64 jmc_instrument:1; - u64 enable_cfi_fixup:1; - u64 mis_expect:1; - u64 xcoff_read_only_pointers:1; - u64 float_abi:2; - u64 thread_model:1; - u32 fp_op_fusion_mode:2; - u32 eabi_version:3; - u32 debugger_kind:3; - u32 exception_handling:3; - u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; - unsigned loop_alignment; - int binutils_version[2]; - - BBLLVMMCTargetOptions mc; -}; - -static_assert(sizeof(BBLLVMTargetOptions) == 136); -static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); - -#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) - -struct BBLLVMTargetMachineCreate -{ - BBLLVMTargetOptions target_options; - BBLLVMString target_triple; - BBLLVMString cpu_model; - BBLLVMString cpu_features; - BBLLVMRelocationModel relocation_model; - BBLLVMCodeModel code_model; - BBLLVMCodeGenerationOptimizationLevel optimization_level; - bool jit; - u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; -}; - -static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); -static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); - -EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, BBLLVMString* error_message) +EXPORT LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message) { std::string error_message_string; - const Target* target = TargetRegistry::lookupTarget(create.target_triple.string_ref(), error_message_string); - - TargetMachine* target_machine; + const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create->target_triple), error_message_string); if (target) { - std::optional code_model; - switch (create.code_model) + std::optional code_model; + switch (create->code_model) { case BBLLVMCodeModel::none: code_model = std::nullopt; break; - case BBLLVMCodeModel::tiny: code_model = CodeModel::Tiny; break; - case BBLLVMCodeModel::small: code_model = CodeModel::Small; break; - case BBLLVMCodeModel::kernel: code_model = CodeModel::Kernel; break; - case BBLLVMCodeModel::medium: code_model = CodeModel::Medium; break; - case BBLLVMCodeModel::large: code_model = CodeModel::Large; break; + case BBLLVMCodeModel::tiny: code_model = llvm::CodeModel::Tiny; break; + case BBLLVMCodeModel::small: code_model = llvm::CodeModel::Small; break; + case BBLLVMCodeModel::kernel: code_model = llvm::CodeModel::Kernel; break; + case BBLLVMCodeModel::medium: code_model = llvm::CodeModel::Medium; break; + case BBLLVMCodeModel::large: code_model = llvm::CodeModel::Large; break; } - std::optional relocation_model; + std::optional relocation_model; - switch (create.relocation_model) + switch (create->relocation_model) { case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break; - case BBLLVMRelocationModel::static_relocation: relocation_model = Reloc::Static; break; - case BBLLVMRelocationModel::pic: relocation_model = Reloc::PIC_; break; - case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = Reloc::DynamicNoPIC; break; - case BBLLVMRelocationModel::ropi: relocation_model = Reloc::ROPI; break; - case BBLLVMRelocationModel::rwpi: relocation_model = Reloc::RWPI; break; - case BBLLVMRelocationModel::ropi_rwpi: relocation_model = Reloc::ROPI_RWPI; break; + case BBLLVMRelocationModel::static_relocation: relocation_model = llvm::Reloc::Static; break; + case BBLLVMRelocationModel::pic: relocation_model = llvm::Reloc::PIC_; break; + case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = llvm::Reloc::DynamicNoPIC; break; + case BBLLVMRelocationModel::ropi: relocation_model = llvm::Reloc::ROPI; break; + case BBLLVMRelocationModel::rwpi: relocation_model = llvm::Reloc::RWPI; break; + case BBLLVMRelocationModel::ropi_rwpi: relocation_model = llvm::Reloc::ROPI_RWPI; break; } - CodeGenOptLevel optimization_level; - switch (create.optimization_level) + llvm::CodeGenOptLevel optimization_level; + switch (create->optimization_level) { - case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = CodeGenOptLevel::None; break; - case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = CodeGenOptLevel::Less; break; - case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = CodeGenOptLevel::Default; break; - case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = CodeGenOptLevel::Aggressive; break; + case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = llvm::CodeGenOptLevel::None; break; + case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = llvm::CodeGenOptLevel::Less; break; + case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = llvm::CodeGenOptLevel::Default; break; + case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = llvm::CodeGenOptLevel::Aggressive; break; } // INFO: This calls the default constructor, so all LLVM defaults are set and we only override what we control - TargetOptions target_options; + llvm::TargetOptions target_options; - target_options.UnsafeFPMath = create.target_options.unsafe_fp_math; - target_options.NoInfsFPMath = create.target_options.no_infs_fp_math; - target_options.NoNaNsFPMath = create.target_options.no_nans_fp_math; - target_options.NoTrappingFPMath = create.target_options.no_trapping_fp_math; - target_options.NoSignedZerosFPMath = create.target_options.no_signed_zeroes_fp_math; - target_options.ApproxFuncFPMath = create.target_options.approx_func_fp_math; - target_options.EnableAIXExtendedAltivecABI = create.target_options.enable_aix_extended_altivec_abi; - target_options.HonorSignDependentRoundingFPMathOption = create.target_options.honor_sign_dependent_rounding_fp_math; - target_options.NoZerosInBSS = create.target_options.no_zeroes_in_bss; - target_options.GuaranteedTailCallOpt = create.target_options.guaranteed_tail_call_optimization; - target_options.StackSymbolOrdering = create.target_options.stack_symbol_ordering; - target_options.EnableFastISel = create.target_options.enable_fast_isel; - target_options.EnableGlobalISel = create.target_options.enable_global_isel; + target_options.UnsafeFPMath = create->target_options.unsafe_fp_math; + target_options.NoInfsFPMath = create->target_options.no_infs_fp_math; + target_options.NoNaNsFPMath = create->target_options.no_nans_fp_math; + target_options.NoTrappingFPMath = create->target_options.no_trapping_fp_math; + target_options.NoSignedZerosFPMath = create->target_options.no_signed_zeroes_fp_math; + target_options.ApproxFuncFPMath = create->target_options.approx_func_fp_math; + target_options.EnableAIXExtendedAltivecABI = create->target_options.enable_aix_extended_altivec_abi; + target_options.HonorSignDependentRoundingFPMathOption = create->target_options.honor_sign_dependent_rounding_fp_math; + target_options.NoZerosInBSS = create->target_options.no_zeroes_in_bss; + target_options.GuaranteedTailCallOpt = create->target_options.guaranteed_tail_call_optimization; + target_options.StackSymbolOrdering = create->target_options.stack_symbol_ordering; + target_options.EnableFastISel = create->target_options.enable_fast_isel; + target_options.EnableGlobalISel = create->target_options.enable_global_isel; - auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create.target_options.global_isel_abort_mode; + auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create->target_options.global_isel_abort_mode; switch (global_isel_abort_mode) { - case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = GlobalISelAbortMode::Disable; break; - case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = GlobalISelAbortMode::Enable; break; - case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = GlobalISelAbortMode::DisableWithDiag; break; + case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Disable; break; + case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Enable; break; + case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::DisableWithDiag; break; } - auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create.target_options.swift_async_frame_pointer; + auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create->target_options.swift_async_frame_pointer; switch (swift_async_frame_pointer) { - case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::DeploymentBased; break; - case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Always; break; - case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = SwiftAsyncFramePointerMode::Never; break; + case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::DeploymentBased; break; + case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Always; break; + case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Never; break; } - target_options.UseInitArray = create.target_options.use_init_array; - target_options.DisableIntegratedAS = create.target_options.disable_integrated_assembler; - target_options.FunctionSections = create.target_options.function_sections; - target_options.DataSections = create.target_options.data_sections; - target_options.IgnoreXCOFFVisibility = create.target_options.ignore_xcoff_visibility; - target_options.XCOFFTracebackTable = create.target_options.xcoff_traceback_table; - target_options.UniqueSectionNames = create.target_options.unique_section_names; - target_options.UniqueBasicBlockSectionNames = create.target_options.unique_basic_block_section_names; + target_options.UseInitArray = create->target_options.use_init_array; + target_options.DisableIntegratedAS = create->target_options.disable_integrated_assembler; + target_options.FunctionSections = create->target_options.function_sections; + target_options.DataSections = create->target_options.data_sections; + target_options.IgnoreXCOFFVisibility = create->target_options.ignore_xcoff_visibility; + target_options.XCOFFTracebackTable = create->target_options.xcoff_traceback_table; + target_options.UniqueSectionNames = create->target_options.unique_section_names; + target_options.UniqueBasicBlockSectionNames = create->target_options.unique_basic_block_section_names; #if LLVM_VERSION_MAJOR >= 19 - target_options.SeparateNamedSections = create.target_options.separate_named_sections; + target_options.SeparateNamedSections = create->target_options.separate_named_sections; #endif - target_options.TrapUnreachable = create.target_options.trap_unreachable; - target_options.NoTrapAfterNoreturn = create.target_options.no_trap_after_noreturn; - target_options.TLSSize = create.target_options.tls_size; - target_options.EmulatedTLS = create.target_options.emulated_tls; - target_options.EnableTLSDESC = create.target_options.enable_tls_descriptors; - target_options.EnableIPRA = create.target_options.enable_ipra; - target_options.EmitStackSizeSection = create.target_options.emit_stack_size_section; - target_options.EnableMachineOutliner = create.target_options.enable_machine_outliner; - target_options.EnableMachineFunctionSplitter = create.target_options.enable_machine_function_splitter; - target_options.SupportsDefaultOutlining = create.target_options.supports_default_outlining; - target_options.EmitAddrsig = create.target_options.emit_address_significance_table; + target_options.TrapUnreachable = create->target_options.trap_unreachable; + target_options.NoTrapAfterNoreturn = create->target_options.no_trap_after_noreturn; + target_options.TLSSize = create->target_options.tls_size; + target_options.EmulatedTLS = create->target_options.emulated_tls; + target_options.EnableTLSDESC = create->target_options.enable_tls_descriptors; + target_options.EnableIPRA = create->target_options.enable_ipra; + target_options.EmitStackSizeSection = create->target_options.emit_stack_size_section; + target_options.EnableMachineOutliner = create->target_options.enable_machine_outliner; + target_options.EnableMachineFunctionSplitter = create->target_options.enable_machine_function_splitter; + target_options.SupportsDefaultOutlining = create->target_options.supports_default_outlining; + target_options.EmitAddrsig = create->target_options.emit_address_significance_table; #if LLVM_VERSION_MAJOR >= 19 - target_options.BBAddrMap = create.target_options.bb_address_map; + target_options.BBAddrMap = create->target_options.bb_address_map; #endif - auto bb_sections = (BBLLVMBasicBlockSection) create.target_options.bb_sections; + auto bb_sections = (BBLLVMBasicBlockSection) create->target_options.bb_sections; switch (bb_sections) { - case BBLLVMBasicBlockSection::all: target_options.BBSections = BasicBlockSection::All; break; - case BBLLVMBasicBlockSection::list: target_options.BBSections = BasicBlockSection::List; break; - case BBLLVMBasicBlockSection::preset: target_options.BBSections = BasicBlockSection::Preset; break; - case BBLLVMBasicBlockSection::none: target_options.BBSections = BasicBlockSection::None; break; + case BBLLVMBasicBlockSection::all: target_options.BBSections = llvm::BasicBlockSection::All; break; + case BBLLVMBasicBlockSection::list: target_options.BBSections = llvm::BasicBlockSection::List; break; + case BBLLVMBasicBlockSection::preset: target_options.BBSections = llvm::BasicBlockSection::Preset; break; + case BBLLVMBasicBlockSection::none: target_options.BBSections = llvm::BasicBlockSection::None; break; } - target_options.EmitCallSiteInfo = create.target_options.emit_call_site_information; - target_options.SupportsDebugEntryValues = create.target_options.supports_debug_entry_values; - target_options.EnableDebugEntryValues = create.target_options.enable_debug_entry_values; - target_options.ValueTrackingVariableLocations = create.target_options.value_tracking_variable_locations; - target_options.ForceDwarfFrameSection = create.target_options.force_dwarf_frame_section; - target_options.XRayFunctionIndex = create.target_options.xray_function_index; - target_options.DebugStrictDwarf = create.target_options.debug_strict_dwarf; - target_options.Hotpatch = create.target_options.hotpatch; - target_options.PPCGenScalarMASSEntries = create.target_options.ppc_gen_scalar_mass_entries; - target_options.JMCInstrument = create.target_options.jmc_instrument; - target_options.EnableCFIFixup = create.target_options.enable_cfi_fixup; - target_options.MisExpect = create.target_options.mis_expect; - target_options.XCOFFReadOnlyPointers = create.target_options.xcoff_read_only_pointers; + target_options.EmitCallSiteInfo = create->target_options.emit_call_site_information; + target_options.SupportsDebugEntryValues = create->target_options.supports_debug_entry_values; + target_options.EnableDebugEntryValues = create->target_options.enable_debug_entry_values; + target_options.ValueTrackingVariableLocations = create->target_options.value_tracking_variable_locations; + target_options.ForceDwarfFrameSection = create->target_options.force_dwarf_frame_section; + target_options.XRayFunctionIndex = create->target_options.xray_function_index; + target_options.DebugStrictDwarf = create->target_options.debug_strict_dwarf; + target_options.Hotpatch = create->target_options.hotpatch; + target_options.PPCGenScalarMASSEntries = create->target_options.ppc_gen_scalar_mass_entries; + target_options.JMCInstrument = create->target_options.jmc_instrument; + target_options.EnableCFIFixup = create->target_options.enable_cfi_fixup; + target_options.MisExpect = create->target_options.mis_expect; + target_options.XCOFFReadOnlyPointers = create->target_options.xcoff_read_only_pointers; - auto float_abi = (BBLLVMFloatAbi) create.target_options.float_abi; + auto float_abi = (BBLLVMFloatAbi) create->target_options.float_abi; switch (float_abi) { - case BBLLVMFloatAbi::normal: target_options.FloatABIType = FloatABI::Default; break; - case BBLLVMFloatAbi::soft: target_options.FloatABIType = FloatABI::Soft; break; - case BBLLVMFloatAbi::hard: target_options.FloatABIType = FloatABI::Hard; break; + case BBLLVMFloatAbi::normal: target_options.FloatABIType = llvm::FloatABI::Default; break; + case BBLLVMFloatAbi::soft: target_options.FloatABIType = llvm::FloatABI::Soft; break; + case BBLLVMFloatAbi::hard: target_options.FloatABIType = llvm::FloatABI::Hard; break; } - auto thread_model = (BBLLVMThreadModel) create.target_options.thread_model; + auto thread_model = (BBLLVMThreadModel) create->target_options.thread_model; switch (thread_model) { - case BBLLVMThreadModel::posix: target_options.ThreadModel = ThreadModel::POSIX; break; - case BBLLVMThreadModel::single: target_options.ThreadModel = ThreadModel::Single; break; + case BBLLVMThreadModel::posix: target_options.ThreadModel = llvm::ThreadModel::POSIX; break; + case BBLLVMThreadModel::single: target_options.ThreadModel = llvm::ThreadModel::Single; break; } - auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create.target_options.fp_op_fusion_mode; + auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create->target_options.fp_op_fusion_mode; switch (fp_op_fusion_mode) { - case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = FPOpFusion::Fast; break; - case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = FPOpFusion::Standard; break; - case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = FPOpFusion::Strict; break; + case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = llvm::FPOpFusion::Fast; break; + case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = llvm::FPOpFusion::Standard; break; + case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = llvm::FPOpFusion::Strict; break; } - auto eabi_version = (BBLLVMEAbi) create.target_options.eabi_version; + auto eabi_version = (BBLLVMEAbi) create->target_options.eabi_version; switch (eabi_version) { - case BBLLVMEAbi::unknown: target_options.EABIVersion = EABI::Unknown; break; - case BBLLVMEAbi::normal: target_options.EABIVersion = EABI::Default; break; - case BBLLVMEAbi::eabi4: target_options.EABIVersion = EABI::EABI4; break; - case BBLLVMEAbi::eabi5: target_options.EABIVersion = EABI::EABI5; break; - case BBLLVMEAbi::gnu: target_options.EABIVersion = EABI::GNU; break; + case BBLLVMEAbi::unknown: target_options.EABIVersion = llvm::EABI::Unknown; break; + case BBLLVMEAbi::normal: target_options.EABIVersion = llvm::EABI::Default; break; + case BBLLVMEAbi::eabi4: target_options.EABIVersion = llvm::EABI::EABI4; break; + case BBLLVMEAbi::eabi5: target_options.EABIVersion = llvm::EABI::EABI5; break; + case BBLLVMEAbi::gnu: target_options.EABIVersion = llvm::EABI::GNU; break; } - auto debugger_kind = (BBLLVMDebuggerKind) create.target_options.debugger_kind; + auto debugger_kind = (BBLLVMDebuggerKind) create->target_options.debugger_kind; switch (debugger_kind) { - case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = DebuggerKind::Default; break; - case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = DebuggerKind::GDB; break; - case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = DebuggerKind::LLDB; break; - case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = DebuggerKind::SCE; break; - case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = DebuggerKind::DBX; break; + case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = llvm::DebuggerKind::Default; break; + case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = llvm::DebuggerKind::GDB; break; + case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = llvm::DebuggerKind::LLDB; break; + case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = llvm::DebuggerKind::SCE; break; + case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = llvm::DebuggerKind::DBX; break; } - auto exception_handling = (BBLLVMExceptionHandling) create.target_options.exception_handling; + auto exception_handling = (BBLLVMExceptionHandling) create->target_options.exception_handling; switch (exception_handling) { - case BBLLVMExceptionHandling::none: target_options.ExceptionModel = ExceptionHandling::None; break; - case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = ExceptionHandling::DwarfCFI; break; - case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = ExceptionHandling::SjLj; break; - case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = ExceptionHandling::ARM; break; - case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = ExceptionHandling::WinEH; break; - case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = ExceptionHandling::Wasm; break; - case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = ExceptionHandling::AIX; break; - case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = ExceptionHandling::ZOS; break; + case BBLLVMExceptionHandling::none: target_options.ExceptionModel = llvm::ExceptionHandling::None; break; + case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = llvm::ExceptionHandling::DwarfCFI; break; + case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = llvm::ExceptionHandling::SjLj; break; + case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = llvm::ExceptionHandling::ARM; break; + case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = llvm::ExceptionHandling::WinEH; break; + case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = llvm::ExceptionHandling::Wasm; break; + case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = llvm::ExceptionHandling::AIX; break; + case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = llvm::ExceptionHandling::ZOS; break; } - target_options.LoopAlignment = create.target_options.loop_alignment; - target_options.BinutilsVersion = { create.target_options.binutils_version[0], create.target_options.binutils_version[1] }; + target_options.LoopAlignment = create->target_options.loop_alignment; + target_options.BinutilsVersion = { create->target_options.binutils_version[0], create->target_options.binutils_version[1] }; - if (create.target_options.mc.abi_name.length) + if (create->target_options.mc.abi_name.length) { - target_options.MCOptions.ABIName = { create.target_options.mc.abi_name.pointer, create.target_options.mc.abi_name.length }; + target_options.MCOptions.ABIName = { (char*)create->target_options.mc.abi_name.pointer, create->target_options.mc.abi_name.length }; } - if (create.target_options.mc.assembly_language.length) + if (create->target_options.mc.assembly_language.length) { - target_options.MCOptions.AssemblyLanguage = { create.target_options.mc.assembly_language.pointer, create.target_options.mc.assembly_language.length }; + target_options.MCOptions.AssemblyLanguage = { (char*)create->target_options.mc.assembly_language.pointer, create->target_options.mc.assembly_language.length }; } - if (create.target_options.mc.split_dwarf_file.length) + if (create->target_options.mc.split_dwarf_file.length) { - target_options.MCOptions.SplitDwarfFile = { create.target_options.mc.split_dwarf_file.pointer, create.target_options.mc.split_dwarf_file.length }; + target_options.MCOptions.SplitDwarfFile = { (char*)create->target_options.mc.split_dwarf_file.pointer, create->target_options.mc.split_dwarf_file.length }; } - if (create.target_options.mc.as_secure_log_file.length) + if (create->target_options.mc.as_secure_log_file.length) { - target_options.MCOptions.AsSecureLogFile = { create.target_options.mc.as_secure_log_file.pointer, create.target_options.mc.as_secure_log_file.length }; + target_options.MCOptions.AsSecureLogFile = { (char*)create->target_options.mc.as_secure_log_file.pointer, create->target_options.mc.as_secure_log_file.length }; } - if (create.target_options.mc.argv_count) + if (create->target_options.mc.argv_count) { - target_options.MCOptions.Argv0 = create.target_options.mc.argv0; + target_options.MCOptions.Argv0 = create->target_options.mc.argv0; // TODO: __builtin_trap(); } - if (create.target_options.mc.integrated_assembler_search_path_count) + if (create->target_options.mc.integrated_assembler_search_path_count) { // TODO: __builtin_trap(); } - target_options.MCOptions.MCRelaxAll = create.target_options.mc.relax_all; - target_options.MCOptions.MCNoExecStack = create.target_options.mc.no_exec_stack; - target_options.MCOptions.MCFatalWarnings = create.target_options.mc.fatal_warnings; - target_options.MCOptions.MCNoWarn = create.target_options.mc.no_warn; - target_options.MCOptions.MCNoDeprecatedWarn = create.target_options.mc.no_deprecated_warn; - target_options.MCOptions.MCNoTypeCheck = create.target_options.mc.no_type_check; - target_options.MCOptions.MCSaveTempLabels = create.target_options.mc.save_temp_labels; - target_options.MCOptions.MCIncrementalLinkerCompatible = create.target_options.mc.incremental_linker_compatible; + target_options.MCOptions.MCRelaxAll = create->target_options.mc.relax_all; + target_options.MCOptions.MCNoExecStack = create->target_options.mc.no_exec_stack; + target_options.MCOptions.MCFatalWarnings = create->target_options.mc.fatal_warnings; + target_options.MCOptions.MCNoWarn = create->target_options.mc.no_warn; + target_options.MCOptions.MCNoDeprecatedWarn = create->target_options.mc.no_deprecated_warn; + target_options.MCOptions.MCNoTypeCheck = create->target_options.mc.no_type_check; + target_options.MCOptions.MCSaveTempLabels = create->target_options.mc.save_temp_labels; + target_options.MCOptions.MCIncrementalLinkerCompatible = create->target_options.mc.incremental_linker_compatible; #if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.FDPIC = create.target_options.mc.fdpic; + target_options.MCOptions.FDPIC = create->target_options.mc.fdpic; #endif - target_options.MCOptions.ShowMCEncoding = create.target_options.mc.show_mc_encoding; - target_options.MCOptions.ShowMCInst = create.target_options.mc.show_mc_inst; - target_options.MCOptions.AsmVerbose = create.target_options.mc.asm_verbose; - target_options.MCOptions.PreserveAsmComments = create.target_options.mc.preserve_asm_comments; - target_options.MCOptions.Dwarf64 = create.target_options.mc.dwarf64; + target_options.MCOptions.ShowMCEncoding = create->target_options.mc.show_mc_encoding; + target_options.MCOptions.ShowMCInst = create->target_options.mc.show_mc_inst; + target_options.MCOptions.AsmVerbose = create->target_options.mc.asm_verbose; + target_options.MCOptions.PreserveAsmComments = create->target_options.mc.preserve_asm_comments; + target_options.MCOptions.Dwarf64 = create->target_options.mc.dwarf64; #if LLVM_VERSION_MAJOR >= 19 - target_options.MCOptions.Crel = create.target_options.mc.crel; - target_options.MCOptions.X86RelaxRelocations = create.target_options.mc.x86_relax_relocations; - target_options.MCOptions.X86Sse2Avx = create.target_options.mc.x86_sse2_avx; + target_options.MCOptions.Crel = create->target_options.mc.crel; + target_options.MCOptions.X86RelaxRelocations = create->target_options.mc.x86_relax_relocations; + target_options.MCOptions.X86Sse2Avx = create->target_options.mc.x86_sse2_avx; #endif - auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create.target_options.mc.emit_dwarf_unwind; + auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create->target_options.mc.emit_dwarf_unwind; switch (emit_dwarf_unwind) { - case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Always; break; - case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::NoCompactUnwind; break; - case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = EmitDwarfUnwindType::Default; break; + case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Always; break; + case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::NoCompactUnwind; break; + case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Default; break; } - auto use_dwarf_directory = (BBLLVMDwarfDirectory) create.target_options.mc.use_dwarf_directory; + auto use_dwarf_directory = (BBLLVMDwarfDirectory) create->target_options.mc.use_dwarf_directory; switch (use_dwarf_directory) { - case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; - case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break; - case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break; + case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break; + case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break; + case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break; } #if LLVM_VERSION_MAJOR >= 19 - auto debug_compression_type = (BBLLVMDebugCompressionType) create.target_options.mc.debug_compression_type; + auto debug_compression_type = (BBLLVMDebugCompressionType) create->target_options.mc.debug_compression_type; switch (debug_compression_type) { - case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = DebugCompressionType::None; break; - case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zlib; break; - case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = DebugCompressionType::Zstd; break; + case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::None; break; + case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zlib; break; + case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zstd; break; } #endif - target_options.MCOptions.EmitCompactUnwindNonCanonical = create.target_options.mc.emit_compact_unwind_non_canonical; - target_options.MCOptions.PPCUseFullRegisterNames = create.target_options.mc.ppc_use_full_register_names; + target_options.MCOptions.EmitCompactUnwindNonCanonical = create->target_options.mc.emit_compact_unwind_non_canonical; + target_options.MCOptions.PPCUseFullRegisterNames = create->target_options.mc.ppc_use_full_register_names; - target_machine = target->createTargetMachine(create.target_triple.string_ref(), create.cpu_model.string_ref(), create.cpu_features.string_ref(), target_options, relocation_model, code_model, optimization_level, create.jit); + return reinterpret_cast(const_cast(target->createTargetMachine(string_ref(create->target_triple), string_ref(create->cpu_model), string_ref(create->cpu_features), target_options, relocation_model, code_model, optimization_level, create->jit))); } else { auto length = error_message_string.length(); - char* result = new char[length]; + auto* result = new u8[length]; memcpy(result, error_message_string.c_str(), length); *error_message = { result, length }; - - target_machine = 0; + return 0; } - - return target_machine; } -EXPORT void llvm_module_set_target(Module& module, TargetMachine& target_machine) +EXPORT void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm) { - module.setDataLayout(target_machine.createDataLayout()); - auto& triple_string = target_machine.getTargetTriple().getTriple(); - module.setTargetTriple(StringRef(triple_string)); + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; + module->setDataLayout(target_machine->createDataLayout()); + auto& triple_string = target_machine->getTargetTriple().getTriple(); + module->setTargetTriple(llvm::StringRef(triple_string)); } -enum class BBLLVMOptimizationLevel : u8 -{ - O0 = 0, - O1 = 1, - O2 = 2, - O3 = 3, - Os = 4, - Oz = 5, -}; - -#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) -struct BBLLVMOptimizationPipelineOptions -{ - u64 optimization_level:3; - u64 debug_info:1; - u64 loop_unrolling:1; - u64 loop_interleaving:1; - u64 loop_vectorization:1; - u64 slp_vectorization:1; - u64 merge_functions:1; - u64 call_graph_profile:1; - u64 unified_lto:1; - u64 assignment_tracking:1; - u64 verify_module:1; - u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); -static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); - -EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& target_machine, BBLLVMOptimizationPipelineOptions options) +EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options) { + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; // TODO: PGO // TODO: CS profile - PipelineTuningOptions pipeline_tuning_options; + llvm::PipelineTuningOptions pipeline_tuning_options; pipeline_tuning_options.LoopUnrolling = options.loop_unrolling; pipeline_tuning_options.LoopInterleaving = options.loop_interleaving; pipeline_tuning_options.LoopVectorization = options.loop_vectorization; @@ -1631,24 +1227,25 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& // TODO: instrumentation - LoopAnalysisManager loop_analysis_manager; - FunctionAnalysisManager function_analysis_manager; - CGSCCAnalysisManager cgscc_analysis_manager; - ModuleAnalysisManager module_analysis_manager; + llvm::LoopAnalysisManager loop_analysis_manager; + llvm::FunctionAnalysisManager function_analysis_manager; + llvm::CGSCCAnalysisManager cgscc_analysis_manager; + llvm::ModuleAnalysisManager module_analysis_manager; - PassBuilder pass_builder(&target_machine, pipeline_tuning_options); + llvm::PassBuilder pass_builder(target_machine, pipeline_tuning_options); if (options.assignment_tracking && options.debug_info != 0) { - pass_builder.registerPipelineStartEPCallback([&](ModulePassManager& MPM, OptimizationLevel Level) { - MPM.addPass(AssignmentTrackingPass()); + pass_builder.registerPipelineStartEPCallback([&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) { + unused(Level); + MPM.addPass(llvm::AssignmentTrackingPass()); }); } - Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) - std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); - function_analysis_manager.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); + function_analysis_manager.registerPass([&] { return llvm::TargetLibraryAnalysis(*TLII); }); pass_builder.registerModuleAnalyses(module_analysis_manager); pass_builder.registerCGSCCAnalyses(cgscc_analysis_manager); @@ -1656,25 +1253,25 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& pass_builder.registerLoopAnalyses(loop_analysis_manager); pass_builder.crossRegisterProxies(loop_analysis_manager, function_analysis_manager, cgscc_analysis_manager, module_analysis_manager); - ModulePassManager module_pass_manager; + llvm::ModulePassManager module_pass_manager; if (options.verify_module) { - module_pass_manager.addPass(VerifierPass()); + module_pass_manager.addPass(llvm::VerifierPass()); } bool thin_lto = false; bool lto = false; - OptimizationLevel optimization_level; + llvm::OptimizationLevel optimization_level; switch ((BBLLVMOptimizationLevel)options.optimization_level) { - case BBLLVMOptimizationLevel::O0: optimization_level = OptimizationLevel::O0; break; - case BBLLVMOptimizationLevel::O1: optimization_level = OptimizationLevel::O1; break; - case BBLLVMOptimizationLevel::O2: optimization_level = OptimizationLevel::O2; break; - case BBLLVMOptimizationLevel::O3: optimization_level = OptimizationLevel::O3; break; - case BBLLVMOptimizationLevel::Os: optimization_level = OptimizationLevel::Os; break; - case BBLLVMOptimizationLevel::Oz: optimization_level = OptimizationLevel::Oz; break; + case BBLLVMOptimizationLevel::O0: optimization_level = llvm::OptimizationLevel::O0; break; + case BBLLVMOptimizationLevel::O1: optimization_level = llvm::OptimizationLevel::O1; break; + case BBLLVMOptimizationLevel::O2: optimization_level = llvm::OptimizationLevel::O2; break; + case BBLLVMOptimizationLevel::O3: optimization_level = llvm::OptimizationLevel::O3; break; + case BBLLVMOptimizationLevel::Os: optimization_level = llvm::OptimizationLevel::Os; break; + case BBLLVMOptimizationLevel::Oz: optimization_level = llvm::OptimizationLevel::Oz; break; } // TODO: thin lto post-link @@ -1691,72 +1288,46 @@ EXPORT void llvm_module_run_optimization_pipeline(Module& module, TargetMachine& // TODO: if emit bitcode/IR - module_pass_manager.run(module, module_analysis_manager); + module_pass_manager.run(*module, module_analysis_manager); } -enum class BBLLVMCodeGenerationFileType : u8 +EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options) { - assembly_file = 0, - object_file = 1, - null = 2, -}; + auto module = llvm::unwrap(m); + auto target_machine = (llvm::TargetMachine*)tm; -#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) - -struct BBLLVMCodeGenerationPipelineOptions -{ - BBLLVMString output_dwarf_file_path; - BBLLVMString output_file_path; - u64 code_generation_file_type:2; - u64 optimize_when_possible:1; - u64 verify_module:1; - u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; -}; - -static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); -static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); - -enum class BBLLVMCodeGenerationPipelineResult : u8 -{ - success = 0, - failed_to_create_file = 1, - failed_to_add_emit_passes = 2, -}; - -EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(Module& module, TargetMachine& target_machine, BBLLVMCodeGenerationPipelineOptions options) -{ // We still use the legacy PM to run the codegen pipeline since the new PM // does not work with the codegen pipeline. // FIXME: make the new PM work with the codegen pipeline. - legacy::PassManager CodeGenPasses; + llvm::legacy::PassManager CodeGenPasses; #if LLVM_VERSION_MAJOR >= 19 - if (options.optimize_when_possible) + if (options->optimize_when_possible) { - CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine.getTargetIRAnalysis())); + CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis())); } #endif - raw_pwrite_stream* dwarf_object_file = 0; - if (options.output_dwarf_file_path.length) + llvm::raw_pwrite_stream* dwarf_object_file = 0; + if (options->output_dwarf_file_path.length) { __builtin_trap(); } - if (options.optimize_when_possible) + if (options->optimize_when_possible) { - Triple target_triple = target_machine.getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 + llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718 // TODO: add library (?) - std::unique_ptr TLII(llvm::driver::createTLII(target_triple, driver::VectorLibrary::NoLibrary)); - CodeGenPasses.add(new TargetLibraryInfoWrapperPass(*TLII)); + std::unique_ptr TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary)); + CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); } - std::unique_ptr stream; + std::unique_ptr stream; - if (options.output_file_path.length) + if (options->output_file_path.length) { std::error_code error_code; - stream = std::make_unique(options.output_file_path.string_ref(), error_code, sys::fs::OF_None); + stream = std::make_unique(string_ref(options->output_file_path), error_code, llvm::sys::fs::OF_None); if (error_code) { @@ -1768,34 +1339,25 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli stream = std::make_unique(); } - CodeGenFileType file_type; - switch ((BBLLVMCodeGenerationFileType)options.code_generation_file_type) + llvm::CodeGenFileType file_type; + switch ((BBLLVMCodeGenerationFileType)options->code_generation_file_type) { - case BBLLVMCodeGenerationFileType::assembly_file: file_type = CodeGenFileType::AssemblyFile; break; - case BBLLVMCodeGenerationFileType::object_file: file_type = CodeGenFileType::ObjectFile; break; - case BBLLVMCodeGenerationFileType::null: file_type = CodeGenFileType::Null; break; + case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break; + case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break; + case BBLLVMCodeGenerationFileType::null: file_type = llvm::CodeGenFileType::Null; break; } - auto disable_verify = !options.verify_module; - if (target_machine.addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify)) + auto disable_verify = !options->verify_module; + if (target_machine->addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify)) { return BBLLVMCodeGenerationPipelineResult::failed_to_add_emit_passes; } - CodeGenPasses.run(module); + CodeGenPasses.run(*module); return BBLLVMCodeGenerationPipelineResult::success; } -struct LLDResult -{ - BBLLVMString stdout_string; - BBLLVMString stderr_string; - bool success; -}; - -#define lld_api_args() const char** argument_pointer, u64 argument_count, bool exit_early, bool disable_output -#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) #define lld_api_function_signature(name) bool name(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) #define lld_link_decl(link_name) \ @@ -1831,7 +1393,7 @@ fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function) auto stdout_length = stdout_string.length(); if (stdout_length) { - auto* stdout_pointer = new char[stdout_length]; + auto* stdout_pointer = new u8[stdout_length]; memcpy(stdout_pointer, stdout_string.data(), stdout_length); result.stdout_string = { stdout_pointer, stdout_length }; } @@ -1839,7 +1401,7 @@ fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function) auto stderr_length = stderr_string.length(); if (stderr_length) { - auto* stderr_pointer = new char[stderr_length]; + auto* stderr_pointer = new u8[stderr_length]; memcpy(stderr_pointer, stderr_string.data(), stderr_length); result.stderr_string = { stderr_pointer, stderr_length }; } @@ -1856,8 +1418,8 @@ EXPORT lld_api_function_decl(link_name)\ return lld_api_generic(argument_pointer, argument_count, exit_early, disable_output, lld::link_name::link);\ } -lld_api_function_impl(coff) +// lld_api_function_impl(coff) lld_api_function_impl(elf) -lld_api_function_impl(mingw) -lld_api_function_impl(macho) -lld_api_function_impl(wasm) +// lld_api_function_impl(mingw) +// lld_api_function_impl(macho) +// lld_api_function_impl(wasm) diff --git a/src/llvm.hpp b/src/llvm.hpp new file mode 100644 index 0000000..9855bca --- /dev/null +++ b/src/llvm.hpp @@ -0,0 +1,645 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +struct LLDResult +{ + String stdout_string; + String stderr_string; + bool success; +}; + +enum class BBLLVMCodeGenerationPipelineResult : u8 +{ + success = 0, + failed_to_create_file = 1, + failed_to_add_emit_passes = 2, +}; + +enum class BBLLVMCodeGenerationFileType : u8 +{ + assembly_file = 0, + object_file = 1, + null = 2, +}; + +#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60) + +struct BBLLVMCodeGenerationPipelineOptions +{ + String output_dwarf_file_path; + String output_file_path; + u64 code_generation_file_type:2; + u64 optimize_when_possible:1; + u64 verify_module:1; + u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64)); +static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60); + +enum class BBLLVMOptimizationLevel : u8 +{ + O0 = 0, + O1 = 1, + O2 = 2, + O3 = 3, + Os = 4, + Oz = 5, +}; + +#define BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (51) +struct BBLLVMOptimizationPipelineOptions +{ + u64 optimization_level:3; + u64 debug_info:1; + u64 loop_unrolling:1; + u64 loop_interleaving:1; + u64 loop_vectorization:1; + u64 slp_vectorization:1; + u64 merge_functions:1; + u64 call_graph_profile:1; + u64 unified_lto:1; + u64 assignment_tracking:1; + u64 verify_module:1; + u64 reserved:BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64)); +static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51); + +enum class BBLLVMUWTableKind : u64 +{ + None = 0, ///< No unwind table requested + Sync = 1, ///< "Synchronous" unwind tables + Async = 2, ///< "Asynchronous" unwind tables (instr precise) + Default = 2, +}; + +enum class BBLLVMFramePointerKind : u64 +{ + none = 0, + reserved = 1, + non_leaf = 2, + all = 3, +}; + +enum class ZeroCallUsedRegsKind : u64 +{ + all = 0, + skip = 1 << 0, + only_used = 1 << 1, + only_gpr = 1 << 2, + only_arg = 1 << 3, + used_gpr_arg = only_used | only_gpr | only_arg, + used_gpr = only_used | only_gpr, + used_arg = only_used | only_arg, + used = only_used, + all_gpr_arg = only_gpr | only_arg, + all_gpr = only_gpr, + all_arg = only_arg, +}; + +struct BBLLVMFunctionAttributesFlags0 +{ + u64 noreturn:1; + u64 cmse_ns_call:1; + u64 nounwind:1; + u64 returns_twice:1; + u64 cold:1; + u64 hot:1; + u64 no_duplicate:1; + u64 convergent:1; + u64 no_merge:1; + u64 will_return:1; + u64 no_caller_saved_registers:1; + u64 no_cf_check:1; + u64 no_callback:1; + u64 alloc_size:1; + u64 uniform_work_group_size:1; + u64 aarch64_pstate_sm_body:1; + u64 aarch64_pstate_sm_enabled:1; + u64 aarch64_pstate_sm_compatible:1; + u64 aarch64_preserves_za:1; + u64 aarch64_in_za:1; + u64 aarch64_out_za:1; + u64 aarch64_inout_za:1; + u64 aarch64_preserves_zt0:1; + u64 aarch64_in_zt0:1; + u64 aarch64_out_zt0:1; + u64 aarch64_inout_zt0:1; + u64 optimize_for_size:1; + u64 min_size:1; + u64 no_red_zone:1; + u64 indirect_tls_seg_refs:1; + u64 no_implicit_floats:1; + u64 sample_profile_suffix_elision_policy:1; + u64 memory_none:1; + u64 memory_readonly:1; + u64 memory_inaccessible_or_arg_memory_only:1; + u64 memory_arg_memory_only:1; + u64 strict_fp:1; + u64 no_inline:1; + u64 always_inline:1; + u64 guard_no_cf:1; + + // TODO: branch protection function attributes + // TODO: cpu features + + // Call-site begin + u64 call_no_builtins:1; + + BBLLVMFramePointerKind definition_frame_pointer_kind:2; + u64 definition_less_precise_fpmad:1; + u64 definition_null_pointer_is_valid:1; + u64 definition_no_trapping_fp_math:1; + u64 definition_no_infs_fp_math:1; + u64 definition_no_nans_fp_math:1; + u64 definition_approx_func_fp_math:1; + u64 definition_unsafe_fp_math:1; + u64 definition_use_soft_float:1; + u64 definition_no_signed_zeroes_fp_math:1; + u64 definition_stack_realignment:1; + u64 definition_backchain:1; + u64 definition_split_stack:1; + u64 definition_speculative_load_hardening:1; + ZeroCallUsedRegsKind definition_zero_call_used_registers:4; + // TODO: denormal builtins + u64 definition_non_lazy_bind:1; + u64 definition_cmse_nonsecure_entry:1; + BBLLVMUWTableKind definition_unwind_table_kind:2; +}; + +static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64)); + +struct BBLLVMFunctionAttributesFlags1 +{ + u64 definition_disable_tail_calls:1; + u64 definition_stack_protect_strong:1; + u64 definition_stack_protect:1; + u64 definition_stack_protect_req:1; + u64 definition_aarch64_new_za:1; + u64 definition_aarch64_new_zt0:1; + u64 definition_optimize_none:1; + u64 definition_naked:1; + u64 definition_inline_hint:1; + u64 _:55; +}; + +static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64)); + +struct BBLLVMFunctionAttributes +{ + String prefer_vector_width; + String stack_protector_buffer_size; + String definition_probe_stack; + String definition_stack_probe_size; + + BBLLVMFunctionAttributesFlags0 flags0; + BBLLVMFunctionAttributesFlags1 flags1; +}; + +static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64)); + +struct BBLLVMArgumentAttributes +{ + LLVMTypeRef semantic_type; + LLVMTypeRef abi_type; + u64 dereferenceable_bytes; + u32 alignment; + u32 no_alias:1; + u32 non_null:1; + u32 no_undef:1; + u32 sign_extend:1; + u32 zero_extend:1; + u32 in_reg:1; + u32 no_fp_class:10; + u32 struct_return:1; + u32 writable:1; + u32 dead_on_unwind:1; + u32 in_alloca:1; + u32 dereferenceable:1; + u32 dereferenceable_or_null:1; + u32 nest:1; + u32 by_value:1; + u32 by_reference:1; + u32 no_capture:1; + u32 _:6; +}; + +static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(LLVMTypeRef) + 2 * sizeof(u64)); + +struct BBLLVMAttributeListOptions +{ + BBLLVMFunctionAttributes function; + BBLLVMArgumentAttributes return_; + BBLLVMArgumentAttributes* argument_pointer; + u64 argument_count; +}; + +static_assert(sizeof(BBLLVMAttributeListOptions) == sizeof(BBLLVMFunctionAttributes) + sizeof(BBLLVMArgumentAttributes) + sizeof(void*) + sizeof(u64)); + +typedef void* BBLLVMAttributeList; + +enum class DwarfEmissionKind +{ + none, + full, + line_tables_only, +}; + +enum class DwarfType +{ + void_type = 0x0, + address = 0x1, + boolean = 0x2, + complex_float = 0x3, + float_type = 0x4, + signed_type = 0x5, + signed_char = 0x6, + unsigned_type = 0x7, + unsigned_char = 0x8, + + // DWARF 3. + imaginary_float = 0x9, + packed_decimal = 0xa, + numeric_string = 0xb, + edited = 0xc, + signed_fixed = 0xd, + unsigned_fixed = 0xe, + decimal_float = 0xf, + + // DWARF 4. + UTF = 0x10, + + // DWARF 5. + UCS = 0x11, + ASCII = 0x12, + + // HP extensions. + HP_float80 = 0x80, // Floating-point (80 bit). + HP_complex_float80 = 0x81, // Complex floating-point (80 bit). + HP_float128 = 0x82, // Floating-point (128 bit). + HP_complex_float128 = 0x83, // Complex fp (128 bit). + HP_floathpintel = 0x84, // Floating-point (82 bit IA64). + HP_imaginary_float80 = 0x85, + HP_imaginary_float128 = 0x86, + HP_VAX_float = 0x88, // F or G floating. + HP_VAX_float_d = 0x89, // D floating. + HP_packed_decimal = 0x8a, // Cobol. + HP_zoned_decimal = 0x8b, // Cobol. + HP_edited = 0x8c, // Cobol. + HP_signed_fixed = 0x8d, // Cobol. + HP_unsigned_fixed = 0x8e, // Cobol. + HP_VAX_complex_float = 0x8f, // F or G floating complex. + HP_VAX_complex_float_d = 0x90, // D floating complex. +}; + +enum class DIFlagsVisibility : u32 +{ + none = 0, + private_ = 1, + protected_ = 2, + public_ = 3, +}; + +enum class DIFlagsInheritance : u32 +{ + none = 0, + single_ = 1, + multiple_ = 2, + virtual_ = 3, +}; + +struct DIFlags +{ + DIFlagsVisibility visibility:2; + u32 forward_declaration:1; + u32 apple_block:1; + u32 block_by_ref_struct:1; + u32 virtual_:1; + u32 artificial:1; + u32 explicit_:1; + u32 prototyped:1; + u32 objective_c_class_complete:1; + u32 object_pointer:1; + u32 vector:1; + u32 static_member:1; + u32 lvalue_reference:1; + u32 rvalue_reference:1; + u32 reserved:1; + DIFlagsInheritance inheritance:2; + u32 introduced_virtual:1; + u32 bit_field:1; + u32 no_return:1; + u32 type_pass_by_value:1; + u32 type_pass_by_reference:1; + u32 enum_class:1; + u32 thunk:1; + u32 non_trivial:1; + u32 big_endian:1; + u32 little_endian:1; + u32 all_calls_described:1; + u32 _:3; +}; + +static_assert(sizeof(DIFlags) == sizeof(u32)); + +enum class BBLLVMEmitDwarfUnwindType : u8 +{ + always = 0, + no_compact_unwind = 1, + normal = 2, +}; + +enum class BBLLVMDwarfDirectory : u8 +{ + disable = 0, + enable = 1, + normal = 2, +}; + +enum class BBLLVMDebugCompressionType : u8 +{ + none = 0, + zlib = 1, + zstd = 2, +}; + +#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7) + +struct BBLLVMMCTargetOptions +{ + String abi_name; + String assembly_language; + String split_dwarf_file; + String as_secure_log_file; + const char* argv0; + String* argv_pointer; + u64 argv_count; + String* integrated_assembler_search_path_pointer; + u64 integrated_assembler_search_path_count; + u32 relax_all:1; + u32 no_exec_stack:1; + u32 fatal_warnings:1; + u32 no_warn:1; + u32 no_deprecated_warn:1; + u32 no_type_check:1; + u32 save_temp_labels:1; + u32 incremental_linker_compatible:1; + u32 fdpic:1; + u32 show_mc_encoding:1; + u32 show_mc_inst:1; + u32 asm_verbose:1; + u32 preserve_asm_comments:1 = true; + u32 dwarf64:1; + u32 crel:1; + u32 x86_relax_relocations:1; + u32 x86_sse2_avx:1; + u32 emit_dwarf_unwind:2 = 2; + u32 use_dwarf_directory:2 = 2; + u32 debug_compression_type:2 = 0; + u32 emit_compact_unwind_non_canonical:1; + u32 ppc_use_full_register_names:1; + u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT; +}; + +static_assert(sizeof(BBLLVMMCTargetOptions) == 112); +static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7); + +enum class BBLLVMCodeModel : u8 +{ + none = 0, + tiny = 1, + small = 2, + kernel = 3, + medium = 4, + large = 5, +}; + +enum class BBLLVMRelocationModel : u8 +{ + default_relocation = 0, + static_relocation = 1, + pic = 2, + dynamic_no_pic = 3, + ropi = 4, + rwpi = 5, + ropi_rwpi = 6, +}; + +enum class BBLLVMCodeGenerationOptimizationLevel : u8 +{ + none = 0, // -O0 + less = 1, // -O1 + normal = 2, // -O2, -Os + aggressive = 3 // -O3 +}; + +enum class BBLLVMGlobalISelAbortMode : u8 +{ + disable = 0, + enable = 1, + disable_with_diag = 2, +}; + +enum class BBLLVMSwiftAsyncFramePointerMode : u8 +{ + deployment_based = 0, + always = 1, + never = 2, +}; + +enum class BBLLVMBasicBlockSection : u8 +{ + all = 0, + list = 1, + preset = 2, + none = 3, +}; + +enum class BBLLVMFloatAbi : u8 +{ + normal = 0, + soft = 1, + hard = 2, +}; + +enum class BBLLVMFPOpFusion : u8 +{ + fast = 0, + standard = 1, + strict = 2, +}; + +enum class BBLLVMThreadModel : u8 +{ + posix = 0, + single = 1, +}; + +enum class BBLLVMEAbi : u8 +{ + unknown = 0, + normal = 1, + eabi4 = 2, + eabi5 = 3, + gnu = 4, +}; + +enum class BBLLVMDebuggerKind : u8 +{ + normal = 0, + gdb = 1, + lldb = 2, + sce = 3, + dbx = 4, +}; + +enum class BBLLVMExceptionHandling : u8 +{ + none = 0, + dwarf_cfi = 1, + setjmp_longjmp = 2, + arm = 3, + win_eh = 4, + wasm = 5, + aix = 6, + zos = 7, +}; + +#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21) + +struct BBLLVMTargetOptions +{ + u64 unsafe_fp_math:1; + u64 no_infs_fp_math:1; + u64 no_nans_fp_math:1; + u64 no_trapping_fp_math:1 = true; + u64 no_signed_zeroes_fp_math:1; + u64 approx_func_fp_math:1; + u64 enable_aix_extended_altivec_abi:1; + u64 honor_sign_dependent_rounding_fp_math:1; + u64 no_zeroes_in_bss:1; + u64 guaranteed_tail_call_optimization:1; + u64 stack_symbol_ordering:1 = true; + u64 enable_fast_isel:1; + u64 enable_global_isel:1 = 1; + u64 global_isel_abort_mode:2; + u64 swift_async_frame_pointer:2 = 1; + u64 use_init_array:1; + u64 disable_integrated_assembler:1; + u64 function_sections:1; + u64 data_sections:1; + u64 ignore_xcoff_visibility:1; + u64 xcoff_traceback_table:1 = true; + u64 unique_section_names:1 = true; + u64 unique_basic_block_section_names:1; + u64 separate_named_sections:1; + u64 trap_unreachable:1; + u64 no_trap_after_noreturn:1; + u64 tls_size:8; + u64 emulated_tls:1; + u64 enable_tls_descriptors:1; + u64 enable_ipra:1; + u64 emit_stack_size_section:1; + u64 enable_machine_outliner:1; + u64 enable_machine_function_splitter:1; + u64 supports_default_outlining:1; + u64 emit_address_significance_table:1; + u64 bb_address_map:1; + u64 bb_sections:3 = 3; + u64 emit_call_site_information:1; + u64 supports_debug_entry_values:1; + u64 enable_debug_entry_values:1; + u64 value_tracking_variable_locations:1; + u64 force_dwarf_frame_section:1; + u64 xray_function_index:1 = true; + u64 debug_strict_dwarf:1; + u64 hotpatch:1; + u64 ppc_gen_scalar_mass_entries:1; + u64 jmc_instrument:1; + u64 enable_cfi_fixup:1; + u64 mis_expect:1; + u64 xcoff_read_only_pointers:1; + u64 float_abi:2 = 0; + u64 thread_model:1 = 0; + u32 fp_op_fusion_mode:2 = 1; + u32 eabi_version:3 = 1; + u32 debugger_kind:3 = 0; + u32 exception_handling:3 = 0; + u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT; + unsigned loop_alignment = 0; + int binutils_version[2]; + + BBLLVMMCTargetOptions mc; +}; + +static_assert(sizeof(BBLLVMTargetOptions) == 136); +static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21); + +#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4) + +struct BBLLVMTargetMachineCreate +{ + BBLLVMTargetOptions target_options; + String target_triple; + String cpu_model; + String cpu_features; + BBLLVMRelocationModel relocation_model; + BBLLVMCodeModel code_model; + BBLLVMCodeGenerationOptimizationLevel optimization_level; + bool jit; + u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT]; +}; + +static_assert(sizeof(BBLLVMTargetMachineCreate) == 192); +static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4); + +fn bool llvm_initialized = false; + +extern "C" String llvm_default_target_triple(); +extern "C" String llvm_host_cpu_name(); +extern "C" String llvm_host_cpu_features(); + +extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name); + +extern "C" LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name); +extern "C" void llvm_function_set_attributes(LLVMValueRef function, BBLLVMAttributeList attribute_list); + +extern "C" LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function); + +extern "C" LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized); + +extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, u32 alignment, String name); +extern "C" bool llvm_value_has_one_use(LLVMValueRef value); +extern "C" LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block); +extern "C" void llvm_basic_block_delete(LLVMBasicBlockRef basic_block); +extern "C" bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block); +extern "C" void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle); +extern "C" void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle); + +extern "C" BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site); +extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et); +extern "C" bool llvm_value_use_empty(LLVMValueRef value); +extern "C" bool llvm_function_verify(LLVMValueRef function_value, String* error_message); +extern "C" bool llvm_module_verify(LLVMModuleRef m, String* error_message); + +extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type); + +extern "C" String llvm_module_to_string(LLVMModuleRef module); + +extern "C" LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message); +extern "C" void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm); +extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options); +extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options); + +#define lld_api_args() const char** argument_pointer, u64 argument_count, bool exit_early, bool disable_output +#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) +extern "C" lld_api_function_decl(elf); diff --git a/src/llvm_api.zig b/src/llvm_api.zig deleted file mode 100644 index 52f8c37..0000000 --- a/src/llvm_api.zig +++ /dev/null @@ -1,296 +0,0 @@ -const llvm = @import("LLVM.zig"); -const lld = llvm.lld; - -const Bool = c_int; - -pub extern fn llvm_context_create_module(context: *llvm.Context, name: llvm.String) *llvm.Module; -pub extern fn LLVMContextCreate() *llvm.Context; -pub extern fn LLVMCreateBuilderInContext(context: *llvm.Context) *llvm.Builder; - -pub extern fn LLVMGetOperand(value: *llvm.Value, index: c_uint) *llvm.Value; -pub extern fn LLVMSetAlignment(value: *llvm.Value, alignment: c_uint) void; -pub extern fn llvm_instruction_is_call_base(instruction: *llvm.Instruction) bool; - -// Module -pub extern fn llvm_module_create_global_variable(module: *llvm.Module, global_type: *llvm.Type, is_constant: bool, linkage: llvm.LinkageType, initial_value: *llvm.Constant, name: llvm.String, before: ?*llvm.GlobalVariable, thread_local_mode: llvm.ThreadLocalMode, address_space: c_uint, externally_initialized: bool) *llvm.GlobalVariable; -pub extern fn LLVMSetUnnamedAddress(global: *llvm.GlobalVariable, unnamed_address: llvm.GlobalVariable.UnnamedAddress) void; -pub extern fn llvm_module_create_function(module: *llvm.Module, function_type: *llvm.Type.Function, linkage_type: llvm.LinkageType, address_space: c_uint, name: llvm.String) *llvm.Function; -pub extern fn llvm_context_create_basic_block(context: *llvm.Context, name: llvm.String, parent: ?*llvm.Function) *llvm.BasicBlock; -pub extern fn LLVMGetNextBasicBlock(basic_block: *llvm.BasicBlock) ?*llvm.BasicBlock; -pub extern fn LLVMDeleteBasicBlock(basic_block: *llvm.BasicBlock) void; -pub extern fn LLVMGetLastBasicBlock(function: *llvm.Function) *llvm.BasicBlock; -pub extern fn LLVMGetBasicBlockParent(basic_block: *llvm.BasicBlock) ?*llvm.BasicBlock; -pub extern fn LLVMAppendExistingBasicBlock(function: *llvm.Function, basic_block: *llvm.BasicBlock) void; -pub extern fn LLVMInsertExistingBasicBlockAfterInsertBlock(builder: *llvm.Builder, basic_block: *llvm.BasicBlock) void; - -pub extern fn LLVMSetValueName2(value: *llvm.Value, name_pointer: [*]const u8, name_length: usize) void; -pub extern fn llvm_value_use_empty(value: *llvm.Value) bool; -pub extern fn llvm_value_has_one_use(value: *llvm.Value) bool; -pub extern fn llvm_value_to_branch(value: ?*llvm.Value) ?*llvm.Instruction.Branch; -pub extern fn LLVMReplaceAllUsesWith(old: *llvm.Value, new: *llvm.Value) void; - -pub extern fn LLVMGetSuccessor(branch: *llvm.Instruction.Branch, index: c_uint) *llvm.BasicBlock; -pub extern fn LLVMIsConditional(branch: *llvm.Instruction.Branch) bool; -pub extern fn LLVMGetInstructionParent(instruction: *llvm.Instruction) *llvm.BasicBlock; - -pub extern fn llvm_basic_block_is_empty(basic_block: *llvm.BasicBlock) bool; -pub extern fn llvm_basic_block_user_begin(basic_block: *llvm.BasicBlock) ?*llvm.Value; -pub extern fn llvm_basic_block_delete(basic_block: *llvm.BasicBlock) void; -pub extern fn LLVMGetBasicBlockTerminator(basic_block: *llvm.BasicBlock) ?*llvm.Value; - -pub extern fn LLVMSetFunctionCallConv(function: *llvm.Function, calling_convention: llvm.CallingConvention) void; -pub extern fn LLVMGetFunctionCallConv(function: *llvm.Function) llvm.CallingConvention; - -pub extern fn LLVMSetInstructionCallConv(instruction: *llvm.Instruction.CallBase, calling_convention: llvm.CallingConvention) void; - -pub extern fn LLVMGetParams(function: *llvm.Function, argument_buffer: [*]*llvm.Argument) void; - -pub extern fn llvm_function_to_string(function: *llvm.Function) llvm.String; -pub extern fn llvm_function_verify(function: *llvm.Function, error_message: *llvm.String) bool; -pub extern fn llvm_module_verify(module: *llvm.Module, error_message: *llvm.String) bool; - -pub extern fn llvm_module_to_string(module: *llvm.Module) llvm.String; - -// Builder API -pub extern fn LLVMPositionBuilderAtEnd(builder: *llvm.Builder, basic_block: *llvm.BasicBlock) void; -pub extern fn LLVMClearInsertionPosition(builder: *llvm.Builder) void; -pub extern fn LLVMGetInsertBlock(builder: *llvm.Builder) ?*llvm.BasicBlock; - -pub extern fn llvm_find_return_value_dominating_store(builder: *llvm.Builder, return_alloca: *llvm.Value, element_type: *llvm.Type) ?*llvm.Instruction.Store; - -pub extern fn LLVMDeleteInstruction(instruction: *llvm.Instruction) void; -pub extern fn LLVMInstructionEraseFromParent(instruction: *llvm.Instruction) void; - -pub extern fn LLVMBuildRet(builder: *llvm.Builder, value: ?*llvm.Value) void; -pub extern fn LLVMBuildAdd(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildSub(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildMul(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildSDiv(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildUDiv(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildSRem(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildURem(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildShl(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildAShr(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildLShr(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildAnd(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildOr(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildXor(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildICmp(builder: *llvm.Builder, predicate: llvm.IntPredicate, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildBr(builder: *llvm.Builder, block: *llvm.BasicBlock) *llvm.Value; -pub extern fn LLVMBuildCondBr(builder: *llvm.Builder, condition: *llvm.Value, taken: *llvm.BasicBlock, not_taken: *llvm.BasicBlock) *llvm.Value; -pub extern fn LLVMBuildNeg(builder: *llvm.Builder, value: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildNot(builder: *llvm.Builder, value: *llvm.Value, name: [*:0]const u8) *llvm.Value; - -pub extern fn llvm_builder_create_alloca(builder: *llvm.Builder, ty: *llvm.Type, address_space: c_uint, name: llvm.String) *llvm.Value; -pub extern fn LLVMBuildStore(builder: *llvm.Builder, value: *llvm.Value, pointer: *llvm.Value) *llvm.Value; -pub extern fn LLVMBuildLoad2(builder: *llvm.Builder, ty: *llvm.Type, pointer: *llvm.Value, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildCall2(builder: *llvm.Builder, ty: *llvm.Type.Function, pointer: *llvm.Value, argument_pointer: [*]const *llvm.Value, argument_count: c_uint, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildStructGEP2(builder: *llvm.Builder, struct_type: *llvm.Type.Struct, pointer: *llvm.Value, index: c_uint, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildGEP2(builder: *llvm.Builder, ty: *llvm.Type, aggregate: *llvm.Value, index_pointer: [*]const *llvm.Value, index_count: c_uint, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildInBoundsGEP2(builder: *llvm.Builder, ty: *llvm.Type, aggregate: *llvm.Value, index_pointer: [*]const *llvm.Value, index_count: c_uint, name: [*:0]const u8) *llvm.Value; - -pub extern fn LLVMBuildInsertValue(builder: *llvm.Builder, aggregate: *llvm.Value, element: *llvm.Value, index: c_uint, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildExtractValue(builder: *llvm.Builder, aggregate: *llvm.Value, index: c_uint, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildUnreachable(builder: *llvm.Builder) *llvm.Value; -pub extern fn LLVMBuildMemCpy(builder: *llvm.Builder, destination: *llvm.Value, destination_alignment: c_uint, source: *llvm.Value, source_alignment: c_uint, size: *llvm.Value) *llvm.Value; -pub extern fn LLVMBuildMemSet(builder: *llvm.Builder, pointer: *llvm.Value, value: *llvm.Value, value_count: *llvm.Value, alignment: c_uint) *llvm.Value; -pub extern fn LLVMBuildPhi(builder: *llvm.Builder, ty: *llvm.Type, name: [*:0]const u8) *llvm.Instruction.Phi; -pub extern fn LLVMAddIncoming(phi: *llvm.Instruction.Phi, incoming_value_pointer: [*]const *llvm.Value, incoming_basic_block_pointer: [*]const *llvm.BasicBlock, incoming_count: c_uint) void; -pub extern fn LLVMBuildSelect(builder: *llvm.Builder, condition: *llvm.Value, true_value: *llvm.Value, false_value: *llvm.Value, name: [*:0]const u8) *llvm.Value; - -pub extern fn LLVMBuildVAArg(builder: *llvm.Builder, va_list: *llvm.Value, arg_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildSwitch(builder: *llvm.Builder, discriminant: *llvm.Value, else_basic_block: *llvm.BasicBlock, case_count: c_uint) *llvm.Instruction.Switch; -pub extern fn LLVMAddCase(switchi: *llvm.Instruction.Switch, case_value: *llvm.Value, case_block: *llvm.BasicBlock) void; - -// Casts -pub extern fn LLVMBuildIntCast2(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, is_signed: Bool, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildZExt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildSExt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildTrunc(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildIntToPtr(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildPtrToInt(builder: *llvm.Builder, value: *llvm.Value, destination_type: *llvm.Type, name: [*:0]const u8) *llvm.Value; -pub extern fn LLVMBuildPointerCast(builder: *llvm.Builder, value: *llvm.Value, ty: *llvm.Type, name: [*:0]const u8) *llvm.Value; - -pub extern fn LLVMGetCurrentDebugLocation2(builder: *llvm.Builder) ?*llvm.DI.Location; -pub extern fn LLVMSetCurrentDebugLocation2(builder: *llvm.Builder, location: ?*llvm.DI.Location) void; - -pub extern fn LLVMTypeOf(value: *llvm.Value) *llvm.Type; -pub extern fn LLVMSizeOf(ty: *llvm.Type) *llvm.Constant; -pub extern fn LLVMAlignOf(ty: *llvm.Type) *llvm.Constant; -pub extern fn LLVMGlobalGetValueType(value: *llvm.GlobalValue) *llvm.Type; -pub extern fn LLVMGetInitializer(global_variable: *llvm.GlobalVariable) *llvm.Constant; -pub extern fn LLVMSetInitializer(global_variable: *llvm.GlobalVariable, initializer: *llvm.Constant) void; -pub extern fn LLVMDeleteGlobal(global_variable: *llvm.GlobalVariable) void; -pub extern fn llvm_global_variable_delete(global_variable: *llvm.GlobalVariable) void; -pub extern fn llvm_value_is_instruction(value: *llvm.Value) bool; - -// Intrinsics -pub extern fn LLVMLookupIntrinsicID(name_pointer: [*]const u8, name_length: usize) llvm.Intrinsic.Id; -pub extern fn LLVMGetNamedFunction(module: *llvm.Module, name: [*:0]const u8) *llvm.Function; -pub extern fn LLVMGetIntrinsicDeclaration(module: *llvm.Module, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Value; -pub extern fn LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function; - -// Attributes -pub extern fn llvm_attribute_list_build(context: *llvm.Context, options: *const llvm.Attribute.List.Options, call_site: bool) *llvm.Attribute.List; -pub extern fn llvm_function_set_attributes(function: *llvm.Function, attribute_list: *llvm.Attribute.List) void; -pub extern fn llvm_call_base_set_attributes(function: *llvm.Instruction.CallBase, attribute_list: *llvm.Attribute.List) void; - -// pub extern fn LLVMGetEnumAttributeKindForName(name_pointer: [*]const u8, name_length: usize) llvm.Attribute.Kind; -// -// pub extern fn LLVMCreateEnumAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, value: u64) *llvm.Attribute; -// pub extern fn LLVMCreateTypeAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, ty: *llvm.Type) *llvm.Attribute; -// pub extern fn LLVMCreateConstantRangeAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, bit_count: c_uint, lower_words: [*]const u64, upper_words: [*]const u64) *llvm.Attribute; -// pub extern fn LLVMCreateStringAttribute(context: *llvm.Context, key_pointer: [*]const u8, key_length: c_uint, value_pointer: [*]const u8, value_length: usize) *llvm.Attribute; -// -// pub extern fn LLVMAddAttributeAtIndex(function: *llvm.Function, attribute_index: llvm.Attribute.Index, attribute: *llvm.Attribute) void; -// pub extern fn LLVMAddCallSiteAttribute(call: *llvm.Instruction.Call, attribute_index: llvm.Attribute.Index, attribute: *llvm.Attribute) void; - -// TYPES -// Types: integers -pub extern fn LLVMVoidTypeInContext(context: *llvm.Context) *llvm.Type; -pub extern fn LLVMInt1TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMInt8TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMInt16TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMInt32TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMInt64TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMInt128TypeInContext(context: *llvm.Context) *llvm.Type.Integer; -pub extern fn LLVMIntTypeInContext(context: *llvm.Context, bit_count: c_uint) *llvm.Type.Integer; - -// Types: floating point -pub extern fn LLVMHalfTypeInContext(context: *llvm.Context) *llvm.Type; -pub extern fn LLVMBFloatTypeInContext(context: *llvm.Context) *llvm.Type; -pub extern fn LLVMFloatTypeInContext(context: *llvm.Context) *llvm.Type; -pub extern fn LLVMDoubleTypeInContext(context: *llvm.Context) *llvm.Type; -pub extern fn LLVMFP128TypeInContext(context: *llvm.Context) *llvm.Type; - -// Types: functions -pub extern fn LLVMFunctionType(return_type: *llvm.Type, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: c_uint, is_var_arg: Bool) *llvm.Type.Function; -pub extern fn LLVMIsFunctionVarArg(function_type: *llvm.Type.Function) Bool; -pub extern fn LLVMGetReturnType(function_type: *llvm.Type.Function) *llvm.Type; -pub extern fn LLVMSetSubprogram(function: *llvm.Function, subprogram: *llvm.DI.Subprogram) void; -pub extern fn LLVMGetSubprogram(function: *llvm.Function) ?*llvm.DI.Subprogram; -pub extern fn llvm_subprogram_replace_type(subprogram: *llvm.DI.Subprogram, subroutine_type: *llvm.DI.Type.Subroutine) void; -pub extern fn LLVMCountParamTypes(function_type: *llvm.Type.Function) c_uint; -pub extern fn LLVMGetParamTypes(function_type: *llvm.Type.Function, types: [*]*llvm.Type) void; - -// Types: struct -pub extern fn LLVMStructSetBody(struct_type: *llvm.Type.Struct, element_type_pointer: [*]const *llvm.Type, element_type_count: c_uint, is_packed: Bool) void; -pub extern fn llvm_context_create_forward_declared_struct_type(context: *llvm.Context, name: llvm.String) *llvm.Type.Struct; -pub extern fn llvm_context_create_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, name: llvm.String, is_packed: bool) *llvm.Type.Struct; -pub extern fn llvm_context_get_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, is_packed: bool) *llvm.Type.Struct; - -// Types: arrays -pub extern fn LLVMArrayType2(element_type: *llvm.Type, element_count: u64) *llvm.Type.Array; - -// Types: pointers -pub extern fn LLVMPointerTypeInContext(context: *llvm.Context, address_space: c_uint) *llvm.Type.Pointer; - -// Types: vectors -pub extern fn LLVMVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.FixedVector; -pub extern fn LLVMScalableVectorType(element_type: *llvm.Type, element_count: c_uint) *llvm.Type.ScalableVector; - -pub extern fn LLVMGetTypeKind(ty: *llvm.Type) llvm.Type.Kind; - -pub extern fn llvm_integer_type_get_bit_count(integer_type: *llvm.Type.Integer) c_uint; - -// VALUES -pub extern fn LLVMGetPoison(type: *llvm.Type) *llvm.Value; -pub extern fn LLVMConstNeg(constant: *llvm.Constant) *llvm.Constant; -pub extern fn LLVMConstNull(type: *llvm.Type) *llvm.Constant; -pub extern fn LLVMConstInt(type: *llvm.Type.Integer, value: c_ulonglong, sign_extend: Bool) *llvm.Constant.Integer; -pub extern fn LLVMConstIntGetZExtValue(constant: *llvm.Constant) u64; -pub extern fn LLVMConstIntGetSExtValue(constant: *llvm.Constant) i64; -pub extern fn LLVMConstArray2(element_type: *llvm.Type, value_pointer: [*]const *llvm.Constant, value_length: u64) *llvm.Constant; -pub extern fn LLVMConstStructInContext(context: *llvm.Context, constant_value_pointer: [*]const *llvm.Constant, constant_value_count: c_uint, is_packed: c_uint) *llvm.Constant; -pub extern fn LLVMConstNamedStruct(struct_type: *llvm.Type.Struct, constant_value_pointer: [*]const *llvm.Constant, constant_value_count: c_uint) *llvm.Constant; -pub extern fn LLVMConstStringInContext2(context: *llvm.Context, string_pointer: [*]const u8, string_length: usize, dont_null_terminate: Bool) *llvm.Constant; - -pub extern fn LLVMGetValueKind(value: *llvm.Value) llvm.Value.Kind; -pub extern fn LLVMIsConstant(value: *llvm.Value) Bool; - -// Debug info API -pub extern fn LLVMCreateDIBuilder(module: *llvm.Module) *llvm.DI.Builder; -pub extern fn LLVMDIBuilderFinalize(builder: *llvm.DI.Builder) void; -pub extern fn LLVMDIBuilderCreateFile(builder: *llvm.DI.Builder, file_name: llvm.String, directory_name: llvm.String) *llvm.DI.File; -pub extern fn LLVMDIBuilderCreateCompileUnit(builder: *llvm.DI.Builder, language: llvm.Dwarf.SourceLanguage, file: *llvm.DI.File, producer_name: llvm.String, optimized: Bool, flags: llvm.String, runtime_version: c_uint, split_name: llvm.String, dwarf_emission_kind: llvm.Dwarf.EmissionKind, debug_with_offset_id: c_uint, split_debug_inlining: Bool, debug_info_for_profiling: Bool, sysroot: llvm.String, sdk: llvm.String) *llvm.DI.CompileUnit; -pub extern fn LLVMDIBuilderCreateSubroutineType(builder: *llvm.DI.Builder, file: *llvm.DI.File, parameter_type_pointer: [*]const *llvm.DI.Type, parameter_type_count: c_uint, flags: llvm.DI.Flags) *llvm.DI.Type.Subroutine; -pub extern fn LLVMDIBuilderCreateFunction(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name: llvm.String, linkage_name: llvm.String, file: *llvm.DI.File, line_number: c_uint, type: ?*llvm.DI.Type.Subroutine, local_to_unit: Bool, is_definition: Bool, scope_line: c_uint, flags: llvm.DI.Flags, is_optimized: Bool) *llvm.DI.Subprogram; -pub extern fn LLVMDIBuilderFinalizeSubprogram(builder: *llvm.DI.Builder, subprogram: *llvm.DI.Subprogram) void; -pub extern fn LLVMDIBuilderCreateExpression(builder: *llvm.DI.Builder, address: ?[*]const u64, length: u64) *llvm.DI.Expression; -pub extern fn LLVMDIBuilderCreateDebugLocation(context: *llvm.Context, line: c_uint, column: c_uint, scope: *llvm.DI.Scope, inlined_at: ?*llvm.DI.Metadata) *llvm.DI.Location; -pub extern fn LLVMDIBuilderCreateBasicType(builder: *llvm.DI.Builder, name_pointer: [*]const u8, name_length: usize, bit_count: u64, dwarf_type: llvm.Dwarf.Type, flags: llvm.DI.Flags) *llvm.DI.Type; -pub extern fn LLVMDIBuilderCreateAutoVariable(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, type: *llvm.DI.Type, always_preserve: Bool, flags: llvm.DI.Flags, align_in_bits: u32) *llvm.DI.LocalVariable; -pub extern fn LLVMDIBuilderInsertDeclareRecordAtEnd(builder: *llvm.DI.Builder, storage: *llvm.Value, local_variable: *llvm.DI.LocalVariable, expression: *llvm.DI.Expression, debug_location: *llvm.DI.Location, basic_block: *llvm.BasicBlock) *llvm.DI.Record; -pub extern fn LLVMDIBuilderCreateParameterVariable(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, argument_number: c_uint, file: *llvm.DI.File, line: c_uint, type: *llvm.DI.Type, always_preserve: Bool, flags: llvm.DI.Flags) *llvm.DI.LocalVariable; -pub extern fn LLVMDIBuilderCreateGlobalVariableExpression(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line: c_uint, global_type: *llvm.DI.Type, local_to_unit: Bool, expression: *llvm.DI.Expression, declaration: ?*llvm.DI.Metadata, align_in_bits: u32) *llvm.DI.GlobalVariableExpression; -pub extern fn llvm_global_variable_add_debug_info(global_variable: *llvm.GlobalVariable, debug_global_variable: *llvm.DI.GlobalVariableExpression) void; -pub extern fn LLVMDIBuilderCreateLexicalBlock(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, file: *llvm.DI.File, line: c_uint, column: c_uint) *llvm.DI.LexicalBlock; -pub extern fn LLVMDIBuilderCreateReplaceableCompositeType(builder: *llvm.DI.Builder, tag: c_uint, name_pointer: [*]const u8, name_length: usize, scope: *llvm.DI.Scope, file: *llvm.DI.File, line: c_uint, runtime_language: c_uint, bit_size: u64, align_in_bits: u32, flags: llvm.DI.Flags, unique_identifier_pointer: ?[*]const u8, unique_identifier_length: usize) *llvm.DI.Type.Composite; -pub extern fn LLVMDIBuilderCreateArrayType(builder: *llvm.DI.Builder, element_count: u64, align_in_bits: u32, element_type: *llvm.DI.Type, subscript_pointer: ?[*]const *llvm.DI.Metadata, subscript_count: c_uint) *llvm.DI.Type.Composite; -pub extern fn LLVMDIBuilderCreateStructType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: llvm.DI.Flags, derived_from: ?*llvm.DI.Type, member_pointer: [*]const *llvm.DI.Type.Derived, member_length: c_uint, runtime_language: c_uint, vtable_holder: ?*llvm.DI.Metadata, unique_id_pointer: ?[*]const u8, unique_id_length: usize) *llvm.DI.Type.Composite; -pub extern fn LLVMDIBuilderCreateUnionType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, flags: llvm.DI.Flags, member_pointer: [*]const *llvm.DI.Type.Derived, member_length: c_uint, runtime_language: c_uint, unique_id_pointer: ?[*]const u8, unique_id_length: usize) *llvm.DI.Type.Composite; -pub extern fn LLVMDIBuilderCreateMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, bit_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived; -pub extern fn LLVMDIBuilderCreateBitFieldMemberType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, bit_offset: u64, bit_storage_offset: u64, flags: llvm.DI.Flags, member_type: *llvm.DI.Type) *llvm.DI.Type.Derived; -pub extern fn LLVMDIBuilderCreatePointerType(builder: *llvm.DI.Builder, element_type: *llvm.DI.Type, bit_size: u64, align_in_bits: u32, address_space: c_uint, name_pointer: [*]const u8, name_length: usize) *llvm.DI.Type.Derived; -pub extern fn LLVMDIBuilderCreateEnumerator(builder: *llvm.DI.Builder, name_pointer: [*]const u8, name_length: usize, value: i64, is_unsigned: Bool) *llvm.DI.Enumerator; -pub extern fn LLVMDIBuilderCreateEnumerationType(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line: c_uint, bit_size: u64, align_in_bits: u32, enumerator_pointer: [*]const *llvm.DI.Enumerator, enumerator_count: c_uint, backing_type: *llvm.DI.Type) *llvm.DI.Type.Composite; -pub extern fn LLVMDIBuilderCreateTypedef(builder: *llvm.DI.Builder, ty: *llvm.DI.Type, name_pointer: [*]const u8, name_length: usize, file: *llvm.DI.File, line_number: c_uint, scope: *llvm.DI.Scope, align_in_bits: u32) *llvm.DI.Type.Derived; - -pub extern fn LLVMMetadataReplaceAllUsesWith(forward: *llvm.DI.Type.Composite, complete: *llvm.DI.Type.Composite) void; - -// Target -pub extern fn llvm_default_target_triple() llvm.String; -pub extern fn llvm_host_cpu_name() llvm.String; -pub extern fn llvm_host_cpu_features() llvm.String; - -pub extern fn llvm_create_target_machine(create: *const llvm.Target.Machine.Create, error_message: *llvm.String) ?*llvm.Target.Machine; -pub extern fn llvm_module_set_target(module: *llvm.Module, target_machine: *llvm.Target.Machine) void; - -pub extern fn llvm_module_run_optimization_pipeline(module: *llvm.Module, target_machine: *llvm.Target.Machine, options: llvm.OptimizationPipelineOptions) void; -pub extern fn llvm_module_run_code_generation_pipeline(module: *llvm.Module, target_machine: *llvm.Target.Machine, options: llvm.CodeGenerationPipelineOptions) llvm.CodeGenerationPipelineResult; - -pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type { - const arch_name = @tagName(llvm_arch); - return struct { - pub const initialize_target_info = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "TargetInfo", - }); - pub const initialize_target = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "Target", - }); - pub const initialize_target_mc = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "TargetMC", - }); - pub const initialize_asm_printer = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "AsmPrinter", - }); - pub const initialize_asm_parser = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "AsmParser", - }); - pub const initialize_disassembler = @extern(*const fn () callconv(.C) void, .{ - .name = "LLVMInitialize" ++ arch_name ++ "Disassembler", - }); - - pub fn initialize(options: llvm.TargetInitializerOptions) void { - initialize_target_info(); - initialize_target(); - initialize_target_mc(); - - if (options.asm_printer) { - initialize_asm_printer(); - } - - if (options.asm_parser) { - initialize_asm_parser(); - } - - if (options.disassembler) { - initialize_disassembler(); - } - } - }; -} - -// LLD - -pub extern fn lld_elf_link(argument_pointer: [*:null]const ?[*:0]const u8, argument_length: u64, exit_early: bool, disable_output: bool) lld.Result; diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index 1f23525..0000000 --- a/src/main.zig +++ /dev/null @@ -1,335 +0,0 @@ -const lib = @import("lib.zig"); -const configuration = @import("configuration"); -const os = lib.os; -const llvm = @import("LLVM.zig"); -const Arena = lib.Arena; - -const compiler = @import("bootstrap.zig"); -const BuildMode = compiler.BuildMode; - -test { - _ = lib; - _ = llvm; - _ = compiler; -} - -fn fail() noreturn { - lib.libc.exit(1); -} - -const Command = enum { - @"test", - compile, -}; - -const Compile = struct { - relative_file_path: [:0]const u8, - build_mode: BuildMode, - has_debug_info: bool, - silent: bool, -}; - -fn compile_file(arena: *Arena, compile: Compile) compiler.Options { - const relative_file_path = compile.relative_file_path; - if (relative_file_path.len < 5) { - fail(); - } - - const extension_start = lib.string.last_character(relative_file_path, '.') orelse fail(); - if (!lib.string.equal(relative_file_path[extension_start..], ".bbb")) { - fail(); - } - - const separator_index = lib.string.last_character(relative_file_path, '/') orelse 0; - const base_start = separator_index + @intFromBool(separator_index != 0 or relative_file_path[separator_index] == '/'); - const base_name = relative_file_path[base_start..extension_start]; - - const is_compiler = lib.string.equal(relative_file_path, "src/compiler.bbb"); - const output_path_dir = arena.join_string(&.{ - base_cache_dir, - if (is_compiler) "/compiler/" else "/", - @tagName(compile.build_mode), - "_", - if (compile.has_debug_info) "di" else "nodi", - }); - - os.make_directory(base_cache_dir); - if (is_compiler) { - os.make_directory(base_cache_dir ++ "/compiler"); - } - - os.make_directory(output_path_dir); - - const output_path_base = arena.join_string(&.{ - output_path_dir, - "/", - base_name, - }); - - const output_object_path = arena.join_string(&.{ output_path_base, ".o" }); - const output_executable_path = output_path_base; - - const file_content = lib.file.read(arena, relative_file_path); - const file_path = os.absolute_path(arena, relative_file_path); - const c_abi_object_path = arena.duplicate_string(configuration.c_abi_object_path); - - const convert_options = compiler.Options{ - .executable = output_executable_path, - .objects = if (lib.string.equal(base_name, "c_abi")) &.{ output_object_path, c_abi_object_path } else &.{output_object_path}, - .name = base_name, - .build_mode = compile.build_mode, - .content = file_content, - .path = file_path, - .has_debug_info = compile.has_debug_info, - .target = compiler.Target.get_native(), - .silent = compile.silent, - }; - - compiler.compile(arena, convert_options); - - return convert_options; -} - -const base_cache_dir = "bb-cache"; - -pub const panic = lib.panic_struct; -pub const std_options = lib.std_options; -pub const main = lib.main; - -pub fn entry_point(arguments: []const [*:0]const u8, environment: [*:null]const ?[*:0]const u8) void { - lib.GlobalState.initialize(); - const arena = lib.global.arena; - - if (arguments.len < 2) { - lib.print_string("error: Not enough arguments\n"); - fail(); - } - - const command = lib.string.to_enum(Command, lib.cstring.to_slice(arguments[1])) orelse fail(); - - switch (command) { - .compile => { - if (arguments.len < 3) { - lib.libc.exit(1); - } - - var build_mode = compiler.BuildMode.debug_none; - var has_debug_info = true; - - if (arguments.len >= 4) { - const build_mode_string = lib.cstring.to_slice(arguments[3]); - build_mode = lib.string.to_enum(compiler.BuildMode, build_mode_string) orelse lib.libc.exit(1); - } - - if (arguments.len >= 5) { - const has_debug_info_string = lib.cstring.to_slice(arguments[4]); - has_debug_info = if (lib.string.equal(has_debug_info_string, "true")) true else if (lib.string.equal(has_debug_info_string, "false")) false else lib.libc.exit(1); - } - - const relative_file_path = lib.cstring.to_slice(arguments[2]); - _ = compile_file(arena, .{ - .relative_file_path = relative_file_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .silent = false, - }); - }, - .@"test" => { - if (arguments.len != 2) { - fail(); - } - - const stop_at_failure = true; - - var build_modes: [@typeInfo(BuildMode).@"enum".fields.len]BuildMode = undefined; - inline for (@typeInfo(BuildMode).@"enum".fields, 0..) |field, field_index| { - const build_mode = @field(BuildMode, field.name); - build_modes[field_index] = build_mode; - } - - for (names) |name| { - for (build_modes) |build_mode| { - for ([2]bool{ true, false }) |has_debug_info| { - const position = arena.position; - defer arena.restore(position); - - const relative_file_path = arena.join_string(&.{ "tests/", name, ".bbb" }); - const compile_result = compile_file(arena, .{ - .relative_file_path = relative_file_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .silent = true, - }); - - const result = lib.os.run_child_process(arena, &.{compile_result.executable}, environment, .{ - .stdout = .inherit, - .stderr = .inherit, - .null_file_descriptor = null, - }); - - if (!result.is_successful()) { - lib.print_string("[BOOTSTRAP] Failed to run test "); - lib.print_string(name); - lib.print_string(" with build mode "); - lib.print_string(@tagName(build_mode)); - lib.print_string("\n"); - - if (stop_at_failure) { - lib.libc.exit(1); - } - } - } - } - } - - const relative_file_path = arena.join_string(&.{"src/compiler.bbb"}); - for (build_modes) |build_mode| { - for ([2]bool{ true, false }) |has_debug_info| { - const position = arena.position; - defer arena.restore(position); - - const compile_result = compile_file(arena, .{ - .relative_file_path = relative_file_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - .silent = true, - }); - - for (names[0..1]) |name| { - for (build_modes) |self_hosted_build_mode| { - for ([2]bool{ true, false }) |self_hosted_has_debug_info| { - const self_hosted_relative_file_path = arena.join_string(&.{ "tests/", name, ".bbb" }); - // TODO: investigar corrupcion de memoria en compile_result.executable porque compile_file borra la memoria - const result = lib.os.run_child_process(arena, &.{ compile_result.executable, "compile", self_hosted_relative_file_path, @tagName(self_hosted_build_mode), if (self_hosted_has_debug_info) "true" else "false" }, environment, .{ - .stdout = .inherit, - .stderr = .inherit, - .null_file_descriptor = null, - }); - - if (!result.is_successful()) { - lib.print_string("[SELF-HOSTED] Failed to compile "); - lib.print_string(name); - lib.print_string(" with build mode "); - lib.print_string(@tagName(build_mode)); - lib.print_string(" and debug info "); - lib.print_string(if (has_debug_info) "on" else "off"); - lib.print_string(", with self-hosted build mode "); - lib.print_string(@tagName(self_hosted_build_mode)); - lib.print_string(" and self-hosted debug info "); - lib.print_string(if (self_hosted_has_debug_info) "on" else "off"); - lib.print_string("\n"); - - if (stop_at_failure) { - lib.libc.exit(1); - } - } - } - } - } - } - } - }, - } -} - -const names = &[_][]const u8{ - "minimal", - "comments", - "constant_add", - "constant_and", - "constant_div", - "constant_mul", - "constant_rem", - "constant_or", - "constant_sub", - "constant_xor", - "constant_shift_left", - "constant_shift_right", - "minimal_stack", - "minimal_stack_arithmetic", - "minimal_stack_arithmetic2", - "minimal_stack_arithmetic3", - "extend", - "stack_negation", - "stack_add", - "stack_sub", - "integer_max", - "integer_hex", - "basic_pointer", - "basic_call", - "pointer", - "pointer_cast", - "u1_return", - "local_type_inference", - "global", - "function_pointer", - "extern", - "byte_size", - "basic_branch", - "basic_array", - "basic_enum", - "argv", - "assignment_operators", - "basic_enum", - "basic_slice", - "basic_string", - "basic_varargs", - "basic_while", - "not_pointer", - "bits", - "bits_no_backing_type", - "bits_return_u1", - "bits_zero", - "comparison", - "global_struct", - "if_no_else", - "if_no_else_void", - "indirect", - "indirect_struct", - "indirect_varargs", - "ret_c_bool", - "return_type_builtin", - "return_u64_u64", - "select", - "slice", - "small_struct_ints", - "struct_assignment", - "struct", - "struct_u64_u64", - "struct_varargs", - "struct_zero", - "unreachable", - "varargs", - "c_abi0", - "c_abi1", - "c_med_struct_ints", - "c_ret_struct_array", - "c_split_struct_ints", - "c_string_to_slice", - "c_struct_with_array", - "c_function_pointer", - "c_abi", - "string_to_enum", - "abi_enum_bool", - "empty_if", - "else_if", - "else_if_complicated", - "shortcircuiting_if", - "field_access_left_assign", - "for_each", - "pointer_decay", - "enum_name", - "slice_of_slices", - "type_alias", - "integer_formats", - "return_small_struct", - "basic_macro", - "generic_macro", - "for_each_int", - "bool_array", - "basic_union", - "constant_global_reference", - "generic_pointer_macro", - "break_continue", - "noreturn_macro", -}; diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..5d1efe6 --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,3641 @@ +#include + +enum class ValueIntrinsic +{ + align_of, + byte_size, + enum_name, + extend, + integer_max, + int_from_enum, + int_from_pointer, + pointer_cast, + pointer_from_int, + select, + string_to_enum, + trap, + truncate, + va_start, + va_end, + va_arg, + va_copy, + count, +}; + +enum class TokenId +{ + none, + comma, + end_of_statement, + integer, + left_brace, + left_bracket, + left_parenthesis, + right_brace, + right_bracket, + right_parenthesis, + + plus, + dash, + asterisk, + forward_slash, + percentage, + caret, + bar, + ampersand, + exclamation, + + assign_plus, + assign_dash, + assign_asterisk, + assign_forward_slash, + assign_percentage, + assign_caret, + assign_bar, + assign_ampersand, + + value_keyword, + operator_keyword, + identifier, + string_literal, + value_intrinsic, + + shift_left, + shift_right, + assign_shift_left, + assign_shift_right, + + compare_less, + compare_less_equal, + compare_greater, + compare_greater_equal, + compare_equal, + compare_not_equal, + + dot, + double_dot, + triple_dot, + + pointer_dereference, + + assign, + tilde, +}; + +enum class TokenIntegerKind +{ + hexadecimal, + decimal, + octal, + binary, + character_literal, +}; + +struct TokenInteger +{ + u64 value; + TokenIntegerKind kind; +}; + +enum class ValueKeyword +{ + undefined, + unreachable, + zero, + count, +}; + +enum class OperatorKeyword +{ + and_op, + or_op, + and_op_shortcircuit, + or_op_shortcircuit, + count, +}; + +struct Token +{ + union + { + TokenInteger integer; + ValueKeyword value_keyword; + String identifier; + OperatorKeyword operator_keyword; + ValueIntrinsic value_intrinsic; + String string_literal; + }; + TokenId id; +}; + +enum class Precedence +{ + none, + assignment, + boolean_or, + boolean_and, + comparison, + bitwise, + shifting, + add_like, + div_like, + prefix, + aggregate_initialization, + postfix, +}; + +struct ValueBuilder +{ + Token token; + Value* left; + Precedence precedence; + ValueKind kind; + bool allow_assignment_operators; + + inline ValueBuilder with_precedence(Precedence precedence) + { + auto result = *this; + result.precedence = precedence; + return result; + } + + inline ValueBuilder with_token(Token token) + { + auto result = *this; + result.token = token; + return result; + } + + inline ValueBuilder with_left(Value* value) + { + auto result = *this; + result.left = value; + return result; + } + + inline ValueBuilder with_kind(ValueKind kind) + { + auto result = *this; + result.kind = kind; + return result; + } +}; + +global_variable constexpr u8 left_bracket = '['; +global_variable constexpr u8 right_bracket = ']'; +global_variable constexpr u8 left_brace = '{'; +global_variable constexpr u8 right_brace = '}'; +global_variable constexpr u8 left_parenthesis = '('; +global_variable constexpr u8 right_parenthesis = ')'; + +fn bool is_space(u8 ch) +{ + return ((ch == ' ') | (ch == '\n')) | ((ch == '\t') | (ch == '\r')); +} + +fn bool is_lower(u8 ch) +{ + return ((ch >= 'a') & (ch <= 'z')); +} + +fn bool is_upper(u8 ch) +{ + return ((ch >= 'A') & (ch <= 'Z')); +} + +fn bool is_decimal(u8 ch) +{ + return ((ch >= '0') & (ch <= '9')); +} + +fn bool is_octal(u8 ch) +{ + return ((ch >= '0') & (ch <= '7')); +} + +fn bool is_binary(u8 ch) +{ + return ((ch == '0') | (ch == '1')); +} + +fn bool is_hexadecimal_alpha_lower(u8 ch) +{ + return ((ch >= 'a') & (ch <= 'f')); +} + +fn bool is_hexadecimal_alpha_upper(u8 ch) +{ + return ((ch >= 'A') & (ch <= 'F')); +} + +fn bool is_hexadecimal_alpha(u8 ch) +{ + return is_hexadecimal_alpha_lower(ch) || is_hexadecimal_alpha_upper(ch); +} + +fn bool is_hexadecimal(u8 ch) +{ + return is_decimal(ch) || is_hexadecimal_alpha(ch); +} + +fn bool is_identifier_start(u8 ch) +{ + return (is_lower(ch) || is_upper(ch)) || (ch == '_'); +} + +fn bool is_identifier(u8 ch) +{ + return is_identifier_start(ch) || is_decimal(ch); +} + +fn u32 get_line(Module* module) +{ + auto line = module->line_offset + 1; + assert(line < ~(u32)0); + return (u32)line; +} + +fn u32 get_column(Module* module) +{ + auto column = module->offset - module->line_character_offset + 1; + assert(column < ~(u32)0); + return (u32)column; +} + +struct Checkpoint +{ + u64 offset; + u64 line_offset; + u64 line_character_offset; +}; + +fn Checkpoint get_checkpoint(Module* module) +{ + return { + .offset = module->offset, + .line_offset = module->line_offset, + .line_character_offset = module->line_character_offset, + }; +} + +fn void set_checkpoint(Module* module, Checkpoint checkpoint) +{ + module->offset = checkpoint.offset; + module->line_offset = checkpoint.line_offset; + module->line_character_offset = checkpoint.line_character_offset; +} + +fn bool consume_character_if_match(Module* module, u8 expected_ch) +{ + bool is_ch = false; + auto i = module->offset; + if (i < module->content.length) + { + auto ch = module->content[i]; + is_ch = expected_ch == ch; + module->offset = i + is_ch; + } + + return is_ch; +} + +fn void expect_character(Module* module, u8 expected_ch) +{ + if (!consume_character_if_match(module, expected_ch)) + { + report_error(); + } +} + +fn void skip_space(Module* module) +{ + while (1) + { + auto iteration_offset = module->offset; + + while (module->offset < module->content.length) + { + auto ch = module->content[module->offset]; + if (!is_space(ch)) + { + break; + } + + module->line_offset += ch == '\n'; + module->line_character_offset = ch == '\n' ? module->offset : module->line_character_offset; + module->offset += 1; + } + + if (module->offset + 1 < module->content.length) + { + auto i = module->offset; + auto first_ch = module->content[i]; + auto second_ch = module->content[i + 1]; + auto is_comment = first_ch == '/' && second_ch == '/'; + + if (is_comment) + { + while (module->offset < module->content.length) + { + auto ch = module->content[module->offset]; + if (ch == '\n') + { + break; + } + module->offset += 1; + } + + if (module->offset < module->content.length) + { + module->line_offset += 1; + module->line_character_offset = module->offset; + module->offset += 1; + } + } + } + + if (module->offset - iteration_offset == 0) + { + break; + } + } +} + +fn String parse_identifier(Module* module) +{ + auto start = module->offset; + + if (is_identifier_start(module->content[start])) + { + module->offset = start + 1; + + while (module->offset < module->content.length) + { + auto i = module->offset; + if (is_identifier(module->content[i])) + { + module->offset = i + 1; + } + else + { + break; + } + } + } + + auto end = module->offset; + if (end - start == 0) + { + report_error(); + } + + return module->content(start, end); +} + +fn u64 accumulate_hexadecimal(u64 accumulator, u8 ch) +{ + u64 value; + + if (is_decimal(ch)) + { + value = ch - '0'; + } + else if (is_hexadecimal_alpha_upper(ch)) + { + value = ch - 'A' + 10; + } + else if (is_hexadecimal_alpha_lower(ch)) + { + value = ch - 'a' + 10; + } + else + { + unreachable(); + } + + auto result = (accumulator * 16) + value; + return result; +} + +fn u64 accumulate_decimal(u64 accumulator, u8 ch) +{ + assert(is_decimal(ch)); + return (accumulator * 10) + (ch - '0'); +} + +fn u64 accumulate_octal(u64 accumulator, u8 ch) +{ + assert(is_octal(ch)); + return (accumulator * 8) + (ch - '0'); +} + +fn u64 accumulate_binary(u64 accumulator, u8 ch) +{ + assert(is_binary(ch)); + return (accumulator * 2) + (ch - '0'); +} + +fn u64 parse_integer_decimal_assume_valid(String string) +{ + u64 value = 0; + + for (u8 ch: string) + { + assert(is_decimal(ch)); + value = accumulate_decimal(value, ch); + } + + return value; +} + +fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); + + +fn Type* parse_type(Module* module, Scope* scope) +{ + auto start_character = module->content[module->offset]; + if (is_identifier_start(start_character)) + { + auto identifier = parse_identifier(module); + if (identifier.equal(string_literal("void"))) + { + return void_type(module); + } + else if (identifier.equal(string_literal("noreturn"))) + { + return noreturn_type(module); + } + else + { + auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u'); + + if (is_int_type) + { + for (auto ch : identifier(1)) + { + is_int_type = is_int_type && is_decimal(ch); + } + } + + if (is_int_type) + { + bool is_signed; + switch (identifier[0]) + { + case 's': is_signed = true; break; + case 'u': is_signed = false; break; + default: unreachable(); + } + + auto bit_count = parse_integer_decimal_assume_valid(identifier(1)); + if (bit_count == 0) + { + report_error(); + } + if (bit_count > 64) + { + if (bit_count != 128) + { + report_error(); + } + } + + auto result = integer_type(module, { .bit_count = (u32)bit_count, .is_signed = is_signed }); + return result; + } + else + { + for (Type* type = module->first_type; type; type = type->next) + { + if (identifier.equal(type->name)) + { + return type; + } + } + + report_error(); + } + } + } + else if (start_character == '&') + { + module->offset += 1; + skip_space(module); + auto element_type = parse_type(module, scope); + auto pointer_type = get_pointer_type(module, element_type); + return pointer_type; + } + else if (start_character == left_bracket) + { + module->offset += 1; + skip_space(module); + + auto is_slice = consume_character_if_match(module, right_bracket); + if (is_slice) + { + skip_space(module); + auto element_type = parse_type(module, scope); + auto slice_type = get_slice_type(module, element_type); + return slice_type; + } + else + { + bool length_inferred = false; + auto checkpoint = get_checkpoint(module); + if (consume_character_if_match(module, '_')) + { + skip_space(module); + + length_inferred = consume_character_if_match(module, ']'); + } + + Value* length_value = 0; + u64 element_count = 0; + bool resolved = false; + if (!length_inferred) + { + set_checkpoint(module, checkpoint); + + length_value = parse_value(module, scope, {}); + assert(length_value); + + if (length_value->is_constant()) + { + switch (length_value->id) + { + case ValueId::constant_integer: + { + element_count = length_value->constant_integer.value; + if (element_count == 0) + { + report_error(); + } + resolved = true; + } break; + default: + { + report_error(); + } break; + } + } + + skip_space(module); + expect_character(module, right_bracket); + } + + skip_space(module); + + auto element_type = parse_type(module, scope); + + if (length_inferred) + { + assert(!length_value); + auto result = type_allocate_init(module, { + .array = { + .element_type = element_type, + .element_count = 0, + }, + .id = TypeId::array, + .name = string_literal(""), + }); + + return result; + } + else + { + if (!resolved) + { + report_error(); + } + + assert(element_count != 0); + + auto array_type = get_array_type(module, element_type, element_count); + return array_type; + } + } + } + else if (start_character == '#') + { + module->offset += 1; + auto identifier = parse_identifier(module); + enum class TypeIntrinsic + { + return_type, + count, + }; + + String type_intrinsics[] = { + string_literal("ReturnType"), + }; + + static_assert(array_length(type_intrinsics) == (u64)TypeIntrinsic::count); + + backing_type(TypeIntrinsic) i; + for (i = 0; i < (backing_type(TypeIntrinsic))TypeIntrinsic::count; i += 1) + { + String type_intrinsic = type_intrinsics[i]; + if (identifier.equal(type_intrinsic)) + { + break; + } + } + + auto intrinsic = (TypeIntrinsic)i; + switch (intrinsic) + { + case TypeIntrinsic::return_type: + { + auto return_type = module->current_function->variable.type->function.semantic_return_type; + return return_type; + } break; + case TypeIntrinsic::count: report_error(); + } + } + else + { + report_error(); + } +} + +fn u64 parse_hexadecimal(Module* module) +{ + u64 value = 0; + + while (true) + { + auto ch = module->content[module->offset]; + + if (!is_hexadecimal(ch)) + { + break; + } + + module->offset += 1; + value = accumulate_hexadecimal(value, ch); + } + + return value; +} + +fn u64 parse_decimal(Module* module) +{ + u64 value = 0; + + while (true) + { + auto ch = module->content[module->offset]; + + if (!is_decimal(ch)) + { + break; + } + + module->offset += 1; + value = accumulate_decimal(value, ch); + } + + return value; +} + +fn u64 parse_octal(Module* module) +{ + u64 value = 0; + + while (true) + { + auto ch = module->content[module->offset]; + + if (!is_octal(ch)) + { + break; + } + + module->offset += 1; + value = accumulate_octal(value, ch); + } + + return value; +} + +fn u64 parse_binary(Module* module) +{ + u64 value = 0; + + while (true) + { + auto ch = module->content[module->offset]; + + if (!is_binary(ch)) + { + break; + } + + module->offset += 1; + value = accumulate_binary(value, ch); + } + + return value; +} + +fn u8 escape_character(u8 ch) +{ + switch (ch) + { + case 'n': return '\n'; + case 't': return '\t'; + case 'r': return '\r'; + default: trap(); + } +} + +fn Token tokenize(Module* module) +{ + skip_space(module); + + auto start_index = module->offset; + if (start_index == module->content.length) + { + report_error(); + } + + auto start_character = module->content[start_index]; + + Token token; + + switch (start_character) + { + case ',': + case ';': + case '~': + case left_brace: + case left_parenthesis: + case left_bracket: + case right_brace: + case right_parenthesis: + case right_bracket: + { + module->offset += 1; + TokenId id; + switch (start_character) + { + case ',': id = TokenId::comma; break; + case ';': id = TokenId::end_of_statement; break; + case '~': id = TokenId::tilde; break; + case left_brace: id = TokenId::left_brace; break; + case left_parenthesis: id = TokenId::left_parenthesis; break; + case left_bracket: id = TokenId::left_bracket; break; + case right_brace: id = TokenId::right_brace; break; + case right_parenthesis: id = TokenId::right_parenthesis; break; + case right_bracket: id = TokenId::right_bracket; break; + default: unreachable(); + } + token = { + .id = id, + }; + } break; + case '#': + { + module->offset += 1; + if (is_identifier_start(module->content[module->offset])) + { + auto identifier = parse_identifier(module); + + String value_intrinsics[] = { + string_literal("align_of"), + string_literal("byte_size"), + string_literal("enum_name"), + string_literal("extend"), + string_literal("integer_max"), + string_literal("int_from_enum"), + string_literal("int_from_pointer"), + string_literal("pointer_cast"), + string_literal("pointer_from_int"), + string_literal("select"), + string_literal("string_to_enum"), + string_literal("trap"), + string_literal("truncate"), + string_literal("va_start"), + string_literal("va_end"), + string_literal("va_arg"), + string_literal("va_copy"), + }; + static_assert(array_length(value_intrinsics) == (u64)ValueIntrinsic::count); + + backing_type(ValueIntrinsic) i; + for (i = 0; i < (backing_type(ValueIntrinsic))(ValueIntrinsic::count); i += 1) + { + String candidate = value_intrinsics[i]; + if (identifier.equal(candidate)) + { + break; + } + } + + auto intrinsic = (ValueIntrinsic)i; + if (intrinsic == ValueIntrinsic::count) + { + report_error(); + } + + token = { + .value_intrinsic = intrinsic, + .id = TokenId::value_intrinsic, + }; + } + else + { + trap(); + } + } break; + case '<': + { + auto next_ch = module->content[start_index + 1]; + TokenId id; + switch (next_ch) + { + case '<': + switch (module->content[start_index + 2]) + { + case '=': id = TokenId::assign_shift_left; break; + default: id = TokenId::shift_left; break; + } break; + case '=': id = TokenId::compare_less_equal; break; + default: id = TokenId::compare_less; break; + } + + u64 add; + switch (id) + { + case TokenId::assign_shift_left: add = 3; break; + case TokenId::shift_left: + case TokenId::compare_less_equal: add = 2; break; + case TokenId::compare_less: add = 1; break; + default: unreachable(); + } + + module->offset += add; + token = { + .id = id, + }; + } break; + case '>': + { + auto next_ch = module->content[start_index + 1]; + TokenId id; + switch (next_ch) + { + case '>': + switch (module->content[start_index + 2]) + { + case '=': id = TokenId::assign_shift_right; break; + default: id = TokenId::shift_right; break; + } break; + case '=': id = TokenId::compare_greater_equal; break; + default: id = TokenId::compare_greater; break; + } + + u64 add; + switch (id) + { + case TokenId::assign_shift_right: add = 3; break; + case TokenId::shift_right: + case TokenId::compare_greater_equal: add = 2; break; + case TokenId::compare_greater: add = 1; break; + default: unreachable(); + } + + module->offset += add; + token = { + .id = id, + }; + } break; + case '=': + { + auto next_ch = module->content[start_index + 1]; + auto is_compare_equal = next_ch == '='; + TokenId id = is_compare_equal ? TokenId::compare_equal : TokenId::assign; + module->offset += is_compare_equal + 1; + token = { + .id = id, + }; + } break; + case '.': + { + auto next_ch = module->content[start_index + 1]; + TokenId id; + switch (next_ch) + { + default: id = TokenId::dot; break; + case '&': id = TokenId::pointer_dereference; break; + case '.': + switch (module->content[start_index + 2]) + { + case '.': id = TokenId::triple_dot; break; + default: id = TokenId::double_dot; break; + } break; + } + + u64 add; + switch (id) + { + case TokenId::dot: add = 1; break; + case TokenId::double_dot: add = 2; break; + case TokenId::triple_dot: add = 3; break; + case TokenId::pointer_dereference: add = 2; break; + default: unreachable(); + } + module->offset += add; + + token = { + .id = id, + }; + } break; + case '"': + { + module->offset += 1; + auto start = module->offset; + + while (1) + { + auto ch = module->content[module->offset]; + if (ch == '"') + { + break; + } + else if (ch == '\\') + { + trap(); + } + else + { + module->offset += 1; + } + } + + auto end = module->offset; + module->offset += 1; + auto string_literal = module->content(start, end); + + token = { + .string_literal = string_literal, + .id = TokenId::string_literal, + }; + } break; + case '\'': + { + module->offset += 1; + + u8 ch; + if (module->content[module->offset] == '\\') + { + module->offset += 1; + ch = escape_character(module->content[module->offset]); + } + else + { + ch = module->content[module->offset]; + if (ch == '\'') + { + report_error(); + } + } + + module->offset += 1; + expect_character(module, '\''); + token = Token{ + .integer = { + .value = ch, + .kind = TokenIntegerKind::character_literal, + }, + .id = TokenId::integer, + }; + } break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + auto next_ch = module->content[start_index + 1]; + TokenIntegerKind token_integer_kind = TokenIntegerKind::decimal; + if (start_character == '0') + { + switch (next_ch) + { + case 'x': token_integer_kind = TokenIntegerKind::hexadecimal; break; + case 'd': token_integer_kind = TokenIntegerKind::decimal; break; + case 'o': token_integer_kind = TokenIntegerKind::octal; break; + case 'b': token_integer_kind = TokenIntegerKind::binary; break; + default: token_integer_kind = TokenIntegerKind::decimal; break; + } + auto inferred_decimal = token_integer_kind == TokenIntegerKind::decimal && next_ch != 'd'; + module->offset += 2 * (token_integer_kind != TokenIntegerKind::decimal || !inferred_decimal); + } + + u64 value; + switch (token_integer_kind) + { + case TokenIntegerKind::hexadecimal: value = parse_hexadecimal(module); break; + case TokenIntegerKind::decimal: value = parse_decimal(module); break; + case TokenIntegerKind::octal: value = parse_octal(module); break; + case TokenIntegerKind::binary: value = parse_binary(module); break; + case TokenIntegerKind::character_literal: report_error(); break; + } + + token = { + .integer = { + .value = value, + .kind = token_integer_kind, + }, + .id = TokenId::integer, + }; + } break; + case '+': + case '-': + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case '!': + { + auto next_ch = module->content[start_index + 1]; + TokenId id; + if (next_ch == '=') + { + switch (start_character) + { + case '+': id = TokenId::assign_plus; break; + case '-': id = TokenId::assign_dash; break; + case '*': id = TokenId::assign_asterisk; break; + case '/': id = TokenId::assign_forward_slash; break; + case '%': id = TokenId::assign_percentage; break; + case '&': id = TokenId::assign_ampersand; break; + case '|': id = TokenId::assign_bar; break; + case '^': id = TokenId::assign_caret; break; + case '!': id = TokenId::compare_not_equal; break; + default: unreachable(); + } + } + else + { + switch (start_character) + { + case '+': id = TokenId::plus; break; + case '-': id = TokenId::dash; break; + case '*': id = TokenId::asterisk; break; + case '/': id = TokenId::forward_slash; break; + case '%': id = TokenId::percentage; break; + case '&': id = TokenId::ampersand; break; + case '|': id = TokenId::bar; break; + case '^': id = TokenId::caret; break; + case '!': id = TokenId::exclamation; break; + default: unreachable(); + } + } + + token.id = id; + + module->offset += 1 + (next_ch == '='); + } break; + default: + { + if (is_identifier_start(start_character)) + { + auto identifier = parse_identifier(module); + + String value_keywords[] = { + string_literal("undefined"), + string_literal("unreachable"), + string_literal("zero"), + }; + static_assert(array_length(value_keywords) == (u64)ValueKeyword::count); + + backing_type(ValueKeyword) i; + for (i = 0; i < (backing_type(ValueKeyword))ValueKeyword::count; i += 1) + { + String candidate = value_keywords[i]; + if (candidate.equal(identifier)) + { + break; + } + } + + auto value_keyword = (ValueKeyword)i; + + if (value_keyword == ValueKeyword::count) + { + auto advance = identifier.pointer[identifier.length] == '?'; + identifier.length += advance; + module->offset += advance; + + String operators[] = { + string_literal("and"), + string_literal("or"), + string_literal("and?"), + string_literal("or?"), + }; + static_assert(array_length(operators) == (u64)OperatorKeyword::count); + + backing_type(OperatorKeyword) i; + for (i = 0; i < (backing_type(OperatorKeyword))OperatorKeyword::count; i += 1) + { + auto candidate = operators[i]; + if (candidate.equal(identifier)) + { + break; + } + } + + auto operator_keyword = (OperatorKeyword)i; + if (operator_keyword == OperatorKeyword::count) + { + token = { + .identifier = identifier, + .id = TokenId::identifier, + }; + } + else + { + token = { + .operator_keyword = operator_keyword, + .id = TokenId::operator_keyword, + }; + } + } + else + { + token = { + .value_keyword = value_keyword, + .id = TokenId::value_keyword, + }; + } + } + else + { + report_error(); + } + } break; + } + + assert(start_index != module->offset); + return token; +} + +fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); + +fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder); +fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) +{ + Token token = builder.token; + Value* result; + switch (token.id) + { + case TokenId::integer: + { + auto integer_value = token.integer.value; + result = new_value(module); + *result = { + .constant_integer = { + .value = integer_value, + .is_signed = false, + }, + .id = ValueId::constant_integer, + .kind = ValueKind::right, + }; + } break; + case TokenId::dash: + case TokenId::ampersand: + case TokenId::exclamation: + case TokenId::tilde: + // Unary + { + assert(!builder.left); + UnaryId id; + switch (token.id) + { + case TokenId::dash: id = UnaryId::minus; break; + case TokenId::ampersand: id = UnaryId::ampersand; break; + case TokenId::exclamation: id = UnaryId::exclamation; break; + case TokenId::tilde: id = UnaryId::bitwise_not; break; + default: unreachable(); + } + + auto unary_value = parse_precedence(module, scope, builder.with_precedence(Precedence::prefix).with_token({}).with_kind(token.id == TokenId::ampersand ? ValueKind::left : builder.kind)); + + result = new_value(module); + *result = { + .unary = { + .value = unary_value, + .id = id, + }, + .id = ValueId::unary, + .kind = ValueKind::right, + }; + } break; + case TokenId::identifier: + { + result = reference_identifier(module, scope, token.identifier, builder.kind); + } break; + case TokenId::value_intrinsic: + { + ValueIntrinsic intrinsic = token.value_intrinsic; + result = new_value(module); + + switch (intrinsic) + { + case ValueIntrinsic::enum_name: + case ValueIntrinsic::extend: + case ValueIntrinsic::int_from_enum: + case ValueIntrinsic::int_from_pointer: + case ValueIntrinsic::truncate: + case ValueIntrinsic::pointer_cast: + case ValueIntrinsic::pointer_from_int: + case ValueIntrinsic::va_end: + { + UnaryId id; + switch (intrinsic) + { + case ValueIntrinsic::enum_name: id = UnaryId::enum_name; break; + case ValueIntrinsic::extend: id = UnaryId::extend; break; + case ValueIntrinsic::int_from_enum: id = UnaryId::int_from_enum; break; + case ValueIntrinsic::int_from_pointer: id = UnaryId::int_from_pointer; break; + case ValueIntrinsic::truncate: id = UnaryId::truncate; break; + case ValueIntrinsic::pointer_cast: id = UnaryId::pointer_cast; break; + case ValueIntrinsic::pointer_from_int: id = UnaryId::pointer_from_int; break; + case ValueIntrinsic::va_end: id = UnaryId::va_end; break; + default: unreachable(); + } + + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + auto argument = parse_value(module, scope, {}); + expect_character(module, right_parenthesis); + + *result = { + .unary = { + .value = argument, + .id = id, + }, + .id = ValueId::unary, + }; + } break; + case ValueIntrinsic::align_of: + case ValueIntrinsic::byte_size: + case ValueIntrinsic::integer_max: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto type = parse_type(module, scope); + + expect_character(module, right_parenthesis); + + UnaryTypeId id; + switch (intrinsic) + { + case ValueIntrinsic::align_of: id = UnaryTypeId::align_of; break; + case ValueIntrinsic::byte_size: id = UnaryTypeId::byte_size; break; + case ValueIntrinsic::integer_max: id = UnaryTypeId::integer_max; break; + default: unreachable(); + } + + *result = { + .unary_type = { + .type = type, + .id = id, + }, + .id = ValueId::unary_type, + }; + } break; + case ValueIntrinsic::select: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto condition = parse_value(module, scope, {}); + + expect_character(module, ','); + skip_space(module); + + auto true_value = parse_value(module, scope, {}); + + expect_character(module, ','); + skip_space(module); + + auto false_value = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, right_parenthesis); + + *result = { + .select = { + .condition = condition, + .true_value = true_value, + .false_value = false_value, + }, + .id = ValueId::select, + }; + } break; + case ValueIntrinsic::string_to_enum: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto type = parse_type(module, scope); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + auto string_value = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, right_parenthesis); + *result = { + .string_to_enum = { + .type = type, + .string = string_value, + }, + .id = ValueId::string_to_enum, + }; + } break; + case ValueIntrinsic::trap: + case ValueIntrinsic::va_start: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + expect_character(module, right_parenthesis); + + ValueId id; + switch (intrinsic) + { + case ValueIntrinsic::trap: id = ValueId::trap; break; + case ValueIntrinsic::va_start: id = ValueId::va_start; break; + default: unreachable(); + } + *result = { + .id = id, + }; + } break; + case ValueIntrinsic::va_arg: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + auto valist = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, ','); + skip_space(module); + auto ty = parse_type(module, scope); + skip_space(module); + expect_character(module, right_parenthesis); + *result = { + .va_arg = { + .va_list = valist, + .type = ty, + }, + .id = ValueId::va_arg, + }; + } break; + case ValueIntrinsic::va_copy: + { + trap(); + } break; + case ValueIntrinsic::count: unreachable(); + } + } break; + case TokenId::left_bracket: + { + u64 element_count = 0; + Value* value_buffer[64]; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + auto value = parse_value(module, scope, {}); + value_buffer[element_count] = value; + element_count += 1; + + consume_character_if_match(module, ','); + } + + auto values = new_value_array(module, element_count); + memcpy(values.pointer, value_buffer, element_count * sizeof(Value*)); + + result = new_value(module); + *result = { + .array_initialization = { + .values = values, + .is_constant = false, // This is analyzed later + }, + .id = ValueId::array_initialization, + }; + } break; + case TokenId::dot: + { + auto identifier = parse_identifier(module); + result = new_value(module); + + *result = { + .enum_literal = identifier, + .id = ValueId::enum_literal, + }; + } break; + case TokenId::left_parenthesis: + { + result = parse_value(module, scope, { + .kind = builder.kind, + }); + expect_character(module, right_parenthesis); + } break; + case TokenId::string_literal: + { + result = new_value(module); + *result = { + .string_literal = token.string_literal, + .id = ValueId::string_literal, + }; + } break; + case TokenId::left_brace: + { + skip_space(module); + + u64 field_count = 0; + String name_buffer[64]; + Value* value_buffer[64]; + bool zero = false; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + auto field_index = field_count; + if (consume_character_if_match(module, '.')) + { + auto name = parse_identifier(module); + name_buffer[field_index] = name; + skip_space(module); + expect_character(module, '='); + skip_space(module); + + auto value = parse_value(module, scope, {}); + value_buffer[field_index] = value; + skip_space(module); + consume_character_if_match(module, ','); + } + else + { + auto token = tokenize(module); + zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero; + if (zero) + { + skip_space(module); + + if (consume_character_if_match(module, ',')) + { + skip_space(module); + } + + expect_character(module, right_brace); + break; + } + else + { + report_error(); + } + } + + field_count += 1; + } + + auto names = arena_allocate(module->arena, field_count); + memcpy(names.pointer, name_buffer, sizeof(String) * field_count); + auto values = new_value_array(module, field_count); + memcpy(values.pointer, value_buffer, sizeof(Value*) * field_count); + + result = new_value(module); + *result = { + .aggregate_initialization = { + .names = names, + .values = values, + .is_constant = false, + .zero = zero, + }, + .id = ValueId::aggregate_initialization, + }; + } break; + case TokenId::value_keyword: + { + result = new_value(module); + Value value; + switch (token.value_keyword) + { + case ValueKeyword::undefined: value = { .id = ValueId::undefined }; break; + case ValueKeyword::unreachable: value = { .id = ValueId::unreachable }; break; + case ValueKeyword::zero: value = { .id = ValueId::zero }; break; + case ValueKeyword::count: unreachable(); + } + *result = value; + } break; + default: report_error(); + } + + return result; +} + +fn Precedence get_token_precedence(Token token) +{ + switch (token.id) + { + case TokenId::none: unreachable(); + case TokenId::comma: + case TokenId::double_dot: + case TokenId::end_of_statement: + case TokenId::right_brace: + case TokenId::right_bracket: + case TokenId::right_parenthesis: + return Precedence::none; + case TokenId::assign: + case TokenId::assign_shift_left: + case TokenId::assign_shift_right: + case TokenId::assign_plus: + case TokenId::assign_dash: + case TokenId::assign_asterisk: + case TokenId::assign_forward_slash: + case TokenId::assign_percentage: + case TokenId::assign_caret: + case TokenId::assign_bar: + case TokenId::assign_ampersand: + return Precedence::assignment; + case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added + { + switch (token.operator_keyword) + { + case OperatorKeyword::and_op: + case OperatorKeyword::and_op_shortcircuit: + return Precedence::boolean_and; + case OperatorKeyword::or_op: + case OperatorKeyword::or_op_shortcircuit: + return Precedence::boolean_or; + case OperatorKeyword::count: unreachable(); + } + } break; + case TokenId::compare_equal: + case TokenId::compare_not_equal: + case TokenId::compare_less: + case TokenId::compare_less_equal: + case TokenId::compare_greater: + case TokenId::compare_greater_equal: + return Precedence::comparison; + case TokenId::ampersand: + case TokenId::bar: + case TokenId::caret: + return Precedence::bitwise; + case TokenId::shift_left: + case TokenId::shift_right: + return Precedence::shifting; + case TokenId::plus: + case TokenId::dash: + return Precedence::add_like; + case TokenId::asterisk: + case TokenId::forward_slash: + case TokenId::percentage: + return Precedence::div_like; + case TokenId::pointer_dereference: + case TokenId::left_parenthesis: + case TokenId::left_bracket: + case TokenId::dot: + return Precedence::postfix; + default: trap(); + } +} + +fn Slice parse_call_arguments(Module* module, Scope* scope) +{ + Slice arguments = {}; + + u32 semantic_argument_count = 0; + Value* semantic_argument_buffer[64]; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + auto argument = parse_value(module, scope, {}); + auto argument_index = semantic_argument_count; + semantic_argument_buffer[argument_index] = argument; + + skip_space(module); + + consume_character_if_match(module, ','); + + semantic_argument_count += 1; + } + + if (semantic_argument_count != 0) + { + arguments = new_value_array(module, semantic_argument_count); + memcpy(arguments.pointer, semantic_argument_buffer, semantic_argument_count * sizeof(Value*)); + } + + return arguments; +} + +fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder) +{ + auto* left = builder.left; + assert(left); + + Token token = builder.token; + Value* result = 0; + + switch (token.id) + { + case TokenId::plus: + case TokenId::dash: + case TokenId::asterisk: + case TokenId::forward_slash: + case TokenId::percentage: + case TokenId::ampersand: + case TokenId::bar: + case TokenId::caret: + case TokenId::shift_left: + case TokenId::shift_right: + case TokenId::compare_equal: + case TokenId::compare_not_equal: + case TokenId::compare_less: + case TokenId::compare_less_equal: + case TokenId::compare_greater: + case TokenId::compare_greater_equal: + case TokenId::operator_keyword: + // Binary + { + auto precedence = get_token_precedence(token); + assert(precedence != Precedence::assignment); + + BinaryId id; + switch (token.id) + { + case TokenId::operator_keyword: + switch (token.operator_keyword) + { + case OperatorKeyword::and_op: id = BinaryId::logical_and; break; + case OperatorKeyword::or_op: id = BinaryId::logical_or; break; + case OperatorKeyword::and_op_shortcircuit: id = BinaryId::logical_and_shortcircuit; break; + case OperatorKeyword::or_op_shortcircuit: id = BinaryId::logical_or_shortcircuit; break; + case OperatorKeyword::count: unreachable(); + } break; + case TokenId::plus: id = BinaryId::add; break; + case TokenId::dash: id = BinaryId::sub; break; + case TokenId::asterisk: id = BinaryId::mul; break; + case TokenId::forward_slash: id = BinaryId::div; break; + case TokenId::percentage: id = BinaryId::rem; break; + case TokenId::ampersand: id = BinaryId::bitwise_and; break; + case TokenId::bar: id = BinaryId::bitwise_or; break; + case TokenId::caret: id = BinaryId::bitwise_xor; break; + case TokenId::shift_left: id = BinaryId::shift_left; break; + case TokenId::shift_right: id = BinaryId::shift_right; break; + case TokenId::compare_equal: id = BinaryId::compare_equal; break; + case TokenId::compare_not_equal: id = BinaryId::compare_not_equal; break; + case TokenId::compare_less: id = BinaryId::compare_less; break; + case TokenId::compare_less_equal: id = BinaryId::compare_less_equal; break; + case TokenId::compare_greater: id = BinaryId::compare_greater; break; + case TokenId::compare_greater_equal: id = BinaryId::compare_greater_equal; break; + default: unreachable(); + } + + auto right_precedence = (Precedence)((backing_type(Precedence))precedence + (precedence != Precedence::assignment)); + auto right = parse_precedence(module, scope, builder.with_precedence(right_precedence).with_token({}).with_left(0)); + + result = new_value(module); + *result = { + .binary = { + .left = left, + .right = right, + .id = id, + }, + .id = ValueId::binary, + .kind = ValueKind::right, + }; + } break; + case TokenId::pointer_dereference: + { + result = new_value(module); + *result = { + .unary = { + .value = left, + .id = UnaryId::dereference, + }, + .id = ValueId::unary, + .kind = ValueKind::right, + }; + } break; + case TokenId::left_parenthesis: + { + result = new_value(module); + // Callable + switch (left->id) + { + case ValueId::macro_reference: + { + auto* declaration = left->macro_reference; + if (declaration->is_generic()) + { + report_error(); + } + + auto instantiation_line = get_line(module); + auto instantiation_column = get_column(module); + + auto arguments = parse_call_arguments(module, scope); + + *result = { + .macro_instantiation = { + .declaration = declaration, + .instantiation_function = module->current_function, + .declaration_arguments = {}, + .instantiation_arguments = arguments, + .constant_arguments = {}, + .return_type = declaration->return_type, + .scope = { + .parent = scope, + .line = declaration->scope.line, + .column = declaration->scope.column, + .kind = ScopeKind::macro_instantiation, + }, + .line = instantiation_line, + .column = instantiation_column, + }, + .id = ValueId::macro_instantiation, + }; + } break; + default: + { + auto arguments = parse_call_arguments(module, scope); + *result = { + .call = { + .callable = left, + .arguments = arguments, + }, + .id = ValueId::call, + .kind = ValueKind::right, + }; + } break; + } + } break; + case TokenId::left_bracket: + { + skip_space(module); + result = new_value(module); + + if (left->id == ValueId::macro_reference) + { + auto* declaration = left->macro_reference; + if (!declaration->is_generic()) + { + report_error(); + } + + auto instantiation_line = get_line(module); + auto instantiation_column = get_column(module); + auto original_constant_argument_count = declaration->constant_arguments.length; + auto constant_arguments = arena_allocate(module->arena, original_constant_argument_count); + u64 constant_argument_count = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + auto constant_argument_index = constant_argument_count; + if (constant_argument_index == original_constant_argument_count) + { + report_error(); + } + + auto constant_argument = declaration->constant_arguments[constant_argument_index]; + + switch (constant_argument.id) + { + case ConstantArgumentId::value: + { + trap(); // TODO + } break; + case ConstantArgumentId::type: + { + auto argument_type = parse_type(module, scope); + constant_arguments[constant_argument_index] = { + .name = constant_argument.name, + .type = argument_type, + .id = ConstantArgumentId::type, + }; + } break; + } + + constant_argument_count += 1; + + skip_space(module); + consume_character_if_match(module, ','); + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + auto instantiation_arguments = parse_call_arguments(module, scope); + + *result = { + .macro_instantiation = { + .declaration = declaration, + .instantiation_function = module->current_function, + .declaration_arguments = {}, + .instantiation_arguments = instantiation_arguments, + .constant_arguments = constant_arguments, + .return_type = declaration->return_type, + .block = 0, + .scope = { + .parent = scope, + .line = declaration->scope.line, + .column = declaration->scope.column, + .kind = ScopeKind::macro_instantiation, + }, + .line = instantiation_line, + .column = instantiation_column, + }, + .id = ValueId::macro_instantiation, + }; + } + else + { + left->kind = ValueKind::left; + + Value* start_value = 0; + auto start = !(module->content[module->offset] == '.' && module->content[module->offset + 1] == '.'); + if (start) + { + start_value = parse_value(module, scope, {}); + } + + auto is_array = consume_character_if_match(module, right_bracket); + if (is_array) + { + if (!start_value) + { + report_error(); + } + + auto index = start_value; + *result = { + .array_expression = { + .array_like = left, + .index = index, + }, + .id = ValueId::array_expression, + .kind = builder.kind, + }; + } + else + { + expect_character(module, '.'); + expect_character(module, '.'); + + Value* end_value = 0; + if (!consume_character_if_match(module, right_bracket)) + { + end_value = parse_value(module, scope, {}); + expect_character(module, right_bracket); + } + + *result = { + .slice_expression = { + .array_like = left, + .start = start_value, + .end = end_value, + }, + .id = ValueId::slice_expression, + }; + } + } + } break; + case TokenId::dot: + { + left->kind = ValueKind::left; + + skip_space(module); + + auto identifier = parse_identifier(module); + result = new_value(module); + *result = { + .field_access = { + .aggregate = left, + .field_name = identifier, + }, + .id = ValueId::field_access, + .kind = builder.kind, + }; + } break; + default: report_error(); + } + + return result; +} + +fn Value* parse_precedence_left(Module* module, Scope* scope, ValueBuilder builder) +{ + auto result = builder.left; + auto precedence = builder.precedence; + + while (1) + { + auto checkpoint = get_checkpoint(module); + auto token = tokenize(module); + auto token_precedence = get_token_precedence(token); + if (token_precedence == Precedence::assignment) + { + token_precedence = builder.allow_assignment_operators ? Precedence::assignment : Precedence::none; + } + + if ((backing_type(Precedence))precedence > (backing_type(Precedence))token_precedence) + { + set_checkpoint(module, checkpoint); + break; + } + + auto left = result; + auto right = parse_right(module, scope, builder.with_token(token).with_precedence(Precedence::none).with_left(left)); + result = right; + } + + return result; +} + +fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder) +{ + assert(builder.token.id == TokenId::none); + auto token = tokenize(module); + auto left = parse_left(module, scope, builder.with_token(token)); + auto result = parse_precedence_left(module, scope, builder.with_left(left)); + return result; +} + +fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder) +{ + assert(builder.precedence == Precedence::none); + assert(!builder.left); + auto value = parse_precedence(module, scope, builder.with_precedence(Precedence::assignment)); + return value; +} + +fn Block* parse_block(Module* module, Scope* parent_scope); + +fn void print_value(Value* value, u32 identation) +{ + unused(identation); + for (u32 i = 0; i < identation; i += 1) + { + print(string_literal(" ")); + } + + switch (value->id) + { + case ValueId::unary: + { + switch (value->unary.id) + { + case UnaryId::extend: + { + print(string_literal("extend")); + } break; + default: unreachable(); + } + + print(string_literal("\n")); + + print_value(value->unary.value, identation + 1); + } break; + case ValueId::binary: + { + switch (value->binary.id) + { + case BinaryId::compare_equal: + { + print(string_literal("==")); + } break; + case BinaryId::compare_not_equal: + { + print(string_literal("!=")); + } break; + case BinaryId::logical_and: + { + print(string_literal("and")); + } break; + case BinaryId::logical_or: + { + print(string_literal("or")); + } break; + case BinaryId::logical_and_shortcircuit: + { + print(string_literal("and?")); + } break; + case BinaryId::logical_or_shortcircuit: + { + print(string_literal("or?")); + } break; + default: unreachable(); + } + print(string_literal("\n")); + + print_value(value->binary.left, identation + 1); + print_value(value->binary.right, identation + 1); + } break; + case ValueId::variable_reference: + { + print(value->variable_reference->name); + } break; + case ValueId::constant_integer: + { + print(string_literal("constant_integer")); + } break; + case ValueId::call: + { + print(string_literal("call ")); + } break; + default: unreachable(); + } + + print(string_literal("\n")); +} + +fn Statement* parse_statement(Module* module, Scope* scope) +{ + bool require_semicolon = true; + + auto statement_line = get_line(module); + auto statement_column = get_column(module); + + auto* statement = &arena_allocate(module->arena, 1)[0]; + *statement = Statement{ + .line = statement_line, + .column = statement_column, + }; + + auto statement_start_character = module->content[module->offset]; + switch (statement_start_character) + { + case '>': + { + module->offset += 1; + skip_space(module); + + auto local_name = parse_identifier(module); + skip_space(module); + + Type* local_type = 0; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + local_type = parse_type(module, scope); + skip_space(module); + } + expect_character(module, '='); + auto initial_value = parse_value(module, scope, {}); + + auto local = new_local(module, scope); + *local = { + .variable = { + .storage = 0, + .initial_value = initial_value, + .type = local_type, + .scope = scope, + .name = local_name, + .line = statement_line, + .column = statement_column, + }, + }; + statement->local = local; + statement->id = StatementId::local; + } break; + case '#': + { + statement->expression = parse_value(module, scope, {}); + statement->id = StatementId::expression; + } break; + case left_brace: + { + auto block = parse_block(module, scope); + statement->block = block; + statement->id = StatementId::block; + require_semicolon = false; + } break; + default: + { + if (is_identifier_start(statement_start_character)) + { + auto checkpoint = get_checkpoint(module); + auto statement_start_identifier = parse_identifier(module); + skip_space(module); + + enum class StatementStartKeyword + { + underscore_st, + return_st, + if_st, + // TODO: make `unreachable` a statement start keyword? + for_st, + while_st, + switch_st, + break_st, + continue_st, + count, + }; + + String statement_start_keywords[] = { + string_literal("_"), + string_literal("return"), + string_literal("if"), + string_literal("for"), + string_literal("while"), + string_literal("switch"), + string_literal("break"), + string_literal("continue"), + }; + + static_assert(array_length(statement_start_keywords) == (u64)StatementStartKeyword::count); + + backing_type(StatementStartKeyword) i; + for (i = 0; i < (backing_type(StatementStartKeyword))StatementStartKeyword::count; i += 1) + { + auto statement_start_keyword = statement_start_keywords[i]; + if (statement_start_keyword.equal(statement_start_identifier)) + { + break; + } + } + + auto statement_start_keyword = (StatementStartKeyword)i; + switch (statement_start_keyword) + { + case StatementStartKeyword::underscore_st: + { + trap(); + } break; + case StatementStartKeyword::return_st: + { + auto return_value = parse_value(module, scope, {}); + statement->return_st = return_value; + statement->id = StatementId::return_st; + } break; + case StatementStartKeyword::if_st: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto condition = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, right_parenthesis); + skip_space(module); + + auto if_statement = parse_statement(module, scope); + + skip_space(module); + + bool is_else = false; + Statement* else_statement = 0; + if (is_identifier_start(module->content[module->offset])) + { + auto checkpoint = get_checkpoint(module); + auto identifier = parse_identifier(module); + is_else = identifier.equal(string_literal("else")); + + if (is_else) + { + skip_space(module); + else_statement = parse_statement(module, scope); + } + else + { + set_checkpoint(module, checkpoint); + } + } + + require_semicolon = false; + + statement->if_st = { + .condition = condition, + .if_statement = if_statement, + .else_statement = else_statement, + }; + statement->id = StatementId::if_st; + } break; + case StatementStartKeyword::for_st: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto parent_scope = scope; + + *statement = Statement{ + .for_each = { + .first_local = 0, + .last_local = 0, + .left_values = {}, + .right_values = {}, + .predicate = 0, + .scope = { + .parent = parent_scope, + .line = statement_line, + .column = statement_column, + .kind = ScopeKind::for_each, + }, + .kind = {}, + }, + .id = StatementId::for_each, + .line = statement_line, + .column = statement_column, + }; + + auto scope = &statement->for_each.scope; + + ValueKind left_value_buffer[64]; + u64 left_value_count = 0; + + while (1) + { + skip_space(module); + + auto is_left = module->content[module->offset] == '&'; + module->offset += is_left; + + auto for_local_line = get_line(module); + auto for_local_column = get_column(module); + + if (is_identifier_start(module->content[module->offset])) + { + auto local_name = parse_identifier(module); + auto local = new_local(module, scope); + *local = { + .variable = { + .storage = 0, + .initial_value = 0, + .type = 0, + .scope = scope, + .name = local_name, + .line = for_local_line, + .column = for_local_column, + }, + }; + + auto kind = is_left ? ValueKind::left : ValueKind::right; + left_value_buffer[left_value_count] = kind; + left_value_count += 1; + } + else + { + trap(); + } + + skip_space(module); + + if (!consume_character_if_match(module, ',')) + { + expect_character(module, ':'); + break; + } + } + + skip_space(module); + + Value* right_value_buffer[64]; + u64 right_value_count = 0; + + right_value_buffer[right_value_count] = parse_value(module, scope, { .kind = ValueKind::left }); + right_value_count += 1; + + skip_space(module); + + auto token = tokenize(module); + + ForEachKind kind; + switch (token.id) + { + case TokenId::double_dot: + { + if (left_value_count != 1) + { + report_error(); + } + + right_value_buffer[0]->kind = ValueKind::right; + + right_value_buffer[right_value_count] = parse_value(module, scope, {}); + right_value_count += 1; + + expect_character(module, right_parenthesis); + kind = ForEachKind::range; + } break; + case TokenId::right_parenthesis: kind = ForEachKind::slice; break; + default: report_error(); + } + + statement->for_each.kind = kind; + + if (kind == ForEachKind::slice && left_value_count != right_value_count) + { + report_error(); + } + + auto left_values = arena_allocate(module->arena, left_value_count); + memcpy(left_values.pointer, left_value_buffer, left_value_count * sizeof(left_value_buffer[0])); + auto right_values = arena_allocate(module->arena, right_value_count); + memcpy(right_values.pointer, right_value_buffer, right_value_count * sizeof(right_value_buffer[0])); + + statement->for_each.left_values = left_values; + statement->for_each.right_values = right_values; + + skip_space(module); + + auto predicate = parse_statement(module, scope); + statement->for_each.predicate = predicate; + + skip_space(module); + + require_semicolon = false; + } break; + case StatementStartKeyword::while_st: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto condition = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, right_parenthesis); + skip_space(module); + + auto block = parse_block(module, scope); + + require_semicolon = false; + statement->while_st = { + .condition = condition, + .block = block, + }; + statement->id = StatementId::while_st; + } break; + case StatementStartKeyword::switch_st: + { + skip_space(module); + expect_character(module, left_parenthesis); + skip_space(module); + + auto discriminant = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, right_parenthesis); + + skip_space(module); + expect_character(module, left_brace); + + StatementSwitchClause clause_buffer[64]; + u64 clause_count = 0; + + while (1) + { + skip_space(module); + + bool is_else = false; + if (is_identifier_start(module->content[module->offset])) + { + auto else_checkpoint = get_checkpoint(module); + auto i = parse_identifier(module); + is_else = i.equal(string_literal("else")); + if (!is_else) + { + set_checkpoint(module, else_checkpoint); + } + } + + + Slice clause_values = {}; + if (is_else) + { + skip_space(module); + + expect_character(module, '='); + expect_character(module, '>'); + } + else + { + Value* case_buffer[64]; + u64 case_count = 0; + + while (1) + { + auto case_value = parse_value(module, scope, {}); + case_buffer[case_count] = case_value; + case_count += 1; + + consume_character_if_match(module, ','); + + skip_space(module); + + if (consume_character_if_match(module, '=')) + { + expect_character(module, '>'); + break; + } + } + + clause_values = new_value_array(module, case_count); + memcpy(clause_values.pointer, case_buffer, case_count * sizeof(Value*)); + } + + skip_space(module); + + auto clause_block = parse_block(module, scope); + + clause_buffer[clause_count] = { + .values = clause_values, + .block = clause_block, + }; + clause_count += 1; + + consume_character_if_match(module, ','); + + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + } + + auto clauses = arena_allocate(module->arena, clause_count); + memcpy(clauses.pointer, clause_buffer, sizeof(clause_buffer[0]) * clause_count); + + require_semicolon = false; + + statement->switch_st = { + .discriminant = discriminant, + .clauses = clauses, + }; + statement->id = StatementId::switch_st; + } break; + case StatementStartKeyword::break_st: + { + statement->id = StatementId::break_st; + } break; + case StatementStartKeyword::continue_st: + { + statement->id = StatementId::continue_st; + } break; + case StatementStartKeyword::count: + { + set_checkpoint(module, checkpoint); + + auto left = parse_value(module, scope, { .kind = ValueKind::left }); + + skip_space(module); + + if (consume_character_if_match(module, ';')) + { + require_semicolon = false; + statement->expression = left; + statement->id = StatementId::expression; + } + else + { + auto token = tokenize(module); + + StatementAssignmentId id; + switch (token.id) + { + case TokenId::assign: id = StatementAssignmentId::assign; break; + case TokenId::assign_plus: id = StatementAssignmentId::assign_add; break; + case TokenId::assign_dash: id = StatementAssignmentId::assign_sub; break; + case TokenId::assign_asterisk: id = StatementAssignmentId::assign_mul; break; + case TokenId::assign_forward_slash: id = StatementAssignmentId::assign_div; break; + case TokenId::assign_percentage: id = StatementAssignmentId::assign_rem; break; + case TokenId::assign_shift_left: id = StatementAssignmentId::assign_shift_left; break; + case TokenId::assign_shift_right: id = StatementAssignmentId::assign_shift_right; break; + case TokenId::assign_ampersand: id = StatementAssignmentId::assign_and; break; + case TokenId::assign_bar: id = StatementAssignmentId::assign_or; break; + case TokenId::assign_caret: id = StatementAssignmentId::assign_xor; break; + default: trap(); + } + + skip_space(module); + + auto right = parse_value(module, scope, {}); + statement->assignment = { + .left = left, + .right = right, + .id = id, + }; + statement->id = StatementId::assignment; + } + } break; + } + } + else + { + trap(); + } + } break; + } + + if (require_semicolon) + { + expect_character(module, ';'); + } + + return statement; +} + +fn Block* parse_block(Module* module, Scope* parent_scope) +{ + auto* block = &arena_allocate(module->arena, 1)[0]; + *block = { + .scope = { + .parent = parent_scope, + .line = get_line(module), + .column = get_column(module), + .kind = ScopeKind::local, + }, + }; + auto* scope = &block->scope; + + expect_character(module, left_brace); + + Statement* current_statement = 0; + + while (true) + { + skip_space(module); + + if (module->offset == module->content.length) + { + break; + } + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + auto* statement = parse_statement(module, scope); + if (!block->first_statement) + { + block->first_statement = statement; + } + + if (current_statement) + { + current_statement->next = statement; + } + + assert(statement->next == 0); + current_statement = statement; + } + + return block; +} + +void parse(Module* module) +{ + auto scope = &module->scope; + while (1) + { + skip_space(module); + + if (module->offset == module->content.length) + { + break; + } + + bool is_export = false; + bool is_extern = false; + + auto global_line = get_line(module); + auto global_column = get_column(module); + + if (consume_character_if_match(module, left_bracket)) + { + while (module->offset < module->content.length) + { + auto global_keyword_string = parse_identifier(module); + enum class GlobalKeyword + { + export_keyword, + extern_keyword, + count, + }; + String global_keyword_strings[] = { + string_literal("export"), + string_literal("extern"), + }; + static_assert(array_length(global_keyword_strings) == (u64)GlobalKeyword::count); + + u32 i; + for (i = 0; i < array_length(global_keyword_strings); i += 1) + { + String keyword = global_keyword_strings[i]; + if (keyword.equal(global_keyword_string)) + { + break; + } + } + + auto global_keyword = (GlobalKeyword)i; + switch (global_keyword) + { + case GlobalKeyword::export_keyword: + { + is_export = true; + } break; + case GlobalKeyword::extern_keyword: + { + is_extern = true; + } break; + case GlobalKeyword::count: + { + report_error(); + } + } + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + + skip_space(module); + } + + auto global_name = parse_identifier(module); + + Global* last_global = module->first_global; + while (last_global) + { + if (global_name.equal(last_global->variable.name)) + { + report_error(); + } + + if (!last_global->next) + { + break; + } + + last_global = last_global->next; + } + + Type* type_it = module->first_type; + Type* forward_declaration = 0; + while (type_it) + { + if (global_name.equal(type_it->name)) + { + if (type_it->id == TypeId::forward_declaration) + { + forward_declaration = type_it; + break; + } + else + { + report_error(); + } + } + + if (!type_it->next) + { + break; + } + + type_it = type_it->next; + } + + skip_space(module); + + Type* global_type = 0; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + + global_type = parse_type(module, scope); + + skip_space(module); + } + + expect_character(module, '='); + + skip_space(module); + + enum class GlobalKeyword + { + bits, + enumerator, + function, + macro, + structure, + typealias, + union_type, + count, + }; + + auto i = (backing_type(GlobalKeyword))GlobalKeyword::count; + + if (is_identifier_start(module->content[module->offset])) + { + auto checkpoint = get_checkpoint(module); + auto global_string = parse_identifier(module); + skip_space(module); + + String global_keywords[] = { + string_literal("bits"), + string_literal("enum"), + string_literal("fn"), + string_literal("macro"), + string_literal("struct"), + string_literal("typealias"), + string_literal("union"), + }; + static_assert(array_length(global_keywords) == (u64)GlobalKeyword::count); + + for (i = 0; i < (backing_type(GlobalKeyword))GlobalKeyword::count; i += 1) + { + String global_keyword = global_keywords[i]; + if (global_string.equal(global_keyword)) + { + break; + } + } + + auto global_keyword = (GlobalKeyword)i; + switch (global_keyword) + { + case GlobalKeyword::bits: + { + auto is_implicit_type = module->content[module->offset] == left_brace; + Type* backing_type = 0; + if (!is_implicit_type) + { + backing_type = parse_type(module, scope); + } + + skip_space(module); + expect_character(module, left_brace); + + u64 field_bit_offset = 0; + u64 field_count = 0; + Field field_buffer[64]; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) { + break; + } + + auto field_line = get_line(module); + auto field_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + auto field_type = parse_type(module, scope); + + auto field_bit_count = get_bit_size(field_type); + + skip_space(module); + + consume_character_if_match(module, ','); + + field_buffer[field_count] = { + .name = field_name, + .type = field_type, + .offset = field_bit_offset, + .line = field_line, + }; + + field_bit_offset += field_bit_count; + field_count += 1; + } + + consume_character_if_match(module, ';'); + + auto fields = arena_allocate(module->arena, field_count); + memcpy(fields.pointer, field_buffer, sizeof(Field) * field_count); + + auto needed_bit_count = MAX(next_power_of_two(field_bit_offset), 8); + if (needed_bit_count > ~(u32)0) + { + report_error(); + } + + auto bit_count = (u32)needed_bit_count; + + if (!backing_type) + { + backing_type = integer_type(module, { .bit_count = bit_count, .is_signed = false }); + } + + if (backing_type->id != TypeId::integer) + { + report_error(); + } + + auto backing_type_bit_size = get_bit_size(backing_type); + if (backing_type_bit_size > 64) + { + report_error(); + } + + auto bits_type = type_allocate_init(module, { + .bits = { + .fields = fields, + .backing_type = backing_type, + .line = global_line, + .is_implicit_backing_type = is_implicit_type, + }, + .id = TypeId::bits, + .name = global_name, + }); + unused(bits_type); + } break; + case GlobalKeyword::enumerator: + { + auto is_implicit_type = module->content[module->offset] == left_brace; + Type* backing_type = 0; + if (!is_implicit_type) + { + backing_type = parse_type(module, scope); + } + + skip_space(module); + expect_character(module, left_brace); + + u64 field_count = 0; + String name_buffer[64]; + u64 int_value_buffer[64]; + + bool is_resolved = true; + bool implicit_value = false; + unused(implicit_value); + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) { + break; + } + + auto field_index = field_count; + field_count += 1; + + auto field_name = parse_identifier(module); + name_buffer[field_index] = field_name; + + skip_space(module); + + u64 field_integer_value = field_index; + + if (consume_character_if_match(module, '=')) + { + skip_space(module); + auto field_value = parse_value(module, scope, {}); + if (is_resolved) + { + if (field_value->is_constant()) + { + switch (field_value->id) + { + case ValueId::constant_integer: + { + field_integer_value = field_value->constant_integer.value; + } break; + default: trap(); + } + } + else + { + trap(); + } + } + else + { + trap(); + } + } + else + { + if (!is_resolved) + { + report_error(); + } + } + + int_value_buffer[field_index] = field_integer_value; + + skip_space(module); + consume_character_if_match(module, ','); + } + + if (is_resolved) + { + auto fields = arena_allocate(module->arena, field_count); + u64 highest_value = 0; + // auto lowest_value = ~(u64)0; + + for (u64 i = 0; i < field_count; i += 1) + { + auto value = int_value_buffer[i]; + highest_value = MAX(highest_value, value); + fields[i] = { + .name = name_buffer[i], + .value = value, + }; + } + + auto needed_bit_count = 64 - (u32)clz(highest_value); + needed_bit_count = needed_bit_count ? needed_bit_count : 1; + + if (!backing_type) + { + backing_type = integer_type(module, { .bit_count = needed_bit_count, .is_signed = false }); + } + + auto enum_type = type_allocate_init(module, { + .enumerator = { + .fields = fields, + .backing_type = backing_type, + .line = global_line, + }, + .id = TypeId::enumerator, + .name = global_name, + }); + + unused(enum_type); + } + else + { + trap(); + } + } break; + case GlobalKeyword::function: + { + auto calling_convention = CallingConvention::c; + auto function_attributes = FunctionAttributes{}; + bool is_variable_arguments = false; + + if (consume_character_if_match(module, left_bracket)) + { + while (module->offset < module->content.length) + { + auto function_identifier = parse_identifier(module); + + enum class FunctionKeyword + { + cc, + count, + }; + + String function_keywords[] = { + string_literal("cc"), + }; + static_assert(array_length(function_keywords) == (u64)FunctionKeyword::count); + + backing_type(FunctionKeyword) i; + for (i = 0; i < (backing_type(FunctionKeyword))(FunctionKeyword::count); i += 1) + { + auto function_keyword = function_keywords[i]; + if (function_keyword.equal(function_identifier)) + { + break; + } + } + + auto function_keyword = (FunctionKeyword)i; + skip_space(module); + + switch (function_keyword) + { + case FunctionKeyword::cc: + { + expect_character(module, left_parenthesis); + skip_space(module); + auto calling_convention_string = parse_identifier(module); + String calling_conventions[] = { + string_literal("c"), + }; + static_assert(array_length(calling_conventions) == (u64)CallingConvention::count); + + backing_type(CallingConvention) i; + for (i = 0; i < (backing_type(CallingConvention))CallingConvention::count; i += 1) + { + auto calling_convention = calling_conventions[i]; + if (calling_convention.equal(calling_convention_string)) + { + break; + } + } + + auto candidate_calling_convention = (CallingConvention)i; + if (candidate_calling_convention == CallingConvention::count) + { + report_error(); + } + + calling_convention = candidate_calling_convention; + + skip_space(module); + expect_character(module, right_parenthesis); + } break; + case FunctionKeyword::count: + { + report_error(); + } break; + } + + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + Type* semantic_argument_type_buffer[64]; + String semantic_argument_name_buffer[64]; + u32 argument_line_buffer[64]; + u32 semantic_argument_count = 0; + + while (module->offset < module->content.length) + { + skip_space(module); + + if (consume_character_if_match(module, '.')) + { + expect_character(module, '.'); + expect_character(module, '.'); + skip_space(module); + expect_character(module, right_parenthesis); + is_variable_arguments = true; + break; + } + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + auto line = get_line(module); + argument_line_buffer[semantic_argument_count] = line; + + auto argument_name = parse_identifier(module); + semantic_argument_name_buffer[semantic_argument_count] = argument_name; + + skip_space(module); + + expect_character(module, ':'); + + skip_space(module); + + auto argument_type = parse_type(module, scope); + semantic_argument_type_buffer[semantic_argument_count] = argument_type; + + skip_space(module); + + unused(consume_character_if_match(module, ',')); + + semantic_argument_count += 1; + } + + skip_space(module); + + auto return_type = parse_type(module, scope); + + skip_space(module); + + Slice argument_types = {}; + if (semantic_argument_count != 0) + { + argument_types = new_type_array(module, semantic_argument_count); + memcpy(argument_types.pointer, semantic_argument_type_buffer, semantic_argument_count * sizeof(Type*)); + } + + auto is_declaration = consume_character_if_match(module, ';'); + + auto function_type = type_allocate_init(module, { + .function = { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_arguments = is_variable_arguments, + }, + .id = TypeId::function, + .name = string_literal(""), + }); + + auto storage = new_value(module); + *storage = { + .type = get_pointer_type(module, function_type), + .id = ValueId::external_function, + // TODO? .kind = ValueKind::left, + }; + auto global = new_global(module); + *global = { + .variable = { + .storage = storage, + .initial_value = 0, + .type = function_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, + }; + + if (!is_declaration) + { + module->current_function = global; + Slice arguments = arena_allocate(module->arena, semantic_argument_count); + for (u32 i = 0; i < semantic_argument_count; i += 1) + { + Argument* argument = &arguments[i]; + auto name = semantic_argument_name_buffer[i]; + auto* type = semantic_argument_type_buffer[i]; + auto line = argument_line_buffer[i]; + + *argument = { + .variable = { + .storage = 0, + .initial_value = 0, + .type = type, + .scope = &storage->function.scope, + .name = name, + .line = line, + .column = 0, + }, + .index = i + 1, + }; + } + + storage->function = { + .arguments = arguments, + .scope = { + .parent = scope, + .line = global_line, + .column = global_column, + .kind = ScopeKind::function, + }, + .block = 0, + .attributes = function_attributes, + }; + storage->id = ValueId::function; + + storage->function.block = parse_block(module, &storage->function.scope); + module->current_function = 0; + } + } break; + case GlobalKeyword::macro: + { + Type* type_argument_buffer[64]; + u64 type_argument_count = 0; + unused(type_argument_buffer); + unused(type_argument_count); + + ConstantArgument constant_argument_buffer[64]; + u64 constant_argument_count = 0; + + auto is_generic = consume_character_if_match(module, left_bracket); + + if (is_generic) + { + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + auto argument_name = parse_identifier(module); + + skip_space(module); + + auto has_value = consume_character_if_match(module, ':'); + + auto constant_argument_index = constant_argument_count; + + if (has_value) + { + trap(); // TODO + } + else + { + auto ty = type_allocate_init(module, { + .id = TypeId::unresolved, + .name = argument_name, + }); + + constant_argument_buffer[constant_argument_index] = { + .name = argument_name, + .type = ty, + .id = ConstantArgumentId::type, + }; + } + + constant_argument_count += 1; + } + + skip_space(module); + } + + expect_character(module, left_parenthesis); + + if (is_generic) + { + if (constant_argument_count == 0) + { + report_error(); + } + } + else + { + assert(constant_argument_count == 0); + } + + auto constant_arguments = arena_allocate(module->arena, constant_argument_count); + memcpy(constant_arguments.pointer, constant_argument_buffer, sizeof(constant_argument_buffer[0]) * constant_argument_count); + + auto macro_declaration = &arena_allocate(module->arena, 1)[0]; + *macro_declaration = { + .arguments = {}, + .constant_arguments = constant_arguments, + .return_type = 0, + .block = 0, + .name = global_name, + .scope = { + .parent = scope, + .line = global_line, + .column = global_column, + .kind = ScopeKind::macro_declaration, + }, + }; + + if (module->last_macro_declaration) + { + assert(module->first_macro_declaration); + module->last_macro_declaration->next = macro_declaration; + module->last_macro_declaration = macro_declaration; + } + else + { + assert(!module->first_macro_declaration); + module->first_macro_declaration = macro_declaration; + module->last_macro_declaration = macro_declaration; + } + + module->current_macro_declaration = macro_declaration; + + auto scope = ¯o_declaration->scope; + + Argument argument_buffer[64]; + u32 argument_count = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + auto argument_index = argument_count; + auto argument_line = get_line(module); + auto argument_column = get_column(module); + + auto argument_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + auto argument_type = parse_type(module, scope); + + auto argument = &argument_buffer[argument_count]; + *argument = { + .variable = { + .storage = 0, + .initial_value = 0, + .type = argument_type, + .scope = scope, + .name = argument_name, + .line = argument_line, + .column = argument_column, + }, + .index = argument_index + 1, + }; + argument_count += 1; + + skip_space(module); + + consume_character_if_match(module, ','); + } + + skip_space(module); + + auto return_type = parse_type(module, scope); + macro_declaration->return_type = return_type; + + auto arguments = arena_allocate(module->arena, argument_count); + memcpy(arguments.pointer, argument_buffer, sizeof(argument_buffer[0]) * argument_count); + macro_declaration->arguments = arguments; + + skip_space(module); + + auto block = parse_block(module, scope); + macro_declaration->block = block; + + // END OF SCOPE + module->current_macro_declaration = 0; + } break; + case GlobalKeyword::structure: + { + skip_space(module); + + Type* struct_type; + if (forward_declaration) + { + struct_type = forward_declaration; + } + else + { + struct_type = type_allocate_init(module, { + .id = TypeId::forward_declaration, + .name = global_name, + }); + } + + if (consume_character_if_match(module, left_brace)) + { + Field field_buffer[256]; + + u64 byte_size = 0; + u32 byte_alignment = 1; + + u32 field_count = 0; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + auto field_index = field_count; + auto field_line = get_line(module); + auto field_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + auto field_type = parse_type(module, scope); + + auto field_byte_alignment = get_byte_alignment(field_type); + auto field_byte_size = get_byte_size(field_type); + // Align struct size by field alignment + auto field_byte_offset = align_forward(byte_size, field_byte_alignment); + + field_buffer[field_index] = { + .name = field_name, + .type = field_type, + .offset = field_byte_offset, + .line = field_line, + }; + + byte_size = field_byte_offset + field_byte_size; + byte_alignment = MAX(byte_alignment, field_byte_alignment); + + skip_space(module); + + consume_character_if_match(module, ','); + + field_count += 1; + } + + skip_space(module); + consume_character_if_match(module, ';'); + + auto fields = arena_allocate(module->arena, field_count); + memcpy(fields.pointer, field_buffer, sizeof(Field) * field_count); + + struct_type->structure = { + .fields = fields, + .byte_size = byte_size, + .byte_alignment = byte_alignment, + .line = global_line, + .is_slice = false, + .next = 0, + }; + struct_type->id = TypeId::structure; + } + else + { + expect_character(module, ';'); + } + } break; + case GlobalKeyword::typealias: + { + auto aliased_type = parse_type(module, scope); + + if (!consume_character_if_match(module, ';')) + { + report_error(); + } + + auto alias_type = type_allocate_init(module, { + .alias = { + .type = aliased_type, + .scope = scope, + .line = global_line, + }, + .id = TypeId::alias, + .name = global_name, + }); + unused(alias_type); + } break; + case GlobalKeyword::union_type: + { + skip_space(module); + expect_character(module, left_brace); + + Type* union_type; + if (forward_declaration) + { + union_type = forward_declaration; + } + else + { + union_type = type_allocate_init(module, { + .id = TypeId::forward_declaration, + .name = global_name, + }); + } + + u32 field_count = 0; + u32 biggest_field = 0; + u32 byte_alignment = 1; + u32 byte_size = 0; + + UnionField field_buffer[64]; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + auto field_index = field_count; + field_count += 1; + + auto field_line = get_line(module); + auto field_name = parse_identifier(module); + + skip_space(module); + expect_character(module, ':'); + skip_space(module); + + auto field_type = parse_type(module, scope); + + auto field_byte_alignment = get_byte_alignment(field_type); + auto field_byte_size = get_byte_size(field_type); + + field_buffer[field_index] = UnionField{ + .type = field_type, + .name = field_name, + .line = field_line, + }; + + biggest_field = byte_size > field_byte_size ? field_index : biggest_field; + byte_alignment = MAX(byte_alignment, field_byte_alignment); + byte_size = MAX(byte_size, field_byte_size); + + skip_space(module); + + consume_character_if_match(module, ','); + } + + skip_space(module); + consume_character_if_match(module, ';'); + + auto fields = arena_allocate(module->arena, field_count); + memcpy(fields.pointer, field_buffer, sizeof(field_buffer[0]) * field_count); + + union_type->union_type = { + .fields = fields, + .byte_size = byte_size, + .byte_alignment = byte_alignment, + .line = global_line, + .biggest_field = biggest_field, + }; + union_type->id = TypeId::union_type; + } break; + case GlobalKeyword::count: + { + set_checkpoint(module, checkpoint); + } break; + } + } + + if (i == (backing_type(GlobalKeyword))GlobalKeyword::count) + { + auto initial_value = parse_value(module, scope, {}); + skip_space(module); + expect_character(module, ';'); + + auto global_storage = new_value(module); + *global_storage = { + .id = ValueId::global, + }; + + auto global = new_global(module); + *global = { + .variable = { + .storage = global_storage, + .initial_value = initial_value, + .type = global_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .linkage = Linkage::internal, // TODO: linkage + }; + } + } +} diff --git a/src/stack_trace.zig b/src/stack_trace.zig deleted file mode 100644 index 2d7f070..0000000 --- a/src/stack_trace.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); -export fn enable_signal_handlers() void { - std.debug.attachSegfaultHandler(); -} - -export fn dump_stack_trace(return_address: usize) void { - const stderr = std.io.getStdErr().writer(); - if (@import("builtin").strip_debug_info) { - stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; - return; - } - const debug_info = std.debug.getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; - return; - }; - std.debug.writeCurrentStackTrace(stderr, debug_info, std.io.tty.detectConfig(std.io.getStdErr()), return_address) catch |err| { - stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; - return; - }; -} diff --git a/tests/basic_bool_call.bbb b/tests/basic_bool_call.bbb new file mode 100644 index 0000000..7b9b28d --- /dev/null +++ b/tests/basic_bool_call.bbb @@ -0,0 +1,16 @@ +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +bb_bool = fn (x: u8) void +{ + require(#truncate(x)); +} + +[export] main = fn [cc(c)] () s32 +{ + bb_bool(1); + return 0; +} + diff --git a/tests/basic_shortcircuiting_if.bbb b/tests/basic_shortcircuiting_if.bbb new file mode 100644 index 0000000..c938530 --- /dev/null +++ b/tests/basic_shortcircuiting_if.bbb @@ -0,0 +1,12 @@ +[export] main = fn [cc(c)] (argument_count: u32) s32 +{ + >a: s32 = 0; + if (argument_count != 3 and? argument_count != 2) + { + return 0; + } + else + { + return 1; + } +} diff --git a/tests/concat_logical_or.bbb b/tests/concat_logical_or.bbb new file mode 100644 index 0000000..5b14498 --- /dev/null +++ b/tests/concat_logical_or.bbb @@ -0,0 +1,9 @@ +is_space = fn (ch: u8) u1 +{ + return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r'; +} + +[export] main = fn [cc(c)] () s32 +{ + return #extend(is_space('f')); +} diff --git a/tests/forward_declared_type.bbb b/tests/forward_declared_type.bbb new file mode 100644 index 0000000..967e39e --- /dev/null +++ b/tests/forward_declared_type.bbb @@ -0,0 +1,17 @@ +T = struct; +Foo = struct +{ + t: &T, +} + +T = struct +{ + f: Foo, +} + +[export] main = fn [cc(c)] () s32 +{ + >t: T = zero; + t.f.t = &t; + return 0; +} diff --git a/tests/generic_pointer_array.bbb b/tests/generic_pointer_array.bbb new file mode 100644 index 0000000..31b9765 --- /dev/null +++ b/tests/generic_pointer_array.bbb @@ -0,0 +1,15 @@ +foo = macro[T](addr: &u64, count: u64) []T +{ + >pointer: &T = #pointer_cast(addr); + return pointer[..count]; +} + +[export] main = fn [cc(c)] () s32 +{ + >address_raw: u64 = 0xaaaaaaaaaaaaaaaa; + >some_var: &u64 = #pointer_from_int(address_raw); + >result: []&u8 = foo[&u8](some_var, 1); + if (#int_from_pointer(result.pointer) != address_raw) #trap(); + if (result.length != 1) #trap(); + return 0; +} diff --git a/tests/pointer_struct_initialization.bbb b/tests/pointer_struct_initialization.bbb new file mode 100644 index 0000000..eda2b5b --- /dev/null +++ b/tests/pointer_struct_initialization.bbb @@ -0,0 +1,32 @@ +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +S = struct +{ + a: u16, + b: u8, + c: u8, + d: u32, +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = zero; + + >p_s = &s; + p_s.& = { + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; + + require(s.a == 1); + require(s.b == 2); + require(s.c == 3); + require(s.d == 4); + + return 0; +} diff --git a/tests/self_referential_struct.bbb b/tests/self_referential_struct.bbb new file mode 100644 index 0000000..f2f5463 --- /dev/null +++ b/tests/self_referential_struct.bbb @@ -0,0 +1,11 @@ +S = struct +{ + self: &S, +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = zero; + s.self = &s; + return 0; +} diff --git a/tests/slice_array_literal.bbb b/tests/slice_array_literal.bbb new file mode 100644 index 0000000..1f51463 --- /dev/null +++ b/tests/slice_array_literal.bbb @@ -0,0 +1,13 @@ +foo = fn (slices: [][]u8) void +{ + if (slices.length != 3) + { + #trap(); + } +} +[export] main = fn [cc(c)] () s32 +{ + >some_bool: u1 = 0; + foo([ "abc", #select(some_bool, "bcd", "cbd"), "sas", ][..]); + return 0; +} diff --git a/tests/slice_only_start.bbb b/tests/slice_only_start.bbb new file mode 100644 index 0000000..2a6afca --- /dev/null +++ b/tests/slice_only_start.bbb @@ -0,0 +1,11 @@ +[export] main = fn [cc(c)] () s32 +{ + >s = "abcde"; + >index: u64 = 3; + >s_sub = s[index..]; + if (s_sub[0] != 'd') + { + #trap(); + } + return 0; +} diff --git a/tests/strict_array_type.bbb b/tests/strict_array_type.bbb new file mode 100644 index 0000000..66ec212 --- /dev/null +++ b/tests/strict_array_type.bbb @@ -0,0 +1,5 @@ +[export] main = fn [cc(c)] () s32 +{ + >arr: [3]s32 = [3, 1, 0]; + return arr[2]; +}