Delete C++ implementation and dead code
Some checks failed
CI / ci (Release, ubuntu-latest) (pull_request) Failing after 2s
CI / ci (Release-assertions, ubuntu-latest) (pull_request) Failing after 1s

This commit is contained in:
David Gonzalez Martin 2025-06-27 14:23:18 -06:00
parent e9b3dea942
commit 16b78d9167
82 changed files with 29 additions and 84903 deletions

View File

@ -10,6 +10,9 @@ on:
env:
BB_CI: 1
BUILD_DEBUG: 1
LLVM_VERSION: 20.1.7
CLANG_PATH: clang-19
CLANGXX_PATH: clang++-19
jobs:
ci:
@ -24,10 +27,7 @@ jobs:
- 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: |
set -eux
ci/reproduce.sh

View File

@ -5,7 +5,9 @@ on:
env:
BB_CI: 1
CLANG_PATH: clang-19
CLANGXX_PATH: clang++-19
LLVM_VERSION: 20.1.7
jobs:
ci:
strategy:
@ -19,12 +21,8 @@ jobs:
- 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: |
set -eux
./generate.sh
./build.sh
./build/bb test
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

View File

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

View File

@ -1,214 +0,0 @@
#pragma once
#include <std/base.h>
#include <std/os.h>
typedef enum CpuArchitecture : u8
{
CPU_ARCH_X86_64,
CPU_ARCH_AARCH64,
} CpuArchitecture;
fn String cpu_to_string(CpuArchitecture cpu)
{
switch (cpu)
{
case CPU_ARCH_X86_64:
return strlit("x86_64");
case CPU_ARCH_AARCH64:
return strlit("aarch64");
}
}
typedef enum OperatingSystem : u8
{
OPERATING_SYSTEM_LINUX,
OPERATING_SYSTEM_MAC,
OPERATING_SYSTEM_WINDOWS,
} OperatingSystem;
fn String operating_system_to_string(OperatingSystem os)
{
switch (os)
{
case OPERATING_SYSTEM_LINUX:
return strlit("linux");
case OPERATING_SYSTEM_MAC:
return strlit("macos");
case OPERATING_SYSTEM_WINDOWS:
return strlit("windows");
}
}
STRUCT(Target)
{
CpuArchitecture cpu;
OperatingSystem os;
};
typedef enum CompilerBackend : u8
{
COMPILER_BACKEND_BB,
COMPILER_BACKEND_LLVM,
COMPILER_BACKEND_COUNT,
} CompilerBackend;
fn String compiler_backend_to_one_char_string(CompilerBackend backend)
{
switch (backend)
{
case COMPILER_BACKEND_BB:
return strlit("b");
case COMPILER_BACKEND_LLVM:
return strlit("l");
case COMPILER_BACKEND_COUNT:
unreachable();
}
}
fn String compiler_backend_to_string(CompilerBackend backend)
{
switch (backend)
{
case COMPILER_BACKEND_BB:
return strlit("bb");
case COMPILER_BACKEND_LLVM:
return strlit("llvm");
case COMPILER_BACKEND_COUNT:
unreachable();
}
}
typedef enum BinaryFileType : u8
{
BINARY_FILE_OBJECT,
BINARY_FILE_STATIC_LIBRARY,
BINARY_FILE_DYNAMIC_LIBRARY,
BINARY_FILE_EXECUTABLE,
} BinaryFileType;
STRUCT(BinaryPathOptions)
{
String build_directory;
String name;
Target target;
CompilerBackend backend;
BinaryFileType binary_file_type;
};
fn String binary_path_from_options(Arena* arena, BinaryPathOptions options)
{
String object_extension;
switch (options.target.os)
{
case OPERATING_SYSTEM_WINDOWS:
object_extension = strlit(".obj");
break;
default:
object_extension = strlit(".o");
break;
}
String executable_extension;
switch (options.target.os)
{
case OPERATING_SYSTEM_WINDOWS:
executable_extension = strlit(".exe");
break;
default:
executable_extension = strlit("");
break;
}
String extension;
switch (options.binary_file_type)
{
case BINARY_FILE_OBJECT:
extension = object_extension;
break;
case BINARY_FILE_STATIC_LIBRARY:
unreachable();
break;
case BINARY_FILE_DYNAMIC_LIBRARY:
unreachable();
break;
case BINARY_FILE_EXECUTABLE:
extension = executable_extension;
break;
}
auto backend_string = compiler_backend_to_string(options.backend);
auto cpu_string = cpu_to_string(options.target.cpu);
auto os_string = operating_system_to_string(options.target.os);
String parts[] = {
options.build_directory,
strlit("/"),
options.name,
// strlit("_"),
// cpu_string,
// strlit("_"),
// os_string,
// strlit("_"),
// backend_string,
extension,
};
auto result = arena_join_string(arena, (Slice(String)) array_to_slice(parts));
return result;
}
fn CompilerBackend one_char_string_to_compiler_backend(String string)
{
CompilerBackend result = COMPILER_BACKEND_COUNT;
for (u32 i = 0; i < COMPILER_BACKEND_COUNT; i += 1)
{
auto candidate = (CompilerBackend)i;
if (s_equal(compiler_backend_to_one_char_string(candidate), string))
{
result = candidate;
break;
}
}
return result;
}
STRUCT(CodegenOptions)
{
String test_name;
Target target;
CompilerBackend backend;
u8 generate_debug_information;
};
fn Target native_target_get()
{
Target target = {
#ifdef __x86_64__
.cpu = CPU_ARCH_X86_64,
#else
.cpu = CPU_ARCH_AARCH64,
#endif
#if _WIN32
.os = OPERATING_SYSTEM_WINDOWS,
#elif defined(__APPLE__)
.os = OPERATING_SYSTEM_MAC,
#elif defined(__linux__)
.os = OPERATING_SYSTEM_LINUX,
#else
#error "Unknown platform"
#endif
};
return target;
}
STRUCT(LinkerArguments)
{
Target target;
String out_path;
Slice(String) objects;
Slice(String) libraries;
u8 link_libc:1;
u8 link_libcpp:1;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,276 +0,0 @@
#if BB_CI == 0
#include <bloat-buster/bb_core.h>
#include <std/virtual_buffer.h>
#include <std/window.h>
#include <std/render.h>
#include <std/image_loader.h>
#include <std/font_provider.h>
#include <std/ui_core.h>
#include <std/ui_builder.h>
#define default_font_height (24)
auto proportional_font_height = default_font_height;
auto monospace_font_height = default_font_height;
fn TextureIndex white_texture_create(Arena* arena, Renderer* renderer)
{
u32 white_texture_width = 1024;
u32 white_texture_height = white_texture_width;
auto* white_texture_buffer = arena_allocate(arena, u32, white_texture_width * white_texture_height);
memset(white_texture_buffer, 0xff, white_texture_width * white_texture_height * sizeof(u32));
auto white_texture = renderer_texture_create(renderer, (TextureMemory) {
.pointer = white_texture_buffer,
.width = white_texture_width,
.height = white_texture_height,
.depth = 1,
.format = TEXTURE_FORMAT_R8G8B8A8_SRGB,
});
return white_texture;
}
STRUCT(BBPanel)
{
BBPanel* first;
BBPanel* last;
BBPanel* next;
BBPanel* previous;
BBPanel* parent;
f32 parent_percentage;
Axis2 split_axis;
};
STRUCT(BBWindow)
{
OSWindow os;
RenderWindow* render;
BBWindow* previous;
BBWindow* next;
BBPanel* root_panel;
UI_State* ui;
};
STRUCT(BBGUIState)
{
Arena* arena;
Timestamp last_frame_timestamp;
BBWindow* first_window;
BBWindow* last_window;
Renderer* renderer;
// TODO: should this not be thread local?
OSEventQueue event_queue;
};
global_variable BBGUIState state;
fn void ui_top_bar()
{
ui_push(pref_height, ui_em(1, 1));
{
ui_push(child_layout_axis, AXIS2_X);
auto* top_bar = ui_widget_make((UI_WidgetFlags) {
}, strlit("top_bar"));
ui_push(parent, top_bar);
{
ui_button(strlit("Button 1"));
ui_button(strlit("Button 2"));
ui_button(strlit("Button 3"));
}
ui_pop(parent);
ui_pop(child_layout_axis);
}
ui_pop(pref_height);
}
STRUCT(UI_Node)
{
String name;
String type;
String value;
String namespace;
String function;
};
fn void ui_node(UI_Node node)
{
auto* node_widget = ui_widget_make_format((UI_WidgetFlags) {
.draw_background = 1,
.draw_text = 1,
}, "{s} : {s} = {s}##{s}{s}", node.name, node.type, node.value, node.function, node.namespace);
}
fn void app_update()
{
auto frame_end = os_timestamp();
os_poll_events(&state.event_queue);
auto frame_ms = os_resolve_timestamps(state.last_frame_timestamp, frame_end, TIME_UNIT_MILLISECONDS);
state.last_frame_timestamp = frame_end;
Renderer* renderer = state.renderer;
BBWindow* window = state.first_window;
while (likely(window))
{
auto* previous = window->previous;
auto* next = window->next;
auto* render_window = window->render;
renderer_window_frame_begin(renderer, render_window);
ui_state_select(window->ui);
if (likely(ui_build_begin(window->os, frame_ms, &state.event_queue)))
{
ui_push(font_size, default_font_height);
ui_top_bar();
ui_push(child_layout_axis, AXIS2_X);
auto* workspace_widget = ui_widget_make_format((UI_WidgetFlags) {}, "workspace{u64}", window->os);
ui_push(parent, workspace_widget);
{
// Node visualizer
ui_push(child_layout_axis, AXIS2_Y);
auto* node_visualizer_widget = ui_widget_make_format((UI_WidgetFlags) {
.draw_background = 1,
}, "node_visualizer{u64}", window->os);
ui_push(parent, node_visualizer_widget);
{
ui_node((UI_Node) {
.name = strlit("a"),
.type = strlit("s32"),
.value = strlit("1"),
.namespace = strlit("foo"),
.function = strlit("main"),
});
ui_node((UI_Node) {
.name = strlit("b"),
.type = strlit("s32"),
.value = strlit("2"),
.namespace = strlit("foo"),
.function = strlit("main"),
});
}
ui_pop(parent);
ui_pop(child_layout_axis);
// Side-panel stub
ui_button(strlit("Options"));
}
ui_pop(parent);
ui_pop(child_layout_axis);
ui_build_end();
ui_draw();
ui_pop(font_size);
renderer_window_frame_end(renderer, render_window);
}
else
{
if (previous)
{
previous->next = next;
}
if (next)
{
next->previous = previous;
}
if (state.first_window == window)
{
state.first_window = next;
}
if (state.last_window == window)
{
state.last_window = previous;
}
}
window = next;
}
}
fn void window_refresh_callback(OSWindow window, void* context)
{
unused(window);
unused(context);
app_update();
}
void run_app()
{
state.arena = arena_init(MB(512), MB(2), MB(2));
os_windowing_init((OSWindowingInitializationOptions) {
#ifdef __linux__
.should_use_x11 = 1,
#else
.should_use_x11 = 0,
#endif
});
state.renderer = renderer_initialize(state.arena);
state.first_window = state.last_window = arena_allocate(state.arena, BBWindow, 1);
state.first_window->os = os_window_create((OSWindowCreate) {
.name = strlit("Bloat Buster"),
.size = {
.width = 1600,
.height= 900,
},
.refresh_callback = &window_refresh_callback,
});
if (!state.first_window->os)
{
failed_execution();
}
state.first_window->render = renderer_window_initialize(state.renderer, state.first_window->os);
state.first_window->ui = ui_state_allocate(state.renderer, state.first_window->render);
state.first_window->root_panel = arena_allocate(state.arena, BBPanel, 1);
state.first_window->root_panel->parent_percentage = 1.0f;
state.first_window->root_panel->split_axis = AXIS2_X;
auto font_path =
#ifdef _WIN32
strlit("C:/Users/David/Downloads/Fira_Sans/FiraSans-Regular.ttf");
#elif defined(__linux__)
strlit("/usr/share/fonts/TTF/FiraSans-Regular.ttf");
#elif defined(__APPLE__)
strlit("/Users/david/Library/Fonts/FiraSans-Regular.ttf");
#else
strlit("WRONG_PATH");
#endif
window_rect_texture_update_begin(state.first_window->render);
auto white_texture = white_texture_create(state.arena, state.renderer);
auto monospace_font = font_texture_atlas_create(state.arena, state.renderer, (TextureAtlasCreate) {
.font_path = font_path,
.text_height = monospace_font_height,
});
auto proportional_font = monospace_font;
window_queue_rect_texture_update(state.first_window->render, RECT_TEXTURE_SLOT_WHITE, white_texture);
renderer_queue_font_update(state.renderer, state.first_window->render, RENDER_FONT_TYPE_MONOSPACE, monospace_font);
renderer_queue_font_update(state.renderer, state.first_window->render, RENDER_FONT_TYPE_PROPORTIONAL, proportional_font);
window_rect_texture_update_end(state.renderer, state.first_window->render);
state.last_frame_timestamp = os_timestamp();
while (state.first_window)
{
app_update();
}
// TODO: deinitialization
}
#endif

View File

@ -1,7 +0,0 @@
#if BB_CI == 0
#include <std/base.h>
#include <std/os.h>
EXPORT void run_app();
#endif

View File

@ -1,281 +0,0 @@
adc: class base_arithmetic(/2, 15, 11, 13)
adcx: class unsigned_add_flag(66)
add: class base_arithmetic(/0, 05, 01, 03)
adox: class unsigned_add_flag(f3)
and: class base_arithmetic(/4, 25, 21, 23)
bsf:
r16, rm16 [rm: rex.r 0f bc /r]
r32, rm32 [rm: 0f bc /r]
r64, rm64 [rm: rex.w 0f bc /r]
bsr:
r16, rm16 [rm: rex.r 0f bd /r]
r32, rm32 [rm: 0f bd /r]
r64, rm64 [rm: rex.w 0f bd /r]
bswap:
r32 [o: 0f c8+r]
r64 [o: rex.w 0f c8+r]
bt: class bittest(/4, a3)
btc: class bittest(/7, bb)
btr: class bittest(/6, b3)
bts: class bittest(/5, ab)
call:
rel [d: e8 rel32]
rm64 [m: ff /2]
cbw: [zo: rex.r 98]
cwde: [zo: 98]
cwqe: [zo: rex.w 98]
clc: [zo: f8]
cld: [zo: fd]
clflush: m8 [m: 0f ae /7]
clflushopt: m8 [m: 66 0f ae /7]
cli: [zo: fa]
clts: [zo: 0f 06]
cmc: [zo: f5]
cmovcc: class cmov
cmp: class base_arithmetic(/7, 3d, 39, 3b)
cmpsb: [zo: a6]
cmpsw: [zo: a7]
cmpsd: [zo: a7]
cmpsq: [zo: a7]
cmpxchg:
rm8, r8 [mr: 0f b0]
rm16, r16 [mr: 0f b1]
rm32, r32 [mr: 0f b1]
rm64, r64 [mr: 0f b1]
cmpxchg8b: m64 [m: 0f c7 /1]
cmpxchg16b: m64 [m: rex.w 0f c7 /1]
cpuid: [zo: 0f a2]
crc32:
r32, rm8 [rm: f2 0f 38 f0]
r32, rm16 [rm: 66 f2 0f 38 f1]
r32, rm32 [rm: f2 0f 38 f1]
r64, rm8 [rm: f2 rex.w 0f 38 f0]
r64, rm64 [rm: f2 rex.w 0f 38 f1]
dec:
rm8 [m: fe /1]
rm16 [m: fe /1]
rm32 [m: fe /1]
rm64 [m: fe /1]
div:
rm8 [m: f6 /6]
rm16 [m: f7 /6]
rm32 [m: f7 /6]
rm64 [m: f7 /6]
hlt: [zo: f4]
idiv:
rm8 [m: f6 /7]
rm16 [m: f7 /7]
rm32 [m: f7 /7]
rm64 [m: f7 /7]
imul:
rm8 [m: f6 /5]
rm16 [m: f7 /5]
rm32 [m: f7 /5]
rm64 [m: f7 /5]
r16, rm16 [rm: 0f af]
r32, rm32 [rm: 0f af]
r64, rm64 [rm: 0f af]
r16, rm16, imm [rmi: 6b ib]
r32, rm32, imm [rmi: 6b ib]
r64, rm64, imm [rmi: 6b ib]
r16, rm16, imm16 [rmi: 69 iw]
r32, rm32, imm32 [rmi: 69 id]
r64, rm64, imm32 [rmi: 69 id]
in:
al, imm8 [-i: e4 ib]
ax, imm8 [-i: e5 ib]
eax, imm8 [-i: e5 ib]
al, dx [--: ec]
ax, dx [--: ed]
eax, dx [--: ed]
inc:
rm8 [m: fe /0]
rm16 [m: fe /0]
rm32 [m: fe /0]
rm64 [m: fe /0]
insb: [zo: 6c]
insw: [zo: 6d]
insd: [zo: 6d]
int: imm [i: cd ib]
int3: [zo: cc]
invd: [zo: 0f 08]
invlpg: m8 [m: 0f 01 /7]
iret: [zo: 66 cf]
iretd: [zo: cf]
iretq: [zo: rex.w cf]
jmp:
rel [d: eb rel8]
rel [d: e9 rel32]
rm64 [m: ff /4]
jcc: class jcc
jrcxz: rel [d: e3 rel8]
lahf: [zo: 9f]
lea:
r16, m16 [rm: 8d /r]
r32, m32 [rm: 8d /r]
r64, m64 [rm: 8d /r]
lodsb: [zo: ac]
lodsw: [zo: ad]
lodsd: [zo: ad]
lodsq: [zo: ad]
loop: rel [d: e2 rel8]
loope: rel [d: e1 rel8]
loopne: rel [d: e0 rel8]
monitor: [zo: 0f 01 c8]
mov:
rm8, r8 [mr: 88 /r]
rm16, r16 [mr: 89 /r]
rm32, r32 [mr: 89 /r]
rm64, r64 [mr: 89 /r]
r8, rm8 [rm: 8a /r]
r16, rm16 [rm: 8b /r]
r32, rm32 [rm: 8b /r]
r64, rm64 [rm: 8b /r]
r8, imm [ri: b0+r ib]
r16, imm [ri: b8+r iw]
r32, imm [ri: b8+r id]
r64, imm [ri: b8+r iq]
r8, imm [ri: c6 /0 ib]
r16, imm [ri: c7 /0 iw]
r32, imm [ri: c7 /0 id]
r64, imm [ri: c7 /0 id]
movsb: [zo: a4]
movsw: [zo: a5]
movsd: [zo: a5]
movsq: [zo: a5]
movsx:
r16, rm8 [rm: 0f be /r]
r32, rm8 [rm: 0f be /r]
r64, rm8 [rm: 0f be /r]
r32, rm16 [rm: 0f bf /r]
r64, rm16 [rm: 0f bf /r]
movsxd: r64, rm32 [rm: rex.w 63 /r]
movzx:
r16, rm8 [rm: 0f b6 /r]
r32, rm8 [rm: 0f b6 /r]
r64, rm8 [rm: 0f b6 /r]
r32, rm16 [rm: 0f b7 /r]
r64, rm16 [rm: 0f b7 /r]
mul:
rm8 [m: f6 /4]
rm16 [m: f7 /4]
rm32 [m: f7 /4]
rm64 [m: f7 /4]
mwait: [zo: 0f 01 c9]
neg:
rm8 [m: f6 /3]
rm16 [m: f7 /3]
rm32 [m: f7 /3]
rm64 [m: f7 /3]
nop:
[zo: 90]
rm16 [m: 0f 1f /0]
rm32 [m: 0f 1f /0]
not:
rm8 [m: f6 /2]
rm16 [m: f7 /2]
rm32 [m: f7 /2]
rm64 [m: f7 /2]
or: class base_arithmetic(/1, 0d, 09, 0b)
out:
imm, al [i-: e6 ib]
imm, ax [i-: e7 ib]
imm, ax [i-: e7 ib]
pause: [zo: f3 90]
pop:
rm16 [m: 8f /0]
rm64 [m: 8f /0]
r16 [o: 58+r]
r64 [o: 58+r]
popcnt:
r16, rm16 [rm: f3 0f b8 /r]
r32, rm32 [rm: f3 0f b8 /r]
r64, rm64 [rm: f3 0f b8 /r]
popf: [zo: 66 9d]
popfq: [zo: 9d]
prefetcht0: m8 [m: 0f 18 /1]
prefetcht1: m8 [m: 0f 18 /2]
prefetcht2: m8 [m: 0f 18 /3]
prefetchnta: m8 [m: 0f 18 /0]
push:
rm16 [m: ff /6]
rm64 [m: ff /6]
r16 [o: 50+r]
r64 [o: 50+r]
imm [i: 6a ib]
imm [i: 68 iw]
imm [i: 68 id]
pushf: [zo: 66 9c]
pushfq: [zo: 9c]
rol: class rotate(/0)
ror: class rotate(/1)
rcl: class rotate(/2)
rcr: class rotate(/3)
rdmsr: [zo: 0f 32]
rdpmc: [zo: 0f 33]
rdtsc: [zo: 0f 31]
rdtscp: [zo: 0f 01 f9]
ret:
[zo: c3]
imm [i: c2 iw]
retf:
[zo: cb]
imm [i: ca iw]
rsm: [zo: 0f aa]
sal: class shift(/4)
sar: class shift(/7)
shl: class shift(/4)
shr: class shift(/5)
scasb: [zo: ae]
scasw: [zo: af]
scasd: [zo: af]
scasq: [zo: af]
setcc: class setcc
stc: [zo: f9]
std: [zo: fd]
sti: [zo: fb]
stosb: [zo: aa]
stosw: [zo: ab]
stosd: [zo: ab]
stosq: [zo: ab]
sub: class base_arithmetic(/5, 2d, 29, 2b)
syscall: [zo: 0f 05]
sysenter: [zo: 0f 34]
sysexit: [zo: 0f 35]
sysret: [zo: 0f 07]
test:
al, imm8 [-i: a8 ib]
ax, imm16 [-i: a9 iw]
eax, imm32 [-i: a9 id]
rax, imm32 [-i: a9 id]
rm8, imm8 [mi: f6 /0 ib]
rm16, imm8 [mi: f7 /0 ib]
rm32, imm8 [mi: f7 /0 ib]
rm64, imm8 [mi: f7 /0 ib]
rm8, r8 [mr: 84 /r]
rm16, r16 [mr: 85 /r]
rm32, r32 [mr: 85 /r]
rm64, r64 [mr: 85 /r]
ud0: r32, rm32 [rm: 0f ff /r]
ud1: r32, rm32 [rm: 0f ff /r]
ud2: [zo: 0f 0b]
xadd:
rm8, r8 [mr: 0f c0 /r]
rm16, r16 [mr: 0f c1 /r]
rm32, r32 [mr: 0f c1 /r]
rm64, r64 [mr: 0f c1 /r]
xchg:
ax, r16 [-o: 90+r]
r16, ax [o-: 90+r]
eax, r32 [-o: 90+r]
r32, eax [o-: 90+r]
rax, r64 [-o: 90+r]
r64, rax [o-: 90+r]
rm8, r8 [mr: 86 /r]
r8, rm8 [rm: 86 /r]
rm16, r16 [mr: 87 /r]
r16, rm16 [rm: 87 /r]
rm32, r32 [mr: 87 /r]
r32, rm32 [rm: 87 /r]
rm64, r64 [mr: 87 /r]
r64, rm64 [rm: 87 /r]

View File

@ -1,226 +0,0 @@
adc
adcx
add
adox
and
bsf
bsr
bswap
bt
btc
btr
bts
call
cbw
cwde
cdqe
cwd
cdq
cqo
clc
cld
clflush
clflushopt
cli
clts
cmc
cmova
cmovae
cmovb
cmovbe
cmovc
cmove
cmovg
cmovge
cmovl
cmovle
cmovna
cmovnae
cmovnb
cmovnbe
cmovnc
cmovne
cmovng
cmovnge
cmovnl
cmovnle
cmovno
cmovnp
cmovns
cmovnz
cmovo
cmovp
cmovpe
cmovpo
cmovs
cmovz
cmp
cmpsb
cmpsw
cmpsd
cmpsq
cmpxchg
cmpxchg8b
cmpxchg16b
cpuid
crc32
dec
div
hlt
idiv
imul
in
inc
insb
insw
insd
int
int3
invd
invlpg
iret
iretd
iretq
jmp
ja
jae
jb
jbe
jc
je
jg
jge
jl
jle
jna
jnae
jnb
jnbe
jnc
jne
jng
jnge
jnl
jnle
jno
jnp
jns
jnz
jo
jp
jpe
jpo
js
jz
jrcxz
lahf
lea
lodsb
lodsw
lodsd
lodsq
loop
loope
loopne
monitor
mov
movsb
movsw
movsd
movsq
movsx
movsxd
movzx
mul
mwait
neg
nop
not
or
out
outsb
outsw
outsd
pause
pop
popcnt
popf
popfq
prefetcht0
prefetcht1
prefetcht2
prefetchnta
push
pushf
pushfq
rcl
rcr
rol
ror
rdmsr
rdpmc
rdtsc
rdtscp
ret
retf
rsm
sal
sar
shl
shr
sbb
scasb
scasw
scasd
scasq
seta
setae
setb
setbe
setc
sete
setg
setge
setl
setle
setna
setnae
setnb
setnbe
setnc
setne
setng
setnge
setnl
setnle
setno
setnp
setns
setnz
seto
setp
setpe
setpo
sets
setz
stc
std
sti
stosb
stosw
stosd
stosq
sub
syscall
sysenter
sysexit
sysret
test
ud0
ud1
ud2
wbinvd
wrmsr
xadd
xchg
xor

View File

@ -1,55 +0,0 @@
#include <bloat-buster/lld_api.h>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/Support/raw_ostream.h>
#include <lld/Common/Driver.h>
#include <std/os.h>
#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 u8 lld_api_generic(LLDArguments args, LinkerFunction linker_function)
{
auto arguments = llvm::ArrayRef(args.argument_pointer, args.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);
u8 result = linker_function(arguments, stdout_stream, stderr_stream, args.exit_early, args.disable_output);
// assert(result == (stdout_string.length() == 0));
// assert(result == (stderr_string.length() == 0));
print_string(String{(u8*)stdout_string.data(), stdout_string.length()});
print_string(String{(u8*)stderr_string.data(), stderr_string.length()});
return result;
}
#define lld_api_function_impl(link_name) \
lld_api_function_decl(link_name)\
{\
return lld_api_generic(args, 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)

View File

@ -1,17 +0,0 @@
#include <std/base.h>
STRUCT(LLDArguments)
{
const char** argument_pointer;
u32 argument_count;
u8 exit_early;
u8 disable_output;
};
#define lld_api_function_decl(link_name) u8 lld_ ## link_name ## _link(LLDArguments args)
EXPORT lld_api_function_decl(coff);
EXPORT lld_api_function_decl(elf);
EXPORT lld_api_function_decl(mingw);
EXPORT lld_api_function_decl(macho);
EXPORT lld_api_function_decl(coff);

View File

@ -1,244 +0,0 @@
#include <bloat-buster/lld_driver.h>
#include <std/virtual_buffer.h>
#include <std/string.h>
fn String linux_crt_find_path()
{
auto flags = (OSFileOpenFlags) {
.read = 1,
};
auto permissions = (OSFilePermissions) {
.readable = 1,
.writable = 1,
};
if (os_file_descriptor_is_valid(os_file_open(strlit("/usr/lib/crti.o"), flags, permissions)))
{
return strlit("/usr/lib");
}
if (os_file_descriptor_is_valid(os_file_open(strlit("/usr/lib/x86_64-linux-gnu/crti.o"), flags, permissions)))
{
return strlit("/usr/lib/x86_64-linux-gnu");
}
if (os_file_descriptor_is_valid(os_file_open(strlit("/usr/lib/aarch64-linux-gnu/crti.o"), flags, permissions)))
{
return strlit("/usr/lib/aarch64-linux-gnu");
}
todo();
}
fn String windows_msvc_find_path()
{
auto flags = (OSFileOpenFlags) {
.read = 1,
.directory = 1,
};
auto permissions = (OSFilePermissions) {
.readable = 1,
};
String possibilities[] = {
strlit("C:/Program Files/Microsoft Visual Studio/2022/Enterprise"),
strlit("C:/Program Files/Microsoft Visual Studio/2022/Community"),
};
for (u64 i = 0; i < array_length(possibilities); i += 1)
{
auto possibility = possibilities[i];
auto fd = os_file_open(possibility, flags, permissions);
if (os_file_descriptor_is_valid(fd))
{
return possibility;
}
}
failed_execution();
}
fn void linux_add_crt_item(Arena* arena, VirtualBufferP(char)* args, String crt_path, String item)
{
String parts[] = {
crt_path,
strlit("/"),
item,
};
*vb_add(args, 1) = string_to_c(arena_join_string(arena, (Slice(String))array_to_slice(parts)));
}
SliceP(char) lld_driver(Arena* arena, LinkerArguments arguments)
{
VirtualBufferP(char) args = {};
char* driver;
switch (arguments.target.os)
{
case OPERATING_SYSTEM_LINUX:
driver = "ld.lld";
break;
case OPERATING_SYSTEM_MAC:
driver = "ld64.lld";
break;
case OPERATING_SYSTEM_WINDOWS:
driver = "lld-link";
break;
}
*vb_add(&args, 1) = driver;
if (arguments.target.os != OPERATING_SYSTEM_WINDOWS)
{
*vb_add(&args, 1) = "--error-limit=0";
}
switch (arguments.target.os)
{
case OPERATING_SYSTEM_WINDOWS:
{
String parts[] = {
strlit("-out:"),
arguments.out_path,
};
auto arg = arena_join_string(arena, (Slice(String))array_to_slice(parts));
*vb_add(&args, 1) = string_to_c(arg);
} break;
default:
{
*vb_add(&args, 1) = "-o";
*vb_add(&args, 1) = string_to_c(arguments.out_path);
} break;
}
if (arguments.target.os != OPERATING_SYSTEM_WINDOWS)
{
for (u64 i = 0; i < arguments.objects.length; i += 1)
{
*vb_add(&args, 1) = string_to_c(arguments.objects.pointer[i]);
}
}
switch (arguments.target.os)
{
case OPERATING_SYSTEM_LINUX:
{
if (arguments.link_libcpp && !arguments.link_libc)
{
failed_execution();
}
if (arguments.link_libc)
{
auto crt_path = linux_crt_find_path();
*vb_add(&args, 1) = "-dynamic-linker";
String dynamic_linker_filename;
switch (arguments.target.cpu)
{
case CPU_ARCH_X86_64:
dynamic_linker_filename = strlit("ld-linux-x86-64.so.2");
break;
case CPU_ARCH_AARCH64:
dynamic_linker_filename = strlit("ld-linux-aarch64.so.1");
break;
}
linux_add_crt_item(arena, &args, crt_path, dynamic_linker_filename);
linux_add_crt_item(arena, &args, crt_path, strlit("crt1.o"));
*vb_add(&args, 1) = "-L";
*vb_add(&args, 1) = string_to_c(crt_path);
*vb_add(&args, 1) = "--as-needed";
*vb_add(&args, 1) = "-lm";
*vb_add(&args, 1) = "-lpthread";
*vb_add(&args, 1) = "-lc";
*vb_add(&args, 1) = "-ldl";
*vb_add(&args, 1) = "-lrt";
*vb_add(&args, 1) = "-lutil";
linux_add_crt_item(arena, &args, crt_path, strlit("crtn.o"));
if (arguments.link_libcpp)
{
// TODO: implement better path finding
linux_add_crt_item(arena, &args, crt_path, strlit("libstdc++.so.6"));
}
}
for (u64 i = 0; i < arguments.libraries.length; i += 1)
{
auto library = arguments.libraries.pointer[i];
String library_pieces[] = {
strlit("-l"),
library,
};
auto library_argument = arena_join_string(arena, (Slice(String))array_to_slice(library_pieces));
*vb_add(&args, 1) = string_to_c(library_argument);
}
} break;
case OPERATING_SYSTEM_MAC:
{
*vb_add(&args, 1) = "-dynamic";
*vb_add(&args, 1) = "-platform_version";
*vb_add(&args, 1) = "macos";
*vb_add(&args, 1) = "15.0.0";
*vb_add(&args, 1) = "15.0.0";
*vb_add(&args, 1) = "-arch";
*vb_add(&args, 1) = "arm64";
*vb_add(&args, 1) = "-syslibroot";
*vb_add(&args, 1) = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk";
if (string_ends_with(arguments.out_path, strlit(".dylib")))
{
*vb_add(&args, 1) = "-e";
*vb_add(&args, 1) = "_main";
}
*vb_add(&args, 1) = "-lSystem";
if (arguments.link_libcpp)
{
*vb_add(&args, 1) = "-lc++";
}
} break;
case OPERATING_SYSTEM_WINDOWS:
{
if (arguments.link_libcpp && !arguments.link_libc)
{
failed_execution();
}
auto msvc_path = windows_msvc_find_path();
if (arguments.link_libc)
{
*vb_add(&args, 1) = "-defaultlib:libcmt";
{
// String parts[] = {
// strlit("-libpath:"),
// msvc_path,
// strlit("/"),
// strlit("VC/Tools/MSVC/14.41.34120/lib/x64"),
// };
// auto arg = arena_join_string(arena, (Slice(String)) array_to_slice(parts));
}
if (arguments.link_libcpp)
{
todo();
}
for (u64 i = 0; i < arguments.objects.length; i += 1)
{
*vb_add(&args, 1) = string_to_c(arguments.objects.pointer[i]);
}
}
// clang -v main.c
// "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.41.34120\\bin\\Hostx64\\x64\\link.exe" -out:a.exe -defaultlib:libcmt -defaultlib:oldnames "-libpath:C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.41.34120\\lib\\x64" "-libpath:C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.41.34120\\atlmfc\\lib\\x64" "-libpath:C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.22621.0\\ucrt\\x64" "-libpath:C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.22621.0\\um\\x64" "-libpath:C:\\Users\\David\\scoop\\apps\\llvm\\19.1.3\\lib\\clang\\19\\lib\\windows" -nologo "C:\\Users\\David\\AppData\\Local\\Temp\\main-706820.o"
} break;
}
return (SliceP(char)){ .pointer = args.pointer, .length = args.length };
}

View File

@ -1,4 +0,0 @@
#include <bloat-buster/base.h>
#include <std/os.h>
EXPORT SliceP(char) lld_driver(Arena* arena, LinkerArguments arguments);

View File

@ -1,166 +0,0 @@
#define unreachable() __builtin_unreachable()
#include <llvm-c/Core.h>
#include <std/os.h>
#include <bloat-buster/base.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm-c/TargetMachine.h>
#define string_ref(lit) StringRef(lit, strlit_len(lit))
namespace llvm
{
#define llvm_initialize_macro(target) \
LLVMInitialize ## target ## Target();\
LLVMInitialize ## target ## TargetInfo();\
LLVMInitialize ## target ## TargetMC();\
LLVMInitialize ## target ## AsmParser();\
LLVMInitialize ## target ## AsmPrinter()
fn void target_initialize(CpuArchitecture architecture)
{
// These are meant to be called globally, so if this code is ever threaded, we need to call this code only once
switch (architecture)
{
case CPU_ARCH_X86_64:
{
llvm_initialize_macro(X86);
} break;
case CPU_ARCH_AARCH64:
{
llvm_initialize_macro(AArch64);
} break;
}
}
EXPORT void llvm_codegen(CodegenOptions options, String object_path)
{
target_initialize(options.target.cpu);
auto context = LLVMContext();
auto module = Module(string_ref("first"), context);
std::string error_message;
// TODO: debug builder
// TODO: attributes
{
u32 return_bit_count = 32;
auto* return_type = IntegerType::get(context, return_bit_count);
ArrayRef<Type*> parameter_types = {};
u8 is_var_args = 0;
auto* function_type = FunctionType::get(return_type, parameter_types, is_var_args);
auto function_name = string_ref("main");
auto linkage = GlobalValue::LinkageTypes::ExternalLinkage;
u32 address_space = 0;
auto* function = Function::Create(function_type, linkage, address_space, function_name, &module);
auto builder = IRBuilder<>(context);
auto entry_block_name = string_ref("entry");
auto* basic_block = BasicBlock::Create(context, entry_block_name, function, 0);
builder.SetInsertPoint(basic_block);
u64 return_value_int = 0;
u8 is_signed = 0;
auto* return_value = ConstantInt::get(context, APInt(return_bit_count, return_value_int, is_signed));
builder.CreateRet(return_value);
{
raw_string_ostream message_stream(error_message);
if (verifyModule(module, &message_stream))
{
// Failure
auto& error_std_string = message_stream.str();
auto error_string = String{ .pointer = (u8*)error_std_string.data(), .length = error_std_string.length() };
print("Verification for module failed:\n{s}\n", error_string);
failed_execution();
}
}
}
// TODO: make a more correct logic
std::string target_triple_str;
switch (options.target.cpu)
{
case CPU_ARCH_X86_64:
target_triple_str += string_ref("x86_64-");
break;
case CPU_ARCH_AARCH64:
target_triple_str += string_ref("aarch64-");
break;
}
switch (options.target.os)
{
case OPERATING_SYSTEM_LINUX:
target_triple_str += string_ref("unknown-linux-gnu");
break;
case OPERATING_SYSTEM_MAC:
target_triple_str += string_ref("apple-macosx-none");
break;
case OPERATING_SYSTEM_WINDOWS:
target_triple_str += string_ref("pc-windows-msvc");
break;
}
auto target_triple = StringRef(target_triple_str);
const Target* target = TargetRegistry::lookupTarget(target_triple, error_message);
if (!target)
{
String string = { .pointer = (u8*)error_message.data(), .length = error_message.length() };
print("Could not find target: {s}\n", string);
failed_execution();
}
module.setTargetTriple(target_triple);
// TODO:
auto cpu_model = string_ref("");
auto cpu_features = string_ref("");
TargetOptions target_options;
std::optional<Reloc::Model> relocation_model = std::nullopt;
std::optional<CodeModel::Model> code_model = std::nullopt;
auto codegen_optimization_level = CodeGenOptLevel::None;
u8 jit = 0;
auto* target_machine = target->createTargetMachine(target_triple, cpu_model, cpu_features, target_options, relocation_model, code_model, codegen_optimization_level, jit);
auto data_layout = target_machine->createDataLayout();
module.setDataLayout(data_layout);
// TODO: optimizations
SmallString<0> object_string;
raw_svector_ostream object_stream(object_string);
auto file_type = CodeGenFileType::ObjectFile;
legacy::PassManager pass;
assert(target_machine->isCompatibleDataLayout(module.getDataLayout()));
raw_pwrite_stream* dwo_stream = 0;
if (target_machine->addPassesToEmitFile(pass, object_stream, dwo_stream, file_type)) {
failed_execution();
}
pass.run(module);
assert(object_path.pointer);
assert(object_path.length);
file_write(FileWriteOptions{
.path = object_path,
.content = { .pointer = (u8*)object_string.str().data(), .length = object_string.str().size() },
.executable = 1,
});
}
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <bloat-buster/base.h>
EXPORT void llvm_codegen(CodegenOptions options, String object_path);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +0,0 @@
#include <std/base.h>
extern u8 pdb_image[143360];

View File

@ -1,463 +0,0 @@
#pragma once
#if _MSC_VER
extern u32 _lzcnt_u32(u32);
extern u32 _tzcnt_u32(u32);
extern u64 _lzcnt_u64(u64);
extern u64 _tzcnt_u64(u64);
#endif
fn u8 leading_zeroes_u32(u32 value)
{
#if _MSC_VER
return (u8)_lzcnt_u32(value);
#else
return __builtin_clz(value);
#endif
}
fn u8 leading_zeroes_u64(u64 value)
{
#if _MSC_VER
return (u8)_lzcnt_u64(value);
#else
return __builtin_clzll(value);
#endif
}
fn u8 log2_alignment(u64 alignment)
{
assert(alignment != 0);
assert((alignment & (alignment - 1)) == 0);
u8 left = (sizeof(alignment) * 8) - 1;
u8 right = leading_zeroes_u64(alignment);
let_cast(u8, result, left - right);
return result;
}
fn u8 log2_u64(u64 v)
{
assert(v != 0);
return (sizeof(u64) * 8 - 1) - leading_zeroes_u64(v);
}
fn u8 log2_u32(u32 v)
{
assert(v != 0);
return (sizeof(u32) * 8 - 1) - leading_zeroes_u32(v);
}
fn u8 hex_digit_count(u64 v)
{
u8 result = 1;
if (v)
{
result = log2_u64(v) / log2_u64(16) + 1;
}
return result;
}
fn u128 u128_from_u64(u64 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = { .low = n };
return result;
#else
return n;
#endif
}
fn u64 u64_from_u128(u128 n)
{
#if defined (__TINYC__) || defined(_MSC_VER)
return n.low;
#else
return (u64)n;
#endif
}
fn u128 u128_shift_right(u128 value, u16 n)
{
#if defined (__TINYC__) || defined(_MSC_VER)
u128 result = {};
if (n < 128)
{
if (n >= 64)
{
// If n >= 64, only the high part contributes to the low part
result.low = value.high >> (n - 64);
result.high = 0;
}
else
{
// Standard case: n < 64
result.low = (value.low >> n) | (value.high << (64 - n));
result.high = value.high >> n;
}
}
return result;
#else
return value >> n;
#endif
}
fn u128 u128_shift_left(u128 value, u16 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = {};
if (n < 128)
{
if (n >= 64)
{
// If n >= 64, only the low part contributes to the high part
result.high = value.low << (n - 64);
result.low = 0;
}
else
{
// Standard case: n < 64
result.high = (value.high << n) | (value.low >> (64 - n));
result.low = value.low << n;
}
}
return result;
#else
return value << n;
#endif
}
fn u128 u128_u64_or(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
a.low |= b;
return a;
#else
return a | b;
#endif
}
fn u128 u128_u64_add(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result;
// Add the lower 64 bits and check for overflow
result.low = a.low + b;
u64 carry = (result.low < a.low) ? 1 : 0;
// Add the carry to the upper 64 bits
result.high = a.high + carry;
return result;
#else
return a + b;
#endif
}
// Multiply two u128 values
fn u128 u128_u64_mul(u128 a, u64 b)
{
#if defined(__TINYC__) || defined(_MSC_VER)
u128 result = {};
// Compute low and high parts of the product
u64 low_low = (a.low & 0xFFFFFFFF) * (b & 0xFFFFFFFF);
u64 low_high = (a.low >> 32) * (b & 0xFFFFFFFF);
u64 high_low = (a.low & 0xFFFFFFFF) * (b >> 32);
u64 high_high = (a.low >> 32) * (b >> 32);
// Combine partial products for the lower 64 bits
u64 carry = (low_low >> 32) + (low_high & 0xFFFFFFFF) + (high_low & 0xFFFFFFFF);
result.low = (low_low & 0xFFFFFFFF) | (carry << 32);
// Add carry from lower to the high product
result.high = a.high * b + (low_high >> 32) + (high_low >> 32) + (carry >> 32) + high_high;
return result;
#else
return a * b;
#endif
}
fn u64 u128_shift_right_by_64(u128 n)
{
#if defined(__TINYC__) || defined(_MSC_VER)
return n.high;
#else
return n >> 64;
#endif
}
// Lehmer's generator
// https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/
global_variable u128 rn_state;
fn u64 generate_random_number()
{
rn_state = u128_u64_mul(rn_state, 0xda942042e4dd58b5);
return u128_shift_right_by_64(rn_state);
}
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 u64 absolute_int(s64 n)
{
return n < 0 ? cast_to(u64, -n) : cast_to(u64, n);
}
fn u64 parse_decimal(String string)
{
u64 value = 0;
for (u64 i = 0; i < string.length; i += 1)
{
u8 ch = s_get(string, i);
assert(((ch >= '0') & (ch <= '9')));
value = (value * 10) + (ch - '0');
}
return value;
}
fn u8 get_next_ch_safe(String string, u64 index)
{
u64 next_index = index + 1;
u64 is_in_range = next_index < string.length;
u64 safe_index = safe_flag(next_index, is_in_range);
u8 unsafe_result = string.pointer[safe_index];
u64 safe_result = safe_flag(unsafe_result, is_in_range);
assert(safe_result < 256);
return (u8)safe_result;
}
fn u32 is_space(u8 ch, u8 next_ch)
{
u32 is_comment = (ch == '/') & (next_ch == '/');
u32 is_whitespace = ch == ' ';
u32 is_vertical_tab = ch == 0x0b;
u32 is_horizontal_tab = ch == '\t';
u32 is_line_feed = ch == '\n';
u32 is_carry_return = ch == '\r';
u32 result = (((is_vertical_tab | is_horizontal_tab) | (is_line_feed | is_carry_return)) | (is_comment | is_whitespace));
return result;
}
fn u64 is_lower(u8 ch)
{
return (ch >= 'a') & (ch <= 'z');
}
fn u64 is_upper(u8 ch)
{
return (ch >= 'A') & (ch <= 'Z');
}
fn u64 is_alphabetic(u8 ch)
{
return is_lower(ch) | is_upper(ch);
}
fn u64 is_decimal_digit(u8 ch)
{
return (ch >= '0') & (ch <= '9');
}
fn u64 is_alphanumeric(u8 ch)
{
return is_alphabetic(ch) | is_decimal_digit(ch);
}
fn u64 is_hex_digit_alpha_lower(u8 ch)
{
return (ch >= 'a') & (ch <= 'f');
}
fn u64 is_hex_digit_alpha_upper(u8 ch)
{
return (ch >= 'A') & (ch <= 'F');
}
fn u64 is_hex_digit_alpha(u8 ch)
{
return is_hex_digit_alpha_lower(ch) | is_hex_digit_alpha_upper(ch);
}
fn u64 is_hex_digit(u8 ch)
{
return is_decimal_digit(ch) | is_hex_digit_alpha(ch);
}
fn u8 hex_ch_to_int(u8 ch)
{
if ((ch >= '0') & (ch <= '9'))
{
return ch - '0';
}
else if ((ch >= 'a') & (ch <= 'f'))
{
return ch - 'a' + 10;
}
else if ((ch >= 'A') & (ch <= 'F'))
{
return ch - 'A' + 10;
}
else
{
unreachable();
}
}
fn u64 is_identifier_start(u8 ch)
{
u64 alphabetic = is_alphabetic(ch);
u64 is_underscore = ch == '_';
return alphabetic | is_underscore;
}
fn u64 is_identifier_ch(u8 ch)
{
u64 identifier_start = is_identifier_start(ch);
u64 decimal = is_decimal_digit(ch);
return identifier_start | decimal;
}
fn Hash64 hash_byte(Hash64 source, u8 ch)
{
source ^= ch;
source *= fnv_prime;
return source;
}
fn Hash64 hash_bytes(String bytes)
{
u64 result = fnv_offset;
for (u64 i = 0; i < bytes.length; i += 1)
{
result = hash_byte(result, bytes.pointer[i]);
}
return result;
}
fn Hash32 hash64_to_hash32(Hash64 hash64)
{
Hash32 low = hash64 & 0xffff;
Hash32 high = (hash64 >> 32) & 0xffff;
Hash32 result = (high << 16) | low;
return result;
}
fn u64 align_forward_u32(u32 value, u32 alignment)
{
u32 mask = alignment - 1;
u32 result = (value + mask) & ~mask;
return result;
}
fn u32 align_backward_u32(u32 value, u32 alignment)
{
u32 result = value & ~(alignment - 1);
return result;
}
fn u64 align_forward_u64(u64 value, u64 alignment)
{
u64 mask = alignment - 1;
u64 result = (value + mask) & ~mask;
return result;
}
fn u64 align_backward_u64(u64 value, u64 alignment)
{
u64 result = value & ~(alignment - 1);
return result;
}
fn u8 is_power_of_two_u64(u64 value)
{
return (value & (value - 1)) == 0;
}
fn u8 first_bit_set_u32(u32 value)
{
#if _MSC_VER
DWORD result_dword;
u8 result_u8 = _BitScanForward(&result_dword, value);
unused(result_u8);
let_cast(u8, result, result_dword);
#else
let(result, (u8)__builtin_ffs((s32)value));
#endif
result -= result != 0;
return result;
}
fn u64 first_bit_set_u64(u64 value)
{
#if _MSC_VER
DWORD result_dword;
u8 result_u8 = _BitScanForward64(&result_dword, value);
unused(result_u8);
let_cast(u8, result, result_dword);
#else
let(result, (u8) __builtin_ffs((s64)value));
#endif
result -= result != 0;
return result;
}
fn Hash32 hash32_fib_end(Hash32 hash)
{
let(result, TRUNCATE(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32));
return result;
}
fn Hash32 hash64_fib_end(Hash64 hash)
{
let(result, TRUNCATE(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32));
return result;
}
fn u64 parse_hexadecimal(String string, u8* error)
{
u8* it = &string.pointer[string.length - 1];
u8 is_error = 0;
u64 result = 0;
while (it >= string.pointer)
{
u8 ch = *it;
u8 is_error_it = !is_hex_digit(ch);
is_error |= is_error_it;
if (is_error_it)
{
break;
}
u8 sub = is_decimal_digit(ch) ? '0' : (is_hex_digit_alpha_lower(ch) ? 'a' : 'A');
u8 hex_value = ch - sub + 10 * is_hex_digit_alpha(ch);
assert((hex_value & 0xf) == hex_value);
result = (result << 4) | hex_value;
it -= 1;
}
*error = is_error;
return result;
}

View File

@ -1,373 +0,0 @@
#pragma once
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define USE_MEMCPY 1
#if _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#define LINK_LIBC 1
#ifndef BB_DEBUG
#define BB_DEBUG 1
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#ifndef BB_SAFETY
#define BB_SAFETY BB_DEBUG
#endif
#define STRUCT_FORWARD_DECL(S) typedef struct S S
#define STRUCT(S) STRUCT_FORWARD_DECL(S); struct S
#define UNION_FORWARD_DECL(U) typedef union U U
#define UNION(U) UNION_FORWARD_DECL(U); union U
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define CLAMP(a, x, b) (((a)>(x))?(a):((b)<(x))?(b):(x))
#ifdef __TINYC__
#define declare_vector_type #error
#else
#ifdef __clang__
#define declare_vector_type(T, count, name) typedef T name __attribute__((ext_vector_type(count)))
#else
#define declare_vector_type(T, count, name) typedef T name __attribute__((vector_size(count)))
#endif
#endif
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
#define KB(n) ((n) * 1024)
#define MB(n) ((n) * 1024 * 1024)
#define GB(n) ((u64)(n) * 1024 * 1024 * 1024)
#define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024)
#define unused(x) (void)(x)
#if _MSC_VER
#define BB_NORETURN __declspec(noreturn)
#define BB_COLD __declspec(noinline)
#elif defined(__TINYC__)
#define BB_NORETURN __attribute__((noreturn))
#define BB_COLD __attribute__((cold))
#else
#define BB_NORETURN [[noreturn]]
#define BB_COLD [[gnu::cold]]
#endif
#define TRUNCATE(Destination, source) (Destination)(source)
#if _MSC_VER
#define ENUM_START(EnumName, T) typedef T EnumName; typedef enum EnumName ## Flags
#define ENUM_END(EnumName) EnumName ## Flags
#else
#define ENUM_START(EnumName, T) typedef enum EnumName : T
#define ENUM_END(EnumName) EnumName
#endif
#define ENUM(EnumName, T, ...) \
ENUM_START(EnumName, T)\
{\
__VA_ARGS__\
} ENUM_END(EnumName)
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#if defined (__TINYC__) || defined(_MSC_VER)
UNION(u128)
{
struct
{
u64 low;
u64 high;
};
u64 v[2];
};
#else
typedef __uint128_t u128;
#endif
typedef unsigned int uint;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
#if !defined(__TINYC__) && !defined(_MSC_VER)
typedef __int128_t s128;
#endif
typedef size_t usize;
#if !defined(__TINYC__) && !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
typedef _Float16 f16;
#pragma GCC diagnostic pop
#endif
typedef float f32;
typedef double f64;
typedef u32 Hash32;
typedef u64 Hash64;
#define Slice(T) Slice_ ## T
#define SliceP(T) SliceP_ ## T
#define declare_slice_ex(T, StructName) STRUCT(StructName) \
{\
T* pointer;\
u64 length;\
}
#define declare_slice(T) declare_slice_ex(T, Slice(T))
#define declare_slice_p(T) declare_slice_ex(T*, SliceP(T))
declare_slice(u8);
declare_slice(u16);
declare_slice(u32);
declare_slice(u64);
declare_slice(s8);
declare_slice(s16);
declare_slice(s32);
declare_slice(s64);
declare_slice_p(char);
declare_slice_p(u8);
declare_slice_p(void);
typedef Slice(u8) String;
declare_slice(String);
#if BB_DEBUG
#define assert(x) (unlikely(!(x)) ? panic("Assert failed: \"" # x "\" at {cstr}:{u32}\n", __FILE__, __LINE__) : unused(0))
#else
#define assert(x) unused(likely(x))
#endif
#ifndef __cplusplus
#if _MSC_VER
#define unreachable_raw() __assume(0)
#else
#define unreachable_raw() __builtin_unreachable()
#endif
// Undefine unreachable if needed to provide a more safe-guard implementation
#ifdef unreachable
#undef unreachable
#endif
#if BB_DEBUG
#define unreachable() panic("Unreachable triggered\n", __FILE__, __LINE__)
#else
#define unreachable() unreachable_raw()
#endif
#define fix_unreachable() unreachable_raw()
#ifndef static_assert
#define static_assert(x) _Static_assert((x), "Static assert failed!")
#endif
#define alignof(x) _Alignof(x)
#else
#define restrict __restrict
#endif
#ifndef BB_INFINITY
#define BB_INFINITY __builtin_inff()
#endif
#ifndef BB_NAN
#define BB_NAN __builtin_nanf("")
#endif
#define fn static
#define method __attribute__((visibility("internal")))
#define global_variable static
#define forceinline __attribute__((always_inline))
#if _MSC_VER
#define expect(x, b) (!!(x))
#else
#define expect(x, b) __builtin_expect(!!(x), b)
#endif
#define likely(x) expect(x, 1)
#define unlikely(x) expect(x, 0)
#define breakpoint() __builtin_debugtrap()
#define failed_execution() panic("Failed execution at {cstr}:{u32}\n", __FILE__, __LINE__)
#define todo() os_is_being_debugged() ? trap() : panic("TODO at {cstr}:{u32}\n", __FILE__, __LINE__); fix_unreachable()
fn void print(const char* format, ...);
BB_NORETURN BB_COLD fn void os_exit(u32 exit_code);
#if _MSC_VER
#define trap() __fastfail(1)
#elif __has_builtin(__builtin_trap)
#define trap() __builtin_trap()
#else
extern BB_NORETURN BB_COLD void abort(void);
fn BB_NORETURN BB_COLD void trap_ext()
{
#ifdef __x86_64__
asm volatile("ud2");
#else
abort();
#endif
}
#define trap() (trap_ext(), __builtin_unreachable())
#endif
fn u8 os_is_being_debugged();
#define panic(format, ...) (!os_is_being_debugged() ? print(format, __VA_ARGS__), os_exit(1) : os_exit(1))
#define let_pointer_cast(PointerChildType, var_name, value) PointerChildType* var_name = (PointerChildType*)(value)
#if defined(__TINYC__) || defined(_MSC_VER)
#define let(name, value) typeof(value) name = (value)
#else
#define let(name, value) __auto_type name = (value)
#endif
#define let_cast(T, name, value) T name = cast_to(T, value)
#define assign_cast(to, from) to = cast_to(typeof(to), from)
#define let_va_arg(T, name, args) T name = va_arg(args, T)
#define transmute(D, source) *(D*)&source
#if BB_SAFETY
#define cast_to(T, value) (assert((typeof(value)) (T) (value) == (value) && ((value) > 0) == ((T) (value) > 0)), (T) (value))
#else
#define cast_to(T, value) (T)(value)
#endif
typedef enum Corner
{
CORNER_00,
CORNER_01,
CORNER_10,
CORNER_11,
CORNER_COUNT,
} Corner;
typedef enum Axis2
{
AXIS2_X,
AXIS2_Y,
AXIS2_COUNT,
} Axis2;
// #ifdef __cplusplus
// #define EXPORT extern "C"
// #else
// #define EXPORT
// #endif
#if defined(__cplusplus) && defined(__linux__)
#define NO_EXCEPT __THROW
#else
#define NO_EXCEPT
#endif
#define NamedEnumMemberEnum(e, enum_member) e ## _ ## enum_member
#define NamedEnumMemberString(e, enum_member) strlit(#enum_member)
typedef SliceP(char) CStringSlice;
#ifdef _WIN32
typedef void* FileDescriptor;
#else
typedef int FileDescriptor;
#endif
#define FOR_N(it, start, end) \
for (u32 it = (start), end__ = (end); it < end__; ++it)
#define FOR_REV_N(it, start, end) \
for (u32 it = (end), start__ = (start); (it--) > start__;)
#define FOR_BIT(it, start, bits) \
for (typeof(bits) _bits_ = (bits), it = (start); _bits_; _bits_ >>= 1, ++it) if (_bits_ & 1)
#define FOREACH_SET(it, set) \
FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.pointer[_i])
#define size_until_end(T, field_name) (sizeof(T) - offsetof(T, field_name))
#define SWAP(a, b) \
do {\
static_assert(typeof(a) == typeof(b));\
let(temp, a);\
a = b;\
b = temp;\
} while (0)
#define slice_from_pointer_range(T, start, end) (Slice(T)) { .pointer = start, .length = (u64)(end - start), }
#define strlit_len(s) (sizeof(s) - 1)
#define strlit(s) (String){ .pointer = (u8*)(s), .length = strlit_len(s), }
#define ch_to_str(ch) (String){ .pointer = &ch, .length = 1 }
#define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) }
#define array_to_bytes(arr) { .pointer = (u8*)(arr), .length = sizeof(arr) }
#define pointer_to_bytes(p) (String) { .pointer = (u8*)(p), .length = sizeof(*p) }
#define scalar_to_bytes(s) pointer_to_bytes(&(s))
#define string_to_c(s) ((char*)((s).pointer))
#define cstr(s) ((String) { .pointer = (u8*)(s), .length = strlen((char*)s), } )
#define case_to_name(prefix, e) case prefix ## e: return strlit(#e)
global_variable const u8 brace_open = '{';
global_variable const u8 brace_close = '}';
global_variable const u8 parenthesis_open = '(';
global_variable const u8 parenthesis_close = ')';
global_variable const u8 bracket_open = '[';
global_variable const u8 bracket_close = ']';
#define s_get(s, i) (s).pointer[i]
#define s_get_pointer(s, i) &((s).pointer[i])
#define s_get_slice(T, s, start, end) (Slice(T)){ .pointer = ((s).pointer) + (start), .length = (end) - (start) }
#define s_equal(a, b) ((a).length == (b).length && memcmp((a).pointer, (b).pointer, sizeof(*((a).pointer)) * (a).length) == 0)
fn u64 align_forward_u64(u64 value, u64 alignment);
fn u64 align_backward_u64(u64 value, u64 alignment);
fn u8 log2_alignment_u64(u64 alignment);
fn u8 is_power_of_two_u64(u64 value);
fn u8 first_bit_set_u32(u32 value);
fn u64 first_bit_set_u64(u64 value);
fn u32 format_decimal(String buffer, u64 decimal);
fn u32 format_hexadecimal(String buffer, u64 hexadecimal);
fn u64 format_float(String buffer, f64 value_double);
fn u64 is_decimal_digit(u8 ch);
fn u32 is_space(u8 ch, u8 next_ch);
fn u8 get_next_ch_safe(String string, u64 index);
fn u64 is_identifier_start(u8 ch);
fn u64 is_identifier_ch(u8 ch);
fn u64 is_alphabetic(u8 ch);
fn u64 is_alphanumeric(u8 ch);
fn u64 parse_decimal(String string);
global_variable const Hash64 fnv_offset = 14695981039346656037ull;
global_variable const u64 fnv_prime = 1099511628211ull;
fn Hash32 hash32_fib_end(Hash32 hash);
fn Hash32 hash64_fib_end(Hash64 hash);
fn Hash64 hash_byte(Hash64 source, u8 ch);
fn Hash64 hash_bytes(String bytes);
fn Hash32 hash64_to_hash32(Hash64 hash64);
fn u64 round_up_to_next_power_of_2(u64 n);
STRUCT(TextureIndex)
{
u32 value;
};
fn u64 safe_flag(u64 value, u64 flag)
{
u64 result = value & ((u64)0 - flag);
return result;
}
#define member_from_offset(pointer, type, memory_offset) (*(type*)((u8*)pointer + memory_offset))
#if _MSC_VER
#define offset_of(T, member) offsetof(T, member)
#else
#define offset_of(T, member) __builtin_offsetof(T, member)
#endif

View File

@ -1,65 +0,0 @@
#pragma once
@implementation AppleApplicationDelegate
- (void)applicationDidFinishLaunching:(NSNotification*)aNotification
{
}
@end
@implementation AppleWindow
@end
global_variable WindowConnection window_connection;
fn u8 windowing_initialize()
{
u8 result = 1;
window_connection.application = [NSApplication sharedApplication];
AppleApplicationDelegate* application_delegate = [[AppleApplicationDelegate alloc] init];
NSApp.delegate = application_delegate;
return result;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate)
{
NSRect rect = { { 0, 0 }, { 800, 600 } };
AppleWindow* window = [[AppleWindow alloc] initWithContentRect:rect styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:NO];
window.title = @"Hello Metal";
[window_connection.application activate];
[window orderFrontRegardless];
return window;
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
WindowingSize size;
@autoreleasepool {
const NSRect contentRect = instance.contentView.frame;
const NSRect fbRect = [instance.contentView convertRectToBacking:contentRect];
size = (WindowingSize) {
.width = fbRect.size.width,
.height = fbRect.size.height,
};
} // autoreleasepool
return size;
}
fn void windowing_poll_events()
{
@autoreleasepool {
while (1)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event == nil)
{
break;
}
[NSApp sendEvent:event];
}
} // autoreleasepool
}

