Delete C++ implementation and dead code
Some checks failed
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 23s
CI / ci (Release-assertions, ubuntu-latest) (pull_request) Successful in 27s
CI / ci (Release, ubuntu-latest) (push) Successful in 24s
CI / ci (Release-assertions, ubuntu-latest) (push) Successful in 30s
CI / ci (Debug, ubuntu-latest) (push) Successful in 4m56s
CI / release (ubuntu-latest) (push) Failing after 2s
Some checks failed
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 23s
CI / ci (Release-assertions, ubuntu-latest) (pull_request) Successful in 27s
CI / ci (Release, ubuntu-latest) (push) Successful in 24s
CI / ci (Release-assertions, ubuntu-latest) (push) Successful in 30s
CI / ci (Debug, ubuntu-latest) (push) Successful in 4m56s
CI / release (ubuntu-latest) (push) Failing after 2s
This commit is contained in:
parent
dd577c2ce7
commit
2575d6d027
@ -10,6 +10,9 @@ on:
|
|||||||
env:
|
env:
|
||||||
BB_CI: 1
|
BB_CI: 1
|
||||||
BUILD_DEBUG: 1
|
BUILD_DEBUG: 1
|
||||||
|
LLVM_VERSION: 20.1.7
|
||||||
|
CLANG_PATH: clang-19
|
||||||
|
CLANGXX_PATH: clang++-19
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
@ -24,10 +27,7 @@ jobs:
|
|||||||
- name: Build and test (Packaged LLVM)
|
- name: Build and test (Packaged LLVM)
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
|
||||||
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
||||||
CLANG_PATH: clang-19
|
|
||||||
CLANGXX_PATH: clang++-19
|
|
||||||
run: |
|
run: |
|
||||||
set -eux
|
set -eux
|
||||||
ci/reproduce.sh
|
ci/reproduce.sh
|
||||||
|
@ -5,7 +5,9 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
BB_CI: 1
|
||||||
|
CLANG_PATH: clang-19
|
||||||
|
CLANGXX_PATH: clang++-19
|
||||||
|
LLVM_VERSION: 20.1.7
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
strategy:
|
strategy:
|
||||||
@ -19,12 +21,8 @@ jobs:
|
|||||||
- name: Build and test (Packaged LLVM)
|
- name: Build and test (Packaged LLVM)
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
|
||||||
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
||||||
CLANG_PATH: clang-19
|
|
||||||
CLANGXX_PATH: clang++-19
|
|
||||||
run: |
|
run: |
|
||||||
set -eux
|
set -eux
|
||||||
./generate.sh
|
./generate.sh
|
||||||
./build.sh
|
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_x86_64-linux-${CMAKE_BUILD_TYPE} $HOME/bloat-buster-artifacts/releases/main/compiler_generic reproduce
|
||||||
./build/bb test
|
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(c_abi tests/c_abi.c)
|
|
||||||
|
|
||||||
include_directories(src)
|
|
||||||
add_compile_definitions(
|
|
||||||
$<$<CONFIG:Debug>:BB_DEBUG=1>
|
|
||||||
$<$<NOT:$<CONFIG:Debug>>:BB_DEBUG=0>
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(llvm_bindings NAMES libllvm_bindings.dylib libllvm_bindings.lib libllvm_bindings.a libllvm_bindingsELF.dll.a libllvm_bindingsELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
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
|
|
||||||
${LLVM_AVAILABLE_LIBS}
|
|
||||||
${LLD_COMMON}
|
|
||||||
${LLD_COFF}
|
|
||||||
${LLD_ELF}
|
|
||||||
${LLD_MACHO}
|
|
||||||
${LLD_MINGW}
|
|
||||||
${LLD_WASM}
|
|
||||||
${llvm_bindings}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -fno-signed-char -fwrapv -fno-strict-aliasing)
|
|
||||||
add_compile_definitions(CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}")
|
|
||||||
add_compile_definitions(BB_CI=${BB_CI})
|
|
||||||
|
|
||||||
# add_compile_options(-fsanitize=address)
|
|
||||||
# add_link_options(-fsanitize=address)
|
|
8
build.sh
8
build.sh
@ -1,4 +1,6 @@
|
|||||||
set -eu
|
set -eu
|
||||||
cd build
|
if [[ -z "${BOOTSTRAP_COMPILER:-}" ]]; then
|
||||||
ninja --quiet
|
BOOTSTRAP_COMPILER=$HOME/bloat-buster-artifacts/releases/main/compiler_generic
|
||||||
cd ..
|
fi
|
||||||
|
|
||||||
|
$BOOTSTRAP_COMPILER compile src/compiler.bbb
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
set -eux
|
set -eux
|
||||||
rm -rf bb-cache self-hosted-bb-cache || true
|
rm -rf bb-cache self-hosted-bb-cache || true
|
||||||
./generate.sh
|
./generate.sh
|
||||||
./build.sh
|
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_x86_64-linux-${CMAKE_BUILD_TYPE} $HOME/bloat-buster-artifacts/releases/main/compiler_generic reproduce
|
||||||
./build/bb test
|
|
||||||
mkdir -p $HOME/bloat-buster-artifacts/$(git rev-parse --abbrev-ref HEAD)/$(git rev-parse HEAD)/$CMAKE_BUILD_TYPE
|
mkdir -p $HOME/bloat-buster-artifacts/$(git rev-parse --abbrev-ref HEAD)/$(git rev-parse HEAD)/$CMAKE_BUILD_TYPE
|
||||||
mv ./self-hosted-bb-cache $HOME/bloat-buster-artifacts/$(git rev-parse --abbrev-ref HEAD)/$(git rev-parse HEAD)/$CMAKE_BUILD_TYPE/cache
|
mv ./self-hosted-bb-cache $HOME/bloat-buster-artifacts/$(git rev-parse --abbrev-ref HEAD)/$(git rev-parse HEAD)/$CMAKE_BUILD_TYPE/cache
|
||||||
|
|
||||||
|
30
generate.sh
30
generate.sh
@ -1,6 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -eux
|
set -eux
|
||||||
|
|
||||||
|
if [[ -z "${LLVM_VERSION:-}" ]]; then
|
||||||
|
LLVM_VERSION=20.1.7
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "${BB_CI:-}" ]]; then
|
if [[ -z "${BB_CI:-}" ]]; then
|
||||||
BB_CI=0
|
BB_CI=0
|
||||||
fi
|
fi
|
||||||
@ -12,8 +16,6 @@ else
|
|||||||
LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE
|
LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_DIR=build
|
|
||||||
|
|
||||||
BIRTH_NATIVE_OS_STRING=$OSTYPE
|
BIRTH_NATIVE_OS_STRING=$OSTYPE
|
||||||
|
|
||||||
case "$BIRTH_NATIVE_OS_STRING" in
|
case "$BIRTH_NATIVE_OS_STRING" in
|
||||||
@ -31,20 +33,22 @@ case "$BIRTH_NATIVE_ARCH_STRING" in
|
|||||||
*) exit 1
|
*) exit 1
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$BIRTH_OS" in
|
if [[ -z "${CMAKE_PREFIX_PATH:-}" ]]; then
|
||||||
linux) LINKER_TYPE=MOLD;;
|
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_${BIRTH_ARCH}-${BIRTH_OS}-${LLVM_CMAKE_BUILD_TYPE}
|
||||||
*) LINKER_TYPE=DEFAULT;;
|
fi
|
||||||
esac
|
|
||||||
|
|
||||||
rm -rf $BUILD_DIR
|
|
||||||
mkdir $BUILD_DIR
|
|
||||||
cd $BUILD_DIR
|
|
||||||
LLVM_PREFIX_PATH=$HOME/dev/llvm/install/llvm_20.1.7_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE
|
|
||||||
|
|
||||||
if [[ -z "${CLANG_PATH:-}" ]]; then
|
if [[ -z "${CLANG_PATH:-}" ]]; then
|
||||||
CLANG_PATH=clang
|
CLANG_PATH=clang
|
||||||
CLANGXX_PATH=clang++
|
CLANGXX_PATH=clang++
|
||||||
fi
|
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_BUILD_TYPE=${CMAKE_BUILD_TYPE%%-*} -DCMAKE_PREFIX_PATH=$LLVM_PREFIX_PATH -DCMAKE_COLOR_DIAGNOSTICS=ON -DBB_CI=$BB_CI
|
OPT_ARGS=""
|
||||||
cd ..
|
|
||||||
|
case "${CMAKE_BUILD_TYPE%%-*}" in
|
||||||
|
Debug) OPT_ARGS="-O0 -g";;
|
||||||
|
Release*) OPT_ARGS="-O3";;
|
||||||
|
*) exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mkdir -p self-hosted-bb-cache
|
||||||
|
$CLANG_PATH -c tests/c_abi.c -o self-hosted-bb-cache/c_abi.o $OPT_ARGS -std=gnu2x
|
||||||
|
@ -17151,6 +17151,10 @@ link = fn (module: &Module) void
|
|||||||
>result = lld_elf_link(linker_arguments.pointer, linker_arguments.length, 1, 0);
|
>result = lld_elf_link(linker_arguments.pointer, linker_arguments.length, 1, 0);
|
||||||
if (!result.success)
|
if (!result.success)
|
||||||
{
|
{
|
||||||
|
print(result.stdout);
|
||||||
|
print("\n");
|
||||||
|
print(result.stderr);
|
||||||
|
print("\n");
|
||||||
@trap();
|
@trap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -18364,7 +18368,6 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile, envp: &&u8) []u8
|
|||||||
|
|
||||||
>file_content = file_read(arena, relative_file_path);
|
>file_content = file_read(arena, relative_file_path);
|
||||||
>file_path = path_absolute(arena, relative_file_path.pointer);
|
>file_path = path_absolute(arena, relative_file_path.pointer);
|
||||||
>c_abi_object_path = ""; // TODO
|
|
||||||
|
|
||||||
>definitions: []Definition = zero;
|
>definitions: []Definition = zero;
|
||||||
>cmake_prefix_path_definition: Definition = {
|
>cmake_prefix_path_definition: Definition = {
|
||||||
@ -18382,8 +18385,8 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile, envp: &&u8) []u8
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>objects = [ output_object_path ][..];
|
>object_array = [ output_object_path, "self-hosted-bb-cache/c_abi.o" ];
|
||||||
>c_abi_library = "build/libc_abi.a";
|
>objects = object_array[..1];
|
||||||
|
|
||||||
>library_buffer: [256][]u8 = undefined;
|
>library_buffer: [256][]u8 = undefined;
|
||||||
>library_directory: []u8 = zero;
|
>library_directory: []u8 = zero;
|
||||||
@ -18520,7 +18523,7 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile, envp: &&u8) []u8
|
|||||||
}
|
}
|
||||||
else if (string_equal(base_name, "tests"))
|
else if (string_equal(base_name, "tests"))
|
||||||
{
|
{
|
||||||
library_paths = { .pointer = &c_abi_library, .length = 1 };
|
objects = object_array[..];
|
||||||
}
|
}
|
||||||
|
|
||||||
>options: CompileOptions = {
|
>options: CompileOptions = {
|
||||||
@ -18722,6 +18725,8 @@ names: [_][]u8 =
|
|||||||
];
|
];
|
||||||
>args = arguments[..arguments.length - 1];
|
>args = arguments[..arguments.length - 1];
|
||||||
>execution = os_execute(arena, args, envp, zero);
|
>execution = os_execute(arena, args, envp, zero);
|
||||||
|
print(executable_path);
|
||||||
|
print("\n");
|
||||||
|
|
||||||
>success = execution.termination_kind == .exit and execution.termination_code == 0;
|
>success = execution.termination_kind == .exit and execution.termination_code == 0;
|
||||||
|
|
||||||
|
618
src/compiler.cpp
618
src/compiler.cpp
@ -1,618 +0,0 @@
|
|||||||
#include <compiler.hpp>
|
|
||||||
|
|
||||||
global_variable Slice<char* const> environment;
|
|
||||||
|
|
||||||
fn void compile(Arena* arena, Options options)
|
|
||||||
{
|
|
||||||
Module module;
|
|
||||||
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,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
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,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
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,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
*noreturn_type = {
|
|
||||||
.id = TypeId::noreturn,
|
|
||||||
.name = string_literal("noreturn"),
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
|
|
||||||
module = Module{
|
|
||||||
.arena = arena,
|
|
||||||
.content = options.content,
|
|
||||||
.scope = {
|
|
||||||
.types = {
|
|
||||||
.first = base_type_allocation.pointer,
|
|
||||||
.last = noreturn_type,
|
|
||||||
},
|
|
||||||
.kind = ScopeKind::global,
|
|
||||||
},
|
|
||||||
.name = options.name,
|
|
||||||
.path = options.path,
|
|
||||||
.executable = options.executable,
|
|
||||||
.objects = options.objects,
|
|
||||||
.library_directories = options.library_directories,
|
|
||||||
.library_names = options.library_names,
|
|
||||||
.library_paths = options.library_paths,
|
|
||||||
.link_libcpp = options.link_libcpp,
|
|
||||||
.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,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto definition: options.definitions)
|
|
||||||
{
|
|
||||||
auto definition_global = new_global(&module);
|
|
||||||
auto definition_value = new_value(&module);
|
|
||||||
auto definition_storage = new_value(&module);
|
|
||||||
*definition_value = {
|
|
||||||
.string_literal = definition.value,
|
|
||||||
.id = ValueId::string_literal,
|
|
||||||
};
|
|
||||||
*definition_storage = {
|
|
||||||
.id = ValueId::global,
|
|
||||||
};
|
|
||||||
*definition_global = Global{
|
|
||||||
.variable = {
|
|
||||||
.storage = definition_storage,
|
|
||||||
.initial_value = definition_value,
|
|
||||||
.type = get_slice_type(&module, uint8(&module)),
|
|
||||||
.scope = &module.scope,
|
|
||||||
.name = definition.name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
|
|
||||||
make_directory(base_cache_dir);
|
|
||||||
|
|
||||||
String cpu_dir_parts[] = {
|
|
||||||
string_literal(base_cache_dir),
|
|
||||||
string_literal("/"),
|
|
||||||
options.host_cpu_model ? string_literal("native") : string_literal("generic"),
|
|
||||||
};
|
|
||||||
|
|
||||||
auto cpu_dir = arena_join_string(arena, array_to_slice(cpu_dir_parts));
|
|
||||||
|
|
||||||
make_directory(cstr(cpu_dir));
|
|
||||||
|
|
||||||
auto base_dir = cpu_dir;
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
String compiler_dir_parts[] = {
|
|
||||||
base_dir,
|
|
||||||
string_literal("/compiler"),
|
|
||||||
};
|
|
||||||
base_dir = arena_join_string(arena, array_to_slice(compiler_dir_parts));
|
|
||||||
make_directory(cstr(base_dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
String output_path_dir_parts[] = {
|
|
||||||
base_dir,
|
|
||||||
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(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);
|
|
||||||
|
|
||||||
Slice<Definition> definitions = {};
|
|
||||||
auto cmake_prefix_path = string_literal(CMAKE_PREFIX_PATH);
|
|
||||||
auto cmake_prefix_path_definition = Definition{
|
|
||||||
.name = string_literal("CMAKE_PREFIX_PATH"),
|
|
||||||
.value = cmake_prefix_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
auto cmake_prefix_path_cstr = os_get_environment_variable("CMAKE_PREFIX_PATH");
|
|
||||||
if (cmake_prefix_path_cstr)
|
|
||||||
{
|
|
||||||
auto cmake_prefix_path_string = c_string_to_slice(cmake_prefix_path_cstr);
|
|
||||||
cmake_prefix_path_definition.value = cmake_prefix_path_string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String objects[] = {
|
|
||||||
output_object_path,
|
|
||||||
};
|
|
||||||
Slice<String> object_slice = array_to_slice(objects);
|
|
||||||
|
|
||||||
String c_abi_library = string_literal("build/libc_abi.a");
|
|
||||||
String llvm_bindings_library = string_literal("build/libllvm_bindings.a");
|
|
||||||
String library_buffer[256];
|
|
||||||
String library_directory = {};
|
|
||||||
|
|
||||||
Slice<String> library_directories = {};
|
|
||||||
Slice<String> library_names = {};
|
|
||||||
Slice<String> library_paths = {};
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
definitions = { .pointer = &cmake_prefix_path_definition, .length = 1 };
|
|
||||||
|
|
||||||
ArgBuilder builder = {};
|
|
||||||
String llvm_config_parts[] = {
|
|
||||||
cmake_prefix_path,
|
|
||||||
string_literal("/bin/llvm-config"),
|
|
||||||
};
|
|
||||||
builder.add(arena, arena_join_string(arena, array_to_slice(llvm_config_parts)));
|
|
||||||
builder.add("--libdir");
|
|
||||||
builder.add("--libs");
|
|
||||||
builder.add("--system-libs");
|
|
||||||
auto arguments = builder.flush();
|
|
||||||
auto llvm_config = os_execute(arena, arguments, environment, {
|
|
||||||
.policies = { ExecuteStandardStreamPolicy::pipe, ExecuteStandardStreamPolicy::ignore },
|
|
||||||
});
|
|
||||||
auto success = llvm_config.termination_kind == TerminationKind::exit && llvm_config.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stream = llvm_config.streams[0];
|
|
||||||
auto line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
library_directory = stream(0, line);
|
|
||||||
library_directories = { &library_directory, 1 };
|
|
||||||
|
|
||||||
stream = stream(line + 1);
|
|
||||||
|
|
||||||
line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto llvm_library_stream = stream(0, line);
|
|
||||||
|
|
||||||
stream = stream(line + 1);
|
|
||||||
|
|
||||||
u64 library_count = 0;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
auto space = string_first_character(llvm_library_stream, ' ');
|
|
||||||
if (space == string_no_match)
|
|
||||||
{
|
|
||||||
auto library_argument = llvm_library_stream;
|
|
||||||
library_buffer[library_count] = library_argument(2);
|
|
||||||
library_count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Omit the first two characters: "-l"
|
|
||||||
auto library_argument = llvm_library_stream(2, space);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
llvm_library_stream = llvm_library_stream(space + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
assert(line == stream.length - 1);
|
|
||||||
auto system_library_stream = stream(0, line);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
auto space = string_first_character(system_library_stream, ' ');
|
|
||||||
if (space == string_no_match)
|
|
||||||
{
|
|
||||||
auto library_argument = system_library_stream(2);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Omit the first two characters: "-l"
|
|
||||||
auto library_argument = system_library_stream(2, space);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
system_library_stream = system_library_stream(space + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("gcc");
|
|
||||||
library_count += 1;
|
|
||||||
library_buffer[library_count] = string_literal("gcc_s");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldCommon");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldCOFF");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldELF");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldMachO");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldMinGW");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldWasm");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("llvm_bindings");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_names = { library_buffer, library_count };
|
|
||||||
}
|
|
||||||
else if (base_name.equal(string_literal("tests")))
|
|
||||||
{
|
|
||||||
library_paths = { &c_abi_library, 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
compile(arena, {
|
|
||||||
.content = file_content,
|
|
||||||
.path = file_path,
|
|
||||||
.executable = output_executable_path,
|
|
||||||
.name = base_name,
|
|
||||||
.definitions = definitions,
|
|
||||||
.objects = object_slice,
|
|
||||||
.library_paths = library_paths,
|
|
||||||
.library_names = library_names,
|
|
||||||
.library_directories = library_directories,
|
|
||||||
.link_libcpp = is_compiler,
|
|
||||||
.target = {
|
|
||||||
.cpu = CPUArchitecture::x86_64,
|
|
||||||
.os = OperatingSystem::linux_,
|
|
||||||
.host_cpu_model = options.host_cpu_model,
|
|
||||||
},
|
|
||||||
.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("tests"),
|
|
||||||
};
|
|
||||||
|
|
||||||
void entry_point(Slice<char* const> arguments, Slice<char* const> envp)
|
|
||||||
{
|
|
||||||
environment = envp;
|
|
||||||
Arena* arena = arena_initialize_default(16 * 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;
|
|
||||||
auto is_host_cpu_model = 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[4]);
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.length >= 6)
|
|
||||||
{
|
|
||||||
auto is_host_cpu_model_string = c_string_to_slice(arguments[5]);
|
|
||||||
if (is_host_cpu_model_string.equal(string_literal("true")))
|
|
||||||
{
|
|
||||||
is_host_cpu_model = true;
|
|
||||||
}
|
|
||||||
else if (is_host_cpu_model_string.equal(string_literal("false")))
|
|
||||||
{
|
|
||||||
is_host_cpu_model = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("Wrong value for is_host_cpu_model\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,
|
|
||||||
.host_cpu_model = is_host_cpu_model,
|
|
||||||
.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<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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildMode compiler_build_mode = BuildMode::debug_none;
|
|
||||||
bool compiler_has_debug_info = true;
|
|
||||||
auto compiler = compile_file(arena, {
|
|
||||||
.relative_file_path = string_literal("src/compiler.bbb"),
|
|
||||||
.build_mode = compiler_build_mode,
|
|
||||||
.has_debug_info = compiler_has_debug_info,
|
|
||||||
.host_cpu_model = true,
|
|
||||||
.silent = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
char* const compiler_arguments[] =
|
|
||||||
{
|
|
||||||
(char*)compiler.pointer,
|
|
||||||
(char*)"test",
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
Slice<char* const> arg_slice = array_to_slice(compiler_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("Self-hosted tests failed: "));
|
|
||||||
print(build_mode_to_string(compiler_build_mode));
|
|
||||||
print(compiler_has_debug_info ? string_literal(" with debug info\n") : string_literal(" with no debug info\n"));
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
char* const reproduce_arguments[] =
|
|
||||||
{
|
|
||||||
(char*)compiler.pointer,
|
|
||||||
(char*)"reproduce",
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
arg_slice = array_to_slice(reproduce_arguments);
|
|
||||||
arg_slice.length -= 1;
|
|
||||||
execution = os_execute(arena, arg_slice, environment, {});
|
|
||||||
success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
print(string_literal("Self-hosted reproduction failed: "));
|
|
||||||
print(build_mode_to_string(compiler_build_mode));
|
|
||||||
print(compiler_has_debug_info ? string_literal(" with debug info\n") : string_literal(" with no debug info\n"));
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Command::count:
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("error: Invalid command\n"));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
2111
src/compiler.hpp
2111
src/compiler.hpp
File diff suppressed because it is too large
Load Diff
10079
src/emitter.cpp
10079
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
|||||||
#include <lib.hpp>
|
|
||||||
void entry_point(Slice<char* const> 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(Slice<char* const>{(char* const*)argv, (u64)argc}, {envp, (u64)(envp_end - envp)});
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
#include <lib.h>
|
|
||||||
|
|
233
src/lib.cpp
233
src/lib.cpp
@ -1,233 +0,0 @@
|
|||||||
#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);
|
|
||||||
extern "C" s32 pipe(int fd[2]);
|
|
||||||
|
|
||||||
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<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)
|
|
||||||
{
|
|
||||||
null_file_descriptor = open("/dev/null", { .access_mode = OPEN::AccessMode::write_only });
|
|
||||||
}
|
|
||||||
|
|
||||||
int pipes[standard_stream_count][2];
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i += 1)
|
|
||||||
{
|
|
||||||
if (options.policies[i] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
if (pipe(pipes[i]) == -1)
|
|
||||||
{
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: better allocation strategy
|
|
||||||
u64 allocation_size = 1024 * 1024;
|
|
||||||
Slice<u8> allocation = {};
|
|
||||||
if (options.policies[0] == ExecuteStandardStreamPolicy::pipe || options.policies[1] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
allocation = arena_allocate<u8>(arena, allocation_size * ((options.policies[0] == ExecuteStandardStreamPolicy::pipe) + (options.policies[1] == ExecuteStandardStreamPolicy::pipe)));
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 offset = 0;
|
|
||||||
for (u64 i = 0; i < standard_stream_count; i += 1)
|
|
||||||
{
|
|
||||||
if (options.policies[i] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
auto buffer = allocation(offset)(0, allocation_size);
|
|
||||||
auto byte_count = read(pipes[i][0], buffer.pointer, buffer.length);
|
|
||||||
assert(byte_count >= 0);
|
|
||||||
execution.streams[i] = buffer(0, byte_count);
|
|
||||||
close(pipes[i][0]);
|
|
||||||
|
|
||||||
offset += allocation_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" char* getenv(const char*);
|
|
||||||
char* os_get_environment_variable(const char* env)
|
|
||||||
{
|
|
||||||
return getenv(env);
|
|
||||||
}
|
|
722
src/lib.hpp
722
src/lib.hpp
@ -1,722 +0,0 @@
|
|||||||
#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)
|
|
||||||
{
|
|
||||||
auto target_commited_size = align_forward(aligned_size_after, arena->granularity);
|
|
||||||
auto size_to_commit = target_commited_size - arena->os_position;
|
|
||||||
auto commit_pointer = ((u8*)arena) + arena->os_position;
|
|
||||||
os_commit(commit_pointer, size_to_commit, { .read = 1, .write = 1 });
|
|
||||||
arena->os_position = target_commited_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 streams[2];
|
|
||||||
TerminationKind termination_kind;
|
|
||||||
u32 termination_code;
|
|
||||||
};
|
|
||||||
|
|
||||||
Execution os_execute(Arena* arena, Slice<char* const> arguments, Slice<char* const> environment, ExecuteOptions options);
|
|
||||||
char* os_get_environment_variable(const char* env);
|
|
316
src/llvm.cpp
316
src/llvm.cpp
@ -1,316 +0,0 @@
|
|||||||
#include <llvm.hpp>
|
|
||||||
|
|
||||||
#include "llvm/Config/llvm-config.h"
|
|
||||||
|
|
||||||
#include "llvm/IR/IRBuilder.h"
|
|
||||||
#include "llvm/IR/Module.h"
|
|
||||||
#include "llvm/IR/Verifier.h"
|
|
||||||
#include "llvm/IR/DebugInfo.h"
|
|
||||||
#include "llvm/IR/LegacyPassManager.h"
|
|
||||||
|
|
||||||
#include "llvm/Passes/PassBuilder.h"
|
|
||||||
|
|
||||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
||||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
||||||
|
|
||||||
#include "llvm/Frontend/Driver/CodeGenOptions.h"
|
|
||||||
|
|
||||||
#include "llvm/TargetParser/Host.h"
|
|
||||||
#include "llvm/TargetParser/SubtargetFeature.h"
|
|
||||||
|
|
||||||
#include "llvm/Target/TargetMachine.h"
|
|
||||||
|
|
||||||
#include "llvm/MC/TargetRegistry.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/FileSystem.h"
|
|
||||||
|
|
||||||
#include "lld/Common/CommonLinkerContext.h"
|
|
||||||
|
|
||||||
EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type)
|
|
||||||
{
|
|
||||||
auto sp = llvm::unwrap<llvm::DISubprogram>(subprogram);
|
|
||||||
sp->replaceType(llvm::unwrap<llvm::DISubroutineType>(subroutine_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are multiple uses of the return-value slot, just check
|
|
||||||
// for something immediately preceding the IP. Sometimes this can
|
|
||||||
// happen with how we generate implicit-returns; it can also happen
|
|
||||||
// with noreturn cleanups.
|
|
||||||
fn llvm::StoreInst* get_store_if_valid(llvm::User* user, llvm::Value* return_alloca, llvm::Type* element_type)
|
|
||||||
{
|
|
||||||
auto *SI = dyn_cast<llvm::StoreInst>(user);
|
|
||||||
if (!SI || SI->getPointerOperand() != return_alloca ||
|
|
||||||
SI->getValueOperand()->getType() != element_type)
|
|
||||||
return nullptr;
|
|
||||||
// These aren't actually possible for non-coerced returns, and we
|
|
||||||
// only care about non-coerced returns on this code path.
|
|
||||||
// All memory instructions inside __try block are volatile.
|
|
||||||
assert(!SI->isAtomic() &&
|
|
||||||
(!SI->isVolatile()
|
|
||||||
//|| CGF.currentFunctionUsesSEHTry())
|
|
||||||
));
|
|
||||||
return SI;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy of static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
|
|
||||||
// in clang/lib/CodeGen/CGCall.cpp:3526 in LLVM 19
|
|
||||||
EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et)
|
|
||||||
{
|
|
||||||
auto builder = llvm::unwrap(b);
|
|
||||||
auto return_alloca = llvm::unwrap(ra);
|
|
||||||
auto element_type = llvm::unwrap(et);
|
|
||||||
// Check if a User is a store which pointerOperand is the ReturnValue.
|
|
||||||
// We are looking for stores to the ReturnValue, not for stores of the
|
|
||||||
// ReturnValue to some other location.
|
|
||||||
if (!return_alloca->hasOneUse()) {
|
|
||||||
llvm::BasicBlock *IP = builder->GetInsertBlock();
|
|
||||||
if (IP->empty()) return nullptr;
|
|
||||||
|
|
||||||
// Look at directly preceding instruction, skipping bitcasts and lifetime
|
|
||||||
// markers.
|
|
||||||
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
|
|
||||||
if (isa<llvm::BitCastInst>(&I))
|
|
||||||
continue;
|
|
||||||
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
|
|
||||||
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return wrap(get_store_if_valid(&I, return_alloca, element_type));
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type);
|
|
||||||
if (!store) return nullptr;
|
|
||||||
|
|
||||||
// Now do a first-and-dirty dominance check: just walk up the
|
|
||||||
// single-predecessors chain from the current insertion point.
|
|
||||||
llvm::BasicBlock *StoreBB = store->getParent();
|
|
||||||
llvm::BasicBlock *IP = builder->GetInsertBlock();
|
|
||||||
llvm::SmallPtrSet<llvm::BasicBlock *, 4> SeenBBs;
|
|
||||||
while (IP != StoreBB) {
|
|
||||||
if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor()))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, the store's basic block dominates the insertion point; we
|
|
||||||
// can do our thing.
|
|
||||||
return wrap(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options)
|
|
||||||
{
|
|
||||||
auto module = llvm::unwrap(m);
|
|
||||||
auto target_machine = (llvm::TargetMachine*)tm;
|
|
||||||
// TODO: PGO
|
|
||||||
// TODO: CS profile
|
|
||||||
|
|
||||||
llvm::PipelineTuningOptions pipeline_tuning_options;
|
|
||||||
pipeline_tuning_options.LoopUnrolling = options.loop_unrolling;
|
|
||||||
pipeline_tuning_options.LoopInterleaving = options.loop_interleaving;
|
|
||||||
pipeline_tuning_options.LoopVectorization = options.loop_vectorization;
|
|
||||||
pipeline_tuning_options.SLPVectorization = options.slp_vectorization;
|
|
||||||
pipeline_tuning_options.MergeFunctions = options.merge_functions;
|
|
||||||
pipeline_tuning_options.CallGraphProfile = options.call_graph_profile;
|
|
||||||
pipeline_tuning_options.UnifiedLTO = options.unified_lto;
|
|
||||||
|
|
||||||
// TODO: instrumentation
|
|
||||||
|
|
||||||
llvm::LoopAnalysisManager loop_analysis_manager;
|
|
||||||
llvm::FunctionAnalysisManager function_analysis_manager;
|
|
||||||
llvm::CGSCCAnalysisManager cgscc_analysis_manager;
|
|
||||||
llvm::ModuleAnalysisManager module_analysis_manager;
|
|
||||||
|
|
||||||
llvm::PassBuilder pass_builder(target_machine, pipeline_tuning_options);
|
|
||||||
|
|
||||||
if (options.assignment_tracking && options.debug_info != 0)
|
|
||||||
{
|
|
||||||
pass_builder.registerPipelineStartEPCallback([&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) {
|
|
||||||
unused(Level);
|
|
||||||
MPM.addPass(llvm::AssignmentTrackingPass());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718
|
|
||||||
// TODO: add library (?)
|
|
||||||
std::unique_ptr<llvm::TargetLibraryInfoImpl> TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary));
|
|
||||||
function_analysis_manager.registerPass([&] { return llvm::TargetLibraryAnalysis(*TLII); });
|
|
||||||
|
|
||||||
pass_builder.registerModuleAnalyses(module_analysis_manager);
|
|
||||||
pass_builder.registerCGSCCAnalyses(cgscc_analysis_manager);
|
|
||||||
pass_builder.registerFunctionAnalyses(function_analysis_manager);
|
|
||||||
pass_builder.registerLoopAnalyses(loop_analysis_manager);
|
|
||||||
pass_builder.crossRegisterProxies(loop_analysis_manager, function_analysis_manager, cgscc_analysis_manager, module_analysis_manager);
|
|
||||||
|
|
||||||
llvm::ModulePassManager module_pass_manager;
|
|
||||||
|
|
||||||
if (options.verify_module)
|
|
||||||
{
|
|
||||||
module_pass_manager.addPass(llvm::VerifierPass());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool thin_lto = false;
|
|
||||||
bool lto = false;
|
|
||||||
|
|
||||||
llvm::OptimizationLevel optimization_level;
|
|
||||||
switch ((BBLLVMOptimizationLevel)options.optimization_level)
|
|
||||||
{
|
|
||||||
case BBLLVMOptimizationLevel::O0: optimization_level = llvm::OptimizationLevel::O0; break;
|
|
||||||
case BBLLVMOptimizationLevel::O1: optimization_level = llvm::OptimizationLevel::O1; break;
|
|
||||||
case BBLLVMOptimizationLevel::O2: optimization_level = llvm::OptimizationLevel::O2; break;
|
|
||||||
case BBLLVMOptimizationLevel::O3: optimization_level = llvm::OptimizationLevel::O3; break;
|
|
||||||
case BBLLVMOptimizationLevel::Os: optimization_level = llvm::OptimizationLevel::Os; break;
|
|
||||||
case BBLLVMOptimizationLevel::Oz: optimization_level = llvm::OptimizationLevel::Oz; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: thin lto post-link
|
|
||||||
// TODO: instrument
|
|
||||||
if (thin_lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else if (lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else if (lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else {
|
|
||||||
module_pass_manager.addPass(pass_builder.buildPerModuleDefaultPipeline(optimization_level));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: if emit bitcode/IR
|
|
||||||
|
|
||||||
module_pass_manager.run(*module, module_analysis_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options)
|
|
||||||
{
|
|
||||||
auto module = llvm::unwrap(m);
|
|
||||||
auto target_machine = (llvm::TargetMachine*)tm;
|
|
||||||
|
|
||||||
// We still use the legacy PM to run the codegen pipeline since the new PM
|
|
||||||
// does not work with the codegen pipeline.
|
|
||||||
// FIXME: make the new PM work with the codegen pipeline.
|
|
||||||
llvm::legacy::PassManager CodeGenPasses;
|
|
||||||
#if LLVM_VERSION_MAJOR >= 19
|
|
||||||
if (options->optimize_when_possible)
|
|
||||||
{
|
|
||||||
CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
llvm::raw_pwrite_stream* dwarf_object_file = 0;
|
|
||||||
if (options->output_dwarf_file_path.length)
|
|
||||||
{
|
|
||||||
__builtin_trap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->optimize_when_possible)
|
|
||||||
{
|
|
||||||
llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718
|
|
||||||
// TODO: add library (?)
|
|
||||||
std::unique_ptr<llvm::TargetLibraryInfoImpl> TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary));
|
|
||||||
CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<llvm::raw_pwrite_stream> stream;
|
|
||||||
|
|
||||||
if (options->output_file_path.length)
|
|
||||||
{
|
|
||||||
std::error_code error_code;
|
|
||||||
|
|
||||||
stream = std::make_unique<llvm::raw_fd_ostream>(llvm::StringRef((char*)options->output_file_path.pointer, options->output_file_path.length), error_code, llvm::sys::fs::OF_None);
|
|
||||||
|
|
||||||
if (error_code)
|
|
||||||
{
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::failed_to_create_file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream = std::make_unique<llvm::raw_null_ostream>();
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::CodeGenFileType file_type;
|
|
||||||
switch (options->file_type)
|
|
||||||
{
|
|
||||||
case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break;
|
|
||||||
case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break;
|
|
||||||
case BBLLVMCodeGenerationFileType::null: file_type = llvm::CodeGenFileType::Null; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto disable_verify = !options->verify_module;
|
|
||||||
if (target_machine->addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify))
|
|
||||||
{
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::failed_to_add_emit_passes;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeGenPasses.run(*module);
|
|
||||||
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define lld_api_function_signature(name) bool name(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput)
|
|
||||||
|
|
||||||
#define lld_link_decl(link_name) \
|
|
||||||
namespace link_name \
|
|
||||||
{\
|
|
||||||
lld_api_function_signature(link);\
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef lld_api_function_signature(LinkerFunction);
|
|
||||||
|
|
||||||
namespace lld
|
|
||||||
{
|
|
||||||
lld_link_decl(coff);
|
|
||||||
lld_link_decl(elf);
|
|
||||||
lld_link_decl(mingw);
|
|
||||||
lld_link_decl(macho);
|
|
||||||
lld_link_decl(wasm);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function)
|
|
||||||
{
|
|
||||||
LLDResult result = {};
|
|
||||||
auto arguments = llvm::ArrayRef(argument_pointer, argument_count);
|
|
||||||
|
|
||||||
std::string stdout_string;
|
|
||||||
llvm::raw_string_ostream stdout_stream(stdout_string);
|
|
||||||
|
|
||||||
std::string stderr_string;
|
|
||||||
llvm::raw_string_ostream stderr_stream(stderr_string);
|
|
||||||
|
|
||||||
result.success = linker_function(arguments, stdout_stream, stderr_stream, exit_early, disable_output);
|
|
||||||
|
|
||||||
auto stdout_length = stdout_string.length();
|
|
||||||
if (stdout_length)
|
|
||||||
{
|
|
||||||
auto* stdout_pointer = new u8[stdout_length + 1];
|
|
||||||
memcpy(stdout_pointer, stdout_string.data(), stdout_length);
|
|
||||||
result.stdout_string = { stdout_pointer, stdout_length };
|
|
||||||
stdout_pointer[stdout_length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stderr_length = stderr_string.length();
|
|
||||||
if (stderr_length)
|
|
||||||
{
|
|
||||||
auto* stderr_pointer = new u8[stderr_length + 1];
|
|
||||||
memcpy(stderr_pointer, stderr_string.data(), stderr_length);
|
|
||||||
result.stderr_string = { stderr_pointer, stderr_length };
|
|
||||||
stderr_pointer[stderr_length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should we only call it on success?
|
|
||||||
lld::CommonLinkerContext::destroy();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define lld_api_function_impl(link_name) \
|
|
||||||
EXPORT lld_api_function_decl(link_name)\
|
|
||||||
{\
|
|
||||||
return lld_api_generic(argument_pointer, argument_count, exit_early, disable_output, lld::link_name::link);\
|
|
||||||
}
|
|
||||||
|
|
||||||
// lld_api_function_impl(coff)
|
|
||||||
lld_api_function_impl(elf)
|
|
||||||
// lld_api_function_impl(mingw)
|
|
||||||
// lld_api_function_impl(macho)
|
|
||||||
// lld_api_function_impl(wasm)
|
|
84
src/llvm.hpp
84
src/llvm.hpp
@ -1,84 +0,0 @@
|
|||||||
#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>
|
|
||||||
#include <llvm-c/Transforms/PassBuilder.h>
|
|
||||||
|
|
||||||
struct LLDResult
|
|
||||||
{
|
|
||||||
String stdout_string;
|
|
||||||
String stderr_string;
|
|
||||||
bool success;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class BBLLVMOptimizationLevel : u8
|
|
||||||
{
|
|
||||||
O0 = 0,
|
|
||||||
O1 = 1,
|
|
||||||
O2 = 2,
|
|
||||||
O3 = 3,
|
|
||||||
Os = 4,
|
|
||||||
Oz = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
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.
|
|
||||||
};
|
|
||||||
|
|
||||||
fn bool llvm_initialized = false;
|
|
||||||
|
|
||||||
extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et);
|
|
||||||
|
|
||||||
extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type);
|
|
||||||
|
|
||||||
#define lld_api_args() char* const* 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);
|
|
4066
src/parser.cpp
4066
src/parser.cpp
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user