diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..72f1b50 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.15) +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) + +add_executable(bb src/main.cpp) +target_compile_definitions(bb PUBLIC + $<$:BB_DEBUG=1> + $<$>:BB_DEBUG=0> +) diff --git a/build.sh b/build.sh index 09b3c32..eead4ed 100755 --- a/build.sh +++ b/build.sh @@ -1,61 +1,4 @@ -#!/usr/bin/env bash set -eu - -MY_CWD=$PWD - -if [[ -z "${BB_CI-}" ]]; then - BB_CI=0 -fi - -if [[ -z "${BB_BUILD_TYPE-}" ]]; then - BB_BUILD_TYPE=debug -fi - -if [[ -z "${BB_ERROR_ON_WARNINGS-}" ]]; then - BB_ERROR_ON_WARNINGS=$BB_CI -fi - -if [[ -z "${BB_ERROR_LIMIT-}" ]]; then - BB_ERROR_LIMIT=$((1 - BB_CI)) -fi - -BB_COMPILE_SHADERS=0 - -BUILD_DIR=cache -LARGE_ASSET_BASE_URL=https://github.com/birth-software/bloat-buster/releases/download/large-assets -mkdir -p $BUILD_DIR - -if [[ ! -f "$BUILD_DIR/large_assembly.s" ]]; then - cd $BUILD_DIR - wget $LARGE_ASSET_BASE_URL/large_assembly.s -o large_assembly.s - cd $MY_CWD -fi - -if [[ "${BB_COMPILE_SHADERS}" == "1" ]]; then - glslangValidator -V bootstrap/std/shaders/rect.vert -o $BUILD_DIR/rect.vert.spv --quiet - glslangValidator -V bootstrap/std/shaders/rect.frag -o $BUILD_DIR/rect.frag.spv --quiet -fi - -BUILD_OUT=$BUILD_DIR/build -C_COMPILER=clang -TIME_TRACE=1 -BB_TIMETRACE=0 -GCC_ARGS= -CLANG_ARGS= -TIME_TRACE_ARG= - -if [[ $C_COMPILER == "clang"* ]]; then - CLANG_ARGS=-ferror-limit=1 - if [[ "$TIME_TRACE" == "1" ]]; then - CLANG_ARGS="$CLANG_ARGS -ftime-trace" - BB_TIMETRACE=1 - else - CLANG_ARGS="$CLANG_ARGS -ftime-trace" - fi -elif [[ $C_COMPILER == "gcc"* ]]; then - GCC_ARGS=-fmax-errors=1 -fi - -$C_COMPILER build.c -g -o $BUILD_OUT -Ibootstrap -std=gnu2x $CLANG_ARGS $GCC_ARGS -DBB_TIMETRACE=$BB_TIMETRACE -DBB_CI=$BB_CI -DBB_BUILD_TYPE=\"$BB_BUILD_TYPE\" -DBB_ERROR_ON_WARNINGS=$BB_ERROR_ON_WARNINGS -DBB_ERROR_LIMIT=$BB_ERROR_LIMIT -$BUILD_OUT $@ -exit 0 +cd build +ninja --quiet +cd .. diff --git a/generate.sh b/generate.sh new file mode 100755 index 0000000..4f76e52 --- /dev/null +++ b/generate.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eu +rm -rf build +mkdir build +cd build +cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=MOLD +cd .. diff --git a/old_build.sh b/old_build.sh new file mode 100755 index 0000000..09b3c32 --- /dev/null +++ b/old_build.sh @@ -0,0 +1,61 @@ +#!/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 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..43fd2c2 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,298 @@ +#define fn static +#define internal static +#define unused(x) (void)(x) +#define breakpoint() __builtin_debugtrap() + +#define unreachable_raw() __builtin_unreachable() +#define trap_raw() __builtin_trap() +#if BB_DEBUG +#define unreachable() trap_raw() +#else +#define unreachable() unreachable_raw() +#endif + +#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)) + +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; + +internal 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; + +template +struct Slice +{ + T* pointer; + u64 length; + + T* begin() { return pointer; } + T* end() { return pointer + length; } +}; + +using String = Slice; + +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)); + +#include +extern "C" void *memcpy (void *__restrict __dest, const void *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); + +extern "C" void* mmap(void*, u64, PROT, MAP, s32, s64); +extern "C" s32 mprotect(void*, u64, PROT); + +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, + }; + + auto map_flags = MAP + { + .type = map.priv ? MAP::Type::priv : MAP::Type::shared, + .anonymous = map.anonymous, + .no_reserve = map.no_reserve, + .populate = map.populate, + }; + + 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, + }; + auto result = mprotect(address, size, protection_flags); + assert(!result); +} + +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) +{ + auto aligned_offset = align_forward(arena.position, alignment); + auto aligned_size_after = aligned_offset + size; + + if (aligned_size_after > arena.os_position) + { + unreachable(); + } + + auto* result = (u8*)&arena + aligned_offset; + arena.position = aligned_size_after; + assert(arena.position <= arena.os_position); + + return result; +} + +template +fn Slice arena_allocate(Arena& arena, u64 count) +{ + return { (T*)arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)), count }; +} + +fn String arena_join_string(Arena& arena, Slice pieces) +{ + u64 size = 0; + for (auto piece : pieces) + { + size += piece.length; + } + + auto* pointer = (u8*)arena_allocate_bytes(arena, size + 1, 1); + u64 i = 0; + for (auto piece : pieces) + { + memcpy(pointer + i, piece.pointer, piece.length); + i += piece.length; + } + + assert(i == size); + pointer[i] = 0; + + return { pointer, size }; +} + +fn String arena_duplicate_string(Arena& arena, String string) +{ + auto memory = (u8*)arena_allocate_bytes(arena, string.length + 1, 1); + memcpy(memory, string.pointer, string.length); + memory[string.length] = 0; + return { memory, string.length}; +} + +fn void arena_restore(Arena& arena, u64 position) +{ + assert(position <= arena.position); + arena.position = position; +} + +fn void arena_reset(Arena& arena) +{ + arena.position = arena_minimum_position; +} + +struct GlobalState +{ + Arena* arena; +}; + +fn void entry_point(Slice arguments, Slice environment); + +int main(int argc, const char* argv[], const char* envp[]) +{ + auto* envp_end = envp; + while (*envp_end) + { + envp_end += 1; + } + + entry_point({argv, (u64)argc}, {envp, (u64)(envp_end - envp)}); + return 0; +} + +fn void entry_point(Slice arguments, Slice environment) +{ + auto arena = arena_initialize_default(8 * mb); + + if (arguments.length < 2) + { + } +}