View File

@ -1,18 +0,0 @@
#pragma once
#import <Cocoa/Cocoa.h>
#import <QuartzCore/CAMetalLayer.h>
@interface AppleApplicationDelegate : NSObject<NSApplicationDelegate>
@end
@interface AppleWindow : NSWindow
@end
@interface AppleWindowDelegate : NSObject<NSWindowDelegate>
@end
typedef AppleWindow WindowingInstance;
STRUCT(WindowConnection)
{
NSApplication* application;
};

View File

@ -1 +0,0 @@
#pragma once

View File

@ -1 +0,0 @@
#pragma once

View File

@ -1,35 +0,0 @@
#include <std/entry_point.h>
#include <std/os.h>
#if LINK_LIBC == 0
[[gnu::naked]] BB_NORETURN void _start()
{
__asm__ __volatile__(
"\nxor %ebp, %ebp"
"\npopq %rdi"
"\nmov %rsp, %rsi"
"\nand $~0xf, %rsp"
"\npushq %rsp"
"\npushq $0"
"\ncallq static_entry_point"
"\nud2\n"
);
}
#endif
#if LINK_LIBC == 0
void static_entry_point(int argc, char* argv[])
{
char** envp = (char**)&argv[argc + 1];
#else
int main(int argc, char* argv[], char* envp[])
{
#endif
calibrate_cpu_timer();
entry_point(argc, argv, envp);
#if LINK_LIBC
return 0;
#else
syscall_exit(0);
#endif
}

View File

@ -1,11 +0,0 @@
#include <std/base.h>
void entry_point(int argc, char* argv[], char* envp[]);
#if LINK_LIBC == 0
[[gnu::naked]] BB_NORETURN void _start();
#endif
#if LINK_LIBC == 0
void static_entry_point(int argc, char* argv[]);
#endif

View File

@ -1,167 +0,0 @@
#pragma once
#include <std/font_provider.h>
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#define stbtt_uint8 u8
#define stbtt_uint16 u16
#define stbtt_uint32 u32
#define stbtt_int8 s8
#define stbtt_int16 s16
#define stbtt_int32 s32
extern float sqrtf(float x);
extern float roundf(float x);
extern float floorf(float x);
extern double sqrt(double x);
extern double fabs(double x);
extern double floor(double x);
extern double ceil(double x);
extern double fmod(double x, double y);
extern double pow(double x, double y);
extern double acos(double x);
extern double cos(double x);
#define STBTT_ifloor(x) ((int) floor(x))
#define STBTT_iceil(x) ((int) ceil(x))
#define STBTT_sqrt(x) sqrt(x)
#define STBTT_pow(x,y) pow(x,y)
#define STBTT_fmod(x,y) fmod(x,y)
#define STBTT_cos(x) cos(x)
#define STBTT_acos(x) acos(x)
#define STBTT_fabs(x) fabs(x)
#define STBTT_malloc(x,u) ((void)(u),malloc(x))
#define STBTT_free(x,u) ((void)(u),free(x))
#define STBTT_assert(x) assert(x)
#define STBTT_strlen(x) strlen(x)
#define STBTT_memcpy memcpy
#define STBTT_memset memset
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#include <stb_truetype.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
fn TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create)
{
let(font_file, file_read(arena, create.font_path));
stbtt_fontinfo font_info;
if (!stbtt_InitFont(&font_info, font_file.pointer, stbtt_GetFontOffsetForIndex(font_file.pointer, 0)))
{
failed_execution();
}
TextureAtlas result = {};
u32 character_count = 256;
result.characters = arena_allocate(arena, FontCharacter, character_count);
result.kerning_tables = arena_allocate(arena, s32, character_count * character_count);
result.height = (u32)sqrtf((f32)(create.text_height * create.text_height * character_count));
result.width = result.height;
result.pointer = arena_allocate(arena, u32, result.width * result.height);
let(scale_factor, stbtt_ScaleForPixelHeight(&font_info, create.text_height));
int ascent;
int descent;
int line_gap;
stbtt_GetFontVMetrics(&font_info, &ascent, &descent, &line_gap);
result.ascent = (u32)roundf(ascent * scale_factor);
result.descent = (u32)roundf(descent * scale_factor);
result.line_gap = (u32)roundf(line_gap * scale_factor);
u32 x = 0;
u32 y = 0;
u32 max_row_height = 0;
u32 first_character = ' ';
u32 last_character = '~';
for (let(i, first_character); i <= last_character; ++i)
{
u32 width;
u32 height;
int advance;
int left_bearing;
let(ch, (u8)i);
let(character, &result.characters[i]);
stbtt_GetCodepointHMetrics(&font_info, ch, &advance, &left_bearing);
character->advance = (u32)roundf(advance * scale_factor);
character->left_bearing = (u32)roundf(left_bearing * scale_factor);
u8* bitmap = stbtt_GetCodepointBitmap(&font_info, 0.0f, scale_factor, ch, (int*)&width, (int*)&height, &character->x_offset, &character->y_offset);
let(kerning_table, result.kerning_tables + i * character_count);
for (u32 j = first_character; j <= last_character; j += 1)
{
let(kerning_advance, stbtt_GetCodepointKernAdvance(&font_info, i, j));
kerning_table[j] = (s32)roundf(kerning_advance * scale_factor);
}
if (x + width > result.width)
{
y += max_row_height;
max_row_height = height;
x = 0;
}
else
{
max_row_height = MAX(height, max_row_height);
}
character->x = x;
character->y = y;
character->width = width;
character->height = height;
let(source, bitmap);
let(destination, result.pointer);
for (u32 bitmap_y = 0; bitmap_y < height; bitmap_y += 1)
{
for (u32 bitmap_x = 0; bitmap_x < width; bitmap_x += 1)
{
let(source_index, bitmap_y * width + bitmap_x);
let(destination_index, (bitmap_y + y) * result.width + (bitmap_x + x));
let(value, source[source_index]);
destination[destination_index] = ((u32)value << 24) | 0xffffff;
}
}
x += width;
stbtt_FreeBitmap(bitmap, 0);
}
result.texture = renderer_texture_create(renderer, (TextureMemory) {
.pointer = result.pointer,
.width = result.width,
.height = result.height,
.depth = 1,
.format = TEXTURE_FORMAT_R8G8B8A8_SRGB,
});
return result;
}
fn uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas)
{
let(height, atlas->ascent - atlas->descent);
u32 x_offset = 0;
u32 y_offset = height;
for (u64 i = 0; i < string.length; i += 1)
{
let(ch, string.pointer[i]);
let(character, &atlas->characters[ch]);
let(kerning, (atlas->kerning_tables + ch * 256)[string.pointer[i + 1]]);
x_offset += character->advance + kerning;
}
return (uint2) { x_offset, y_offset };
}

