Port the compiler from Zig to C++
All checks were successful
CI / ci (MinSizeRel, ubuntu-latest) (pull_request) Successful in 1m10s
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 1m9s
CI / ci (RelWithDebInfo, ubuntu-latest) (pull_request) Successful in 1m13s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 2m44s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Successful in 1m4s
CI / ci (MinSizeRel, ubuntu-latest) (push) Successful in 1m7s
CI / ci (Release, ubuntu-latest) (push) Successful in 1m4s
CI / ci (Debug, ubuntu-latest) (push) Successful in 2m40s

This commit is contained in:
David Gonzalez Martin 2025-05-23 07:47:39 -06:00
parent 0eee2a4ff3
commit 510f27e13f
33 changed files with 16936 additions and 17938 deletions

View File

@ -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

52
CMakeLists.txt Normal file
View File

@ -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
$<$<CONFIG:Debug>:BB_DEBUG=1>
$<$<NOT:$<CONFIG:Debug>>: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)

View File

@ -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%

View File

@ -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 ..

367
build.zig
View File

@ -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);
}
}

51
generate.sh Executable file
View File

@ -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 ..

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

506
src/compiler.cpp Normal file
View File

@ -0,0 +1,506 @@
#include <compiler.hpp>
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<Type>(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<String> object_slice = array_to_slice(objects);
String libraries[] = {
string_literal("build/libc_abi.a"),
};
Slice<String> 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<const char*> arguments, Slice<char* const> 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<const char* const> 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<const char* const> 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;
}
}

1718
src/compiler.hpp Normal file

File diff suppressed because it is too large Load Diff

8681
src/emitter.cpp Normal file

File diff suppressed because it is too large Load Diff

13
src/entry_point.cpp Normal file
View File

@ -0,0 +1,13 @@
#include <lib.hpp>
void entry_point(Slice<const char*> arguments, Slice<char* const> 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;
}

2
src/entry_point.hpp Normal file
View File

@ -0,0 +1,2 @@
#include <lib.h>

212
src/lib.cpp Normal file
View File

@ -0,0 +1,212 @@
#include <lib.hpp>
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<const char* const> arguments, Slice<char* const> 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;
}

718
src/lib.hpp Normal file
View File

@ -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 <typename T>
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<T> other)
{
return raw_slice_equal(*(RawSlice*)this, *(RawSlice*)&other, sizeof(T));
}
Slice<T> operator()(u64 start, u64 end)
{
return {pointer + start, end - start};
}
Slice<T> operator()(u64 start)
{
return {pointer + start, length - start};
}
};
using String = Slice<u8>;
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 <typename T>
fn Slice<T> 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<String> 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<u8>(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<const char* const> arguments, Slice<char* const> environment, ExecuteOptions options);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

645
src/llvm.hpp Normal file
View File

@ -0,0 +1,645 @@
#pragma once
#include <lib.hpp>
#include <llvm-c/Core.h>
#include <llvm-c/DebugInfo.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/Target.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/TargetMachine.h>
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);

View File

@ -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;

View File

@ -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",
};

3641
src/parser.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
};
}

16
tests/basic_bool_call.bbb Normal file
View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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'));
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,11 @@
S = struct
{
self: &S,
}
[export] main = fn [cc(c)] () s32
{
>s: S = zero;
s.self = &s;
return 0;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,5 @@
[export] main = fn [cc(c)] () s32
{
>arr: [3]s32 = [3, 1, 0];
return arr[2];
}