Delete C++ implementation and dead code
This commit is contained in:
parent
e9b3dea942
commit
16b78d9167
@ -10,6 +10,9 @@ on:
|
|||||||
env:
|
env:
|
||||||
BB_CI: 1
|
BB_CI: 1
|
||||||
BUILD_DEBUG: 1
|
BUILD_DEBUG: 1
|
||||||
|
LLVM_VERSION: 20.1.7
|
||||||
|
CLANG_PATH: clang-19
|
||||||
|
CLANGXX_PATH: clang++-19
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
@ -24,10 +27,7 @@ jobs:
|
|||||||
- name: Build and test (Packaged LLVM)
|
- name: Build and test (Packaged LLVM)
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
|
||||||
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
||||||
CLANG_PATH: clang-19
|
|
||||||
CLANGXX_PATH: clang++-19
|
|
||||||
run: |
|
run: |
|
||||||
set -eux
|
set -eux
|
||||||
ci/reproduce.sh
|
ci/reproduce.sh
|
||||||
|
@ -5,7 +5,9 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
BB_CI: 1
|
||||||
|
CLANG_PATH: clang-19
|
||||||
|
CLANGXX_PATH: clang++-19
|
||||||
|
LLVM_VERSION: 20.1.7
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
strategy:
|
strategy:
|
||||||
@ -19,12 +21,8 @@ jobs:
|
|||||||
- name: Build and test (Packaged LLVM)
|
- name: Build and test (Packaged LLVM)
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
BB_CI: 1
|
|
||||||
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
CMAKE_BUILD_TYPE: ${{matrix.BIRTH_CMAKE_BUILD_TYPE}}
|
||||||
CLANG_PATH: clang-19
|
|
||||||
CLANGXX_PATH: clang++-19
|
|
||||||
run: |
|
run: |
|
||||||
set -eux
|
set -eux
|
||||||
./generate.sh
|
./generate.sh
|
||||||
./build.sh
|
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_x86_64-linux-${CMAKE_BUILD_TYPE} $HOME/bloat-buster-artifacts/releases/main/compiler_generic reproduce
|
||||||
./build/bb test
|
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
|
||||||
include(CMakePrintHelpers)
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
|
|
||||||
endif()
|
|
||||||
# Set C++ standard
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
|
||||||
project(bb)
|
|
||||||
|
|
||||||
find_package(LLVM REQUIRED CONFIG)
|
|
||||||
find_package(LLD REQUIRED CONFIG)
|
|
||||||
|
|
||||||
add_executable(bb
|
|
||||||
src/lib.cpp
|
|
||||||
src/entry_point.cpp
|
|
||||||
src/compiler.cpp
|
|
||||||
src/parser.cpp
|
|
||||||
src/emitter.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(c_abi tests/c_abi.c)
|
|
||||||
|
|
||||||
include_directories(src)
|
|
||||||
add_compile_definitions(
|
|
||||||
$<$<CONFIG:Debug>:BB_DEBUG=1>
|
|
||||||
$<$<NOT:$<CONFIG:Debug>>:BB_DEBUG=0>
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(llvm_bindings NAMES libllvm_bindings.dylib libllvm_bindings.lib libllvm_bindings.a libllvm_bindingsELF.dll.a libllvm_bindingsELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
|
||||||
|
|
||||||
target_link_libraries(bb PUBLIC
|
|
||||||
${LLVM_AVAILABLE_LIBS}
|
|
||||||
${LLD_COMMON}
|
|
||||||
${LLD_COFF}
|
|
||||||
${LLD_ELF}
|
|
||||||
${LLD_MACHO}
|
|
||||||
${LLD_MINGW}
|
|
||||||
${LLD_WASM}
|
|
||||||
${llvm_bindings}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_compile_options(-Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -fno-signed-char -fwrapv -fno-strict-aliasing)
|
|
||||||
add_compile_definitions(CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}")
|
|
||||||
add_compile_definitions(BB_CI=${BB_CI})
|
|
||||||
|
|
||||||
# add_compile_options(-fsanitize=address)
|
|
||||||
# add_link_options(-fsanitize=address)
|
|
@ -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
@ -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
|
|
@ -1,7 +0,0 @@
|
|||||||
#if BB_CI == 0
|
|
||||||
#include <std/base.h>
|
|
||||||
#include <std/os.h>
|
|
||||||
|
|
||||||
EXPORT void run_app();
|
|
||||||
|
|
||||||
#endif
|
|
@ -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]
|
|
@ -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
|
|
@ -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)
|
|
@ -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);
|
|
@ -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 };
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
#include <bloat-buster/base.h>
|
|
||||||
#include <std/os.h>
|
|
||||||
|
|
||||||
EXPORT SliceP(char) lld_driver(Arena* arena, LinkerArguments arguments);
|
|
@ -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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
@ -1,2 +0,0 @@
|
|||||||
#include <std/base.h>
|
|
||||||
extern u8 pdb_image[143360];
|
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
#pragma once
|
|
@ -1 +0,0 @@
|
|||||||
#pragma once
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
@ -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 };
|
|
||||||
}
|
|
@ -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
@ -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);
|
|
@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -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);
|
|
@ -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;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#include <std/base.h>
|
|
||||||
|
|
||||||
STRUCT(MD5Result)
|
|
||||||
{
|
|
||||||
u8 hash[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
MD5Result md5_string(String string);
|
|
@ -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();
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
1701
bootstrap/std/os.c
1701
bootstrap/std/os.c
File diff suppressed because it is too large
Load Diff
@ -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);
|
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
|
||||||
// }
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
};
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
@ -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;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <std/base.h>
|
|
||||||
#include <std/ui_core.h>
|
|
||||||
|
|
||||||
fn UI_Signal ui_button(String string);
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
@ -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;
|
|
||||||
}
|
|
@ -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
@ -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);
|
|
@ -1 +0,0 @@
|
|||||||
#pragma once
|
|
@ -1 +0,0 @@
|
|||||||
#pragma once
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
STRUCT(WindowingConnection)
|
|
||||||
{
|
|
||||||
HINSTANCE instance;
|
|
||||||
WNDCLASSEXW window_class;
|
|
||||||
};
|
|
||||||
|
|
||||||
STRUCT(WindowingInstance)
|
|
||||||
{
|
|
||||||
HWND handle;
|
|
||||||
};
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
8
build.sh
8
build.sh
@ -1,4 +1,6 @@
|
|||||||
set -eu
|
set -eu
|
||||||
cd build
|
if [[ -z "${BOOTSTRAP_COMPILER:-}" ]]; then
|
||||||
ninja --quiet
|
BOOTSTRAP_COMPILER=$HOME/bloat-buster-artifacts/releases/main/compiler_generic
|
||||||
cd ..
|
fi
|
||||||
|
|
||||||
|
$BOOTSTRAP_COMPILER compile src/compiler.bbb
|
||||||
|
3
dependencies/stb/.gitignore
vendored
3
dependencies/stb/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
*.o
|
|
||||||
*.obj
|
|
||||||
*.exe
|
|
37
dependencies/stb/LICENSE
vendored
37
dependencies/stb/LICENSE
vendored
@ -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.
|
|
7988
dependencies/stb/stb_image.h
vendored
7988
dependencies/stb/stb_image.h
vendored
File diff suppressed because it is too large
Load Diff
5089
dependencies/stb/stb_truetype.h
vendored
5089
dependencies/stb/stb_truetype.h
vendored
File diff suppressed because it is too large
Load Diff
30
generate.sh
30
generate.sh
@ -1,6 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -eux
|
set -eux
|
||||||
|
|
||||||
|
if [[ -z "${LLVM_VERSION:-}" ]]; then
|
||||||
|
LLVM_VERSION=20.1.7
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -z "${BB_CI:-}" ]]; then
|
if [[ -z "${BB_CI:-}" ]]; then
|
||||||
BB_CI=0
|
BB_CI=0
|
||||||
fi
|
fi
|
||||||
@ -12,8 +16,6 @@ else
|
|||||||
LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE
|
LLVM_CMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_DIR=build
|
|
||||||
|
|
||||||
BIRTH_NATIVE_OS_STRING=$OSTYPE
|
BIRTH_NATIVE_OS_STRING=$OSTYPE
|
||||||
|
|
||||||
case "$BIRTH_NATIVE_OS_STRING" in
|
case "$BIRTH_NATIVE_OS_STRING" in
|
||||||
@ -31,20 +33,22 @@ case "$BIRTH_NATIVE_ARCH_STRING" in
|
|||||||
*) exit 1
|
*) exit 1
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$BIRTH_OS" in
|
if [[ -z "${CMAKE_PREFIX_PATH:-}" ]]; then
|
||||||
linux) LINKER_TYPE=MOLD;;
|
CMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_${LLVM_VERSION}_${BIRTH_ARCH}-${BIRTH_OS}-${LLVM_CMAKE_BUILD_TYPE}
|
||||||
*) LINKER_TYPE=DEFAULT;;
|
fi
|
||||||
esac
|
|
||||||
|
|
||||||
rm -rf $BUILD_DIR
|
|
||||||
mkdir $BUILD_DIR
|
|
||||||
cd $BUILD_DIR
|
|
||||||
LLVM_PREFIX_PATH=$HOME/dev/llvm/install/llvm_20.1.7_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE
|
|
||||||
|
|
||||||
if [[ -z "${CLANG_PATH:-}" ]]; then
|
if [[ -z "${CLANG_PATH:-}" ]]; then
|
||||||
CLANG_PATH=clang
|
CLANG_PATH=clang
|
||||||
CLANGXX_PATH=clang++
|
CLANGXX_PATH=clang++
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cmake .. -G Ninja -DCMAKE_C_COMPILER=$CLANG_PATH -DCMAKE_CXX_COMPILER=$CLANGXX_PATH -DCMAKE_LINKER_TYPE=$LINKER_TYPE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE%%-*} -DCMAKE_PREFIX_PATH=$LLVM_PREFIX_PATH -DCMAKE_COLOR_DIAGNOSTICS=ON -DBB_CI=$BB_CI
|
OPT_ARGS=""
|
||||||
cd ..
|
|
||||||
|
case "${CMAKE_BUILD_TYPE%%-*}" in
|
||||||
|
Debug) OPT_ARGS="-O0 -g";;
|
||||||
|
Release*) OPT_ARGS="-O3";;
|
||||||
|
*) exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mkdir -p self-hosted-bb-cache
|
||||||
|
$CLANG_PATH -c tests/c_abi.c -o self-hosted-bb-cache/c_abi.o $OPT_ARGS -std=gnu2x
|
||||||
|
618
src/compiler.cpp
618
src/compiler.cpp
@ -1,618 +0,0 @@
|
|||||||
#include <compiler.hpp>
|
|
||||||
|
|
||||||
global_variable Slice<char* const> environment;
|
|
||||||
|
|
||||||
fn void compile(Arena* arena, Options options)
|
|
||||||
{
|
|
||||||
Module module;
|
|
||||||
auto base_allocation_type_count = i128_offset + // 64 * 2 for basic integer types
|
|
||||||
2 + // u128, s128
|
|
||||||
2; // void, noreturn
|
|
||||||
auto base_type_allocation = arena_allocate<Type>(arena, base_allocation_type_count);
|
|
||||||
|
|
||||||
auto* type_it = base_type_allocation.pointer;
|
|
||||||
|
|
||||||
bool signs[] = {false, true};
|
|
||||||
Type* previous = 0;
|
|
||||||
|
|
||||||
for (bool sign: signs)
|
|
||||||
{
|
|
||||||
for (u32 bit_index = 0; bit_index < 64; bit_index += 1)
|
|
||||||
{
|
|
||||||
auto bit_count = bit_index + 1;
|
|
||||||
auto first_digit = (u8)(bit_count < 10 ? bit_count % 10 + '0' : bit_count / 10 + '0');
|
|
||||||
auto second_digit = (u8)(bit_count > 9 ? bit_count % 10 + '0' : 0);
|
|
||||||
u8 name_buffer[] = { u8(sign ? 's' : 'u'), first_digit, second_digit };
|
|
||||||
u64 name_length = 2 + (bit_count > 9);
|
|
||||||
auto name_stack = String{name_buffer, name_length};
|
|
||||||
|
|
||||||
auto name = arena_duplicate_string(arena, name_stack);
|
|
||||||
|
|
||||||
*type_it = {
|
|
||||||
.integer = {
|
|
||||||
.bit_count = bit_count,
|
|
||||||
.is_signed = sign,
|
|
||||||
},
|
|
||||||
.id = TypeId::integer,
|
|
||||||
.name = name,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
if (previous) previous->next = type_it;
|
|
||||||
previous = type_it;
|
|
||||||
type_it += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bool sign: signs)
|
|
||||||
{
|
|
||||||
auto name = sign ? string_literal("s128") : string_literal("u128");
|
|
||||||
*type_it = {
|
|
||||||
.integer = {
|
|
||||||
.bit_count = 128,
|
|
||||||
.is_signed = sign,
|
|
||||||
},
|
|
||||||
.id = TypeId::integer,
|
|
||||||
.name = name,
|
|
||||||
.next = previous,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
if (previous) previous->next = type_it;
|
|
||||||
previous = type_it;
|
|
||||||
type_it += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto void_type = type_it;
|
|
||||||
type_it += 1;
|
|
||||||
auto noreturn_type = type_it;
|
|
||||||
type_it += 1;
|
|
||||||
assert((u64)(type_it - base_type_allocation.pointer) == base_allocation_type_count);
|
|
||||||
|
|
||||||
previous->next = void_type;
|
|
||||||
*void_type = {
|
|
||||||
.id = TypeId::void_type,
|
|
||||||
.name = string_literal("void"),
|
|
||||||
.next = noreturn_type,
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
*noreturn_type = {
|
|
||||||
.id = TypeId::noreturn,
|
|
||||||
.name = string_literal("noreturn"),
|
|
||||||
.scope = &module.scope,
|
|
||||||
};
|
|
||||||
|
|
||||||
module = Module{
|
|
||||||
.arena = arena,
|
|
||||||
.content = options.content,
|
|
||||||
.scope = {
|
|
||||||
.types = {
|
|
||||||
.first = base_type_allocation.pointer,
|
|
||||||
.last = noreturn_type,
|
|
||||||
},
|
|
||||||
.kind = ScopeKind::global,
|
|
||||||
},
|
|
||||||
.name = options.name,
|
|
||||||
.path = options.path,
|
|
||||||
.executable = options.executable,
|
|
||||||
.objects = options.objects,
|
|
||||||
.library_directories = options.library_directories,
|
|
||||||
.library_names = options.library_names,
|
|
||||||
.library_paths = options.library_paths,
|
|
||||||
.link_libcpp = options.link_libcpp,
|
|
||||||
.target = options.target,
|
|
||||||
.build_mode = options.build_mode,
|
|
||||||
.has_debug_info = options.has_debug_info,
|
|
||||||
.silent = options.silent,
|
|
||||||
};
|
|
||||||
module.void_value = new_value(&module);
|
|
||||||
*module.void_value = {
|
|
||||||
.type = void_type,
|
|
||||||
.id = ValueId::infer_or_ignore,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto definition: options.definitions)
|
|
||||||
{
|
|
||||||
auto definition_global = new_global(&module);
|
|
||||||
auto definition_value = new_value(&module);
|
|
||||||
auto definition_storage = new_value(&module);
|
|
||||||
*definition_value = {
|
|
||||||
.string_literal = definition.value,
|
|
||||||
.id = ValueId::string_literal,
|
|
||||||
};
|
|
||||||
*definition_storage = {
|
|
||||||
.id = ValueId::global,
|
|
||||||
};
|
|
||||||
*definition_global = Global{
|
|
||||||
.variable = {
|
|
||||||
.storage = definition_storage,
|
|
||||||
.initial_value = definition_value,
|
|
||||||
.type = get_slice_type(&module, uint8(&module)),
|
|
||||||
.scope = &module.scope,
|
|
||||||
.name = definition.name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse(&module);
|
|
||||||
emit(&module);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String compile_file(Arena* arena, Compile options)
|
|
||||||
{
|
|
||||||
auto relative_file_path = options.relative_file_path;
|
|
||||||
if (relative_file_path.length < 5)
|
|
||||||
{
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto extension_start = string_last_character(relative_file_path, '.');
|
|
||||||
if (extension_start == string_no_match)
|
|
||||||
{
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!relative_file_path(extension_start).equal(string_literal(".bbb")))
|
|
||||||
{
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto separator_index = string_last_character(relative_file_path, '/');
|
|
||||||
separator_index = separator_index == string_no_match ? 0 : separator_index;
|
|
||||||
|
|
||||||
auto base_start = separator_index + (separator_index != 0 || relative_file_path[separator_index] == '/');
|
|
||||||
auto base_name = relative_file_path(base_start, extension_start);
|
|
||||||
|
|
||||||
auto is_compiler = relative_file_path.equal(string_literal("src/compiler.bbb"));
|
|
||||||
|
|
||||||
make_directory(base_cache_dir);
|
|
||||||
|
|
||||||
String cpu_dir_parts[] = {
|
|
||||||
string_literal(base_cache_dir),
|
|
||||||
string_literal("/"),
|
|
||||||
options.host_cpu_model ? string_literal("native") : string_literal("generic"),
|
|
||||||
};
|
|
||||||
|
|
||||||
auto cpu_dir = arena_join_string(arena, array_to_slice(cpu_dir_parts));
|
|
||||||
|
|
||||||
make_directory(cstr(cpu_dir));
|
|
||||||
|
|
||||||
auto base_dir = cpu_dir;
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
String compiler_dir_parts[] = {
|
|
||||||
base_dir,
|
|
||||||
string_literal("/compiler"),
|
|
||||||
};
|
|
||||||
base_dir = arena_join_string(arena, array_to_slice(compiler_dir_parts));
|
|
||||||
make_directory(cstr(base_dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
String output_path_dir_parts[] = {
|
|
||||||
base_dir,
|
|
||||||
string_literal("/"),
|
|
||||||
build_mode_to_string(options.build_mode),
|
|
||||||
string_literal("_"),
|
|
||||||
options.has_debug_info ? string_literal("di") : string_literal("nodi"),
|
|
||||||
};
|
|
||||||
auto output_path_dir = arena_join_string(arena, array_to_slice(output_path_dir_parts));
|
|
||||||
|
|
||||||
make_directory(cstr(output_path_dir));
|
|
||||||
|
|
||||||
String output_path_base_parts[] = {
|
|
||||||
output_path_dir,
|
|
||||||
string_literal("/"),
|
|
||||||
base_name,
|
|
||||||
};
|
|
||||||
auto output_path_base = arena_join_string(arena, array_to_slice(output_path_base_parts));
|
|
||||||
String output_object_path_parts[] = {
|
|
||||||
output_path_base,
|
|
||||||
string_literal(".o"),
|
|
||||||
};
|
|
||||||
auto output_object_path = arena_join_string(arena, array_to_slice(output_object_path_parts));
|
|
||||||
auto output_executable_path = output_path_base;
|
|
||||||
|
|
||||||
auto file_content = file_read(arena, relative_file_path);
|
|
||||||
auto file_path = path_absolute(arena, relative_file_path);
|
|
||||||
|
|
||||||
Slice<Definition> definitions = {};
|
|
||||||
auto cmake_prefix_path = string_literal(CMAKE_PREFIX_PATH);
|
|
||||||
auto cmake_prefix_path_definition = Definition{
|
|
||||||
.name = string_literal("CMAKE_PREFIX_PATH"),
|
|
||||||
.value = cmake_prefix_path,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
auto cmake_prefix_path_cstr = os_get_environment_variable("CMAKE_PREFIX_PATH");
|
|
||||||
if (cmake_prefix_path_cstr)
|
|
||||||
{
|
|
||||||
auto cmake_prefix_path_string = c_string_to_slice(cmake_prefix_path_cstr);
|
|
||||||
cmake_prefix_path_definition.value = cmake_prefix_path_string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String objects[] = {
|
|
||||||
output_object_path,
|
|
||||||
};
|
|
||||||
Slice<String> object_slice = array_to_slice(objects);
|
|
||||||
|
|
||||||
String c_abi_library = string_literal("build/libc_abi.a");
|
|
||||||
String llvm_bindings_library = string_literal("build/libllvm_bindings.a");
|
|
||||||
String library_buffer[256];
|
|
||||||
String library_directory = {};
|
|
||||||
|
|
||||||
Slice<String> library_directories = {};
|
|
||||||
Slice<String> library_names = {};
|
|
||||||
Slice<String> library_paths = {};
|
|
||||||
|
|
||||||
if (is_compiler)
|
|
||||||
{
|
|
||||||
definitions = { .pointer = &cmake_prefix_path_definition, .length = 1 };
|
|
||||||
|
|
||||||
ArgBuilder builder = {};
|
|
||||||
String llvm_config_parts[] = {
|
|
||||||
cmake_prefix_path,
|
|
||||||
string_literal("/bin/llvm-config"),
|
|
||||||
};
|
|
||||||
builder.add(arena, arena_join_string(arena, array_to_slice(llvm_config_parts)));
|
|
||||||
builder.add("--libdir");
|
|
||||||
builder.add("--libs");
|
|
||||||
builder.add("--system-libs");
|
|
||||||
auto arguments = builder.flush();
|
|
||||||
auto llvm_config = os_execute(arena, arguments, environment, {
|
|
||||||
.policies = { ExecuteStandardStreamPolicy::pipe, ExecuteStandardStreamPolicy::ignore },
|
|
||||||
});
|
|
||||||
auto success = llvm_config.termination_kind == TerminationKind::exit && llvm_config.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stream = llvm_config.streams[0];
|
|
||||||
auto line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
library_directory = stream(0, line);
|
|
||||||
library_directories = { &library_directory, 1 };
|
|
||||||
|
|
||||||
stream = stream(line + 1);
|
|
||||||
|
|
||||||
line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto llvm_library_stream = stream(0, line);
|
|
||||||
|
|
||||||
stream = stream(line + 1);
|
|
||||||
|
|
||||||
u64 library_count = 0;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
auto space = string_first_character(llvm_library_stream, ' ');
|
|
||||||
if (space == string_no_match)
|
|
||||||
{
|
|
||||||
auto library_argument = llvm_library_stream;
|
|
||||||
library_buffer[library_count] = library_argument(2);
|
|
||||||
library_count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Omit the first two characters: "-l"
|
|
||||||
auto library_argument = llvm_library_stream(2, space);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
llvm_library_stream = llvm_library_stream(space + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
line = string_first_character(stream, '\n');
|
|
||||||
if (line == string_no_match)
|
|
||||||
{
|
|
||||||
report_error();
|
|
||||||
}
|
|
||||||
assert(line == stream.length - 1);
|
|
||||||
auto system_library_stream = stream(0, line);
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
auto space = string_first_character(system_library_stream, ' ');
|
|
||||||
if (space == string_no_match)
|
|
||||||
{
|
|
||||||
auto library_argument = system_library_stream(2);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Omit the first two characters: "-l"
|
|
||||||
auto library_argument = system_library_stream(2, space);
|
|
||||||
library_buffer[library_count] = library_argument;
|
|
||||||
library_count += 1;
|
|
||||||
system_library_stream = system_library_stream(space + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("gcc");
|
|
||||||
library_count += 1;
|
|
||||||
library_buffer[library_count] = string_literal("gcc_s");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldCommon");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldCOFF");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldELF");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldMachO");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldMinGW");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("lldWasm");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_buffer[library_count] = string_literal("llvm_bindings");
|
|
||||||
library_count += 1;
|
|
||||||
|
|
||||||
library_names = { library_buffer, library_count };
|
|
||||||
}
|
|
||||||
else if (base_name.equal(string_literal("tests")))
|
|
||||||
{
|
|
||||||
library_paths = { &c_abi_library, 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
compile(arena, {
|
|
||||||
.content = file_content,
|
|
||||||
.path = file_path,
|
|
||||||
.executable = output_executable_path,
|
|
||||||
.name = base_name,
|
|
||||||
.definitions = definitions,
|
|
||||||
.objects = object_slice,
|
|
||||||
.library_paths = library_paths,
|
|
||||||
.library_names = library_names,
|
|
||||||
.library_directories = library_directories,
|
|
||||||
.link_libcpp = is_compiler,
|
|
||||||
.target = {
|
|
||||||
.cpu = CPUArchitecture::x86_64,
|
|
||||||
.os = OperatingSystem::linux_,
|
|
||||||
.host_cpu_model = options.host_cpu_model,
|
|
||||||
},
|
|
||||||
.build_mode = options.build_mode,
|
|
||||||
.has_debug_info = options.has_debug_info,
|
|
||||||
.silent = options.silent,
|
|
||||||
});
|
|
||||||
|
|
||||||
return output_executable_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
global_variable String names[] =
|
|
||||||
{
|
|
||||||
string_literal("tests"),
|
|
||||||
};
|
|
||||||
|
|
||||||
void entry_point(Slice<char* const> arguments, Slice<char* const> envp)
|
|
||||||
{
|
|
||||||
environment = envp;
|
|
||||||
Arena* arena = arena_initialize_default(16 * mb);
|
|
||||||
|
|
||||||
if (arguments.length < 2)
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("error: Not enough arguments\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
String command_string = c_string_to_slice(arguments[1]);
|
|
||||||
String command_strings[] = {
|
|
||||||
string_literal("compile"),
|
|
||||||
string_literal("test"),
|
|
||||||
};
|
|
||||||
static_assert(array_length(command_strings) == (u64)Command::count);
|
|
||||||
|
|
||||||
backing_type(Command) i;
|
|
||||||
for (i = 0; i < (backing_type(Command))Command::count; i += 1)
|
|
||||||
{
|
|
||||||
String candidate = command_strings[i];
|
|
||||||
if (candidate.equal(command_string))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto command = (Command)i;
|
|
||||||
|
|
||||||
switch (command)
|
|
||||||
{
|
|
||||||
case Command::compile:
|
|
||||||
{
|
|
||||||
if (arguments.length < 3)
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("Not enough arguments for command 'compile'\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto build_mode = BuildMode::debug_none;
|
|
||||||
auto has_debug_info = true;
|
|
||||||
auto is_host_cpu_model = true;
|
|
||||||
|
|
||||||
if (arguments.length >= 4)
|
|
||||||
{
|
|
||||||
auto build_mode_string = c_string_to_slice(arguments[3]);
|
|
||||||
String build_mode_strings[] = {
|
|
||||||
string_literal("debug_none"),
|
|
||||||
string_literal("debug"),
|
|
||||||
string_literal("soft_optimize"),
|
|
||||||
string_literal("optimize_for_speed"),
|
|
||||||
string_literal("optimize_for_size"),
|
|
||||||
string_literal("aggressively_optimize_for_speed"),
|
|
||||||
string_literal("aggressively_optimize_for_size"),
|
|
||||||
};
|
|
||||||
|
|
||||||
backing_type(BuildMode) i;
|
|
||||||
for (i = 0; i < (backing_type(BuildMode))BuildMode::count; i += 1)
|
|
||||||
{
|
|
||||||
String candidate = build_mode_strings[i];
|
|
||||||
if (build_mode_string.equal(candidate))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build_mode = (BuildMode)i;
|
|
||||||
if (build_mode == BuildMode::count)
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("Invalid build mode\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.length >= 5)
|
|
||||||
{
|
|
||||||
auto has_debug_info_string = c_string_to_slice(arguments[4]);
|
|
||||||
if (has_debug_info_string.equal(string_literal("true")))
|
|
||||||
{
|
|
||||||
has_debug_info = true;
|
|
||||||
}
|
|
||||||
else if (has_debug_info_string.equal(string_literal("false")))
|
|
||||||
{
|
|
||||||
has_debug_info = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("Wrong value for has_debug_info\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.length >= 6)
|
|
||||||
{
|
|
||||||
auto is_host_cpu_model_string = c_string_to_slice(arguments[5]);
|
|
||||||
if (is_host_cpu_model_string.equal(string_literal("true")))
|
|
||||||
{
|
|
||||||
is_host_cpu_model = true;
|
|
||||||
}
|
|
||||||
else if (is_host_cpu_model_string.equal(string_literal("false")))
|
|
||||||
{
|
|
||||||
is_host_cpu_model = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("Wrong value for is_host_cpu_model\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relative_file_path = c_string_to_slice(arguments[2]);
|
|
||||||
|
|
||||||
compile_file(arena, {
|
|
||||||
.relative_file_path = relative_file_path,
|
|
||||||
.build_mode = build_mode,
|
|
||||||
.has_debug_info = has_debug_info,
|
|
||||||
.host_cpu_model = is_host_cpu_model,
|
|
||||||
.silent = false,
|
|
||||||
});
|
|
||||||
} break;
|
|
||||||
case Command::test:
|
|
||||||
{
|
|
||||||
// TODO: provide more arguments
|
|
||||||
if (arguments.length != 2)
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("error: 'test' command takes no arguments"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_debug_info_array[] = {true, false};
|
|
||||||
|
|
||||||
for (auto name: names)
|
|
||||||
{
|
|
||||||
for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1))
|
|
||||||
{
|
|
||||||
for (bool has_debug_info : has_debug_info_array)
|
|
||||||
{
|
|
||||||
auto position = arena->position;
|
|
||||||
|
|
||||||
String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") };
|
|
||||||
auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts));
|
|
||||||
|
|
||||||
auto executable_path = compile_file(arena, {
|
|
||||||
.relative_file_path = relative_file_path,
|
|
||||||
.build_mode = build_mode,
|
|
||||||
.has_debug_info = has_debug_info,
|
|
||||||
.silent = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
char* const arguments[] =
|
|
||||||
{
|
|
||||||
(char*)executable_path.pointer,
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
Slice<char* const> arg_slice = array_to_slice(arguments);
|
|
||||||
arg_slice.length -= 1;
|
|
||||||
auto execution = os_execute(arena, arg_slice, environment, {});
|
|
||||||
auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
print(string_literal("Test failed: "));
|
|
||||||
print(executable_path);
|
|
||||||
print(string_literal("\n"));
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
arena_restore(arena, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildMode compiler_build_mode = BuildMode::debug_none;
|
|
||||||
bool compiler_has_debug_info = true;
|
|
||||||
auto compiler = compile_file(arena, {
|
|
||||||
.relative_file_path = string_literal("src/compiler.bbb"),
|
|
||||||
.build_mode = compiler_build_mode,
|
|
||||||
.has_debug_info = compiler_has_debug_info,
|
|
||||||
.host_cpu_model = true,
|
|
||||||
.silent = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
char* const compiler_arguments[] =
|
|
||||||
{
|
|
||||||
(char*)compiler.pointer,
|
|
||||||
(char*)"test",
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
Slice<char* const> arg_slice = array_to_slice(compiler_arguments);
|
|
||||||
arg_slice.length -= 1;
|
|
||||||
auto execution = os_execute(arena, arg_slice, environment, {});
|
|
||||||
auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
print(string_literal("Self-hosted tests failed: "));
|
|
||||||
print(build_mode_to_string(compiler_build_mode));
|
|
||||||
print(compiler_has_debug_info ? string_literal(" with debug info\n") : string_literal(" with no debug info\n"));
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
char* const reproduce_arguments[] =
|
|
||||||
{
|
|
||||||
(char*)compiler.pointer,
|
|
||||||
(char*)"reproduce",
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
arg_slice = array_to_slice(reproduce_arguments);
|
|
||||||
arg_slice.length -= 1;
|
|
||||||
execution = os_execute(arena, arg_slice, environment, {});
|
|
||||||
success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0;
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
print(string_literal("Self-hosted reproduction failed: "));
|
|
||||||
print(build_mode_to_string(compiler_build_mode));
|
|
||||||
print(compiler_has_debug_info ? string_literal(" with debug info\n") : string_literal(" with no debug info\n"));
|
|
||||||
bb_fail();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Command::count:
|
|
||||||
{
|
|
||||||
bb_fail_with_message(string_literal("error: Invalid command\n"));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
2111
src/compiler.hpp
2111
src/compiler.hpp
File diff suppressed because it is too large
Load Diff
10079
src/emitter.cpp
10079
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
|||||||
#include <lib.hpp>
|
|
||||||
void entry_point(Slice<char* const> arguments, Slice<char* const> environment);
|
|
||||||
int main(int argc, const char* argv[], char* const envp[])
|
|
||||||
{
|
|
||||||
auto* envp_end = envp;
|
|
||||||
while (*envp_end)
|
|
||||||
{
|
|
||||||
envp_end += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry_point(Slice<char* const>{(char* const*)argv, (u64)argc}, {envp, (u64)(envp_end - envp)});
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
#include <lib.h>
|
|
||||||
|
|
233
src/lib.cpp
233
src/lib.cpp
@ -1,233 +0,0 @@
|
|||||||
#include <lib.hpp>
|
|
||||||
using uid_t = u32;
|
|
||||||
using gid_t = u32;
|
|
||||||
using off_t = s64;
|
|
||||||
using ino_t = u64;
|
|
||||||
using dev_t = u64;
|
|
||||||
|
|
||||||
struct timespec
|
|
||||||
{
|
|
||||||
s64 seconds;
|
|
||||||
s64 nanoseconds;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Stat
|
|
||||||
{
|
|
||||||
dev_t dev;
|
|
||||||
ino_t ino;
|
|
||||||
u64 nlink;
|
|
||||||
|
|
||||||
u32 mode;
|
|
||||||
uid_t uid;
|
|
||||||
gid_t gid;
|
|
||||||
u32 _0;
|
|
||||||
dev_t rdev;
|
|
||||||
off_t size;
|
|
||||||
s64 blksize;
|
|
||||||
s64 blocks;
|
|
||||||
|
|
||||||
timespec atim;
|
|
||||||
timespec mtim;
|
|
||||||
timespec ctim;
|
|
||||||
s64 _1[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" s32 fstat(s32, Stat*);
|
|
||||||
extern "C" s32 fork();
|
|
||||||
extern "C" s32 dup2(s32, s32);
|
|
||||||
extern "C" s32 execve(const char* path_name, const char* const argv[], char* const envp[]);
|
|
||||||
extern "C" s32 waitpid(s32 pid, int* wstatus, int options);
|
|
||||||
extern "C" s32 pipe(int fd[2]);
|
|
||||||
|
|
||||||
u64 os_file_size(s32 fd)
|
|
||||||
{
|
|
||||||
Stat stat;
|
|
||||||
auto result = fstat(fd, &stat);
|
|
||||||
assert(result == 0);
|
|
||||||
return (u64)stat.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u8 EXITSTATUS(u32 s)
|
|
||||||
{
|
|
||||||
return (u8)((s & 0xff00) >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u32 TERMSIG(u32 s)
|
|
||||||
{
|
|
||||||
return s & 0x7f;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u32 STOPSIG(u32 s)
|
|
||||||
{
|
|
||||||
return EXITSTATUS(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool IFEXITED(u32 s)
|
|
||||||
{
|
|
||||||
return TERMSIG(s) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool IFSTOPPED(u32 s)
|
|
||||||
{
|
|
||||||
return u16(((s & 0xffff) * 0x10001) >> 8) > 0x7f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool IFSIGNALED(u32 s)
|
|
||||||
{
|
|
||||||
return (s & 0xffff) - 1 < 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
Execution os_execute(Arena* arena, Slice<char* const> arguments, Slice<char* const> environment, ExecuteOptions options)
|
|
||||||
{
|
|
||||||
unused(arena);
|
|
||||||
assert(arguments.pointer[arguments.length] == 0);
|
|
||||||
assert(environment.pointer[environment.length] == 0);
|
|
||||||
|
|
||||||
Execution execution = {};
|
|
||||||
|
|
||||||
s32 null_file_descriptor = -1;
|
|
||||||
if (options.null_file_descriptor >= 0)
|
|
||||||
{
|
|
||||||
null_file_descriptor = options.null_file_descriptor;
|
|
||||||
}
|
|
||||||
else if (options.policies[0] == ExecuteStandardStreamPolicy::ignore || options.policies[1] == ExecuteStandardStreamPolicy::ignore)
|
|
||||||
{
|
|
||||||
null_file_descriptor = open("/dev/null", { .access_mode = OPEN::AccessMode::write_only });
|
|
||||||
}
|
|
||||||
|
|
||||||
int pipes[standard_stream_count][2];
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i += 1)
|
|
||||||
{
|
|
||||||
if (options.policies[i] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
if (pipe(pipes[i]) == -1)
|
|
||||||
{
|
|
||||||
trap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pid = fork();
|
|
||||||
|
|
||||||
switch (pid)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
{
|
|
||||||
trap();
|
|
||||||
} break;
|
|
||||||
case 0: // Child process
|
|
||||||
{
|
|
||||||
for (u64 i = 0; i < standard_stream_count; i += 1)
|
|
||||||
{
|
|
||||||
auto fd = (s32)i + 1;
|
|
||||||
switch (options.policies[i])
|
|
||||||
{
|
|
||||||
case ExecuteStandardStreamPolicy::inherit:
|
|
||||||
{
|
|
||||||
} break;
|
|
||||||
case ExecuteStandardStreamPolicy::pipe:
|
|
||||||
{
|
|
||||||
close(pipes[i][0]);
|
|
||||||
dup2(pipes[i][1], fd);
|
|
||||||
close(pipes[i][1]);
|
|
||||||
} break;
|
|
||||||
case ExecuteStandardStreamPolicy::ignore:
|
|
||||||
{
|
|
||||||
dup2(null_file_descriptor, fd);
|
|
||||||
close(null_file_descriptor);
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = execve(arguments[0], arguments.pointer, environment.pointer);
|
|
||||||
|
|
||||||
if (result != -1)
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
trap();
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
for (u64 i = 0; i < standard_stream_count; i += 1)
|
|
||||||
{
|
|
||||||
if (options.policies[i] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
close(pipes[i][1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: better allocation strategy
|
|
||||||
u64 allocation_size = 1024 * 1024;
|
|
||||||
Slice<u8> allocation = {};
|
|
||||||
if (options.policies[0] == ExecuteStandardStreamPolicy::pipe || options.policies[1] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
allocation = arena_allocate<u8>(arena, allocation_size * ((options.policies[0] == ExecuteStandardStreamPolicy::pipe) + (options.policies[1] == ExecuteStandardStreamPolicy::pipe)));
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 offset = 0;
|
|
||||||
for (u64 i = 0; i < standard_stream_count; i += 1)
|
|
||||||
{
|
|
||||||
if (options.policies[i] == ExecuteStandardStreamPolicy::pipe)
|
|
||||||
{
|
|
||||||
auto buffer = allocation(offset)(0, allocation_size);
|
|
||||||
auto byte_count = read(pipes[i][0], buffer.pointer, buffer.length);
|
|
||||||
assert(byte_count >= 0);
|
|
||||||
execution.streams[i] = buffer(0, byte_count);
|
|
||||||
close(pipes[i][0]);
|
|
||||||
|
|
||||||
offset += allocation_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int status = 0;
|
|
||||||
auto waitpid_result = waitpid(pid, &status, 0);
|
|
||||||
|
|
||||||
if (waitpid_result == pid)
|
|
||||||
{
|
|
||||||
if (IFEXITED(status))
|
|
||||||
{
|
|
||||||
execution.termination_kind = TerminationKind::exit;
|
|
||||||
execution.termination_code = EXITSTATUS(status);
|
|
||||||
}
|
|
||||||
else if (IFSIGNALED(status))
|
|
||||||
{
|
|
||||||
execution.termination_kind = TerminationKind::signal;
|
|
||||||
execution.termination_code = TERMSIG(status);
|
|
||||||
}
|
|
||||||
else if (IFSTOPPED(status))
|
|
||||||
{
|
|
||||||
execution.termination_kind = TerminationKind::stop;
|
|
||||||
execution.termination_code = STOPSIG(status);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
execution.termination_kind = TerminationKind::unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.null_file_descriptor < 0 && null_file_descriptor >= 0)
|
|
||||||
{
|
|
||||||
close(null_file_descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (waitpid_result == -1)
|
|
||||||
{
|
|
||||||
trap();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trap();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return execution;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" char* getenv(const char*);
|
|
||||||
char* os_get_environment_variable(const char* env)
|
|
||||||
{
|
|
||||||
return getenv(env);
|
|
||||||
}
|
|
722
src/lib.hpp
722
src/lib.hpp
@ -1,722 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define global_variable static
|
|
||||||
|
|
||||||
#define EXPORT extern "C"
|
|
||||||
#define fn static
|
|
||||||
#define unused(x) (void)(x)
|
|
||||||
#define breakpoint() __builtin_debugtrap()
|
|
||||||
#define string_literal_length(s) (sizeof(s) - 1)
|
|
||||||
#define string_literal(s) ((String){ .pointer = (u8*)(s), .length = string_literal_length(s), })
|
|
||||||
#define split_string_literal(s) (char*)(s), string_literal_length(s)
|
|
||||||
#define offsetof(S, f) __builtin_offsetof(S, f)
|
|
||||||
|
|
||||||
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
|
|
||||||
#define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) }
|
|
||||||
#define array_to_bytes(arr) { .pointer = (u8*)(arr), .length = sizeof(arr) }
|
|
||||||
#define backing_type(E) __underlying_type(E)
|
|
||||||
|
|
||||||
#define unreachable_raw() __builtin_unreachable()
|
|
||||||
#define trap() __builtin_trap()
|
|
||||||
#if BB_DEBUG
|
|
||||||
#define unreachable() trap()
|
|
||||||
#else
|
|
||||||
#define unreachable() unreachable_raw()
|
|
||||||
#endif
|
|
||||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
||||||
|
|
||||||
#define expect(x, b) __builtin_expect(!!(x), b)
|
|
||||||
#define likely(x) expect(x, 1)
|
|
||||||
#define unlikely(x) expect(x, 0)
|
|
||||||
|
|
||||||
#define assert(x) (unlikely(!(x)) ? unreachable() : unused(0))
|
|
||||||
|
|
||||||
#define clz(x) __builtin_clzg(x)
|
|
||||||
#define ctz(x) __builtin_ctzg(x)
|
|
||||||
|
|
||||||
#define case_to_name(E,n) case E::n: return string_literal(#n)
|
|
||||||
|
|
||||||
typedef unsigned char u8;
|
|
||||||
typedef unsigned short u16;
|
|
||||||
typedef unsigned int u32;
|
|
||||||
typedef unsigned long u64;
|
|
||||||
|
|
||||||
typedef signed char s8;
|
|
||||||
typedef signed short s16;
|
|
||||||
typedef signed int s32;
|
|
||||||
typedef signed long s64;
|
|
||||||
|
|
||||||
typedef float f32;
|
|
||||||
typedef double f64;
|
|
||||||
|
|
||||||
fn u64 align_forward(u64 value, u64 alignment)
|
|
||||||
{
|
|
||||||
assert(alignment != 0);
|
|
||||||
auto mask = alignment - 1;
|
|
||||||
auto result = (value + mask) & ~mask;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u64 kb = 1024;
|
|
||||||
constexpr u64 mb = 1024 * 1024;
|
|
||||||
constexpr u64 gb = 1024 * 1024 * 1024;
|
|
||||||
|
|
||||||
extern "C" [[noreturn]] void exit(s32 status) noexcept(true);
|
|
||||||
extern "C" void *memcpy (void* __restrict destination, const void *__restrict source, u64 byte_count) noexcept(true);
|
|
||||||
extern "C" s32 memcmp (const void* a, const void *b, u64 __n) noexcept(true);
|
|
||||||
extern "C" char* realpath(const char* __restrict path, char* resolved_path) noexcept(true);
|
|
||||||
|
|
||||||
struct RawSlice
|
|
||||||
{
|
|
||||||
void* pointer;
|
|
||||||
u64 length;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn bool raw_slice_equal(RawSlice a, RawSlice b, u64 size_of_T)
|
|
||||||
{
|
|
||||||
bool result = a.length == b.length;
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
if (a.pointer != b.pointer)
|
|
||||||
{
|
|
||||||
result = memcmp(a.pointer, b.pointer, a.length * size_of_T) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn RawSlice raw_slice_slice(RawSlice s, u64 start, u64 end, u64 size_of_T)
|
|
||||||
{
|
|
||||||
return {(u8*)s.pointer + (size_of_T * start), end - start};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct Slice
|
|
||||||
{
|
|
||||||
T* pointer;
|
|
||||||
u64 length;
|
|
||||||
|
|
||||||
T* begin()
|
|
||||||
{
|
|
||||||
return pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* end() {
|
|
||||||
return pointer + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator[](u64 index)
|
|
||||||
{
|
|
||||||
assert(index < length);
|
|
||||||
return pointer[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool equal(Slice<T> other)
|
|
||||||
{
|
|
||||||
return raw_slice_equal(*(RawSlice*)this, *(RawSlice*)&other, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
Slice<T> operator()(u64 start, u64 end)
|
|
||||||
{
|
|
||||||
return {pointer + start, end - start};
|
|
||||||
}
|
|
||||||
|
|
||||||
Slice<T> operator()(u64 start)
|
|
||||||
{
|
|
||||||
return {pointer + start, length - start};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using String = Slice<u8>;
|
|
||||||
fn const char* cstr(String string)
|
|
||||||
{
|
|
||||||
assert(string.pointer[string.length] == 0);
|
|
||||||
return (const char*) string.pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String c_string_to_slice(const char* cstr)
|
|
||||||
{
|
|
||||||
const auto* end = cstr;
|
|
||||||
while (*end)
|
|
||||||
{
|
|
||||||
end += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { (u8*)cstr, u64(end - cstr) };
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto string_no_match = ~(u64)0;
|
|
||||||
|
|
||||||
fn u64 string_first_character(String string, u8 ch)
|
|
||||||
{
|
|
||||||
u64 result = string_no_match;
|
|
||||||
|
|
||||||
for (u64 i = 0; i < string.length; i += 1)
|
|
||||||
{
|
|
||||||
if (string[i] == ch)
|
|
||||||
{
|
|
||||||
result = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u64 string_last_character(String string, u8 ch)
|
|
||||||
{
|
|
||||||
u64 result = string_no_match;
|
|
||||||
u64 i = string.length;
|
|
||||||
|
|
||||||
while (i > 0)
|
|
||||||
{
|
|
||||||
i -= 1;
|
|
||||||
|
|
||||||
if (string[i] == ch)
|
|
||||||
{
|
|
||||||
result = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProtectionFlags
|
|
||||||
{
|
|
||||||
u8 read:1;
|
|
||||||
u8 write:1;
|
|
||||||
u8 execute:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MapFlags
|
|
||||||
{
|
|
||||||
u8 priv:1;
|
|
||||||
u8 anonymous:1;
|
|
||||||
u8 no_reserve:1;
|
|
||||||
u8 populate:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PROT
|
|
||||||
{
|
|
||||||
u32 read:1;
|
|
||||||
u32 write:1;
|
|
||||||
u32 execute:1;
|
|
||||||
u32 sem:1;
|
|
||||||
u32 _:28;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PROT) == sizeof(u32));
|
|
||||||
|
|
||||||
struct MAP
|
|
||||||
{
|
|
||||||
enum class Type : u32
|
|
||||||
{
|
|
||||||
shared = 0,
|
|
||||||
priv = 1,
|
|
||||||
shared_validate = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
Type type:4;
|
|
||||||
u32 fixed:1;
|
|
||||||
u32 anonymous:1;
|
|
||||||
u32 bit32:1;
|
|
||||||
u32 _0: 1;
|
|
||||||
u32 grows_down:1;
|
|
||||||
u32 _1: 2;
|
|
||||||
u32 deny_write:1;
|
|
||||||
u32 executable:1;
|
|
||||||
u32 locked:1;
|
|
||||||
u32 no_reserve:1;
|
|
||||||
u32 populate:1;
|
|
||||||
u32 non_block:1;
|
|
||||||
u32 stack:1;
|
|
||||||
u32 huge_tlb:1;
|
|
||||||
u32 sync:1;
|
|
||||||
u32 fixed_no_replace:1;
|
|
||||||
u32 _2:5;
|
|
||||||
u32 uninitialized:1;
|
|
||||||
u32 _3:5;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(MAP) == sizeof(u32));
|
|
||||||
|
|
||||||
struct OPEN
|
|
||||||
{
|
|
||||||
enum class AccessMode : u32
|
|
||||||
{
|
|
||||||
read_only = 0,
|
|
||||||
write_only = 1,
|
|
||||||
read_write = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
AccessMode access_mode:2;
|
|
||||||
u32 _0:4;
|
|
||||||
u32 creat:1;
|
|
||||||
u32 excl:1;
|
|
||||||
u32 no_ctty:1;
|
|
||||||
u32 trunc:1;
|
|
||||||
u32 append:1;
|
|
||||||
u32 non_block:1;
|
|
||||||
u32 d_sync:1;
|
|
||||||
u32 a_sync:1;
|
|
||||||
u32 direct:1;
|
|
||||||
u32 _1:1;
|
|
||||||
u32 directory:1;
|
|
||||||
u32 no_follow:1;
|
|
||||||
u32 no_a_time:1;
|
|
||||||
u32 cloexec:1;
|
|
||||||
u32 sync:1;
|
|
||||||
u32 path:1;
|
|
||||||
u32 tmp_file:1;
|
|
||||||
u32 _2:9;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(OPEN) == sizeof(u32));
|
|
||||||
|
|
||||||
extern "C" s32* __errno_location() noexcept(true);
|
|
||||||
extern "C" void* mmap(void*, u64, PROT, MAP, s32, s64);
|
|
||||||
extern "C" s32 mprotect(void*, u64, PROT);
|
|
||||||
extern "C" s64 ptrace(s32, s32, u64, u64);
|
|
||||||
extern "C" s32 open(const char*, OPEN, ...);
|
|
||||||
extern "C" s32 close(s32);
|
|
||||||
extern "C" s64 write(s32, const void*, u64);
|
|
||||||
extern "C" s64 read(s32, void*, u64);
|
|
||||||
extern "C" s32 mkdir(const char*, u64);
|
|
||||||
|
|
||||||
enum class Error : u32
|
|
||||||
{
|
|
||||||
success = 0,
|
|
||||||
perm = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn Error errno()
|
|
||||||
{
|
|
||||||
return (Error)*__errno_location();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void* os_reserve(void* base, u64 size, ProtectionFlags protection, MapFlags map)
|
|
||||||
{
|
|
||||||
auto protection_flags = PROT
|
|
||||||
{
|
|
||||||
.read = protection.read,
|
|
||||||
.write = protection.write,
|
|
||||||
.execute = protection.execute,
|
|
||||||
.sem = 0,
|
|
||||||
._ = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto map_flags = MAP
|
|
||||||
{
|
|
||||||
.type = map.priv ? MAP::Type::priv : MAP::Type::shared,
|
|
||||||
.fixed = 0,
|
|
||||||
.anonymous = map.anonymous,
|
|
||||||
.bit32 = 0,
|
|
||||||
._0 = 0,
|
|
||||||
.grows_down = 0,
|
|
||||||
._1 = 0,
|
|
||||||
.deny_write = 0,
|
|
||||||
.executable = 0,
|
|
||||||
.locked = 0,
|
|
||||||
.no_reserve = map.no_reserve,
|
|
||||||
.populate = map.populate,
|
|
||||||
.non_block = 0,
|
|
||||||
.stack = 0,
|
|
||||||
.huge_tlb = 0,
|
|
||||||
.sync = 0,
|
|
||||||
.fixed_no_replace = 0,
|
|
||||||
._2 = 0,
|
|
||||||
.uninitialized = 0,
|
|
||||||
._3 = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* address = mmap(base, size, protection_flags, map_flags, -1, 0);
|
|
||||||
assert((u64)address != ~(u64)0);
|
|
||||||
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void os_commit(void* address, u64 size, ProtectionFlags protection)
|
|
||||||
{
|
|
||||||
auto protection_flags = PROT
|
|
||||||
{
|
|
||||||
.read = protection.read,
|
|
||||||
.write = protection.write,
|
|
||||||
.execute = protection.execute,
|
|
||||||
.sem = 0,
|
|
||||||
._ = 0,
|
|
||||||
};
|
|
||||||
auto result = mprotect(address, size, protection_flags);
|
|
||||||
assert(!result);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OpenFlags
|
|
||||||
{
|
|
||||||
u32 truncate:1;
|
|
||||||
u32 execute:1;
|
|
||||||
u32 write:1;
|
|
||||||
u32 read:1;
|
|
||||||
u32 create:1;
|
|
||||||
u32 directory:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Permissions
|
|
||||||
{
|
|
||||||
u32 read:1;
|
|
||||||
u32 write:1;
|
|
||||||
u32 execute:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
fn s32 os_open(String path, OpenFlags flags, Permissions permissions)
|
|
||||||
{
|
|
||||||
OPEN::AccessMode access_mode;
|
|
||||||
if (flags.read && flags.write)
|
|
||||||
{
|
|
||||||
access_mode = OPEN::AccessMode::read_write;
|
|
||||||
}
|
|
||||||
else if (flags.read)
|
|
||||||
{
|
|
||||||
access_mode = OPEN::AccessMode::read_only;
|
|
||||||
}
|
|
||||||
else if (flags.write)
|
|
||||||
{
|
|
||||||
access_mode = OPEN::AccessMode::read_only;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto o = OPEN {
|
|
||||||
.access_mode = access_mode,
|
|
||||||
.creat = flags.create,
|
|
||||||
.trunc = flags.truncate,
|
|
||||||
.directory = flags.directory,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
auto mode = permissions.execute ? 0755 : 0644;
|
|
||||||
|
|
||||||
auto fd = open(cstr(path), o, mode);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool is_file_valid(s32 fd)
|
|
||||||
{
|
|
||||||
return fd >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void os_close(s32 fd)
|
|
||||||
{
|
|
||||||
assert(is_file_valid(fd));
|
|
||||||
|
|
||||||
auto result = close(fd);
|
|
||||||
assert(result == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 os_file_size(s32 fd);
|
|
||||||
|
|
||||||
fn u64 os_read_partially(s32 fd, u8* buffer, u64 byte_count)
|
|
||||||
{
|
|
||||||
auto result = read(fd, buffer, byte_count);
|
|
||||||
assert(result > 0);
|
|
||||||
return (u64)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void os_read(s32 fd, String buffer, u64 byte_count)
|
|
||||||
{
|
|
||||||
assert(byte_count <= buffer.length);
|
|
||||||
u64 it_byte_count = 0;
|
|
||||||
while (it_byte_count < byte_count)
|
|
||||||
{
|
|
||||||
auto read_byte_count = os_read_partially(fd, buffer.pointer + it_byte_count, byte_count - it_byte_count);
|
|
||||||
it_byte_count += read_byte_count;
|
|
||||||
}
|
|
||||||
assert(it_byte_count == byte_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u64 os_write_partially(s32 fd, u8* buffer, u64 byte_count)
|
|
||||||
{
|
|
||||||
auto result = write(fd, buffer, byte_count);
|
|
||||||
assert(result > 0);
|
|
||||||
return (u64)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void os_write(s32 fd, String content)
|
|
||||||
{
|
|
||||||
u64 it_byte_count = 0;
|
|
||||||
while (it_byte_count < content.length)
|
|
||||||
{
|
|
||||||
auto written_byte_count = os_write_partially(fd, content.pointer + it_byte_count, content.length - it_byte_count);
|
|
||||||
it_byte_count += written_byte_count;
|
|
||||||
}
|
|
||||||
assert(it_byte_count == content.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String path_absolute_stack(String buffer, String relative_path)
|
|
||||||
{
|
|
||||||
const char* absolute_path = realpath(cstr(relative_path), (char*)buffer.pointer);
|
|
||||||
if (absolute_path)
|
|
||||||
{
|
|
||||||
auto slice = c_string_to_slice(absolute_path);
|
|
||||||
assert(slice.length < buffer.length);
|
|
||||||
return slice;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool os_is_debugger_present()
|
|
||||||
{
|
|
||||||
bool result = false;
|
|
||||||
if (ptrace(0, 0, 0, 0) == -1)
|
|
||||||
{
|
|
||||||
auto errno_error = errno();
|
|
||||||
result = errno_error == Error::perm;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void make_directory(const char* path)
|
|
||||||
{
|
|
||||||
auto result = mkdir(path, 0755);
|
|
||||||
unused(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void print(String string)
|
|
||||||
{
|
|
||||||
os_write(1, string);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArenaInitialization
|
|
||||||
{
|
|
||||||
u64 reserved_size;
|
|
||||||
u64 granularity;
|
|
||||||
u64 initial_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Arena
|
|
||||||
{
|
|
||||||
u64 reserved_size;
|
|
||||||
u64 position;
|
|
||||||
u64 os_position;
|
|
||||||
u64 granularity;
|
|
||||||
u8 reserved[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u64 arena_minimum_position = sizeof(Arena);
|
|
||||||
|
|
||||||
fn Arena* arena_initialize(ArenaInitialization i)
|
|
||||||
{
|
|
||||||
ProtectionFlags protection_flags = {
|
|
||||||
.read = 1,
|
|
||||||
.write = 1,
|
|
||||||
};
|
|
||||||
MapFlags map_flags = {
|
|
||||||
.priv = 1,
|
|
||||||
.anonymous = 1,
|
|
||||||
.no_reserve = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* arena = (Arena*)os_reserve(0, i.reserved_size, protection_flags, map_flags);
|
|
||||||
os_commit(arena, i.initial_size, { .read = 1, .write = 1 });
|
|
||||||
|
|
||||||
*arena = {
|
|
||||||
.reserved_size = i.reserved_size,
|
|
||||||
.position = arena_minimum_position,
|
|
||||||
.os_position = i.initial_size,
|
|
||||||
.granularity = i.granularity,
|
|
||||||
};
|
|
||||||
|
|
||||||
return arena;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inline Arena* arena_initialize_default(u64 initial_size)
|
|
||||||
{
|
|
||||||
ArenaInitialization i = {
|
|
||||||
.reserved_size = 4 * gb,
|
|
||||||
.granularity = 4 * kb,
|
|
||||||
.initial_size = initial_size,
|
|
||||||
};
|
|
||||||
return arena_initialize(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment)
|
|
||||||
{
|
|
||||||
void* result = 0;
|
|
||||||
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
auto aligned_offset = align_forward(arena->position, alignment);
|
|
||||||
auto aligned_size_after = aligned_offset + size;
|
|
||||||
|
|
||||||
if (aligned_size_after > arena->os_position)
|
|
||||||
{
|
|
||||||
auto target_commited_size = align_forward(aligned_size_after, arena->granularity);
|
|
||||||
auto size_to_commit = target_commited_size - arena->os_position;
|
|
||||||
auto commit_pointer = ((u8*)arena) + arena->os_position;
|
|
||||||
os_commit(commit_pointer, size_to_commit, { .read = 1, .write = 1 });
|
|
||||||
arena->os_position = target_commited_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (u8*)arena + aligned_offset;
|
|
||||||
arena->position = aligned_size_after;
|
|
||||||
assert(arena->position <= arena->os_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
fn Slice<T> arena_allocate(Arena* arena, u64 count)
|
|
||||||
{
|
|
||||||
return { (T*)arena_allocate_bytes(arena, sizeof(T) * count, alignof(T)), count };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String arena_join_string(Arena* arena, Slice<String> pieces)
|
|
||||||
{
|
|
||||||
u64 size = 0;
|
|
||||||
for (auto piece : pieces)
|
|
||||||
{
|
|
||||||
size += piece.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* pointer = (u8*)arena_allocate_bytes(arena, size + 1, 1);
|
|
||||||
u64 i = 0;
|
|
||||||
for (auto piece : pieces)
|
|
||||||
{
|
|
||||||
memcpy(pointer + i, piece.pointer, piece.length);
|
|
||||||
i += piece.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(i == size);
|
|
||||||
pointer[i] = 0;
|
|
||||||
|
|
||||||
return { pointer, size };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String arena_duplicate_string(Arena* arena, String string)
|
|
||||||
{
|
|
||||||
auto memory = (u8*)arena_allocate_bytes(arena, string.length + 1, 1);
|
|
||||||
memcpy(memory, string.pointer, string.length);
|
|
||||||
memory[string.length] = 0;
|
|
||||||
return { memory, string.length};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void arena_restore(Arena* arena, u64 position)
|
|
||||||
{
|
|
||||||
assert(position <= arena->position);
|
|
||||||
arena->position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn void arena_reset(Arena* arena)
|
|
||||||
{
|
|
||||||
arena->position = arena_minimum_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String path_absolute(Arena* arena, String relative_path)
|
|
||||||
{
|
|
||||||
u8 buffer[4096];
|
|
||||||
auto stack = path_absolute_stack(array_to_slice(buffer), relative_path);
|
|
||||||
auto result = arena_duplicate_string(arena, stack);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn String file_read(Arena* arena, String file_path)
|
|
||||||
{
|
|
||||||
auto fd = os_open(file_path, { .read = 1 }, { .read = 1 });
|
|
||||||
String result = {};
|
|
||||||
|
|
||||||
if (is_file_valid(fd))
|
|
||||||
{
|
|
||||||
auto file_size = os_file_size(fd);
|
|
||||||
result = arena_allocate<u8>(arena, file_size);
|
|
||||||
os_read(fd, result, file_size);
|
|
||||||
os_close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define bb_fail() os_is_debugger_present() ? trap() : exit(1)
|
|
||||||
#define bb_fail_with_message(message) (print(message), bb_fail())
|
|
||||||
|
|
||||||
fn u64 next_power_of_two(u64 n)
|
|
||||||
{
|
|
||||||
n -= 1;
|
|
||||||
n |= n >> 1;
|
|
||||||
n |= n >> 2;
|
|
||||||
n |= n >> 4;
|
|
||||||
n |= n >> 8;
|
|
||||||
n |= n >> 16;
|
|
||||||
n |= n >> 32;
|
|
||||||
n += 1;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u8 format_integer_decimal(String buffer, u64 v)
|
|
||||||
{
|
|
||||||
u8 byte_count = 0;
|
|
||||||
auto value = v;
|
|
||||||
|
|
||||||
if (value != 0)
|
|
||||||
{
|
|
||||||
u8 reverse_buffer[64];
|
|
||||||
u8 reverse_index = 0;
|
|
||||||
|
|
||||||
while (value != 0)
|
|
||||||
{
|
|
||||||
auto digit_value = (u8)(value % 10);
|
|
||||||
auto ascii_character = digit_value + '0';
|
|
||||||
value /= 10;
|
|
||||||
reverse_buffer[reverse_index] = ascii_character;
|
|
||||||
reverse_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (reverse_index != 0)
|
|
||||||
{
|
|
||||||
reverse_index -= 1;
|
|
||||||
buffer[byte_count] = reverse_buffer[reverse_index];
|
|
||||||
byte_count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer[0] = '0';
|
|
||||||
byte_count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return byte_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ExecuteStandardStreamPolicy : u8
|
|
||||||
{
|
|
||||||
inherit,
|
|
||||||
pipe,
|
|
||||||
ignore,
|
|
||||||
};
|
|
||||||
|
|
||||||
global_variable constexpr u64 standard_stream_count = 2;
|
|
||||||
|
|
||||||
struct ExecuteOptions
|
|
||||||
{
|
|
||||||
ExecuteStandardStreamPolicy policies[standard_stream_count]; // INDICES: stdout = 0, stderr = 1
|
|
||||||
s32 null_file_descriptor = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TerminationKind : u8
|
|
||||||
{
|
|
||||||
unknown,
|
|
||||||
exit,
|
|
||||||
signal,
|
|
||||||
stop,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Execution
|
|
||||||
{
|
|
||||||
String streams[2];
|
|
||||||
TerminationKind termination_kind;
|
|
||||||
u32 termination_code;
|
|
||||||
};
|
|
||||||
|
|
||||||
Execution os_execute(Arena* arena, Slice<char* const> arguments, Slice<char* const> environment, ExecuteOptions options);
|
|
||||||
char* os_get_environment_variable(const char* env);
|
|
316
src/llvm.cpp
316
src/llvm.cpp
@ -1,316 +0,0 @@
|
|||||||
#include <llvm.hpp>
|
|
||||||
|
|
||||||
#include "llvm/Config/llvm-config.h"
|
|
||||||
|
|
||||||
#include "llvm/IR/IRBuilder.h"
|
|
||||||
#include "llvm/IR/Module.h"
|
|
||||||
#include "llvm/IR/Verifier.h"
|
|
||||||
#include "llvm/IR/DebugInfo.h"
|
|
||||||
#include "llvm/IR/LegacyPassManager.h"
|
|
||||||
|
|
||||||
#include "llvm/Passes/PassBuilder.h"
|
|
||||||
|
|
||||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
||||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
||||||
|
|
||||||
#include "llvm/Frontend/Driver/CodeGenOptions.h"
|
|
||||||
|
|
||||||
#include "llvm/TargetParser/Host.h"
|
|
||||||
#include "llvm/TargetParser/SubtargetFeature.h"
|
|
||||||
|
|
||||||
#include "llvm/Target/TargetMachine.h"
|
|
||||||
|
|
||||||
#include "llvm/MC/TargetRegistry.h"
|
|
||||||
|
|
||||||
#include "llvm/Support/FileSystem.h"
|
|
||||||
|
|
||||||
#include "lld/Common/CommonLinkerContext.h"
|
|
||||||
|
|
||||||
EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type)
|
|
||||||
{
|
|
||||||
auto sp = llvm::unwrap<llvm::DISubprogram>(subprogram);
|
|
||||||
sp->replaceType(llvm::unwrap<llvm::DISubroutineType>(subroutine_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are multiple uses of the return-value slot, just check
|
|
||||||
// for something immediately preceding the IP. Sometimes this can
|
|
||||||
// happen with how we generate implicit-returns; it can also happen
|
|
||||||
// with noreturn cleanups.
|
|
||||||
fn llvm::StoreInst* get_store_if_valid(llvm::User* user, llvm::Value* return_alloca, llvm::Type* element_type)
|
|
||||||
{
|
|
||||||
auto *SI = dyn_cast<llvm::StoreInst>(user);
|
|
||||||
if (!SI || SI->getPointerOperand() != return_alloca ||
|
|
||||||
SI->getValueOperand()->getType() != element_type)
|
|
||||||
return nullptr;
|
|
||||||
// These aren't actually possible for non-coerced returns, and we
|
|
||||||
// only care about non-coerced returns on this code path.
|
|
||||||
// All memory instructions inside __try block are volatile.
|
|
||||||
assert(!SI->isAtomic() &&
|
|
||||||
(!SI->isVolatile()
|
|
||||||
//|| CGF.currentFunctionUsesSEHTry())
|
|
||||||
));
|
|
||||||
return SI;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy of static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
|
|
||||||
// in clang/lib/CodeGen/CGCall.cpp:3526 in LLVM 19
|
|
||||||
EXPORT LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et)
|
|
||||||
{
|
|
||||||
auto builder = llvm::unwrap(b);
|
|
||||||
auto return_alloca = llvm::unwrap(ra);
|
|
||||||
auto element_type = llvm::unwrap(et);
|
|
||||||
// Check if a User is a store which pointerOperand is the ReturnValue.
|
|
||||||
// We are looking for stores to the ReturnValue, not for stores of the
|
|
||||||
// ReturnValue to some other location.
|
|
||||||
if (!return_alloca->hasOneUse()) {
|
|
||||||
llvm::BasicBlock *IP = builder->GetInsertBlock();
|
|
||||||
if (IP->empty()) return nullptr;
|
|
||||||
|
|
||||||
// Look at directly preceding instruction, skipping bitcasts and lifetime
|
|
||||||
// markers.
|
|
||||||
for (llvm::Instruction &I : make_range(IP->rbegin(), IP->rend())) {
|
|
||||||
if (isa<llvm::BitCastInst>(&I))
|
|
||||||
continue;
|
|
||||||
if (auto *II = dyn_cast<llvm::IntrinsicInst>(&I))
|
|
||||||
if (II->getIntrinsicID() == llvm::Intrinsic::lifetime_end)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return wrap(get_store_if_valid(&I, return_alloca, element_type));
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::StoreInst *store = get_store_if_valid(return_alloca->user_back(), return_alloca, element_type);
|
|
||||||
if (!store) return nullptr;
|
|
||||||
|
|
||||||
// Now do a first-and-dirty dominance check: just walk up the
|
|
||||||
// single-predecessors chain from the current insertion point.
|
|
||||||
llvm::BasicBlock *StoreBB = store->getParent();
|
|
||||||
llvm::BasicBlock *IP = builder->GetInsertBlock();
|
|
||||||
llvm::SmallPtrSet<llvm::BasicBlock *, 4> SeenBBs;
|
|
||||||
while (IP != StoreBB) {
|
|
||||||
if (!SeenBBs.insert(IP).second || !(IP = IP->getSinglePredecessor()))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, the store's basic block dominates the insertion point; we
|
|
||||||
// can do our thing.
|
|
||||||
return wrap(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options)
|
|
||||||
{
|
|
||||||
auto module = llvm::unwrap(m);
|
|
||||||
auto target_machine = (llvm::TargetMachine*)tm;
|
|
||||||
// TODO: PGO
|
|
||||||
// TODO: CS profile
|
|
||||||
|
|
||||||
llvm::PipelineTuningOptions pipeline_tuning_options;
|
|
||||||
pipeline_tuning_options.LoopUnrolling = options.loop_unrolling;
|
|
||||||
pipeline_tuning_options.LoopInterleaving = options.loop_interleaving;
|
|
||||||
pipeline_tuning_options.LoopVectorization = options.loop_vectorization;
|
|
||||||
pipeline_tuning_options.SLPVectorization = options.slp_vectorization;
|
|
||||||
pipeline_tuning_options.MergeFunctions = options.merge_functions;
|
|
||||||
pipeline_tuning_options.CallGraphProfile = options.call_graph_profile;
|
|
||||||
pipeline_tuning_options.UnifiedLTO = options.unified_lto;
|
|
||||||
|
|
||||||
// TODO: instrumentation
|
|
||||||
|
|
||||||
llvm::LoopAnalysisManager loop_analysis_manager;
|
|
||||||
llvm::FunctionAnalysisManager function_analysis_manager;
|
|
||||||
llvm::CGSCCAnalysisManager cgscc_analysis_manager;
|
|
||||||
llvm::ModuleAnalysisManager module_analysis_manager;
|
|
||||||
|
|
||||||
llvm::PassBuilder pass_builder(target_machine, pipeline_tuning_options);
|
|
||||||
|
|
||||||
if (options.assignment_tracking && options.debug_info != 0)
|
|
||||||
{
|
|
||||||
pass_builder.registerPipelineStartEPCallback([&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) {
|
|
||||||
unused(Level);
|
|
||||||
MPM.addPass(llvm::AssignmentTrackingPass());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718
|
|
||||||
// TODO: add library (?)
|
|
||||||
std::unique_ptr<llvm::TargetLibraryInfoImpl> TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary));
|
|
||||||
function_analysis_manager.registerPass([&] { return llvm::TargetLibraryAnalysis(*TLII); });
|
|
||||||
|
|
||||||
pass_builder.registerModuleAnalyses(module_analysis_manager);
|
|
||||||
pass_builder.registerCGSCCAnalyses(cgscc_analysis_manager);
|
|
||||||
pass_builder.registerFunctionAnalyses(function_analysis_manager);
|
|
||||||
pass_builder.registerLoopAnalyses(loop_analysis_manager);
|
|
||||||
pass_builder.crossRegisterProxies(loop_analysis_manager, function_analysis_manager, cgscc_analysis_manager, module_analysis_manager);
|
|
||||||
|
|
||||||
llvm::ModulePassManager module_pass_manager;
|
|
||||||
|
|
||||||
if (options.verify_module)
|
|
||||||
{
|
|
||||||
module_pass_manager.addPass(llvm::VerifierPass());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool thin_lto = false;
|
|
||||||
bool lto = false;
|
|
||||||
|
|
||||||
llvm::OptimizationLevel optimization_level;
|
|
||||||
switch ((BBLLVMOptimizationLevel)options.optimization_level)
|
|
||||||
{
|
|
||||||
case BBLLVMOptimizationLevel::O0: optimization_level = llvm::OptimizationLevel::O0; break;
|
|
||||||
case BBLLVMOptimizationLevel::O1: optimization_level = llvm::OptimizationLevel::O1; break;
|
|
||||||
case BBLLVMOptimizationLevel::O2: optimization_level = llvm::OptimizationLevel::O2; break;
|
|
||||||
case BBLLVMOptimizationLevel::O3: optimization_level = llvm::OptimizationLevel::O3; break;
|
|
||||||
case BBLLVMOptimizationLevel::Os: optimization_level = llvm::OptimizationLevel::Os; break;
|
|
||||||
case BBLLVMOptimizationLevel::Oz: optimization_level = llvm::OptimizationLevel::Oz; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: thin lto post-link
|
|
||||||
// TODO: instrument
|
|
||||||
if (thin_lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else if (lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else if (lto) {
|
|
||||||
__builtin_trap(); // TODO
|
|
||||||
} else {
|
|
||||||
module_pass_manager.addPass(pass_builder.buildPerModuleDefaultPipeline(optimization_level));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: if emit bitcode/IR
|
|
||||||
|
|
||||||
module_pass_manager.run(*module, module_analysis_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options)
|
|
||||||
{
|
|
||||||
auto module = llvm::unwrap(m);
|
|
||||||
auto target_machine = (llvm::TargetMachine*)tm;
|
|
||||||
|
|
||||||
// We still use the legacy PM to run the codegen pipeline since the new PM
|
|
||||||
// does not work with the codegen pipeline.
|
|
||||||
// FIXME: make the new PM work with the codegen pipeline.
|
|
||||||
llvm::legacy::PassManager CodeGenPasses;
|
|
||||||
#if LLVM_VERSION_MAJOR >= 19
|
|
||||||
if (options->optimize_when_possible)
|
|
||||||
{
|
|
||||||
CodeGenPasses.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
llvm::raw_pwrite_stream* dwarf_object_file = 0;
|
|
||||||
if (options->output_dwarf_file_path.length)
|
|
||||||
{
|
|
||||||
__builtin_trap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->optimize_when_possible)
|
|
||||||
{
|
|
||||||
llvm::Triple target_triple = target_machine->getTargetTriple(); // Need to make a copy, incoming bugfix: https://github.com/llvm/llvm-project/pull/127718
|
|
||||||
// TODO: add library (?)
|
|
||||||
std::unique_ptr<llvm::TargetLibraryInfoImpl> TLII(llvm::driver::createTLII(target_triple, llvm::driver::VectorLibrary::NoLibrary));
|
|
||||||
CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<llvm::raw_pwrite_stream> stream;
|
|
||||||
|
|
||||||
if (options->output_file_path.length)
|
|
||||||
{
|
|
||||||
std::error_code error_code;
|
|
||||||
|
|
||||||
stream = std::make_unique<llvm::raw_fd_ostream>(llvm::StringRef((char*)options->output_file_path.pointer, options->output_file_path.length), error_code, llvm::sys::fs::OF_None);
|
|
||||||
|
|
||||||
if (error_code)
|
|
||||||
{
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::failed_to_create_file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream = std::make_unique<llvm::raw_null_ostream>();
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::CodeGenFileType file_type;
|
|
||||||
switch (options->file_type)
|
|
||||||
{
|
|
||||||
case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break;
|
|
||||||
case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break;
|
|
||||||
case BBLLVMCodeGenerationFileType::null: file_type = llvm::CodeGenFileType::Null; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto disable_verify = !options->verify_module;
|
|
||||||
if (target_machine->addPassesToEmitFile(CodeGenPasses, *stream, dwarf_object_file, file_type, disable_verify))
|
|
||||||
{
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::failed_to_add_emit_passes;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeGenPasses.run(*module);
|
|
||||||
|
|
||||||
return BBLLVMCodeGenerationPipelineResult::success;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define lld_api_function_signature(name) bool name(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput)
|
|
||||||
|
|
||||||
#define lld_link_decl(link_name) \
|
|
||||||
namespace link_name \
|
|
||||||
{\
|
|
||||||
lld_api_function_signature(link);\
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef lld_api_function_signature(LinkerFunction);
|
|
||||||
|
|
||||||
namespace lld
|
|
||||||
{
|
|
||||||
lld_link_decl(coff);
|
|
||||||
lld_link_decl(elf);
|
|
||||||
lld_link_decl(mingw);
|
|
||||||
lld_link_decl(macho);
|
|
||||||
lld_link_decl(wasm);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function)
|
|
||||||
{
|
|
||||||
LLDResult result = {};
|
|
||||||
auto arguments = llvm::ArrayRef(argument_pointer, argument_count);
|
|
||||||
|
|
||||||
std::string stdout_string;
|
|
||||||
llvm::raw_string_ostream stdout_stream(stdout_string);
|
|
||||||
|
|
||||||
std::string stderr_string;
|
|
||||||
llvm::raw_string_ostream stderr_stream(stderr_string);
|
|
||||||
|
|
||||||
result.success = linker_function(arguments, stdout_stream, stderr_stream, exit_early, disable_output);
|
|
||||||
|
|
||||||
auto stdout_length = stdout_string.length();
|
|
||||||
if (stdout_length)
|
|
||||||
{
|
|
||||||
auto* stdout_pointer = new u8[stdout_length + 1];
|
|
||||||
memcpy(stdout_pointer, stdout_string.data(), stdout_length);
|
|
||||||
result.stdout_string = { stdout_pointer, stdout_length };
|
|
||||||
stdout_pointer[stdout_length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto stderr_length = stderr_string.length();
|
|
||||||
if (stderr_length)
|
|
||||||
{
|
|
||||||
auto* stderr_pointer = new u8[stderr_length + 1];
|
|
||||||
memcpy(stderr_pointer, stderr_string.data(), stderr_length);
|
|
||||||
result.stderr_string = { stderr_pointer, stderr_length };
|
|
||||||
stderr_pointer[stderr_length] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should we only call it on success?
|
|
||||||
lld::CommonLinkerContext::destroy();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define lld_api_function_impl(link_name) \
|
|
||||||
EXPORT lld_api_function_decl(link_name)\
|
|
||||||
{\
|
|
||||||
return lld_api_generic(argument_pointer, argument_count, exit_early, disable_output, lld::link_name::link);\
|
|
||||||
}
|
|
||||||
|
|
||||||
// lld_api_function_impl(coff)
|
|
||||||
lld_api_function_impl(elf)
|
|
||||||
// lld_api_function_impl(mingw)
|
|
||||||
// lld_api_function_impl(macho)
|
|
||||||
// lld_api_function_impl(wasm)
|
|
84
src/llvm.hpp
84
src/llvm.hpp
@ -1,84 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lib.hpp>
|
|
||||||
#include <llvm-c/Core.h>
|
|
||||||
#include <llvm-c/DebugInfo.h>
|
|
||||||
#include <llvm-c/Analysis.h>
|
|
||||||
#include <llvm-c/Target.h>
|
|
||||||
#include <llvm-c/Analysis.h>
|
|
||||||
#include <llvm-c/TargetMachine.h>
|
|
||||||
#include <llvm-c/Transforms/PassBuilder.h>
|
|
||||||
|
|
||||||
struct LLDResult
|
|
||||||
{
|
|
||||||
String stdout_string;
|
|
||||||
String stderr_string;
|
|
||||||
bool success;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class BBLLVMOptimizationLevel : u8
|
|
||||||
{
|
|
||||||
O0 = 0,
|
|
||||||
O1 = 1,
|
|
||||||
O2 = 2,
|
|
||||||
O3 = 3,
|
|
||||||
Os = 4,
|
|
||||||
Oz = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DwarfType
|
|
||||||
{
|
|
||||||
void_type = 0x0,
|
|
||||||
address = 0x1,
|
|
||||||
boolean = 0x2,
|
|
||||||
complex_float = 0x3,
|
|
||||||
float_type = 0x4,
|
|
||||||
signed_type = 0x5,
|
|
||||||
signed_char = 0x6,
|
|
||||||
unsigned_type = 0x7,
|
|
||||||
unsigned_char = 0x8,
|
|
||||||
|
|
||||||
// DWARF 3.
|
|
||||||
imaginary_float = 0x9,
|
|
||||||
packed_decimal = 0xa,
|
|
||||||
numeric_string = 0xb,
|
|
||||||
edited = 0xc,
|
|
||||||
signed_fixed = 0xd,
|
|
||||||
unsigned_fixed = 0xe,
|
|
||||||
decimal_float = 0xf,
|
|
||||||
|
|
||||||
// DWARF 4.
|
|
||||||
UTF = 0x10,
|
|
||||||
|
|
||||||
// DWARF 5.
|
|
||||||
UCS = 0x11,
|
|
||||||
ASCII = 0x12,
|
|
||||||
|
|
||||||
// HP extensions.
|
|
||||||
HP_float80 = 0x80, // Floating-point (80 bit).
|
|
||||||
HP_complex_float80 = 0x81, // Complex floating-point (80 bit).
|
|
||||||
HP_float128 = 0x82, // Floating-point (128 bit).
|
|
||||||
HP_complex_float128 = 0x83, // Complex fp (128 bit).
|
|
||||||
HP_floathpintel = 0x84, // Floating-point (82 bit IA64).
|
|
||||||
HP_imaginary_float80 = 0x85,
|
|
||||||
HP_imaginary_float128 = 0x86,
|
|
||||||
HP_VAX_float = 0x88, // F or G floating.
|
|
||||||
HP_VAX_float_d = 0x89, // D floating.
|
|
||||||
HP_packed_decimal = 0x8a, // Cobol.
|
|
||||||
HP_zoned_decimal = 0x8b, // Cobol.
|
|
||||||
HP_edited = 0x8c, // Cobol.
|
|
||||||
HP_signed_fixed = 0x8d, // Cobol.
|
|
||||||
HP_unsigned_fixed = 0x8e, // Cobol.
|
|
||||||
HP_VAX_complex_float = 0x8f, // F or G floating complex.
|
|
||||||
HP_VAX_complex_float_d = 0x90, // D floating complex.
|
|
||||||
};
|
|
||||||
|
|
||||||
fn bool llvm_initialized = false;
|
|
||||||
|
|
||||||
extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et);
|
|
||||||
|
|
||||||
extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type);
|
|
||||||
|
|
||||||
#define lld_api_args() char* const* argument_pointer, u64 argument_count, bool exit_early, bool disable_output
|
|
||||||
#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args())
|
|
||||||
extern "C" lld_api_function_decl(elf);
|
|
4066
src/parser.cpp
4066
src/parser.cpp
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user