View File

@ -1,35 +0,0 @@
#pragma once
STRUCT(FontCharacter)
{
u32 advance;
u32 left_bearing;
u32 x;
u32 y;
u32 width;
u32 height;
s32 x_offset;
s32 y_offset;
};
STRUCT(TextureAtlas)
{
u32* pointer;
FontCharacter* characters;
s32* kerning_tables;
u32 width;
u32 height;
s32 ascent;
s32 descent;
s32 line_gap;
TextureIndex texture;
};
STRUCT(TextureAtlasCreate)
{
String font_path;
u32 text_height;
};
fn TextureAtlas font_texture_atlas_create(Arena* arena, Renderer* renderer, TextureAtlasCreate create);
fn uint2 texture_atlas_compute_string_rect(String string, const TextureAtlas* atlas);

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
#pragma once
STRUCT(StringFormatter)
{
String buffer;
u64 index;
};
fn void formatter_append(StringFormatter* formatter, const char* format, ...);
fn void formatter_append_string(StringFormatter* formatter, String string);
fn void formatter_append_character(StringFormatter* formatter, u8 ch);
fn String format_string(String buffer, const char* format, ...);
fn String format_string_va(String buffer, const char* format, va_list args);

View File

@ -1,26 +0,0 @@
#include <std/image_loader.h>
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#include <stb_image.h>
#pragma clang diagnostic pop
EXPORT TextureMemory texture_load_from_file(Arena* arena, String path)
{
auto file = file_read(arena, path);
int width;
int height;
int channels;
u8* buffer = stbi_load_from_memory(file.pointer, file.length, &width, &height, &channels, STBI_rgb_alpha);
channels += 1;
return (TextureMemory) {
.pointer = buffer,
.width = width,
.height = height,
.format = TEXTURE_FORMAT_R8G8B8A8_SRGB,
.depth = 1,
};
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <std/base.h>
#include <std/os.h>
#include <std/render.h>
EXPORT TextureMemory texture_load_from_file(Arena* arena, String path);

View File

@ -1,178 +0,0 @@
#include <std/md5.h>
STRUCT(MD5Context)
{
u32 buffer[4];
u8 input[64];
u64 size;
};
// Took from: https://github.com/Zunawe/md5-c
#define MD5_A 0x67452301
#define MD5_B 0xefcdab89
#define MD5_C 0x98badcfe
#define MD5_D 0x10325476
#define MD5_F(X, Y, Z) ((X & Y) | (~X & Z))
#define MD5_G(X, Y, Z) ((X & Z) | (Y & ~Z))
#define MD5_H(X, Y, Z) (X ^ Y ^ Z)
#define MD5_I(X, Y, Z) (Y ^ (X | ~Z))
global_variable u32 md5_s[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
global_variable u32 md5_k[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
/*
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
*/
global_variable u8 md5_padding[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
fn MD5Context md5_init()
{
return (MD5Context) {
.buffer = { MD5_A, MD5_B, MD5_C, MD5_D },
};
}
fn u32 rotate_left_u32(u32 x, u32 n)
{
return (x << n) | (x >> (32 - n));
}
fn void md5_step(u32* buffer, u32* input)
{
u32 aa = buffer[0];
u32 bb = buffer[1];
u32 cc = buffer[2];
u32 dd = buffer[3];
for (u32 i = 0; i < 64; i += 1)
{
u32 j;
u32 e;
switch (i / 16)
{
case 0:
{
e = MD5_F(bb, cc, dd);
j = i;
} break;
case 1:
{
e = MD5_G(bb, cc, dd);
j = ((i * 5) + 1) % 16;
} break;
case 2:
{
e = MD5_H(bb, cc, dd);
j = ((i * 3) + 5) % 16;
} break;
default:
{
e = MD5_I(bb, cc, dd);
j = (i * 7) % 16;
} break;
}
u32 old_dd = dd;
dd = cc;
cc = bb;
bb = bb + rotate_left_u32(aa + e + md5_k[i] + input[j], md5_s[i]);
aa = old_dd;
}
buffer[0] += aa;
buffer[1] += bb;
buffer[2] += cc;
buffer[3] += dd;
}
fn void md5_update(MD5Context* context, String input_argument)
{
u32 input_local[16];
auto offset = context->size % 64;
context->size += input_argument.length;
for (u64 i = 0; i < input_argument.length; i += 1)
{
context->input[offset] = input_argument.pointer[i];
offset += 1;
if (offset % 64 == 0)
{
// TODO: convert to little-endian in case we are big-endian?
for (u16 i = 0; i < 16; i += 1)
{
auto existing = *(u32*)&input_argument.pointer[i * 4];
input_local[i] = existing;
}
md5_step(context->buffer, input_local);
offset = 0;
}
}
}
fn MD5Result md5_end(MD5Context* context)
{
u32 input[16];
auto offset = context->size % 64;
auto padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
md5_update(context, (String) { .pointer = md5_padding, .length = padding_length });
context->size -= (u64)padding_length;
for (u32 i = 0; i < 14; i += 1)
{
input[i] = *(u32*)&context->input[i * 4];
}
input[14] = (u32)(context->size * 8);
input[15] = (u32)((context->size * 8) >> 32);
md5_step(context->buffer, input);
MD5Result result;
for (u32 i = 0; i < 4; i += 1)
{
result.hash[(i * 4) + 0] = (u8)((context->buffer[i] & 0x000000ff) >> 0);
result.hash[(i * 4) + 1] = (u8)((context->buffer[i] & 0x0000ff00) >> 8);
result.hash[(i * 4) + 2] = (u8)((context->buffer[i] & 0x00ff0000) >> 16);
result.hash[(i * 4) + 3] = (u8)((context->buffer[i] & 0xff000000) >> 24);
}
return result;
}
MD5Result md5_string(String string)
{
auto context = md5_init();
md5_update(&context, string);
auto result = md5_end(&context);
return result;
}

View File

@ -1,8 +0,0 @@
#include <std/base.h>
STRUCT(MD5Result)
{
u8 hash[16];
};
MD5Result md5_string(String string);

View File

@ -1,148 +0,0 @@
#pragma once
global_variable Renderer renderer_memory;
fn NSString* apple_string(String string)
{
NSString* result = [[NSString alloc] initWithBytes:string.pointer length:string.length encoding:NSUTF8StringEncoding];
return result;
}
fn Renderer* rendering_initialize(Arena* arena)
{
Renderer* renderer = &renderer_memory;
@autoreleasepool {
renderer->device = MTLCreateSystemDefaultDevice();
String shader_source = file_read(arena, strlit("bootstrap/std/shaders/rect.metal"));
NSString* apple_shader_source = apple_string(shader_source);
NSError* error = nil;
id<MTLLibrary> library = [renderer->device newLibraryWithSource: apple_shader_source options:nil error:&error];
if (!library)
{
// Inspect the error
NSLog(@"Error Domain: %@", error.domain);
NSLog(@"Error Code: %ld", (long)error.code);
NSLog(@"Localized Description: %@", error.localizedDescription);
NSDictionary *userInfo = error.userInfo;
if (userInfo) {
NSLog(@"Additional Info: %@", userInfo);
}
// Take action based on the error
if ([error.domain isEqualToString:MTLLibraryErrorDomain]) {
NSLog(@"Metal Library Compilation Error. Check the shader source.");
} else {
NSLog(@"Unexpected error occurred.");
}
}
id<MTLFunction> vertex = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragment = [library newFunctionWithName:@"fragment_main"];
MTLRenderPipelineDescriptor* pipeline_descriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipeline_descriptor.vertexFunction = vertex;
pipeline_descriptor.fragmentFunction = fragment;
pipeline_descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
id<MTLRenderPipelineState> pipeline_state = [renderer->device newRenderPipelineStateWithDescriptor:pipeline_descriptor error:&error];
if (!pipeline_state)
{
// Inspect the error
NSLog(@"Error Domain: %@", error.domain);
NSLog(@"Error Code: %ld", (long)error.code);
NSLog(@"Localized Description: %@", error.localizedDescription);
NSDictionary *userInfo = error.userInfo;
if (userInfo) {
NSLog(@"Additional Info: %@", userInfo);
}
}
id<MTLCommandQueue> command_queue = [renderer->device newCommandQueue];
}
return renderer;
}
global_variable RenderWindow render_window_memory;
fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window)
{
RenderWindow* render_window = &render_window_memory;
CAMetalLayer* layer = [CAMetalLayer layer];
render_window->layer = layer;
layer.device = renderer->device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
layer.framebufferOnly = true;
layer.frame = window.frame;
window.contentView.layer = layer;
window.opaque = true;
window.backgroundColor = nil;
return render_window;
}
fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window)
{
@autoreleasepool {
id<CAMetalDrawable> drawable = [window->layer nextDrawable];
MTLRenderPassDescriptor* render_pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
MTLRenderPassColorAttachmentDescriptor* color_attachment = render_pass_descriptor.colorAttachments[0];
color_attachment.clearColor = MTLClearColorMake(1, 1, 1, 1);
color_attachment.storeAction = MTLStoreActionStore;
color_attachment.texture = drawable.texture;
id<MTLCommandBuffer> command_buffer = [renderer->command_queue commandBuffer];
id<MTLRenderCommandEncoder> render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor];
[render_command_encoder setRenderPipelineState: renderer->pipeline_state];
[render_command_encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
[render_command_encoder endEncoding];
[command_buffer presentDrawable:drawable];
[command_buffer commit];
}
}
fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window)
{
// todo();
}
fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory)
{
todo();
}
fn void window_rect_texture_update_begin(RenderWindow* window)
{
todo();
}
fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas)
{
todo();
}
fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index)
{
todo();
}
fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window)
{
todo();
}
fn void window_render_rect(RenderWindow* window, RectDraw draw)
{
// todo();
}
fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset)
{
// todo();
}

View File

@ -1,16 +0,0 @@
#pragma once
#import <Metal/Metal.h>
STRUCT(Renderer)
{
id<MTLDevice> device;
id<MTLCommandQueue> command_queue;
id<MTLRenderPipelineState> pipeline_state;
};
STRUCT(RenderWindow)
{
CAMetalLayer* layer;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,195 +0,0 @@
#pragma once
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
#else
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/ptrace.h>
#endif
typedef enum TimeUnit
{
TIME_UNIT_NANOSECONDS,
TIME_UNIT_MICROSECONDS,
TIME_UNIT_MILLISECONDS,
TIME_UNIT_SECONDS,
} TimeUnit;
ENUM(ProcessTerminationKind, u8,
PROCESS_TERMINATION_UNKNOWN,
PROCESS_TERMINATION_EXIT,
PROCESS_TERMINATION_SIGNAL,
PROCESS_TERMINATION_STOP,
);
STRUCT(RunCommandResult)
{
u32 termination_code;
ProcessTerminationKind termination_kind;
u8 reserved[3];
};
typedef enum ChildProcessStreamPolicy
{
CHILD_PROCESS_STREAM_INHERIT,
CHILD_PROCESS_STREAM_PIPE,
CHILD_PROCESS_STREAM_IGNORE,
} ChildProcessStreamPolicy;
STRUCT(ChildProcessStream)
{
u8* buffer;
u32* length;
u32 capacity;
ChildProcessStreamPolicy policy;
};
STRUCT(RunCommandOptions)
{
ChildProcessStream stdout_stream;
ChildProcessStream stderr_stream;
FileDescriptor null_file_descriptor;
u64 use_null_file_descriptor:1;
u64 debug:1;
u64 reserved:62;
};
STRUCT(Timestamp)
{
u128 value;
};
STRUCT(OSFileOpenFlags)
{
u32 truncate:1;
u32 executable:1;
u32 write:1;
u32 read:1;
u32 create:1;
u32 directory:1;
};
STRUCT(OSFilePermissions)
{
u8 readable:1;
u8 writable:1;
u8 executable:1;
};
STRUCT(OSReserveProtectionFlags)
{
u32 read:1;
u32 write:1;
u32 execute:1;
u32 reserved:29;
};
STRUCT(OSReserveMapFlags)
{
u32 priv:1;
u32 anon:1;
u32 populate:1;
u32 noreserve:1;
u32 reserved:29;
};
STRUCT(Arena)
{
u64 reserved_size;
u64 position;
u64 os_position;
u64 granularity;
u8 reserved[4 * 8];
};
STRUCT(FileWriteOptions)
{
String path;
String content;
u64 executable:1;
};
#ifndef __APPLE__
#define MY_PAGE_SIZE KB(4)
#else
#define MY_PAGE_SIZE KB(16)
#endif
global_variable const u64 page_size = MY_PAGE_SIZE;
global_variable u64 minimum_granularity = MY_PAGE_SIZE;
// global_variable u64 middle_granularity = MB(2);
global_variable u64 default_size = GB(4);
fn void vprint(const char* format, va_list args);
fn void print(const char* format, ...);
fn RunCommandResult run_command(Arena* arena, CStringSlice arguments, char* envp[], RunCommandOptions options);
fn String file_read(Arena* arena, String path);
fn void file_write(FileWriteOptions options);
fn String path_dir(String string);
fn String path_base(String string);
fn String path_no_extension(String string);
fn Arena* arena_initialize(u64 reserved_size, u64 granularity, u64 initial_size);
fn Arena* arena_initialize_default(u64 initial_size);
fn void arena_clear(Arena* arena);
fn String arena_join_string(Arena* arena, Slice(String) pieces);
fn u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment);
fn void arena_reset(Arena* arena);
#define arena_allocate(arena, T, count) (T*)(arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)))
#define arena_allocate_slice(arena, T, count) (Slice(T)){ .pointer = arena_allocate(arena, T, count), .length = count }
fn u8* os_reserve(u64 base, u64 size, OSReserveProtectionFlags protection, OSReserveMapFlags map);
fn void os_commit(void* address, u64 size);
fn u8 os_file_descriptor_is_valid(FileDescriptor fd);
fn FileDescriptor os_file_open(String path, OSFileOpenFlags flags, OSFilePermissions permissions);
fn void os_file_close(FileDescriptor fd);
fn u64 os_file_get_size(FileDescriptor fd);
fn void os_file_write(FileDescriptor fd, String content);
fn FileDescriptor os_stdout_get();
fn void os_directory_make(String path);
BB_NORETURN BB_COLD fn void os_exit(u32 exit_code);
fn void calibrate_cpu_timer();
fn void print_string(String string);
fn Timestamp os_timestamp();
fn f64 os_resolve_timestamps(Timestamp start, Timestamp end, TimeUnit time_unit);
fn u8 os_is_being_debugged();
#if _WIN32
typedef void* HANDLE;
fn HANDLE os_windows_get_module_handle();
#endif
#if _WIN32
#define EXECUTABLE_EXTENSION ".exe"
#else
#define EXECUTABLE_EXTENSION ""
#endif
STRUCT(OSLibrary)
{
void* handle;
};
typedef void* OSSymbol;
fn OSLibrary os_library_load(const char* library_name);
fn OSSymbol os_symbol_load(OSLibrary library, const char* symbol_name);

View File

@ -1,60 +0,0 @@
#pragma once
#ifndef BUILD_DIR
#define BUILD_DIR "cache"
#endif
typedef enum RenderingBackend
{
RENDERING_BACKEND_NONE,
RENDERING_BACKEND_METAL,
RENDERING_BACKEND_VULKAN,
RENDERING_BACKEND_DIRECTX12,
RENDERING_BACKEND_COUNT,
} RenderingBackend;
typedef enum WindowingBackend
{
WINDOWING_BACKEND_NONE,
WINDOWING_BACKEND_WIN32,
WINDOWING_BACKEND_X11,
WINDOWING_BACKEND_WAYLAND,
WINDOWING_BACKEND_COCOA,
WINDOWING_BACKEND_COUNT,
} WindowingBackend;
fn u8 rendering_backend_is_valid(RenderingBackend rendering_backend)
{
u8 valid = rendering_backend != RENDERING_BACKEND_COUNT;
if (valid && rendering_backend != RENDERING_BACKEND_NONE)
{
#ifdef __linux__
valid = rendering_backend == RENDERING_BACKEND_VULKAN;
#elif defined(__APPLE__)
valid = rendering_backend == RENDERING_BACKEND_METAL || rendering_backend == RENDERING_BACKEND_VULKAN;
#elif _WIN32
valid = rendering_backend == RENDERING_BACKEND_DIRECTX12 || rendering_backend == RENDERING_BACKEND_VULKAN;
#endif
}
return valid;
}
fn u8 windowing_backend_is_valid(WindowingBackend windowing_backend)
{
u8 valid = windowing_backend != WINDOWING_BACKEND_COUNT;
if (valid && windowing_backend != WINDOWING_BACKEND_NONE)
{
#ifdef __linux__
valid = windowing_backend == WINDOWING_BACKEND_WAYLAND || windowing_backend == WINDOWING_BACKEND_X11;
#elif _WIN32
valid = windowing_backend == WINDOWING_BACKEND_WIN32;
#elif __APPLE__
valid = windowing_backend == WINDOWING_BACKEND_COCOA;
#endif
}
return valid;
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <std/font_provider.c>
#if BB_RENDERING_BACKEND_VULKAN
#include <std/vulkan_rendering.c>
#endif
#if BB_RENDERING_BACKEND_METAL
#include <std/metal_rendering.c>
#endif
#if BB_RENDERING_BACKEND_DIRECTX12
#include <std/directx12_rendering.c>
#endif

View File

@ -1,334 +0,0 @@
#pragma once
#if BB_RENDERING_BACKEND_VULKAN
#include <std/vulkan_rendering.h>
#endif
#if BB_RENDERING_BACKEND_METAL
#include <std/metal_rendering.h>
#endif
#if BB_RENDERING_BACKEND_DIRECTX12
#include <std/directx12_rendering.h>
#endif
#ifdef __clang__
#define BB_HAS_NATIVE_FLOAT2 1
#define BB_HAS_NATIVE_FLOAT3 1
#define BB_HAS_NATIVE_FLOAT4 1
#define BB_HAS_NATIVE_UINT2 1
#define BB_HAS_NATIVE_UINT3 1
#define BB_HAS_NATIVE_UINT4 1
#else
#define BB_HAS_NATIVE_FLOAT2 0
#define BB_HAS_NATIVE_FLOAT3 0
#define BB_HAS_NATIVE_FLOAT4 0
#define BB_HAS_NATIVE_UINT2 0
#define BB_HAS_NATIVE_UINT3 0
#define BB_HAS_NATIVE_UINT4 0
#endif
#if BB_HAS_NATIVE_FLOAT2
declare_vector_type(float, 2, float2);
#else
UNION(float2)
{
struct
{
float x, y;
};
float v[2];
};
#endif
#if BB_HAS_NATIVE_FLOAT3
declare_vector_type(float, 3, float3);
#else
UNION(float3)
{
struct
{
float x, y, z;
};
float v[3];
};
#endif
#if BB_HAS_NATIVE_FLOAT4
declare_vector_type(float, 4, float4);
#else
UNION(float4)
{
struct
{
float x, y, z, w;
};
float v[4];
};
#endif
#if BB_HAS_NATIVE_UINT2
declare_vector_type(uint, 2, uint2);
#else
UNION(uint2)
{
struct
{
uint x, y;
};
uint v[2];
};
#endif
#if BB_HAS_NATIVE_UINT3
declare_vector_type(uint, 3, uint3);
#else
UNION(uint3)
{
struct
{
uint x, y, z;
};
uint v[3];
};
#endif
#if BB_HAS_NATIVE_UINT4
declare_vector_type(uint, 4, uint4);
#else
UNION(uint4)
{
struct
{
uint x, y, z, w;
};
uint v[4];
};
#endif
typedef float2 vec2;
typedef float3 vec3;
typedef float4 vec4;
#if BB_HAS_NATIVE_FLOAT2
#define VEC2(_x, y) ((vec2){_x, _y})
#else
#define VEC2(_x, _y) ((vec2){ .x = _x, .y = _y})
#endif
#if BB_HAS_NATIVE_FLOAT3
#define VEC3(_x, _y, _z) ((vec3){_x, _y, _z})
#else
#define VEC3(_x, _y, _z) ((vec3){ .x = _x, .y = _y, .z = _z})
#endif
#if BB_HAS_NATIVE_FLOAT4
#define VEC4(_x, _y, _z, _w) ((vec4){_x, _y, _z, _w})
#else
#define VEC4(_x, _y, _z, _w) ((vec4){ .x = _x, .y = _y, .z = _z, .w = _w})
#endif
fn float2 float2_add(float2 a, float2 b)
{
#if BB_HAS_NATIVE_FLOAT2
return a + b;
#else
float2 result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
#endif
}
fn float2 float2_sub(float2 a, float2 b)
{
#if BB_HAS_NATIVE_FLOAT2
return a - b;
#else
float2 result;
result.x = a.x - b.x;
result.y = a.y - b.y;
return result;
#endif
}
UNION(F32Interval2)
{
struct
{
vec2 min;
vec2 max;
};
struct
{
float2 p0;
float2 p1;
};
struct
{
f32 x0;
f32 y0;
f32 x1;
f32 y1;
};
float2 v[2];
};
static_assert(sizeof(F32Interval2) == 4 * sizeof(f32));
typedef struct Renderer Renderer;
typedef struct RenderWindow RenderWindow;
typedef struct Pipeline Pipeline;
STRUCT(RectDraw)
{
F32Interval2 vertex;
F32Interval2 texture;
vec4 colors[4];
u32 texture_index;
};
#include "../std/shaders/rect.inc"
typedef struct RectVertex RectVertex;
decl_vb(RectVertex);
typedef enum BBPipeline
{
BB_PIPELINE_RECT,
BB_PIPELINE_COUNT,
} BBPipeline;
typedef enum RenderFontType
{
RENDER_FONT_TYPE_MONOSPACE,
RENDER_FONT_TYPE_PROPORTIONAL,
RENDER_FONT_TYPE_COUNT,
} RenderFontType;
typedef enum RectTextureSlot
{
RECT_TEXTURE_SLOT_WHITE,
RECT_TEXTURE_SLOT_MONOSPACE_FONT,
RECT_TEXTURE_SLOT_PROPORTIONAL_FONT,
RECT_TEXTURE_SLOT_COUNT
} RectTextureSlot;
typedef enum TextureFormat
{
TEXTURE_FORMAT_R8_UNORM,
TEXTURE_FORMAT_R8G8B8A8_SRGB,
} TextureFormat;
STRUCT(TextureMemory)
{
void* pointer;
u32 width;
u32 height;
u32 depth;
TextureFormat format;
};
ENUM(ShaderStage, u8,
SHADER_STAGE_VERTEX,
SHADER_STAGE_FRAGMENT,
);
STRUCT(PipelineCreate)
{
Slice(u16) shader_source_indices;
u16 layout_index;
};
declare_slice(PipelineCreate);
STRUCT(PushConstantRange)
{
u16 offset;
u16 size;
ShaderStage stage;
};
declare_slice(PushConstantRange);
ENUM(DescriptorType, u8,
DESCRIPTOR_TYPE_IMAGE_PLUS_SAMPLER,
DESCRIPTOR_TYPE_COUNT,
);
STRUCT(DescriptorSetLayoutBinding)
{
u8 binding;
DescriptorType type;
ShaderStage stage;
u8 count;
};
declare_slice(DescriptorSetLayoutBinding);
STRUCT(DescriptorSetLayoutCreate)
{
Slice(DescriptorSetLayoutBinding) bindings;
};
declare_slice(DescriptorSetLayoutCreate);
STRUCT(PipelineLayoutCreate)
{
Slice(PushConstantRange) push_constant_ranges;
Slice(DescriptorSetLayoutCreate) descriptor_set_layouts;
};
declare_slice(PipelineLayoutCreate);
STRUCT(GraphicsPipelinesCreate)
{
Slice(String) shader_binaries;
Slice(PipelineLayoutCreate) layouts;
Slice(PipelineCreate) pipelines;
};
STRUCT(PipelineIndex)
{
u32 value;
};
STRUCT(PipelineLayoutIndex)
{
u32 value;
};
STRUCT(DescriptorSetIndex)
{
u32 value;
};
ENUM(BufferType, u8,
BUFFER_TYPE_VERTEX,
BUFFER_TYPE_INDEX,
BUFFER_TYPE_STAGING,
);
STRUCT(HostBufferCopy)
{
String source;
u64 destination_offset;
};
declare_slice(HostBufferCopy);
STRUCT(LocalBufferCopyRegion)
{
u64 source_offset;
u64 destination_offset;
u64 size;
};
declare_slice(LocalBufferCopyRegion);
#include <std/font_provider.h>
fn Renderer* rendering_initialize(Arena* arena);
fn RenderWindow* rendering_initialize_window(Renderer* renderer, WindowingInstance* window);
fn void renderer_window_frame_begin(Renderer* renderer, RenderWindow* window);
fn void renderer_window_frame_end(Renderer* renderer, RenderWindow* window);
fn TextureIndex renderer_texture_create(Renderer* renderer, TextureMemory texture_memory);
fn void window_rect_texture_update_begin(RenderWindow* window);
fn void renderer_queue_font_update(Renderer* renderer, RenderWindow* window, RenderFontType type, TextureAtlas atlas);
fn void window_queue_rect_texture_update(RenderWindow* window, RectTextureSlot slot, TextureIndex texture_index);
fn void window_rect_texture_update_end(Renderer* renderer, RenderWindow* window);
fn void window_render_rect(RenderWindow* window, RectDraw draw);
fn void window_render_text(Renderer* renderer, RenderWindow* window, String string, float4 color, RenderFontType font_type, u32 x_offset, u32 y_offset);

View File

@ -1,163 +0,0 @@
#include <std/sha1.h>
// https://github.com/jasinb/sha1.git
// STRUCT(Sha1Digest)
// {
// u32 digest[5];
// };
// static uint32_t rotl32(uint32_t x, int b)
// {
// return (x << b) | (x >> (32-b));
// }
//
// switch endianness
// fn u32 sha1_get32(u8* p)
// {
// return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
// }
// fn u32 sha1_f(int t, u32 b, u32 c, u32 d)
// {
// assert(0 <= t && t < 80);
//
// if (t < 20)
// {
// return (b & c) | ((~b) & d);
// }
// else if (t < 40)
// {
// return b ^ c ^ d;
// }
// else if (t < 60)
// {
// return (b & c) | (b & d) | (c & d);
// }
// else
// //if (t < 80)
// {
// return b ^ c ^ d;
// }
// }
// STRUCT(Sha1Context)
// {
// u8 block[64];
// u32 h[5];
// u64 bytes;
// u32 cur;
// };
// fn void sha1_reset(Sha1Context* ctx)
// {
// ctx->h[0] = 0x67452301;
// ctx->h[1] = 0xefcdab89;
// ctx->h[2] = 0x98badcfe;
// ctx->h[3] = 0x10325476;
// ctx->h[4] = 0xc3d2e1f0;
// ctx->bytes = 0;
// ctx->cur = 0;
// }
// fn void sha1_process_block(Sha1Context* ctx)
// {
// global_variable const u32 k[4] =
// {
// 0x5A827999,
// 0x6ED9EBA1,
// 0x8F1BBCDC,
// 0xCA62C1D6
// };
//
// u32 w[16];
// u32 a = ctx->h[0];
// u32 b = ctx->h[1];
// u32 c = ctx->h[2];
// u32 d = ctx->h[3];
// u32 e = ctx->h[4];
// u32 t;
//
// for (t = 0; t < 16; t++)
// w[t] = sha1_get32((u8*)(&((uint32_t*)ctx->block)[t]));
//
// for (t = 0; t < 80; t++)
// {
// auto s = t & 0xf;
// u32 temp;
// if (t >= 16)
// w[s] = rotate_left_u32(w[(s + 13) & 0xf] ^ w[(s + 8) & 0xf] ^ w[(s + 2) & 0xf] ^ w[s], 1);
//
// temp = rotate_left_u32(a, 5) + sha1_f(t, b,c,d) + e + w[s] + k[t/20];
//
// e = d; d = c; c = rotate_left_u32(b, 30); b = a; a = temp;
// }
//
// ctx->h[0] += a;
// ctx->h[1] += b;
// ctx->h[2] += c;
// ctx->h[3] += d;
// ctx->h[4] += e;
// }
// fn void sha1_write(Sha1Context* ctx, String bytes)
// {
// auto length = bytes.length;
// ctx->bytes += length;
//
// const uint8_t* src = bytes.pointer;
// while (length--)
// {
// // TODO: could optimize the first and last few bytes, and then copy
// // 128 bit blocks with SIMD in between
// ctx->block[ctx->cur++] = *src++;
// if (ctx->cur == 64)
// {
// sha1_process_block(ctx);
// ctx->cur = 0;
// }
// }
// }
// fn Sha1Digest sha1_get_digest(Sha1Context* ctx)
// {
// // append separator
// ctx->block[ctx->cur++] = 0x80;
// if (ctx->cur > 56)
// {
// // no space in block for the 64-bit message length, flush
// memset(&ctx->block[ctx->cur], 0, 64 - ctx->cur);
// sha1_process_block(ctx);
// ctx->cur = 0;
// }
//
// memset(&ctx->block[ctx->cur], 0, 56 - ctx->cur);
// uint64_t bits = ctx->bytes * 8;
//
// // TODO a few instructions could be shaven
// ctx->block[56] = (uint8_t)(bits >> 56 & 0xff);
// ctx->block[57] = (uint8_t)(bits >> 48 & 0xff);
// ctx->block[58] = (uint8_t)(bits >> 40 & 0xff);
// ctx->block[59] = (uint8_t)(bits >> 32 & 0xff);
// ctx->block[60] = (uint8_t)(bits >> 24 & 0xff);
// ctx->block[61] = (uint8_t)(bits >> 16 & 0xff);
// ctx->block[62] = (uint8_t)(bits >> 8 & 0xff);
// ctx->block[63] = (uint8_t)(bits >> 0 & 0xff);
// sha1_process_block(ctx);
//
// {
// Sha1Digest ret;
// int i;
// for (i = 0; i < 5; i++)
// ret.digest[i] = sha1_get32((u8*)&ctx->h[i]);
// sha1_reset(ctx);
// return ret;
// }
// }
// fn Sha1Digest sha1_compute(String bytes)
// {
// Sha1Context ctx;
// sha1_reset(&ctx);
// sha1_write(&ctx, bytes);
// return sha1_get_digest(&ctx);
// }

View File

View File

@ -1,43 +0,0 @@
#version 450
#extension GL_EXT_nonuniform_qualifier : require
#extension GL_EXT_debug_printf : require
#extension GL_GOOGLE_include_directive : require
#include "rect.inc"
layout (location = 0) in flat uint texture_index;
layout (location = 1) in RectFragmentShaderInput inputs;
layout (location = 0) out vec4 color;
layout(set = 0, binding = 0) uniform sampler2D textures[];
float rounded_rect_sdf(vec2 position, vec2 center, vec2 half_size, float radius)
{
vec2 r2 = vec2(radius, radius);
// This is 0 when the point is at the border
vec2 d2_no_r2 = abs(center - position) - half_size;
vec2 d2 = d2_no_r2 + r2;
// 0 when outside the rectangle
float negative_euclidean_distance = min(max(d2.x, d2.y), 0.0);
// 0 when inside the rectangle
float positive_euclidean_distance = length(max(d2, 0.0));
float result = negative_euclidean_distance + positive_euclidean_distance - radius;
return result;
}
void main()
{
// WARN: do not cache nonuniformEXT indexing
vec2 texture_size = textureSize(textures[nonuniformEXT(texture_index)], 0);
vec2 uv = vec2(inputs.uv.x / texture_size.x, inputs.uv.y / texture_size.y);
// WARN: do not cache nonuniformEXT indexing
vec4 sampled = texture(textures[nonuniformEXT(texture_index)], uv);
float softness = inputs.softness;
float softness_padding_scalar = max(0, softness * 2 - 1);
vec2 softness_padding = vec2(softness_padding_scalar, softness_padding_scalar);
float distance = rounded_rect_sdf(inputs.position, inputs.center, inputs.half_size - softness_padding, inputs.corner_radius);
float sdf_factor = 1.0 - smoothstep(0, 2 * softness, distance);
color = inputs.color * sampled * sdf_factor;
}

View File

@ -1,22 +0,0 @@
struct RectVertex
{
vec2 p0;
vec2 uv0;
vec2 extent;
float corner_radius;
float softness;
vec4 colors[4];
uint texture_index;
uint reserved[3];
};
struct RectFragmentShaderInput
{
vec4 color;
vec2 uv;
vec2 position;
vec2 center;
vec2 half_size;
float corner_radius;
float softness;
};

View File

@ -1,122 +0,0 @@
#include <metal_stdlib>
using namespace metal;
// Structs to match buffer data
struct RectVertex
{
float2 p0; // Bottom-left corner position
float2 extent; // Rectangle size (width and height)
float2 uv0; // UV coordinates for bottom-left
float corner_radius; // Corner radius
float softness; // Edge softness
float4 colors[4]; // Per-vertex colors
uint texture_index; // Texture index
};
struct RectFragmentShaderInput
{
float4 vertex_position [[position]];
float4 color; // Vertex color
float2 uv; // UV coordinates
float2 position; // Position in world space
float2 center; // Center of the rectangle
float2 half_size; // Half dimensions of the rectangle
float corner_radius; // Corner radius
float softness; // Edge softness
};
// Push constants (Metal uses constant buffers or argument buffers for this)
struct PushConstants
{
device const RectVertex* vertex_buffer;
float width; // Screen width
float height; // Screen height
};
// Vertex shader main function
vertex RectFragmentShaderInput vertex_main(const device PushConstants& push_constants [[buffer(0)]], uint vertex_id [[vertex_id]])
{
// Constants for normalized quad corners
constexpr float2 vertices[] = {
float2(-1.0, -1.0),
float2( 1.0, -1.0),
float2(-1.0, 1.0),
float2( 1.0, 1.0)
};
// Fetch the vertex data from the buffer
const RectVertex v = push_constants.vertex_buffer[vertex_id / 4];
// Rectangle calculations
float2 p0 = v.p0;
float2 p1 = p0 + v.extent;
float2 position_center = (p1 + p0) * 0.5;
float2 half_size = (p1 - p0) * 0.5;
float2 position = vertices[vertex_id % 4] * half_size + position_center;
// Screen-space transformation
float2 ndc_position = float2(2.0 * position.x / push_constants.width - 1.0,
2.0 * position.y / push_constants.height - 1.0);
// Texture UV calculations
float2 uv0 = v.uv0;
float2 uv1 = uv0 + v.extent;
float2 texture_center = (uv1 + uv0) * 0.5;
float2 uv = vertices[vertex_id % 4] * half_size + texture_center;
// Output to fragment shader
RectFragmentShaderInput out_data;
out_data.color = v.colors[vertex_id % 4];
out_data.uv = uv;
out_data.position = position;
out_data.vertex_position = float4(ndc_position.x, ndc_position.y, 0, 1);
out_data.center = position_center;
out_data.half_size = half_size;
out_data.corner_radius = v.corner_radius;
out_data.softness = v.softness;
return out_data;
}
// Uniform buffer for textures
struct FragmentUniforms
{
array<texture2d<float>, 100> textures; // Array of textures
sampler texture_sampler; // Sampler for texture sampling
};
// Calculate the signed distance field (SDF) for a rounded rectangle
float rounded_rect_sdf(float2 position, float2 center, float2 half_size, float radius)
{
float2 r2 = float2(radius, radius);
float2 d2_no_r2 = abs(center - position) - half_size;
float2 d2 = d2_no_r2 + r2;
float negative_euclidean_distance = min(max(d2.x, d2.y), 0.0);
float positive_euclidean_distance = length(max(d2, 0.0));
return negative_euclidean_distance + positive_euclidean_distance - radius;
}
// Fragment shader function
fragment float4 fragment_main(RectFragmentShaderInput inputs [[stage_in]], constant FragmentUniforms &uniforms [[buffer(0)]], uint texture_index)
{
// Texture size
float2 texture_size = float2(uniforms.textures[texture_index].get_width(), uniforms.textures[texture_index].get_height());
float2 uv = float2(inputs.uv.x / texture_size.x, inputs.uv.y / texture_size.y);
// Sample texture
float4 sampled = uniforms.textures[texture_index].sample(uniforms.texture_sampler, uv);
// Compute softness padding
float softness = inputs.softness;
float softness_padding_scalar = max(0.0, softness * 2.0 - 1.0);
float2 softness_padding = float2(softness_padding_scalar, softness_padding_scalar);
// Compute signed distance
float distance = rounded_rect_sdf(inputs.position, inputs.center, inputs.half_size - softness_padding, inputs.corner_radius);
// Compute SDF factor
float sdf_factor = 1.0 - smoothstep(0.0, 2.0 * softness, distance);
// Compute final color
return inputs.color * sampled * sdf_factor;
}

View File

@ -1,61 +0,0 @@
#version 450
#extension GL_EXT_buffer_reference : require
#extension GL_EXT_debug_printf : require
#extension GL_GOOGLE_include_directive : require
#include "rect.inc"
layout (location = 0) out uint texture_index;
layout (location = 1) out RectFragmentShaderInput outputs;
layout(buffer_reference, std430) readonly buffer VertexBuffer{
RectVertex vertices[];
};
layout(push_constant) uniform constants
{
VertexBuffer vertex_buffer;
float width;
float height;
} PushConstants;
const vec2 vertices[] = {
{ -1, -1 },
{ +1, -1 },
{ -1, +1 },
{ +1, +1 },
};
void main()
{
RectVertex v = PushConstants.vertex_buffer.vertices[gl_VertexIndex];
uint vertex_id = gl_VertexIndex % 4;
float width = PushConstants.width;
float height = PushConstants.height;
vec2 extent = v.extent;
vec2 p0 = v.p0;
vec2 p1 = p0 + extent;
vec2 position_center = (p1 + p0) / 2;
vec2 half_size = (p1 - p0) / 2;
vec2 position = vertices[vertex_id] * half_size + position_center;
//debugPrintfEXT("Vertex index: (%u). P0: (%f, %f). P1: (%f, %f). Center: (%f, %f). Half size: (%f, %f). Position: (%f, %f)\n", gl_VertexIndex, p0.x, p0.y, p1.x, p1.y, center.x, center.y, half_size.x, half_size.y, position.x, position.y);
gl_Position = vec4(2 * position.x / width - 1, 2 * position.y / height - 1, 0, 1);
vec2 uv0 = v.uv0;
vec2 uv1 = uv0 + extent;
vec2 texture_center = (uv1 + uv0) / 2;
vec2 uv = vertices[vertex_id] * half_size + texture_center;
texture_index = v.texture_index;
outputs.color = v.colors[vertex_id];
outputs.uv = uv;
outputs.position = position;
outputs.center = position_center;
outputs.half_size = half_size;
outputs.corner_radius = v.corner_radius;
outputs.softness = v.softness;
}

View File

@ -1,136 +0,0 @@
#include <std/string.h>
u64 string_first_ch(String string, u8 ch)
{
u64 result = STRING_NO_MATCH;
for (u64 i = 0; i < string.length; i += 1)
{
if (string.pointer[i] == ch)
{
result = i;
break;
}
}
return result;
}
u64 string_last_ch(String string, u8 ch)
{
u64 result = STRING_NO_MATCH;
u64 i = string.length;
while (i > 0)
{
i -= 1;
if (string.pointer[i] == ch)
{
result = i;
break;
}
}
return result;
}
u8 string_starts_with(String string, String start)
{
u8 result = 0;
if (likely(start.length <= string.length))
{
if (unlikely(start.pointer == string.pointer))
{
result = 1;
}
else
{
u64 i;
for (i = 0; i < start.length; i += 1)
{
let(start_ch, start.pointer[i]);
let(string_ch, string.pointer[i]);
if (unlikely(string_ch != start_ch))
{
break;
}
}
result = i == start.length;
}
}
return result;
}
u8 string_ends_with(String string, String end)
{
u8 result = 0;
if (likely(end.length <= string.length))
{
u64 i;
u64 offset = string.length - end.length;
for (i = 0; i < end.length; i += 1)
{
let(start_ch, end.pointer[i]);
let(string_ch, string.pointer[i + offset]);
if (unlikely(string_ch != start_ch))
{
break;
}
}
result = i == end.length;
}
return result;
}
u64 string_first_occurrence(String string, String substring)
{
u64 result = STRING_NO_MATCH;
if (substring.length < string.length)
{
for (u64 i = 0; i < string.length; i += 1)
{
if ((string.length - i) < substring.length)
{
break;
}
String s = s_get_slice(u8, string, i, i + substring.length);
if (s_equal(s, substring))
{
result = i;
break;
}
}
}
else if (unlikely(substring.length == string.length))
{
if (unlikely(string.pointer == substring.pointer))
{
result = 0;
}
else if (memcmp(string.pointer, substring.pointer, substring.length) == 0)
{
result = 0;
}
}
return result;
}
fn u64 string_last_occurrence(String string, String substring)
{
unused(string);
unused(substring);
todo();
}
fn u8 string_contains(String string, String substring)
{
return string_first_occurrence(string, substring) != STRING_NO_MATCH;
}

View File

@ -1,10 +0,0 @@
#include <std/base.h>
#define STRING_NO_MATCH UINT64_MAX
fn u64 string_first_ch(String string, u8 ch);
fn u64 string_last_ch(String string, u8 ch);
fn u8 string_starts_with(String string, String start);
fn u8 string_ends_with(String string, String end);
fn u64 string_first_occurrence(String string, String substring);
fn u64 string_last_occurrence(String string, String substring);

View File

@ -1,14 +0,0 @@
#pragma once
fn UI_Signal ui_button(String string)
{
let(widget, ui_widget_make((UI_WidgetFlags) {
.draw_text = 1,
.draw_background = 1,
.mouse_clickable = 1,
.keyboard_pressable = 1,
}, string));
UI_Signal signal = ui_signal_from_widget(widget);
return signal;
}

View File

@ -1,6 +0,0 @@
#pragma once
#include <std/base.h>
#include <std/ui_core.h>
fn UI_Signal ui_button(String string);

View File

@ -1,810 +0,0 @@
// This UI is heavily inspired by the ideas of Casey Muratori and Ryan Fleury ideas on GUI programming, to whom I am deeply grateful.
// Here are some links which helped me achieve this build
// https://www.youtube.com/watch?v=Z1qyvQsjK5Y
// https://www.rfleury.com/p/ui-part-1-the-interaction-medium
// https://www.rfleury.com/p/ui-part-2-build-it-every-frame-immediate
// https://www.rfleury.com/p/ui-part-3-the-widget-building-language
// https://www.rfleury.com/p/ui-part-4-the-widget-is-a-lie-node
// https://www.rfleury.com/p/ui-part-5-visual-content
// https://www.rfleury.com/p/ui-part-6-rendering
// https://www.rfleury.com/p/ui-part-7-where-imgui-ends
// https://www.rfleury.com/p/ui-part-8-state-mutation-jank-and
// https://www.rfleury.com/p/ui-part-9-keyboard-and-gamepad-navigation
// https://www.rfleury.com/p/ui-bonus-1-simple-single-line-text
// https://www.rfleury.com/p/codebase-walkthrough-multi-window
#pragma once
global_variable UI_State* ui_state = 0;
fn void ui_autopop(UI_State* state)
{
let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
let(bitset_pointer, (u64*)&state->stack_autopops);
u64 bitset_index = 0;
for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{
let(bitset, *bitset_pointer);
let(shift_value, 1 << bitset_index);
let(autopop, (bitset & shift_value) != 0);
let(mask, ~shift_value);
*bitset_pointer = bitset & mask;
let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
let(current_length, *length_pointer);
assert(!autopop | current_length);
*length_pointer -= autopop;
u64 increment_bitset_element = (bitset_index > 0) & (bitset_index % 64 == 0);
bitset_pointer += increment_bitset_element;
bitset_index = increment_bitset_element ? 0 : bitset_index + 1;
}
}
fn void ui_state_select(UI_State* state)
{
ui_state = state;
}
fn UI_State* ui_state_get()
{
return ui_state;
}
fn Arena* ui_build_arena()
{
let(arena, ui_state->build_arenas[ui_state->build_count % array_length(ui_state->build_arenas)]);
return arena;
}
fn UI_Key ui_key_null()
{
UI_Key key = {};
return key;
}
fn UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window)
{
Arena* arena = arena_initialize(GB(8), MB(2), MB(2));
UI_State* state = arena_allocate(arena, UI_State, 1);
state->renderer = renderer;
state->render_window = window;
state->arena = arena;
state->widget_table.length = 4096;
state->widget_table.pointer = arena_allocate(arena, UI_WidgetSlot, state->widget_table.length);
for (u64 i = 0; i < array_length(state->build_arenas); i += 1)
{
state->build_arenas[i] = arena_initialize(GB(8), MB(2), MB(2));
}
state->stack_nulls = (UI_StateStackNulls){
.parent = 0,
.child_layout_axis = AXIS2_COUNT,
.pref_width = {},
.pref_height = {},
};
let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{
let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
assert(*length_pointer == 0);
}
return state;
}
fn u64 ui_widget_index_from_key(UI_Key key)
{
let(length, ui_state->widget_table.length);
assert(is_power_of_two(length));
return key.value & (length - 1);
}
fn String ui_text_from_key_string(String string)
{
String result = string;
String text_end_delimiter = strlit("##");
let(index, string_first_occurrence(string, text_end_delimiter));
if (index < string.length)
{
result.length = index;
}
return result;
}
fn String ui_hash_from_key_string(String string)
{
String result = string;
String hash_start_delimiter = strlit("###");
let(index, string_first_occurrence(string, hash_start_delimiter));
if (index < string.length)
{
result = s_get_slice(u8, string, index, string.length);
}
return result;
}
fn UI_Key ui_key_from_string(UI_Key seed, String string)
{
UI_Key key = ui_key_null();
if (string.length)
{
key = seed;
for (u64 i = 0; i < string.length; i += 1)
{
key.value = ((key.value << 5) + key.value) + string.pointer[i];
}
}
return key;
}
fn UI_Key ui_key_from_string_format(UI_Key seed, char* format, ...)
{
u8 buffer[256];
va_list args;
va_start(args, format);
let(string, format_string_va((String)array_to_slice(buffer), format, args));
va_end(args);
let(result, ui_key_from_string(seed, string));
return result;
}
fn u8 ui_key_equal(UI_Key a, UI_Key b)
{
return a.value == b.value;
}
fn UI_Widget* ui_widget_from_key(UI_Key key)
{
UI_Widget* result = 0;
if (!ui_key_equal(key, ui_key_null()))
{
let(index, ui_widget_index_from_key(key));
for (UI_Widget* widget = ui_state->widget_table.pointer[index].first; widget; widget = widget->hash_next)
{
if (ui_key_equal(widget->key, key))
{
result = widget;
break;
}
}
}
return result;
}
fn UI_Widget* ui_widget_make_from_key(UI_WidgetFlags flags, UI_Key key)
{
let(widget, ui_widget_from_key(key));
static let(count, 0);
count += 1;
if (widget)
{
if (widget->last_build_touched == ui_state->build_count)
{
key = ui_key_null();
widget = 0;
}
}
u8 first_frame = 0;
if (!widget)
{
let(index, ui_widget_index_from_key(key));
first_frame = 1;
widget = arena_allocate(ui_state->arena, UI_Widget, 1);
let(table_widget_slot, &ui_state->widget_table.pointer[index]);
if (!table_widget_slot->last)
{
table_widget_slot->first = widget;
table_widget_slot->last = widget;
}
else
{
table_widget_slot->last->hash_next = widget;
widget->hash_previous = table_widget_slot->last;
table_widget_slot->last = widget;
}
}
let(parent, ui_top(parent));
if (parent)
{
if (!parent->last)
{
parent->last = widget;
parent->first = widget;
}
else
{
let(previous_last, parent->last);
previous_last->next = widget;
widget->previous = previous_last;
parent->last = widget;
}
parent->child_count += 1;
widget->parent = parent;
}
else
{
ui_state->root = widget;
}
widget->key = key;
for (u64 i = 0; i < array_length(widget->background_colors); i += 1)
{
widget->background_colors[i] = ui_top(background_color);
}
widget->text_color = ui_top(text_color);
widget->flags = flags;
widget->first = 0;
widget->last = 0;
widget->last_build_touched = ui_state->build_count;
widget->pref_size[AXIS2_X] = ui_top(pref_width);
widget->pref_size[AXIS2_Y] = ui_top(pref_height);
widget->child_layout_axis = ui_top(child_layout_axis);
ui_autopop(ui_state);
return widget;
}
fn UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string)
{
// TODO:
let(seed, ui_key_null());
let(hash_string, ui_hash_from_key_string(string));
let(key, ui_key_from_string(seed, hash_string));
let(widget, ui_widget_make_from_key(flags, key));
if (flags.draw_text)
{
widget->text = ui_text_from_key_string(string);
}
return widget;
}
fn UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...)
{
va_list args;
u8 buffer[4096];
va_start(args, format);
let(string, format_string_va((String)array_to_slice(buffer), format, args));
va_end(args);
let(result, ui_widget_make(flags, string));
return result;
}
fn UI_Signal ui_signal_from_widget(UI_Widget* widget)
{
let(rect, widget->rect);
let(mouse_position, ui_state->mouse_position);
UI_Signal signal = {
.clicked_left =
(widget->flags.mouse_clickable & (ui_state->mouse_button_events[WINDOWING_EVENT_MOUSE_LEFT].action == WINDOWING_EVENT_MOUSE_RELEASE)) &
((mouse_position.x >= rect.x0) & (mouse_position.x <= rect.x1)) &
((mouse_position.y >= rect.y0) & (mouse_position.y <= rect.y1)),
};
return signal;
}
fn void ui_stack_reset(UI_State* state)
{
let(stack_end, (u32*)((u8*)&state->stacks + sizeof(state->stacks)));
for (let(stack_pointer, (u32*)&state->stacks); stack_pointer != stack_end; stack_pointer += sizeof(VirtualBuffer(u8)) / sizeof(u32))
{
let(length_pointer, stack_pointer + (offsetof(VirtualBuffer(u8), length) / sizeof(u32)));
*length_pointer = 0;
}
}
fn UI_Size ui_pixels(u32 width, f32 strictness)
{
return (UI_Size) {
.kind = UI_SIZE_PIXEL_COUNT,
.strictness = strictness,
.value = (f32)width,
};
}
fn UI_Size ui_percentage(f32 percentage, f32 strictness)
{
return (UI_Size) {
.kind = UI_SIZE_PERCENTAGE,
.strictness = strictness,
.value = percentage,
};
}
fn UI_Size ui_em(f32 value, f32 strictness)
{
let(font_size, ui_top(font_size));
assert(font_size);
return (UI_Size) {
.kind = UI_SIZE_PIXEL_COUNT,
.strictness = strictness,
.value = value * font_size,
};
}
fn u8 ui_build_begin(WindowingInstance* window, f64 frame_time, WindowingEventQueue* event_queue)
{
ui_state->build_count += 1;
let(build_arena, ui_build_arena());
arena_reset(build_arena);
ui_state->frame_time = frame_time;
ui_state->window = window;
ui_stack_reset(ui_state);
u8 open = 1;
let(mouse_button_count, 0);
for (u32 generic_event_index = 0; open & (generic_event_index < event_queue->descriptors.length); generic_event_index += 1)
{
let(event_descriptor, event_queue->descriptors.pointer[generic_event_index]);
u32 event_index = event_descriptor.index;
switch (event_descriptor.type)
{
case WINDOWING_EVENT_TYPE_MOUSE_BUTTON:
{
let(button, event_queue->mouse_buttons.pointer[event_index]);
let(previous_button_event, ui_state->mouse_button_events[button.button]);
switch (button.event.action)
{
case WINDOWING_EVENT_MOUSE_RELAX:
unreachable();
case WINDOWING_EVENT_MOUSE_RELEASE:
{
assert(previous_button_event.action == WINDOWING_EVENT_MOUSE_PRESS);
} break;
case WINDOWING_EVENT_MOUSE_PRESS:
{
// TODO: handle properly
assert(previous_button_event.action == WINDOWING_EVENT_MOUSE_RELAX || mouse_button_count);
} break;
case WINDOWING_EVENT_MOUSE_REPEAT:
{
unreachable();
} break;
}
ui_state->mouse_button_events[button.button] = button.event;
mouse_button_count += 1;
} break;
case WINDOWING_EVENT_TYPE_WINDOW_FOCUS:
{
} break;
case WINDOWING_EVENT_TYPE_CURSOR_POSITION:
{
let(mouse_position, event_queue->cursor_positions.pointer[event_index]);
ui_state->mouse_position = (UI_MousePosition) {
.x = mouse_position.x,
.y = mouse_position.y,
};
} break;
case WINDOWING_EVENT_TYPE_CURSOR_ENTER:
{
todo();
} break;
case WINDOWING_EVENT_TYPE_WINDOW_POSITION:
{
// event_queue->window_positions.pointer[event_index];
// todo();
} break;
case WINDOWING_EVENT_TYPE_WINDOW_CLOSE:
{
open = 0;
} break;
}
}
if (open)
{
for (u64 i = 0; i < ui_state->widget_table.length; i += 1)
{
let(widget_table_element, &ui_state->widget_table.pointer[i]);
for (UI_Widget* widget = widget_table_element->first, *next = 0; widget; widget = next)
{
next = widget->hash_next;
if (ui_key_equal(widget->key, ui_key_null()) || widget->last_build_touched + 1 < ui_state->build_count)
{
// Remove from the list
if (widget->hash_previous)
{
widget->hash_previous->hash_next = widget->hash_next;
}
if (widget->hash_next)
{
widget->hash_next->hash_previous = widget->hash_previous;
}
if (widget_table_element->first == widget)
{
widget_table_element->first = widget->hash_next;
}
if (widget_table_element->last == widget)
{
widget_table_element->last = widget->hash_previous;
}
}
}
}
let(framebuffer_size, windowing_get_instance_framebuffer_size(window));
ui_push_next_only(pref_width, ui_pixels(framebuffer_size.width, 1.0f));
ui_push_next_only(pref_height, ui_pixels(framebuffer_size.height, 1.0f));
ui_push_next_only(child_layout_axis, AXIS2_Y);
let(root, ui_widget_make_format((UI_WidgetFlags) {}, "window_root_{u64}", window));
assert(!ui_state->stack_autopops.child_layout_axis);
ui_push(parent, root);
ui_push(font_size, 12);
ui_push(text_color, VEC4(0.9, 0.9, 0.02, 1));
ui_push(background_color, VEC4(0.1, 0.1, 0.1, 1));
ui_push(pref_width, ui_percentage(1.0, 0.0));
ui_push(pref_height, ui_percentage(1.0, 0.0));
// ui_push(pref_height, ui_em(1.8, 0.0));
}
return open;
}
fn void ui_compute_independent_sizes(UI_Widget* widget)
{
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{
let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind)
{
default: break; case UI_SIZE_COUNT: unreachable();
case UI_SIZE_PIXEL_COUNT:
{
#if BB_HAS_NATIVE_FLOAT2
widget->computed_size[axis] = floorf(widget->pref_size[axis].value);
#else
widget->computed_size.v[axis] = floorf(widget->pref_size[axis].value);
#endif
} break;
}
}
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
ui_compute_independent_sizes(child_widget);
}
}
fn void ui_compute_upward_dependent_sizes(UI_Widget* widget)
{
// TODO: optimize loop out if possible
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{
let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind)
{
default: break; case UI_SIZE_COUNT: unreachable();
case UI_SIZE_PERCENTAGE:
{
for (UI_Widget* ancestor = widget->parent; ancestor; ancestor = ancestor->parent)
{
if (ancestor->pref_size[axis].kind != UI_SIZE_BY_CHILDREN)
{
#if BB_HAS_NATIVE_FLOAT2
widget->computed_size[axis] = floorf(ancestor->computed_size[axis] * widget->pref_size[axis].value);
#else
widget->computed_size.v[axis] = floorf(ancestor->computed_size.v[axis] * widget->pref_size[axis].value);
#endif
break;
}
}
} break;
}
}
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
ui_compute_upward_dependent_sizes(child_widget);
}
}
fn void ui_compute_downward_dependent_sizes(UI_Widget* widget)
{
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
ui_compute_downward_dependent_sizes(child_widget);
}
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{
let(pref_size, widget->pref_size[axis]);
switch (pref_size.kind)
{
default: break; case UI_SIZE_COUNT: unreachable();
case UI_SIZE_BY_CHILDREN:
{
todo();
} break;
}
}
}
fn void ui_resolve_conflicts(UI_Widget* widget)
{
for (Axis2 axis = 0; axis < AXIS2_COUNT; axis += 1)
{
#if BB_HAS_NATIVE_FLOAT2
let(available_space, widget->computed_size[axis]);
#else
let(available_space, widget->computed_size.v[axis]);
#endif
f32 taken_space = 0;
f32 total_fixup_budget = 0;
if (!(widget->flags.v & (UI_WIDGET_FLAG_OVERFLOW_X << axis)))
{
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
if (axis == widget->child_layout_axis)
{
#if BB_HAS_NATIVE_FLOAT2
taken_space += child_widget->computed_size[axis];
#else
taken_space += child_widget->computed_size.v[axis];
#endif
}
else
{
#if BB_HAS_NATIVE_FLOAT2
taken_space = MAX(taken_space, child_widget->computed_size[axis]);
#else
taken_space = MAX(taken_space, child_widget->computed_size.v[axis]);
#endif
}
#if BB_HAS_NATIVE_FLOAT2
let(fixup_budget_this_child, child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness));
#else
let(fixup_budget_this_child, child_widget->computed_size.v[axis] * (1 - child_widget->pref_size[axis].strictness));
#endif
total_fixup_budget += fixup_budget_this_child;
}
}
let(conflict, taken_space - available_space);
if (conflict > 0 && total_fixup_budget > 0)
{
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
#if BB_HAS_NATIVE_FLOAT2
let(fixup_budget_this_child, child_widget->computed_size[axis] * (1 - child_widget->pref_size[axis].strictness));
#else
let(fixup_budget_this_child, child_widget->computed_size.v[axis] * (1 - child_widget->pref_size[axis].strictness));
#endif
f32 fixup_size_this_child = 0;
if (axis == widget->child_layout_axis)
{
fixup_size_this_child = fixup_budget_this_child * (conflict / total_fixup_budget);
}
else
{
#if BB_HAS_NATIVE_FLOAT2
fixup_size_this_child = child_widget->computed_size[axis] - available_space;
#else
fixup_size_this_child = child_widget->computed_size.v[axis] - available_space;
#endif
}
fixup_size_this_child = CLAMP(0, fixup_size_this_child, fixup_budget_this_child);
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_size[axis] = floorf(child_widget->computed_size[axis] - fixup_size_this_child);
#else
child_widget->computed_size.v[axis] = floorf(child_widget->computed_size.v[axis] - fixup_size_this_child);
#endif
}
}
}
}
if (axis == widget->child_layout_axis)
{
f32 p = 0;
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_relative_position[axis] = p;
p += child_widget->computed_size[axis];
#else
child_widget->computed_relative_position.v[axis] = p;
p += child_widget->computed_size.v[axis];
#endif
}
}
}
else
{
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
#if BB_HAS_NATIVE_FLOAT2
child_widget->computed_relative_position[axis] = 0;
#else
child_widget->computed_relative_position.v[axis] = 0;
#endif
}
}
}
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
let(last_relative_rect, child_widget->relative_rect);
#if BB_HAS_NATIVE_FLOAT2
child_widget->relative_rect.p0[axis] = child_widget->computed_relative_position[axis];
child_widget->relative_rect.p1[axis] = child_widget->relative_rect.p0[axis] + child_widget->computed_size[axis];
#else
child_widget->relative_rect.p0.v[axis] = child_widget->computed_relative_position.v[axis];
child_widget->relative_rect.p1.v[axis] = child_widget->relative_rect.p0.v[axis] + child_widget->computed_size.v[axis];
#endif
float2 last_corner_01 = { last_relative_rect.x0, last_relative_rect.y1 };
float2 last_corner_10 = { last_relative_rect.x1, last_relative_rect.y0 };
float2 this_corner_01 = { child_widget->relative_rect.x0, child_widget->relative_rect.y1 };
float2 this_corner_10 = { child_widget->relative_rect.x1, child_widget->relative_rect.y0 };
#if BB_HAS_NATIVE_FLOAT2
child_widget->relative_corner_delta[CORNER_00][axis] = child_widget->relative_rect.p0[axis] - last_relative_rect.p0[axis];
child_widget->relative_corner_delta[CORNER_01][axis] = this_corner_01[axis] - last_corner_01[axis];
child_widget->relative_corner_delta[CORNER_10][axis] = this_corner_10[axis] - last_corner_10[axis];
child_widget->relative_corner_delta[CORNER_11][axis] = child_widget->relative_rect.p1[axis] - last_relative_rect.p1[axis];
child_widget->rect.p0[axis] = widget->rect.p0[axis] + child_widget->relative_rect.p0[axis] - widget->view_offset[axis];
child_widget->rect.p1[axis] = child_widget->rect.p0[axis] + child_widget->computed_size[axis];
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
child_widget->rect.p0[axis] = floorf(child_widget->rect.p0[axis]);
child_widget->rect.p1[axis] = floorf(child_widget->rect.p1[axis]);
}
#else
child_widget->relative_corner_delta[CORNER_00].v[axis] = child_widget->relative_rect.p0.v[axis] - last_relative_rect.p0.v[axis];
child_widget->relative_corner_delta[CORNER_01].v[axis] = this_corner_01.v[axis] - last_corner_01.v[axis];
child_widget->relative_corner_delta[CORNER_10].v[axis] = this_corner_10.v[axis] - last_corner_10.v[axis];
child_widget->relative_corner_delta[CORNER_11].v[axis] = child_widget->relative_rect.p1.v[axis] - last_relative_rect.p1.v[axis];
child_widget->rect.p0.v[axis] = widget->rect.p0.v[axis] + child_widget->relative_rect.p0.v[axis] - widget->view_offset.v[axis];
child_widget->rect.p1.v[axis] = child_widget->rect.p0.v[axis] + child_widget->computed_size.v[axis];
if (!(child_widget->flags.v & (UI_WIDGET_FLAG_FLOATING_X << axis)))
{
child_widget->rect.p0.v[axis] = floorf(child_widget->rect.p0.v[axis]);
child_widget->rect.p1.v[axis] = floorf(child_widget->rect.p1.v[axis]);
}
#endif
}
for (UI_Widget* child_widget = widget->first; child_widget; child_widget = child_widget->next)
{
ui_resolve_conflicts(child_widget);
}
}
}
fn void ui_build_end()
{
// Clear release button presses
for (u32 i = 0; i < array_length(ui_state->mouse_button_events); i += 1)
{
let(event, &ui_state->mouse_button_events[i]);
if (event->action == WINDOWING_EVENT_MOUSE_RELEASE)
{
event->action = WINDOWING_EVENT_MOUSE_RELAX;
}
}
ui_pop(parent);
ui_compute_independent_sizes(ui_state->root);
ui_compute_upward_dependent_sizes(ui_state->root);
ui_compute_downward_dependent_sizes(ui_state->root);
ui_resolve_conflicts(ui_state->root);
}
STRUCT(WidgetIterator)
{
UI_Widget* next;
u32 push_count;
u32 pop_count;
};
#define ui_widget_recurse_depth_first_preorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, next), offset_of(UI_Widget, first))
#define ui_widget_recurse_depth_first_postorder(widget) ui_widget_recurse_depth_first((widget), offset_of(UI_Widget, previous), offset_of(UI_Widget, last))
fn WidgetIterator ui_widget_recurse_depth_first(UI_Widget* widget, u64 sibling_offset, u64 child_offset)
{
WidgetIterator it = {};
let(child, member_from_offset(widget, UI_Widget*, child_offset));
if (child)
{
it.next = child;
it.push_count += 1;
}
else
{
for (UI_Widget* w = widget; w; w = w->parent)
{
let(sibling, member_from_offset(w, UI_Widget*, sibling_offset));
if (sibling)
{
it.next = sibling;
break;
}
it.pop_count += 1;
}
}
return it;
}
fn void ui_draw()
{
UI_Widget* root = ui_state->root;
UI_Widget* widget = root;
RenderWindow* window = ui_state->render_window;
Renderer* renderer = ui_state->renderer;
while (widget)
{
if (widget->flags.draw_background)
{
window_render_rect(window, (RectDraw) {
.colors = { widget->background_colors[0], widget->background_colors[1], widget->background_colors[2], widget->background_colors[3] },
.vertex = widget->rect,
});
}
if (widget->flags.draw_text)
{
window_render_text(renderer, window, widget->text, widget->text_color, RENDER_FONT_TYPE_PROPORTIONAL, widget->rect.x0, widget->rect.y0);
}
widget = ui_widget_recurse_depth_first_postorder(widget).next;
}
}

View File

@ -1,223 +0,0 @@
#pragma once
ENUM(UI_SizeKind, u8,
UI_SIZE_PIXEL_COUNT,
UI_SIZE_PERCENTAGE,
UI_SIZE_BY_CHILDREN,
UI_SIZE_COUNT,
);
STRUCT(UI_Size)
{
UI_SizeKind kind;
f32 value;
f32 strictness;
};
static_assert(sizeof(UI_Size) == 12);
decl_vb(UI_Size);
STRUCT(UI_Key)
{
u64 value;
};
STRUCT(UI_MousePosition)
{
f64 x;
f64 y;
};
ENUM(UI_WidgetFlagEnum, u64,
UI_WIDGET_FLAG_DISABLED = 1 << 0,
UI_WIDGET_FLAG_MOUSE_CLICKABLE = 1 << 1,
UI_WIDGET_FLAG_KEYBOARD_PRESSABLE = 1 << 2,
UI_WIDGET_FLAG_DRAW_TEXT = 1 << 3,
UI_WIDGET_FLAG_DRAW_BACKGROUND = 1 << 4,
UI_WIDGET_FLAG_OVERFLOW_X = 1 << 5,
UI_WIDGET_FLAG_OVERFLOW_Y = 1 << 6,
UI_WIDGET_FLAG_FLOATING_X = 1 << 7,
UI_WIDGET_FLAG_FLOATING_Y = 1 << 8,
);
UNION(UI_WidgetFlags)
{
struct
{
u64 disabled:1;
u64 mouse_clickable:1;
u64 keyboard_pressable:1;
u64 draw_text:1;
u64 draw_background:1;
u64 overflow_x:1;
u64 overflow_y:1;
u64 floating_x:1;
u64 floating_y:1;
};
u64 v;
};
static_assert(sizeof(UI_WidgetFlags) == sizeof(u64));
STRUCT(UI_Widget)
{
// Random category I temporarily introduce
String text;
UI_Widget* hash_previous;
UI_Widget* hash_next;
UI_Widget* first;
UI_Widget* last;
UI_Widget* next;
UI_Widget* previous;
UI_Widget* parent;
u64 child_count;
UI_Key key;
// Input parameters
UI_Size pref_size[AXIS2_COUNT];
Axis2 child_layout_axis;
UI_WidgetFlags flags;
// Data known after size determination happens
float2 computed_size;
float2 computed_relative_position;
// Data known after layout computation happens
F32Interval2 relative_rect;
F32Interval2 rect;
float2 relative_corner_delta[CORNER_COUNT];
// Persistent data across frames
u64 last_build_touched;
float2 view_offset;
float4 background_colors[4];
float4 text_color;
};
decl_vbp(UI_Widget);
STRUCT(UI_WidgetSlot)
{
UI_Widget* first;
UI_Widget* last;
};
declare_slice(UI_WidgetSlot);
decl_vb(Axis2);
decl_vb(float4);
STRUCT(UI_StateStackAutoPops)
{
u64 parent:1;
u64 pref_width:1;
u64 pref_height:1;
u64 child_layout_axis:1;
u64 text_color:1;
u64 background_color:1;
u64 font_size:1;
};
static_assert(sizeof(UI_StateStackAutoPops) % sizeof(u64) == 0);
STRUCT(UI_StateStackNulls)
{
UI_Widget* parent;
UI_Size pref_width;
UI_Size pref_height;
Axis2 child_layout_axis;
float4 text_color;
float4 background_color;
f32 font_size;
};
STRUCT(UI_StateStacks)
{
VirtualBufferP(UI_Widget) parent;
VirtualBuffer(UI_Size) pref_width;
VirtualBuffer(UI_Size) pref_height;
VirtualBuffer(Axis2) child_layout_axis;
VirtualBuffer(float4) text_color;
VirtualBuffer(float4) background_color;
VirtualBuffer(f32) font_size;
};
STRUCT(UI_State)
{
Arena* arena;
Arena* build_arenas[2];
Renderer* renderer;
RenderWindow* render_window;
WindowingInstance* window;
u64 build_count;
f64 frame_time;
UI_Widget* root;
UI_MousePosition mouse_position;
Slice(UI_WidgetSlot) widget_table;
UI_Widget* free_widget_list;
u64 free_widget_count;
WindowingEventMouseButtonEvent mouse_button_events[WINDOWING_EVENT_MOUSE_BUTTON_COUNT];
u8 focused:1;
UI_StateStacks stacks;
UI_StateStackNulls stack_nulls;
UI_StateStackAutoPops stack_autopops;
};
enum
{
UI_SignalFlag_ClickedLeft = (1 << 0),
};
typedef u32 UI_SignalFlags;
STRUCT(UI_Signal)
{
UI_Widget* widget;
union
{
UI_SignalFlags flags;
struct
{
u32 clicked_left:1;
u32 reserved:31;
};
};
};
#define ui_stack_autopop_set(field_name, value) ui_state->stack_autopops.field_name = (value)
#define ui_stack_push_impl(field_name, value, auto_pop_value) do \
{\
*vb_add(&ui_state->stacks.field_name, 1) = (value);\
ui_stack_autopop_set(field_name, auto_pop_value);\
} while (0)
#define ui_push(field_name, value) ui_stack_push_impl(field_name, value, 0)
#define ui_push_next_only(field_name, value) ui_stack_push_impl(field_name, value, 1)
#define ui_pop(field_name) (typeof(ui_state->stacks.field_name.pointer)) ui_pop_generic((VirtualBuffer(u8)*)&ui_state->stacks.field_name, sizeof(*ui_state->stacks.field_name.pointer))
#define ui_top(field_name) (ui_state->stacks.field_name.length ? ui_state->stacks.field_name.pointer[ui_state->stacks.field_name.length - 1] : ui_state->stack_nulls.field_name)
fn u8* ui_pop_generic(VirtualBuffer(u8)* stack, u32 element_size)
{
let(length, stack->length);
assert(length > 0);
let(next_length, length - 1);
let(index, next_length);
let(result, &stack->pointer[index * element_size]);
stack->length = next_length;
return result;
}
fn UI_State* ui_state_allocate(Renderer* renderer, RenderWindow* window);
fn void ui_state_select(UI_State* state);
fn u8 ui_build_begin(WindowingInstance* window, f64 frame_time, WindowingEventQueue* event_queue);
fn void ui_build_end();
fn void ui_draw();
fn UI_Signal ui_signal_from_widget(UI_Widget* widget);
fn UI_State* ui_state_get();
fn UI_Widget* ui_widget_make(UI_WidgetFlags flags, String string);
fn UI_Widget* ui_widget_make_format(UI_WidgetFlags flags, const char* format, ...);
fn UI_Size ui_pixels(u32 width, f32 strictness);
fn UI_Size ui_percentage(f32 percentage, f32 strictness);
fn UI_Size ui_em(f32 value, f32 strictness);

View File

@ -1,88 +0,0 @@
#pragma once
fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{
u32 old_capacity = vb->capacity;
u32 wanted_capacity = vb->length + item_count;
if (old_capacity < wanted_capacity)
{
if (old_capacity == 0)
{
vb->pointer = os_reserve(0, item_size * UINT32_MAX, (OSReserveProtectionFlags) {}, (OSReserveMapFlags) { .priv = 1, .anon = 1, .noreserve = 1 });
}
let_cast(u32, old_page_capacity, align_forward_u64(old_capacity * item_size, minimum_granularity));
let_cast(u32, new_page_capacity, align_forward_u64(wanted_capacity * item_size, minimum_granularity));
let(commit_size, new_page_capacity - old_page_capacity);
void* commit_pointer = vb->pointer + old_page_capacity;
os_commit(commit_pointer, commit_size);
let(new_capacity, new_page_capacity / item_size);
vb->capacity = new_capacity;
}
}
fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{
u32 index = vb->length;
assert(vb->capacity >= index + item_count);
vb->length = index + item_count;
return vb->pointer + (index * item_size);
}
fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
{
vb_generic_ensure_capacity(vb, item_size, item_count);
return vb_generic_add_assume_capacity(vb, item_size, item_count);
}
fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes)
{
let_cast(u32, len, bytes.length);
vb_generic_ensure_capacity(vb, sizeof(u8), len);
let(pointer, vb_generic_add_assume_capacity(vb, sizeof(u8), len));
memcpy(pointer, bytes.pointer, len);
return pointer;
}
fn u32 vb_copy_string(VirtualBuffer(u8)* buffer, String string)
{
let(offset, buffer->length);
let_cast(u32, length, string.length);
let(pointer, vb_add(buffer, length));
memcpy(pointer, string.pointer, length);
return offset;
}
fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string)
{
assert(string.pointer[string.length] == 0);
string.length += 1;
vb_copy_string(buffer, string);
return string.length;
}
fn void vb_copy_byte_repeatedly(VirtualBuffer(u8)* buffer, u8 byte, u32 times)
{
u8* ptr = vb_generic_add(buffer, 1, times);
memset(ptr, byte, times);
}
fn u64 vb_format(VirtualBuffer(u8)* vb, const char* format, ...)
{
u8 buffer[4096];
va_list args;
va_start(args, format);
let(result, format_string_va((String)array_to_slice(buffer), format, args));
va_end(args);
assert(result.length <= array_length(buffer));
vb_copy_string(vb, result);
return result.length;
}

View File

@ -1,54 +0,0 @@
#pragma once
#include <std/base.h>
#define VirtualBuffer(T) VirtualBuffer_ ## T
#define VirtualBufferP(T) VirtualBufferPointerTo_ ## T
#define decl_vb_ex(T, StructName) \
struct StructName \
{\
T* pointer;\
u32 length;\
u32 capacity;\
};\
typedef struct StructName StructName
#define decl_vb(T) decl_vb_ex(T, VirtualBuffer(T))
#define decl_vbp(T) decl_vb_ex(T*, VirtualBufferP(T))
decl_vb(u8);
decl_vbp(u8);
decl_vb(u16);
decl_vbp(u16);
decl_vb(u32);
decl_vbp(u32);
decl_vb(s32);
decl_vbp(s32);
decl_vb(s64);
decl_vbp(s64);
decl_vb(String);
decl_vbp(char);
decl_vb(f32);
decl_vb(f64);
#define vb_size_of_element(vb) sizeof(*((vb)->pointer))
#define vb_add(vb, count) (typeof((vb)->pointer)) vb_generic_add((VirtualBuffer(u8)*)(vb), (vb_size_of_element(vb)), (count))
#define vb_add_scalar(vb, S) (S*) vb_generic_add(vb, 1, sizeof(S))
#define vb_copy_scalar(vb, s) *vb_add_scalar(vb, typeof(s)) = s
#define vb_append_struct(vb, T, s) *(vb_add_struct(vb, T)) = s
#define vb_append_one(vb, item) (typeof((vb)->pointer)) vb_generic_append((VirtualBuffer(u8)*)(vb), &(item), (vb_size_of_element(vb)), 1)
#define vb_to_bytes(vb) (Slice(u8)) { .pointer = (u8*)((vb).pointer), .length = (vb_size_of_element(vb)) * (vb).length, }
#define vb_ensure_capacity(vb, count) vb_generic_ensure_capacity((VirtualBuffer(u8)*)(vb), vb_size_of_element(vb), (count))
#define vb_copy_array(vb, arr) memcpy(vb_add(vb, array_length(arr)), arr, sizeof(arr))
#define vb_add_any_array(vb, E, count) (E*)vb_generic_add(vb, vb_size_of_element(vb), sizeof(E) * count)
#define vb_copy_any_array(vb, arr) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(arr)), (arr), sizeof(arr))
#define vb_copy_any_slice(vb, slice) memcpy(vb_generic_add(vb, vb_size_of_element(vb), sizeof(*((slice).pointer)) * (slice).length), (slice).pointer, sizeof(*((slice).pointer)) * (slice).length)
fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count);
fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes);
fn u32 vb_copy_string(VirtualBuffer(u8)* buffer, String string);
fn u64 vb_copy_string_zero_terminated(VirtualBuffer(u8)* buffer, String string);
fn u64 vb_format(VirtualBuffer(u8)* vb, const char* format, ...);

File diff suppressed because it is too large Load Diff

View File

@ -1,130 +0,0 @@
#pragma once
#if BB_WINDOWING_BACKEND_X11
#define VK_USE_PLATFORM_XCB_KHR
#endif
#if BB_WINDOWING_BACKEND_COCOA
#define VK_USE_PLATFORM_METAL_EXT
#endif
#if BB_WINDOWING_BACKEND_WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
// Code from Volk
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#ifndef VULKAN_H_
#if defined(VK_USE_PLATFORM_WIN32_KHR)
#include <vulkan/vk_platform.h>
#include <vulkan/vulkan_core.h>
/* When VK_USE_PLATFORM_WIN32_KHR is defined, instead of including vulkan.h directly, we include individual parts of the SDK
* This is necessary to avoid including <windows.h> which is very heavy - it takes 200ms to parse without WIN32_LEAN_AND_MEAN
* and 100ms to parse with it. vulkan_win32.h only needs a few symbols that are easy to redefine ourselves.
*/
typedef unsigned long DWORD;
typedef const wchar_t* LPCWSTR;
typedef void* HANDLE;
typedef struct HINSTANCE__* HINSTANCE;
typedef struct HWND__* HWND;
typedef struct HMONITOR__* HMONITOR;
typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES;
#include <vulkan/vulkan_win32.h>
#ifdef VK_ENABLE_BETA_EXTENSIONS
#include <vulkan/vulkan_beta.h>
#endif
#else
#include <vulkan/vulkan.h>
#endif
#endif
#define vulkan_function_pointer(n) PFN_ ## n n
#define vulkan_global_function_pointer(n) global_variable vulkan_function_pointer(n)
// INSTANCE FUNCTIONS START
// These functions require no instance
vulkan_global_function_pointer(vkGetInstanceProcAddr);
vulkan_global_function_pointer(vkEnumerateInstanceVersion);
vulkan_global_function_pointer(vkEnumerateInstanceLayerProperties);
vulkan_global_function_pointer(vkCreateInstance);
// These functions require an instance as a parameter
vulkan_global_function_pointer(vkGetDeviceProcAddr);
vulkan_global_function_pointer(vkCreateDebugUtilsMessengerEXT);
vulkan_global_function_pointer(vkEnumeratePhysicalDevices);
vulkan_global_function_pointer(vkGetPhysicalDeviceMemoryProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceQueueFamilyProperties);
vulkan_global_function_pointer(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
vulkan_global_function_pointer(vkGetPhysicalDeviceSurfacePresentModesKHR);
vulkan_global_function_pointer(vkCreateDevice);
#if defined(VK_KHR_xcb_surface)
vulkan_global_function_pointer(vkCreateXcbSurfaceKHR);
#endif
#if defined(VK_KHR_win32_surface)
vulkan_global_function_pointer(vkCreateWin32SurfaceKHR);
#endif
#if defined(VK_EXT_metal_surface)
vulkan_global_function_pointer(vkCreateMetalSurfaceEXT);
#endif
// INSTANCE FUNCTIONS END
vulkan_global_function_pointer(vkCreateSwapchainKHR);
vulkan_global_function_pointer(vkCmdCopyBuffer2);
vulkan_global_function_pointer(vkAllocateMemory);
vulkan_global_function_pointer(vkCreateBuffer);
vulkan_global_function_pointer(vkGetBufferMemoryRequirements);
vulkan_global_function_pointer(vkBindBufferMemory);
vulkan_global_function_pointer(vkMapMemory);
vulkan_global_function_pointer(vkGetBufferDeviceAddress);
vulkan_global_function_pointer(vkResetFences);
vulkan_global_function_pointer(vkResetCommandBuffer);
vulkan_global_function_pointer(vkBeginCommandBuffer);
vulkan_global_function_pointer(vkEndCommandBuffer);
vulkan_global_function_pointer(vkQueueSubmit2);
vulkan_global_function_pointer(vkWaitForFences);
vulkan_global_function_pointer(vkCreateImage);
vulkan_global_function_pointer(vkGetImageMemoryRequirements);
vulkan_global_function_pointer(vkBindImageMemory);
vulkan_global_function_pointer(vkCreateImageView);
vulkan_global_function_pointer(vkCmdPipelineBarrier2);
vulkan_global_function_pointer(vkCmdBlitImage2);
vulkan_global_function_pointer(vkGetDeviceQueue);
vulkan_global_function_pointer(vkCreateCommandPool);
vulkan_global_function_pointer(vkAllocateCommandBuffers);
vulkan_global_function_pointer(vkCreateFence);
vulkan_global_function_pointer(vkCreateSampler);
vulkan_global_function_pointer(vkCreateShaderModule);
vulkan_global_function_pointer(vkCreateDescriptorSetLayout);
vulkan_global_function_pointer(vkCreatePipelineLayout);
vulkan_global_function_pointer(vkCreateGraphicsPipelines);
vulkan_global_function_pointer(vkDestroyImageView);
vulkan_global_function_pointer(vkDestroyImage);
vulkan_global_function_pointer(vkFreeMemory);
vulkan_global_function_pointer(vkDeviceWaitIdle);
vulkan_global_function_pointer(vkDestroySwapchainKHR);
vulkan_global_function_pointer(vkGetSwapchainImagesKHR);
vulkan_global_function_pointer(vkCreateDescriptorPool);
vulkan_global_function_pointer(vkAllocateDescriptorSets);
vulkan_global_function_pointer(vkCreateSemaphore);
vulkan_global_function_pointer(vkAcquireNextImageKHR);
vulkan_global_function_pointer(vkDestroyBuffer);
vulkan_global_function_pointer(vkUnmapMemory);
vulkan_global_function_pointer(vkCmdSetViewport);
vulkan_global_function_pointer(vkCmdSetScissor);
vulkan_global_function_pointer(vkCmdBeginRendering);
vulkan_global_function_pointer(vkCmdBindPipeline);
vulkan_global_function_pointer(vkCmdBindDescriptorSets);
vulkan_global_function_pointer(vkCmdBindIndexBuffer);
vulkan_global_function_pointer(vkCmdPushConstants);
vulkan_global_function_pointer(vkCmdDrawIndexed);
vulkan_global_function_pointer(vkCmdEndRendering);
vulkan_global_function_pointer(vkQueuePresentKHR);
vulkan_global_function_pointer(vkCmdCopyBufferToImage);
vulkan_global_function_pointer(vkUpdateDescriptorSets);

View File

@ -1 +0,0 @@
#pragma once

View File

@ -1 +0,0 @@
#pragma once

View File

@ -1,59 +0,0 @@
#pragma once
#pragma comment(lib, "user32")
fn LRESULT window_callback(HWND window, UINT message, WPARAM w_parameter, LPARAM l_parameter)
{
return DefWindowProcW(window, message, w_parameter, l_parameter);
}
fn u8 windowing_initialize()
{
HINSTANCE instance = GetModuleHandleW(0);
windowing_connection.instance = instance;
WNDCLASSEXW window_class = {
.cbSize = sizeof(window_class),
.lpfnWndProc = window_callback,
.hInstance = instance,
.lpszClassName = L"window",
.hCursor = LoadCursorA(0, IDC_ARROW),
.hIcon = LoadIcon(instance, MAKEINTRESOURCE(1)),
.style = CS_VREDRAW|CS_HREDRAW,
};
RegisterClassExW(&window_class);
windowing_connection.window_class = window_class;
return 1;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate)
{
// TODO:
WindowingInstance* window = &windowing_instances[0];
window->handle = CreateWindowExW(WS_EX_APPWINDOW, L"window", L"Bloat Buster", WS_OVERLAPPEDWINDOW | WS_SIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, instantiate.size.width, instantiate.size.height, 0, 0, windowing_connection.instance, 0);
ShowWindow(window->handle, SW_SHOW);
return window;
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
RECT area;
GetClientRect(instance->handle, &area);
WindowingSize size = {
.width = area.right,
.height = area.bottom,
};
return size;
}
fn void windowing_poll_events()
{
MSG msg;
HWND handle;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}

View File

@ -1,13 +0,0 @@
#pragma once
STRUCT(WindowingConnection)
{
HINSTANCE instance;
WNDCLASSEXW window_class;
};
STRUCT(WindowingInstance)
{
HWND handle;
};

View File

@ -1,15 +0,0 @@
#if BB_WINDOWING_BACKEND_X11
#include <std/x11_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_WAYLAND
#include <std/wayland_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_COCOA
#include <std/cocoa_windowing.c>
#endif
#if BB_WINDOWING_BACKEND_WIN32
#include <std/win32_windowing.c>
#endif

View File

@ -1,206 +0,0 @@
#pragma once
#if BB_WINDOWING_BACKEND_X11
#include <std/x11_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_WAYLAND
#include <std/wayland_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_COCOA
#include <std/cocoa_windowing.h>
#endif
#if BB_WINDOWING_BACKEND_WIN32
#include <std/win32_windowing.h>
#endif
typedef enum WindowingEventType
{
WINDOWING_EVENT_TYPE_MOUSE_BUTTON,
WINDOWING_EVENT_TYPE_CURSOR_POSITION,
WINDOWING_EVENT_TYPE_CURSOR_ENTER,
WINDOWING_EVENT_TYPE_WINDOW_FOCUS,
WINDOWING_EVENT_TYPE_WINDOW_POSITION,
WINDOWING_EVENT_TYPE_WINDOW_CLOSE,
} WindowingEventType;
STRUCT(WindowingEventDescriptor)
{
u32 index:24;
WindowingEventType type:8;
};
static_assert(sizeof(WindowingEventDescriptor) == 4);
decl_vb(WindowingEventDescriptor);
ENUM_START(WindowingEventMouseButtonKind, u8)
{
WINDOWING_EVENT_MOUSE_BUTTON_1 = 0,
WINDOWING_EVENT_MOUSE_BUTTON_2 = 1,
WINDOWING_EVENT_MOUSE_BUTTON_3 = 2,
WINDOWING_EVENT_MOUSE_BUTTON_4 = 3,
WINDOWING_EVENT_MOUSE_BUTTON_5 = 4,
WINDOWING_EVENT_MOUSE_BUTTON_6 = 5,
WINDOWING_EVENT_MOUSE_BUTTON_7 = 6,
WINDOWING_EVENT_MOUSE_BUTTON_8 = 7,
WINDOWING_EVENT_MOUSE_LEFT = WINDOWING_EVENT_MOUSE_BUTTON_1,
WINDOWING_EVENT_MOUSE_RIGHT = WINDOWING_EVENT_MOUSE_BUTTON_2,
WINDOWING_EVENT_MOUSE_MIDDLE = WINDOWING_EVENT_MOUSE_BUTTON_3,
}
ENUM_END(WindowingEventMouseButtonKind);
#define WINDOWING_EVENT_MOUSE_BUTTON_COUNT (WINDOWING_EVENT_MOUSE_BUTTON_8 + 1)
ENUM_START(WindowingEventMouseButtonAction, u8)
{
WINDOWING_EVENT_MOUSE_RELAX = 0,
WINDOWING_EVENT_MOUSE_RELEASE = 1,
WINDOWING_EVENT_MOUSE_PRESS = 2,
WINDOWING_EVENT_MOUSE_REPEAT = 3,
} ENUM_END(WindowingEventMouseButtonAction);
STRUCT(WindowingEventMouseButtonEvent)
{
WindowingEventMouseButtonAction action:2;
u8 mod_shift:1;
u8 mod_control:1;
u8 mod_alt:1;
u8 mod_super:1;
u8 mod_caps_lock:1;
u8 mod_num_lock:1;
};
STRUCT(WindowingEventMouseButton)
{
WindowingEventMouseButtonKind button:3;
u8 reserved:5;
WindowingEventMouseButtonEvent event;
};
static_assert(sizeof(WindowingEventMouseButton) == sizeof(u16));
decl_vb(WindowingEventMouseButton);
#define WINDOWING_EVENT_BITSET_SIZE (64)
STRUCT(WindowingEventBitset)
{
u64 value;
};
decl_vb(WindowingEventBitset);
STRUCT(WindowingEventCursorPosition)
{
f64 x;
f64 y;
};
decl_vb(WindowingEventCursorPosition);
STRUCT(WindowingEventWindowPosition)
{
u32 x;
u32 y;
};
decl_vb(WindowingEventWindowPosition);
STRUCT(WindowingEventQueue)
{
VirtualBuffer(WindowingEventDescriptor) descriptors;
VirtualBuffer(WindowingEventMouseButton) mouse_buttons;
VirtualBuffer(WindowingEventBitset) window_focuses;
u32 window_focuses_count;
u32 cursor_enter_count;
VirtualBuffer(WindowingEventBitset) cursor_enters;
VirtualBuffer(WindowingEventCursorPosition) cursor_positions;
VirtualBuffer(WindowingEventWindowPosition) window_positions;
};
// typedef void OSFramebufferResize(OSWindow window, void* context, u32 width, u32 height);
// typedef void OSWindowResize(OSWindow window, void* context, u32 width, u32 height);
// typedef void OSWindowRefresh(OSWindow window, void* context);
// typedef void OSWindowPosition(OSWindow window, void* context, u32 x, u32 y);
// typedef void OSWindowClose(OSWindow window, void* context);
// typedef void OSWindowFocus(OSWindow window, void* context, u8 focused);
// typedef void OSWindowIconify(OSWindow window, void* context, u8 iconified);
// typedef void OSWindowMaximize(OSWindow window, void* context, u8 maximized);
// typedef void OSWindowContentScale(OSWindow window, void* context, f32 x, f32 y);
// typedef void OSWindowKey(OSWindow window, void* context, s32 key, s32 scancode, s32 action, s32 mods);
// typedef void OSWindowCharacter(OSWindow window, void* context, u32 codepoint);
// typedef void OSWindowCharacterModifier(OSWindow window, void* context, u32 codepoint, s32 mods);
// typedef void OSWindowMouseButton(OSWindow window, void* context, s32 button, s32 action, s32 mods);
// typedef void OSWindowCursorPosition(OSWindow window, void* context, f64 x, f64 y);
// typedef void OSWindowCursorEnter(OSWindow window, void* context, u8 entered);
// typedef void OSWindowScroll(OSWindow window, void* context, f64 x, f64 y);
// typedef void OSWindowDrop(OSWindow window, void* context, CStringSlice paths);
// STRUCT(OSWindowingCallbacks)
// {
// // OSFramebufferResize* framebuffer_resize;
// // OSWindowResize* window_resize;
// // OSWindowRefresh* window_refresh;
// // OSWindowPosition* window_position;
// // OSWindowClose* window_close;
// // OSWindowFocus* window_focus;
// // OSWindowIconify* window_iconify;
// // OSWindowMaximize* window_maximize;
// // OSWindowContentScale* window_content_scale;
// // OSWindowKey* window_key;
// // OSWindowCharacter* window_character;
// // OSWindowCharacterModifier* window_character_modifier;
// // OSWindowMouseButton* window_mouse_button;
// // OSWindowCursorPosition* window_cursor_position;
// // OSWindowCursorEnter* window_cursor_enter;
// // OSWindowScroll* window_scroll;
// // OSWindowDrop* window_drop;
// };
// STRUCT(OSWindowCreate)
// {
// String name;
// OSWindowSize size;
// void* context;
// // OSWindowResize* resize_callback;
// // OSWindowRefresh* refresh_callback;
// };
STRUCT(WindowingCursorPosition)
{
f64 x;
f64 y;
};
// NEW API START
STRUCT(WindowingOffset)
{
u32 x;
u32 y;
};
STRUCT(WindowingSize)
{
u32 width;
u32 height;
};
STRUCT(WindowingInstantiate)
{
String name;
WindowingOffset offset;
WindowingSize size;
void* context;
};
fn u8 windowing_initialize();
fn WindowingInstance* windowing_instantiate(WindowingInstantiate instantiate);
fn void windowing_poll_events();
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* window);
// NEW API END
// fn OSWindow os_window_create(OSWindowCreate create);
// fn u8 os_window_should_close(OSWindow window);
// fn OSCursorPosition os_window_cursor_position_get(OSWindow window);
//
// fn u8 os_event_queue_get_window_focus(OSEventQueue* queue, u32 index);
#ifndef __APPLE__
global_variable WindowingConnection windowing_connection;
global_variable WindowingInstance windowing_instances[256];
#endif

View File

@ -1,197 +0,0 @@
#pragma once
global_variable xcb_window_t windowing_instance_handles[256];
typedef enum WindowingEvent : u32
{
WINDOWING_EVENT_CLOSE,
WINDOWING_EVENT_COUNT,
} WindowingEvent;
fn xcb_connection_t* xcb_connection_get()
{
return windowing_connection.handle;
}
fn xcb_window_t xcb_window_from_windowing_instance(WindowingInstance* instance)
{
return instance->handle;
}
fn void x11_intern_atoms(u32 atom_count, String* names, xcb_intern_atom_cookie_t* cookies, xcb_intern_atom_reply_t** replies)
{
xcb_connection_t* connection = windowing_connection.handle;
for (u64 i = 0; i < atom_count; i += 1)
{
String atom_name = names[i];
cookies[i] = xcb_intern_atom(connection, 0, atom_name.length, string_to_c(atom_name));
}
for (u64 i = 0; i < atom_count; i += 1)
{
replies[i] = xcb_intern_atom_reply(connection, cookies[i], 0);
}
}
typedef enum X11Atom
{
X11_ATOM_WM_PROTOCOLS,
X11_ATOM_WM_DELETE_WINDOW,
X11_ATOM_COUNT,
} X11Atom;
global_variable String atom_names[X11_ATOM_COUNT] = {
strlit("WM_PROTOCOLS"),
strlit("WM_DELETE_WINDOW"),
};
global_variable xcb_intern_atom_reply_t* atom_replies[array_length(atom_names)];
global_variable xcb_intern_atom_cookie_t atom_cookies[array_length(atom_names)];
fn u8 windowing_initialize()
{
u8 result = 0;
windowing_connection.handle = xcb_connect(0, 0);
if (windowing_connection.handle)
{
if (!xcb_connection_has_error(windowing_connection.handle))
{
windowing_connection.setup = xcb_get_setup(windowing_connection.handle);
if (windowing_connection.setup)
{
x11_intern_atoms(array_length(atom_names), atom_names, atom_cookies, atom_replies);
if (atom_replies[X11_ATOM_WM_PROTOCOLS])
{
if (atom_replies[X11_ATOM_WM_DELETE_WINDOW])
{
result = 1;
}
}
}
}
}
return result;
}
fn WindowingInstance* windowing_instantiate(WindowingInstantiate create)
{
xcb_connection_t* connection = windowing_connection.handle;
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(windowing_connection.setup);
xcb_screen_t *screen = iter.data;
/* Create a window */
xcb_window_t window_handle = xcb_generate_id(connection);
u32 i;
for (i = 0; i < array_length(windowing_instance_handles); i += 1)
{
xcb_window_t* window_handle_pointer = &windowing_instance_handles[i];
if (!*window_handle_pointer)
{
*window_handle_pointer = window_handle;
break;
}
}
WindowingInstance* window = &windowing_instances[i];
window->handle = window_handle;
u32 value_mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
u32 value_list[] = {
screen->black_pixel,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
xcb_create_window(
connection, /* Connection */
XCB_COPY_FROM_PARENT, /* Depth (same as parent) */
window_handle, /* Window ID */
screen->root, /* Parent window (root) */
create.offset.x, create.offset.y, /* X, Y */
create.size.width, create.size.height,
10, /* Border width */
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* Class */
screen->root_visual, /* Visual */
value_mask, /* Value mask */
value_list /* Value list */
);
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window_handle, atom_replies[X11_ATOM_WM_PROTOCOLS]->atom, XCB_ATOM_ATOM, 32, 1, &atom_replies[X11_ATOM_WM_DELETE_WINDOW]->atom);
xcb_map_window(connection, window_handle);
/* Flush requests to the X server */
xcb_flush(connection);
return window;
}
fn void windowing_poll_events()
{
xcb_generic_event_t *event;
xcb_connection_t* connection = windowing_connection.handle;
while ((event = xcb_poll_for_event(connection)))
{
switch (event->response_type & ~0x80) {
case XCB_EXPOSE:
break;
case XCB_KEY_PRESS:
break;
case XCB_CLIENT_MESSAGE:
{
let_pointer_cast(xcb_client_message_event_t, client_message_event, event);
if (client_message_event->data.data32[0] == atom_replies[X11_ATOM_WM_DELETE_WINDOW]->atom)
{
xcb_window_t window_handle = client_message_event->window;
u32 i;
u32 window_handle_count = array_length(windowing_instance_handles);
for (i = 0; i < window_handle_count; i += 1)
{
xcb_window_t* window_handle_pointer = &windowing_instance_handles[i];
if (window_handle == *window_handle_pointer)
{
windowing_instances[i].handle = 0;
*window_handle_pointer = 0;
// TODO: For now do this
os_exit(0);
break;
}
}
if (i == window_handle_count)
{
os_exit(1);
}
}
else
{
trap();
}
} break;
case XCB_DESTROY_NOTIFY:
trap();
default:
break;
}
os_free(event);
}
}
fn WindowingSize windowing_get_instance_framebuffer_size(WindowingInstance* instance)
{
WindowingSize result = {};
xcb_connection_t* connection = windowing_connection.handle;
xcb_window_t window = instance->handle;
xcb_get_geometry_cookie_t cookie = xcb_get_geometry(connection, window);
xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(connection, cookie, 0);
result.width = reply->width;
result.height = reply->height;
os_free(reply);
return result;
}

View File

@ -1,17 +0,0 @@
#pragma once
#include <xcb/xcb.h>
STRUCT(WindowingConnection)
{
xcb_connection_t* handle;
const xcb_setup_t* setup;
};
STRUCT(WindowingInstance)
{
xcb_window_t handle;
};
fn xcb_connection_t* xcb_connection_get();
fn xcb_window_t xcb_window_from_windowing_instance(WindowingInstance* instance);

2656
build.c

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
set -eu
cd build
ninja --quiet
cd ..
if [[ -z "${BOOTSTRAP_COMPILER:-}" ]]; then
BOOTSTRAP_COMPILER=$HOME/bloat-buster-artifacts/releases/main/compiler_generic
fi
$BOOTSTRAP_COMPILER compile src/compiler.bbb

View File

@ -1,3 +0,0 @@
*.o
*.obj
*.exe

View File

@ -1,37 +0,0 @@
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
#!/usr/bin/env bash
set -eux
if [[ -z "${LLVM_VERSION:-}" ]]; then
LLVM_VERSION=20.1.7
fi
if [[ -z "${BB_CI:-}" ]]; then
BB_CI=0
fi
@ -12,8 +16,6 @@ else
LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE
fi
BUILD_DIR=build
BIRTH_NATIVE_OS_STRING=$OSTYPE
case "$BIRTH_NATIVE_OS_STRING" in
@ -31,20 +33,22 @@ case "$BIRTH_NATIVE_ARCH_STRING" in
*) 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.7_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE
if [[ -z "${CMAKE_PREFIX_PATH:-}" ]]; then
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_${BIRTH_ARCH}-${BIRTH_OS}-${LLVM_CMAKE_BUILD_TYPE}
fi
if [[ -z "${CLANG_PATH:-}" ]]; then
CLANG_PATH=clang
CLANGXX_PATH=clang++
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
cd ..
OPT_ARGS=""
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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff