From ace033c3f64d5f7ffffcc044c369a07f7ad4bbdb Mon Sep 17 00:00:00 2001
From: David Gonzalez Martin <davidgm94.work@protonmail.com>
Date: Tue, 22 Oct 2024 08:15:07 -0600
Subject: [PATCH] Start writing LLVM code

---
 .github/workflows/ci.yml       |   8 +-
 CMakeLists.txt                 |  78 +++++++
 bootstrap/include/nest/base.h  |  27 +++
 bootstrap/include/nest/llvm.h  |   8 +
 bootstrap/include/std/base.h   |  66 +++---
 bootstrap/include/std/os.h     |  12 +-
 bootstrap/nest/llvm.cpp        | 158 +++++++++++++++
 bootstrap/nest/main.c          | 361 ++++++++++++++++-----------------
 bootstrap/runner/runner.c      |   9 +-
 bootstrap/std/base.c           |  26 +--
 bootstrap/std/md5.c            |   6 +-
 bootstrap/std/os.c             |  64 +++---
 bootstrap/std/sha1.c           |   2 +-
 bootstrap/std/string.c         |   2 +-
 bootstrap/std/virtual_buffer.c |   8 +-
 project.sh                     |   2 +-
 16 files changed, 551 insertions(+), 286 deletions(-)
 create mode 100644 bootstrap/include/nest/llvm.h
 create mode 100644 bootstrap/nest/llvm.cpp

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b35c496..b38cc98 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4
       - name: Install dependencies
-        run: sudo apt install -y ninja-build mold
+        run: sudo apt install -y llvm llvm-dev ninja-build mold curl libssl-dev libcurl4-openssl-dev
       - name: System information
         run: |
           uname -a
@@ -32,7 +32,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4
       - name: Install dependencies
-        run: sudo apt install -y ninja-build mold
+        run: sudo apt install -y llvm llvm-dev ninja-build mold curl libssl-dev libcurl4-openssl-dev
       - name: System information
         run: |
           uname -a
@@ -48,7 +48,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4
       - name: Install dependencies
-        run: sudo apt install -y ninja-build mold
+        run: sudo apt install -y llvm llvm-dev ninja-build mold curl libssl-dev libcurl4-openssl-dev
       - name: System information
         run: |
           uname -a
@@ -64,7 +64,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v4
       - name: Install dependencies
-        run: sudo apt install -y ninja-build mold
+        run: sudo apt install -y llvm llvm-dev ninja-build mold curl libssl-dev libcurl4-openssl-dev
       - name: System information
         run: |
           uname -a
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 829f3fc..c94758f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,7 @@ project(nest)
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 set(CMAKE_C_STANDARD 23)
 set(CMAKE_CXX_STANDARD 23)
+
 add_compile_options(
     -pedantic
     -Wall -Wextra -Wpedantic
@@ -16,6 +17,82 @@ include_directories("bootstrap/include")
 
 find_package(LLVM REQUIRED CONFIG)
 
+find_program(LLVM_CONFIG_EXE
+    NAMES llvm-config-19 llvm-config-19.0 llvm-config190 llvm-config19 llvm-config NAMES_PER_DIR
+    PATHS
+    "/mingw64/bin"
+    "/c/msys64/mingw64/bin"
+    "c:/msys64/mingw64/bin"
+    "C:/Libraries/llvm-19.0.0/bin")
+
+if ("${LLVM_CONFIG_EXE}" STREQUAL "LLVM_CONFIG_EXE-NOTFOUND")
+    if (NOT LLVM_CONFIG_ERROR_MESSAGES STREQUAL "")
+        list(JOIN LLVM_CONFIG_ERROR_MESSAGES "\n" LLVM_CONFIG_ERROR_MESSAGE)
+        message(FATAL_ERROR ${LLVM_CONFIG_ERROR_MESSAGE})
+    else()
+        message(FATAL_ERROR "unable to find llvm-config")
+    endif()
+endif()
+
+
+execute_process(
+    COMMAND ${LLVM_CONFIG_EXE} --libs
+    OUTPUT_VARIABLE LLVM_LIBRARIES_SPACES
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+string(REPLACE " " ";" LLVM_LIBRARIES "${LLVM_LIBRARIES_SPACES}")
+
+execute_process(
+    COMMAND ${LLVM_CONFIG_EXE} --libdir
+    OUTPUT_VARIABLE LLVM_LIBDIRS_SPACES
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+string(REPLACE " " ";" LLVM_LIBDIRS "${LLVM_LIBDIRS_SPACES}")
+
+execute_process(
+    COMMAND ${LLVM_CONFIG_EXE} --system-libs
+    OUTPUT_VARIABLE LLVM_SYSTEM_LIBS_SPACES
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+string(REPLACE " " ";" LLVM_SYSTEM_LIBS "${LLVM_SYSTEM_LIBS_SPACES}")
+
+execute_process(
+    COMMAND ${LLVM_CONFIG_EXE} --shared-mode
+    OUTPUT_VARIABLE LLVM_LINK_MODE
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if (${LLVM_LINK_MODE} STREQUAL "shared")
+    # We always ask for the system libs corresponding to static linking,
+    # since on some distros LLD is only available as a static library
+    # and we need these libraries to link it successfully
+    execute_process(
+        COMMAND ${LLVM_CONFIG_EXE} --system-libs --link-static
+        OUTPUT_VARIABLE LLVM_STATIC_SYSTEM_LIBS_SPACES
+        ERROR_QUIET # Some installations have no static libs, we just ignore the failure
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+    string(REPLACE " " ";" LLVM_STATIC_SYSTEM_LIBS "${LLVM_STATIC_SYSTEM_LIBS_SPACES}")
+
+    set(LLVM_LIBRARIES ${LLVM_LIBRARIES} ${LLVM_SYSTEM_LIBS} ${LLVM_STATIC_SYSTEM_LIBS})
+else()
+    set(LLVM_LIBRARIES ${LLVM_LIBRARIES} ${LLVM_SYSTEM_LIBS})
+endif()
+
+execute_process(
+    COMMAND ${LLVM_CONFIG_EXE} --includedir
+    OUTPUT_VARIABLE LLVM_INCLUDE_DIRS_SPACES
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+string(REPLACE " " ";" LLVM_INCLUDE_DIRS "${LLVM_INCLUDE_DIRS_SPACES}")
+
+if (APPLE)
+    if (MSVC)
+        list(REMOVE_ITEM LLVM_LIBRARIES "zstd.lib")
+    else()
+        list(REMOVE_ITEM LLVM_LIBRARIES "-lzstd")
+    endif()
+
+    find_library(ZSTD NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR)
+    list(APPEND LLVM_LIBRARIES "${ZSTD}")
+endif()
+
+link_directories("${LLVM_LIBDIRS}")
+
 set(LIBRARY_NAME "std")
 set(RUNNER_NAME "runner")
 set(COMPILER_NAME "nest")
@@ -35,6 +112,7 @@ target_link_libraries(${RUNNER_NAME} PRIVATE ${LIBRARY_NAME})
 add_executable("${COMPILER_NAME}"
     "bootstrap/nest/main.c"
     "bootstrap/nest/pdb_image.c"
+    "bootstrap/nest/llvm.cpp"
 )
 
 target_compile_definitions(${COMPILER_NAME} PRIVATE ${LLVM_DEFINITIONS})
diff --git a/bootstrap/include/nest/base.h b/bootstrap/include/nest/base.h
index 1f7f431..2548b75 100644
--- a/bootstrap/include/nest/base.h
+++ b/bootstrap/include/nest/base.h
@@ -1,3 +1,5 @@
+#pragma once
+
 #include <std/base.h>
 
 typedef enum CompilerBackend : u8
@@ -7,4 +9,29 @@ typedef enum CompilerBackend : u8
     COMPILER_BACKEND_COUNT,
 } CompilerBackend;
 
+typedef enum CpuArchitecture : u8
+{
+    CPU_ARCH_X86_64,
+    CPU_ARCH_AARCH64,
+} CpuArchitecture;
 
+typedef enum OperatingSystem : u8
+{
+    OPERATING_SYSTEM_LINUX,
+    OPERATING_SYSTEM_MAC,
+    OPERATING_SYSTEM_WINDOWS,
+} OperatingSystem;
+
+STRUCT(Target)
+{
+    CpuArchitecture cpu;
+    OperatingSystem os;
+};
+
+STRUCT(CodegenOptions)
+{
+    String test_name;
+    Target target;
+    CompilerBackend backend;
+    u8 generate_debug_information;
+};
diff --git a/bootstrap/include/nest/llvm.h b/bootstrap/include/nest/llvm.h
new file mode 100644
index 0000000..4897672
--- /dev/null
+++ b/bootstrap/include/nest/llvm.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <nest/base.h>
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void llvm_codegen(CodegenOptions options);
diff --git a/bootstrap/include/std/base.h b/bootstrap/include/std/base.h
index 5c47126..47a9f80 100644
--- a/bootstrap/include/std/base.h
+++ b/bootstrap/include/std/base.h
@@ -39,6 +39,18 @@ typedef double f64;
 typedef u32 Hash32;
 typedef u64 Hash64;
 
+#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 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
@@ -69,6 +81,9 @@ declare_slice_p(char);
 typedef Slice(u8) String;
 declare_slice(String);
 
+#define NamedEnumMemberEnum(e, enum_member) e ## _ ## enum_member
+#define NamedEnumMemberString(e, enum_member) strlit(#enum_member)
+
 typedef SliceP(char) CStringSlice;
 
 #ifdef _WIN32
@@ -92,17 +107,18 @@ FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.poin
 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
-
 #define INFINITY __builtin_inff()
 #define NAN __builtin_nanf("")
 #define fn static
 #define method __attribute__((visibility("internal")))
-#define global static
+#define global_variable static
 #define forceinline __attribute__((always_inline))
 #define likely(x) __builtin_expect(!!(x), 1)
 #define unlikely(x) __builtin_expect(!!(x), 0)
 #define breakpoint() __builtin_debugtrap()
-#define fail() trap()
+#define failed_execution() trap()
+
+
 #define trap() bad_exit("Trap reached", __FILE__, __LINE__)
 #define array_length(arr) sizeof(arr) / sizeof((arr)[0])
 #define KB(n) ((n) * 1024)
@@ -111,8 +127,8 @@ FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.poin
 #define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024)
 #define unused(x) (void)(x)
 #define may_be_unused __attribute__((unused))
-#define trunc(Destination, source) (Destination)(source)
-#define cast(Destination, Source, source) cast_ ## Source ## _to_ ## Destination (source, __FILE__, __LINE__)
+#define truncate_value(Destination, source) (Destination)(source)
+#define cast_to(Destination, Source, source) cast_ ## Source ## _to_ ## Destination (source, __FILE__, __LINE__)
 #define bad_exit(message, file, line) do { print(message " at {cstr}:{u32}\n", file, line); __builtin_trap(); } while(0)
 #define size_until_end(T, field_name) (sizeof(T) - offsetof(T, field_name))
 #define SWAP(a, b) \
@@ -122,7 +138,6 @@ FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.poin
         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)
@@ -137,14 +152,14 @@ FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.poin
 
 #define case_to_name(prefix, e) case prefix ## e: return strlit(#e)
 
-const may_be_unused global u8 brace_open = '{';
-const may_be_unused global u8 brace_close = '}';
+const may_be_unused global_variable u8 brace_open = '{';
+const may_be_unused global_variable u8 brace_close = '}';
 
-const may_be_unused global u8 parenthesis_open = '(';
-const may_be_unused global u8 parenthesis_close = ')';
+const may_be_unused global_variable u8 parenthesis_open = '(';
+const may_be_unused global_variable u8 parenthesis_close = ')';
 
-const may_be_unused global u8 bracket_open = '[';
-const may_be_unused global u8 bracket_close = ']';
+const may_be_unused global_variable u8 bracket_open = '[';
+const may_be_unused global_variable u8 bracket_close = ']';
 
 #define s_get(s, i) (s).pointer[i]
 #define s_get_pointer(s, i) &((s).pointer[i])
@@ -157,22 +172,23 @@ const may_be_unused global u8 bracket_close = ']';
 #define assert(x) unlikely(!(x))
 #endif
 
+#ifndef __cplusplus
+// Undefine unreachable if needed to provide a more safe-guard implementation
 #ifdef unreachable
 #undef unreachable
 #endif
-
 #if _DEBUG
 #define unreachable() bad_exit("Unreachable triggered", __FILE__, __LINE__)
 #else
 #define unreachable() __builtin_unreachable()
 #endif
 
-#ifdef static_assert
-#undef static_assert
-#endif
 #define static_assert(x) _Static_assert((x), "Static assert failed!")
 #define alignof(x) _Alignof(x)
 #define auto __auto_type
+#else
+#define restrict __restrict
+#endif
 
 #define todo() do { print("TODO at {cstr}:{u32}\n", __FILE__, __LINE__); __builtin_trap(); } while(0)
 
@@ -183,13 +199,13 @@ u8 is_power_of_two(u64 value);
 u8 first_bit_set_32(u32 value);
 u64 first_bit_set_64(u64 value);
 
-void* memcpy(void* const restrict dst, const void* const restrict src, usize size);
-void* memmove(void* const dst, const void* const src, usize n);
-void* memset(void* dst, int n, usize size);
-int memcmp(const void* a, const void* b, usize n);
-usize strlen (const char* c_string);
-int strcmp(const char* s1, const char* s2);
-int strncmp(const char* s1, const char* s2, usize length);
+EXPORT void* memcpy(void* const restrict dst, const void* const restrict src, usize size) NO_EXCEPT;
+EXPORT void* memmove(void* const dst, const void* const src, usize n) NO_EXCEPT;
+EXPORT void* memset(void* dst, int n, usize size) NO_EXCEPT;
+EXPORT int memcmp(const void* a, const void* b, usize n) NO_EXCEPT;
+EXPORT usize strlen (const char* c_string) NO_EXCEPT;
+EXPORT int strcmp(const char* s1, const char* s2) NO_EXCEPT;
+EXPORT int strncmp(const char* s1, const char* s2, usize length) NO_EXCEPT;
 
 u8 cast_u32_to_u8(u32 source, const char* name, int line);
 u16 cast_u32_to_u16(u32 source, const char* name, int line);
@@ -223,8 +239,8 @@ u64 is_alphabetic(u8 ch);
 
 u64 parse_decimal(String string);
 
-global const Hash64 fnv_offset = 14695981039346656037ull;
-global const u64 fnv_prime = 1099511628211ull;
+global_variable const Hash64 fnv_offset = 14695981039346656037ull;
+global_variable const u64 fnv_prime = 1099511628211ull;
 
 Hash32 hash32_fib_end(Hash32 hash);
 Hash32 hash64_fib_end(Hash64 hash);
diff --git a/bootstrap/include/std/os.h b/bootstrap/include/std/os.h
index 8a84106..514b520 100644
--- a/bootstrap/include/std/os.h
+++ b/bootstrap/include/std/os.h
@@ -42,16 +42,16 @@ STRUCT(Arena)
 };
 
 #if __APPLE__
-    const global u64 page_size = KB(16);
+    const global_variable u64 page_size = KB(16);
 #else
-    const global u64 page_size = KB(4);
+    const global_variable u64 page_size = KB(4);
 #endif
 
-global u64 minimum_granularity = page_size;
-// global u64 middle_granularity = MB(2);
-global u64 default_size = GB(4);
+global_variable u64 minimum_granularity = page_size;
+// global_variable u64 middle_granularity = MB(2);
+global_variable u64 default_size = GB(4);
 
-void print(const char* format, ...);
+EXPORT void print(const char* format, ...);
 void run_command(Arena* arena, CStringSlice arguments, char* envp[]);
 String file_read(Arena* arena, String path);
 
diff --git a/bootstrap/nest/llvm.cpp b/bootstrap/nest/llvm.cpp
new file mode 100644
index 0000000..459a368
--- /dev/null
+++ b/bootstrap/nest/llvm.cpp
@@ -0,0 +1,158 @@
+#include <std/os.h>
+#include <nest/base.h>
+
+#include <llvm/IR/IRBuilder.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>
+
+#define string_ref(lit) StringRef(lit, strlit_len(lit))
+
+namespace llvm
+{
+    // #define LLVMAttributeMembers(cb) \
+    //     cb(LLVMAttribute, naked), \
+    //     cb(LLVMAttribute, noreturn), \
+    //     cb(LLVMAttribute, nounwind), \
+    //     cb(LLVMAttribute, inreg), \
+    //     cb(LLVMAttribute, noalias), \
+    //     cb(LLVMAttribute, signext), \
+    //     cb(LLVMAttribute, zeroext), \
+    //
+    // typedef enum LLVMAttributeId : u32
+    // {
+    //     LLVMAttributeMembers(NamedEnumMemberEnum)
+    //     LLVM_ATTRIBUTE_COUNT,
+    // } LLVMAttribute;
+    //
+    // String llvm_attribute_names[] = {
+    //     LLVMAttributeMembers(NamedEnumMemberString)
+    // };
+    //
+    // STRUCT(LLVMAttributeLookupTable)
+    // {
+    //     u32 ids[LLVM_ATTRIBUTE_COUNT];
+    // };
+    //
+    // fn u32 llvm_attribute_id(String string)
+    // {
+    //     auto result = LLVMGetEnumAttributeKindForName(string_to_c(string), string.length);
+    //     static_assert(sizeof(result) == sizeof(u32));
+    //     return result;
+    // }
+
+#define llvm_initialize_target(target) \
+    LLVMInitialize ## target ## Target();\
+    LLVMInitialize ## target ## TargetInfo();\
+    LLVMInitialize ## target ## TargetMC();\
+    LLVMInitialize ## target ## AsmParser();\
+    LLVMInitialize ## target ## AsmPrinter()
+
+    fn void llvm_initialize_cpu(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_target(X86);
+                } break;
+            case CPU_ARCH_AARCH64:
+                {
+                    llvm_initialize_target(AArch64);
+                } break;
+        }
+    }
+
+    extern "C" void llvm_codegen(CodegenOptions options)
+    {
+        llvm_initialize_cpu(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
+        StringRef target_triple;
+        switch (options.target.os)
+        {
+            case OPERATING_SYSTEM_LINUX:
+                target_triple = string_ref("x86_64-unknown-linux-gnu");
+                break;
+            case OPERATING_SYSTEM_MAC:
+                target_triple = string_ref("aarch64-apple-macosx-none");
+                break;
+            case OPERATING_SYSTEM_WINDOWS:
+                target_triple = string_ref("x86_64-windows-gnu");
+                break;
+        }
+
+        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("baseline");
+        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);
+    }
+}
diff --git a/bootstrap/nest/main.c b/bootstrap/nest/main.c
index f713852..c05d230 100644
--- a/bootstrap/nest/main.c
+++ b/bootstrap/nest/main.c
@@ -7,6 +7,7 @@
 
 #include <nest/base.h>
 #include <nest/pdb_image.h>
+#include <nest/llvm.h>
 
 #ifdef __APPLE__
 #define clang_path "/opt/homebrew/opt/llvm/bin/clang"
@@ -1665,9 +1666,9 @@ STRUCT(TypePair)
     u32 raw;
 };
 decl_vb(TypePair);
-global const TypePair type_pair_invalid;
+global_variable const TypePair type_pair_invalid;
 
-global const u32 debug_mask = 0xffffff;
+global_variable const u32 debug_mask = 0xffffff;
 
 fn TypePair type_pair_make(DebugTypeIndex debug_type, BackendTypeId backend_type)
 {
@@ -1978,7 +1979,7 @@ fn void bitset_ensure_length(Bitset* bitset, u64 max)
     auto old_length = bitset->arr.length;
     if (old_length < length)
     {
-        auto new_element_count = cast(u32, u64, length - old_length);
+        auto new_element_count = cast_to(u32, u64, length - old_length);
         unused(vb_add(&bitset->arr, new_element_count));
     }
 }
@@ -2030,7 +2031,7 @@ typedef enum x86_64_RegisterClass : u8
     REGISTER_CLASS_X86_64_COUNT
 } x86_64_RegisterClass;
 
-const global u8 register_count_per_class[] = {
+const global_variable u8 register_count_per_class[] = {
     [0] = 0,
     [REGISTER_CLASS_X86_64_GPR] = 16,
     [REGISTER_CLASS_X86_64_XMM] = 16,
@@ -2065,7 +2066,7 @@ typedef enum RegisterMask_x86_64: u8
     REGISTER_MASK_EMPTY = 0,
     REGISTER_MASK_GPR = 1,
 } RegisterMask_x86_64;
-const global auto empty_register_mask = Index(RegisterMask, REGISTER_MASK_EMPTY);
+const global_variable auto empty_register_mask = Index(RegisterMask, REGISTER_MASK_EMPTY);
 
 STRUCT(RegisterMask)
 {
@@ -2158,7 +2159,7 @@ fn WorkListHandle thread_worklist_acquire(Thread* thread)
     u8 bitset = thread->worklist_bitset;
     if (bitset)
     {
-        auto index = cast(u8, s32, __builtin_ctz(~thread->worklist_bitset));
+        auto index = cast_to(u8, s32, __builtin_ctz(~thread->worklist_bitset));
         thread->worklist_bitset |= (1 << index);
         return (WorkListHandle)
         {
@@ -2424,12 +2425,12 @@ fn void node_ensure_capacity(Thread* thread, u32* offset, u16* capacity, u16 cur
 {
     auto current_offset = *offset;
     auto current_capacity = *capacity;
-    auto desired_capacity = cast(u16, u32, current_length + additional);
+    auto desired_capacity = cast_to(u16, u32, current_length + additional);
 
     if (desired_capacity > current_capacity)
     {
         auto* ptr = vb_add(&thread->buffer.uses, desired_capacity);
-        u32 new_offset = cast(u32, s64, ptr - thread->buffer.uses.pointer);
+        u32 new_offset = cast_to(u32, s64, ptr - thread->buffer.uses.pointer);
         memcpy(ptr, &thread->buffer.uses.pointer[current_offset], current_length * sizeof(NodeIndex));
         memset(ptr + current_length, 0, (desired_capacity - current_length) * sizeof(NodeIndex));
         *offset = new_offset;
@@ -2576,7 +2577,7 @@ fn s64 node_find(Slice(NodeIndex) nodes, NodeIndex node_index)
     {
         if (index_equal(nodes.pointer[i], node_index))
         {
-            result = cast(s64, u64, i);
+            result = cast_to(s64, u64, i);
             break;
         }
     }
@@ -2600,7 +2601,7 @@ fn u8 node_remove_output(Thread* thread, NodeIndex node_index, NodeIndex use_ind
     auto outputs = node_get_outputs(thread, node);
     auto maybe_index = node_find(outputs, use_index);
     assert(maybe_index != -1);
-    auto index = cast(u16, s64, maybe_index);
+    auto index = cast_to(u16, s64, maybe_index);
     thread_node_remove_use(thread, node->output_offset, &node->output_count, index);
     return node->output_count == 0;
 }
@@ -2637,7 +2638,7 @@ fn u8 node_remove_output(Thread* thread, NodeIndex node_index, NodeIndex use_ind
 //     auto inputs = node_get_inputs(thread, node);
 //     while (node->input_count > 0)
 //     {
-//         auto input_index = cast(u16, u32, node->input_count - 1);
+//         auto input_index = cast_to(u16, u32, node->input_count - 1);
 //         node->input_count = input_index;
 //         auto old_input_index = inputs.pointer[input_index];
 //
@@ -2717,12 +2718,12 @@ STRUCT(NodeCreate)
 
 fn NodeIndex thread_node_add(Thread* thread, NodeCreate data)
 {
-    auto input_count = cast(u16, u64, data.inputs.length);
+    auto input_count = cast_to(u16, u64, data.inputs.length);
     auto input_result = thread_get_node_reference_array(thread, input_count);
     memcpy(input_result.pointer, data.inputs.pointer, sizeof(NodeIndex) * input_count);
 
     auto* node = vb_add(&thread->buffer.nodes, 1);
-    auto node_index = Index(Node, cast(u32, s64, node - thread->buffer.nodes.pointer));
+    auto node_index = Index(Node, cast_to(u32, s64, node - thread->buffer.nodes.pointer));
     memset(node, 0, sizeof(Node));
     node->id = data.id;
     node->input_offset = input_result.index;
@@ -3067,7 +3068,7 @@ fn Hash32 debug_type_hash_index(Thread* thread, DebugTypeIndex type_index)
     return debug_type_hash(thread, type);
 }
 
-global const u64 intern_pool_min_capacity = 64;
+global_variable const u64 intern_pool_min_capacity = 64;
 STRUCT(GenericInternPool)
 {
     u32* pointer;
@@ -3150,7 +3151,7 @@ fn s64 ip_generic_find_slot(GenericInternPool* pool, Thread* thread, u32 item_in
         auto chunk = _mm256_loadu_si256((const __m256i_u*) ptr);
         auto is_zero = _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpeq_epi32(chunk, _mm256_setzero_si256())));
 #endif
-        auto occupied_slots_ahead = cast(u32, s32, __builtin_ctz((u32)is_zero));
+        auto occupied_slots_ahead = cast_to(u32, s32, __builtin_ctz((u32)is_zero));
 #else
         u32 occupied_slots_ahead = 0;
         for (u32 fake_i = it_index; fake_i < it_index + existing_capacity; fake_i += 1)
@@ -3191,7 +3192,7 @@ fn s64 ip_generic_find_slot(GenericInternPool* pool, Thread* thread, u32 item_in
 fn GenericInternPoolBufferResult ip_DebugType_add_to_buffer(Thread* thread)
 {
     auto* result = vb_add(&thread->buffer.debug_types, 1);
-    auto buffer_index = cast(u32, s64, result - thread->buffer.debug_types.pointer);
+    auto buffer_index = cast_to(u32, s64, result - thread->buffer.debug_types.pointer);
     auto type_index = Index(DebugType, buffer_index);
     static_assert(sizeof(type_index) == sizeof(u32));
     return (GenericInternPoolBufferResult) {
@@ -3234,7 +3235,7 @@ fn void ip_generic_ensure_capacity(GenericInternPool* pool, Thread* thread, u32
 
     if (destination_length > half_capacity)
     {
-        auto new_capacity = cast(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), intern_pool_min_capacity));
+        auto new_capacity = cast_to(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), intern_pool_min_capacity));
         auto* new_array = arena_allocate(thread->arena, u32, new_capacity);
         memset(new_array, 0, sizeof(u32) * new_capacity);
 
@@ -3269,7 +3270,7 @@ fn GenericGetOrPut ip_generic_get_or_put(GenericInternPool* pool, Thread* thread
         auto maybe_slot = ip_generic_find_slot(pool, thread, item_index, hash, interface);
         if (maybe_slot != -1)
         {
-            auto index = cast(u32, s64, maybe_slot);
+            auto index = cast_to(u32, s64, maybe_slot);
             auto element = pool->pointer[index];
             u8 is_valid_or_existing = element != 0;
             if (!is_valid_or_existing)
@@ -3417,16 +3418,16 @@ fn s64 ip_find_slot_register_mask(GenericInternPool* generic_pool, Thread* threa
     return result;
 }
 
-global const auto ip_interface_debug_type = (InternPoolInterface) {
+global_variable const auto ip_interface_debug_type = (InternPoolInterface) {
     .add_to_buffer = &ip_DebugType_add_to_buffer,
     .find_slot = &ip_find_slot_debug_type,
 };
 
-global const auto ip_interface_node = (InternPoolInterface) {
+global_variable const auto ip_interface_node = (InternPoolInterface) {
     .find_slot = &ip_find_slot_node,
 };
 
-global const auto ip_interface_register_mask = (InternPoolInterface) {
+global_variable const auto ip_interface_register_mask = (InternPoolInterface) {
     .find_slot = &ip_find_slot_register_mask,
 };
 
@@ -3468,7 +3469,7 @@ may_be_unused fn T ## Index ip_ ## T ## _remove(InternPool(T)* pool, Thread* thr
     \
     if (maybe_slot != -1)\
     {\
-        auto i = cast(u32, s64, maybe_slot);\
+        auto i = cast_to(u32, s64, maybe_slot);\
         auto* slot_pointer = &pool->pointer[i];\
         auto old_item_index = *slot_pointer;\
         assert(validi(old_item_index));\
@@ -3657,7 +3658,7 @@ fn Hash64 type_get_hash_tuple(Thread* thread, Type* type)
 // \
 //     if (destination_length > half_capacity) \
 //     {\
-//         auto new_capacity = cast(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), 32)); \
+//         auto new_capacity = cast_to(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), 32)); \
 //         auto* new_array = arena_allocate(thread->arena, u32, new_capacity); \
 //         memset(new_array, 0, sizeof(u32) * new_capacity); \
 //         \
@@ -3835,7 +3836,7 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 // fn TypeIndex intern_pool_put_new_type_at_assume_not_existent_assume_capacity(Thread* thread, Type* type, u32 index)
 // {
 //     auto* result = vb_add(&thread->buffer.types, 1);
-//     auto buffer_index = cast(u32, s64, result - thread->buffer.types.pointer);
+//     auto buffer_index = cast_to(u32, s64, result - thread->buffer.types.pointer);
 //     auto type_index = Index(Type, buffer_index);
 //     *result = *type;
 //
@@ -3852,7 +3853,7 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 //     assert(thread->interned.types.length < thread->interned.types.capacity);
 //     Hash64 hash = type->hash;
 //     assert(hash);
-//     auto index = cast(u32, u64, hash & (thread->interned.types.capacity - 1));
+//     auto index = cast_to(u32, u64, hash & (thread->interned.types.capacity - 1));
 //
 //     return intern_pool_put_new_type_at_assume_not_existent_assume_capacity(thread, type, index);
 // }
@@ -3901,13 +3902,13 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 
 // fn NodeIndex intern_pool_get_node(Thread* thread, NodeIndex key, Hash64 hash)
 // {
-//     auto original_index = cast(u32, u64, hash & (thread->interned.nodes.capacity - 1));
+//     auto original_index = cast_to(u32, u64, hash & (thread->interned.nodes.capacity - 1));
 //     auto maybe_slot = intern_pool_find_node_slot(thread, original_index, key);
 //     auto node_index = invalidi(Node);
 //
 //     if (maybe_slot != -1)
 //     {
-//         auto slot = cast(u32, s64, maybe_slot);
+//         auto slot = cast_to(u32, s64, maybe_slot);
 //         auto* pointer_to_slot = &thread->interned.nodes.pointer[slot];
 //         node_index = *(NodeIndex*)pointer_to_slot;
 //     }
@@ -3929,12 +3930,12 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 // {
 //     auto capacity = thread->interned.nodes.capacity;
 //     assert(thread->interned.nodes.length < capacity);
-//     auto original_index = cast(u32, u64, hash & (capacity - 1));
+//     auto original_index = cast_to(u32, u64, hash & (capacity - 1));
 //
 //     auto slot = intern_pool_find_node_slot(thread, original_index, node);
 //     if (slot == -1)
 //     {
-//         fail();
+//         failed_execution();
 //     }
 //     auto index = (u32)slot;
 //
@@ -3950,7 +3951,7 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 //
 //     if (destination_length > half_capacity)
 //     {
-//         auto new_capacity = cast(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), 32));
+//         auto new_capacity = cast_to(u32, u64, MAX(round_up_to_next_power_of_2(destination_length), 32));
 //         auto* new_array = arena_allocate(thread->arena, u32, new_capacity);
 //         memset(new_array, 0, sizeof(u32) * new_capacity);
 //
@@ -4119,7 +4120,7 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 // fn DebugTypeIndex intern_pool_put_new_debug_type_at_assume_not_existent_assume_capacity(Thread* thread, const DebugType* type, u32 index)
 // {
 //     auto* result = vb_add(&thread->buffer.debug_types, 1);
-//     auto buffer_index = cast(u32, s64, result - thread->buffer.debug_types.pointer);
+//     auto buffer_index = cast_to(u32, s64, result - thread->buffer.debug_types.pointer);
 //     auto type_index = Index(DebugType, buffer_index);
 //     *result = *type;
 //
@@ -4150,12 +4151,12 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 // {
 //     auto existing_capacity = thread->interned.types.capacity;
 //     auto hash = hash_debug_type(type);
-//     auto original_index = cast(u32, u64, hash & (existing_capacity - 1));
+//     auto original_index = cast_to(u32, u64, hash & (existing_capacity - 1));
 //     
 //     auto maybe_slot = intern_pool_find_debug_type_slot(thread, original_index, type);
 //     if (maybe_slot != -1)
 //     {
-//         auto index = cast(u32, s64, maybe_slot);
+//         auto index = cast_to(u32, s64, maybe_slot);
 //         auto type_index = *(DebugTypeIndex*)&thread->interned.types.pointer[index];
 //         u8 existing = validi(type_index);
 //         if (!existing)
@@ -4193,12 +4194,12 @@ fn NodeIndex return_get_value(Thread* thread, Node* node)
 // {
 //     auto existing_capacity = thread->interned.types.capacity;
 //     auto hash = hash_type(thread, type);
-//     auto original_index = cast(u32, u64, hash & (existing_capacity - 1));
+//     auto original_index = cast_to(u32, u64, hash & (existing_capacity - 1));
 //     
 //     auto maybe_slot = intern_pool_find_type_slot(thread, original_index, type);
 //     if (maybe_slot != -1)
 //     {
-//         auto index = cast(u32, s64, maybe_slot);
+//         auto index = cast_to(u32, s64, maybe_slot);
 //         TypeIndex type_index = *(TypeIndex*)&thread->interned.types.pointer[index];
 //         u8 existing = validi(type_index);
 //         if (!existing)
@@ -4548,7 +4549,7 @@ fn NodeIndex idealize_return(Thread* thread, NodeIndex node_index)
 //     }
 // }
 
-global const TypeVirtualTable type_functions[TYPE_COUNT] = {
+global_variable const TypeVirtualTable type_functions[TYPE_COUNT] = {
     [TYPE_BOTTOM] = { .get_hash = &type_get_hash_default },
     [TYPE_TOP] = { .get_hash = &type_get_hash_default },
     [TYPE_LIVE_CONTROL] = { .get_hash = &type_get_hash_default },
@@ -4557,7 +4558,7 @@ global const TypeVirtualTable type_functions[TYPE_COUNT] = {
     [TYPE_TUPLE] = { .get_hash = &type_get_hash_tuple },
 };
 
-global const NodeVirtualTable node_functions[NODE_COUNT] = {
+global_variable const NodeVirtualTable node_functions[NODE_COUNT] = {
     // [NODE_START] = {
     //     .compute_type = &compute_type_start,
     //     .idealize = &idealize_null,
@@ -4770,7 +4771,7 @@ fn Hash64 hash_type(Thread* thread, Type* type)
 //     auto hash = hash_node(thread, node, node_index);
 //     
 //     auto original_index = hash & (existing_capacity - 1);
-//     auto slot = intern_pool_find_node_slot(thread, cast(u32, u64, original_index), node_index);
+//     auto slot = intern_pool_find_node_slot(thread, cast_to(u32, u64, original_index), node_index);
 //
 //     if (slot != -1)
 //     {
@@ -4852,7 +4853,7 @@ STRUCT(Parser)
                 if (new_line)
                 {
                     // TODO: is this a bug?
-                    parser->column = cast(u32, u64, index + 1);
+                    parser->column = cast_to(u32, u64, index + 1);
                 }
 
                 if (!is_space(ch, get_next_ch_safe(src, parser->i)))
@@ -4889,7 +4890,7 @@ STRUCT(Parser)
     if (likely(index < src.length))
     {
         u8 ch = src.pointer[index];
-        auto matches = cast(u64, s64, likely(ch == expected_ch));
+        auto matches = cast_to(u64, s64, likely(ch == expected_ch));
         parser->i += matches;
         if (!matches)
         {
@@ -4898,7 +4899,7 @@ STRUCT(Parser)
             print_string(strlit("', but found '"));
             print_string(ch_to_str(ch));
             print_string(strlit("'\n"));
-            fail();
+            failed_execution();
         }
     }
     else
@@ -4906,7 +4907,7 @@ STRUCT(Parser)
         print_string(strlit("expected character '"));
         print_string(ch_to_str(expected_ch));
         print_string(strlit("', but found end of file\n"));
-        fail();
+        failed_execution();
     }
 }
 
@@ -4924,7 +4925,7 @@ STRUCT(Parser)
         while (parser->i < src.length)
         {
             u8 ch = src.pointer[parser->i];
-            auto is_identifier = cast(u64, s64, likely(is_identifier_ch(ch)));
+            auto is_identifier = cast_to(u64, s64, likely(is_identifier_ch(ch)));
             parser->i += is_identifier;
 
             if (!is_identifier)
@@ -4939,11 +4940,11 @@ STRUCT(Parser)
             }
         }
 
-        fail();
+        failed_execution();
     }
     else
     {
-        fail();
+        failed_execution();
     }
 }
 
@@ -5068,7 +5069,7 @@ fn NodeIndex dead_code_elimination(Thread* thread, NodePair nodes)
 //     // }
 // }
 
-global auto enable_peephole = 1;
+global_variable auto enable_peephole = 1;
 
 fn NodeIndex peephole_optimize(Thread* thread, Function* function, NodeIndex node_index)
 {
@@ -5259,13 +5260,13 @@ fn TypePair analyze_type(Thread* thread, Parser* parser, String src)
 
             if (integer_start)
             {
-                auto signedness = cast(u8, u64, s_start);
+                auto signedness = cast_to(u8, u64, s_start);
                 u64 bit_size;
                 u64 current_i = parser->i;
                 assert(src.pointer[current_i] >= '0' & src.pointer[current_i] <= '9');
                 switch (decimal_digit_count) {
                     case 0:
-                        fail();
+                        failed_execution();
                     case 1:
                         bit_size = src.pointer[current_i] - '0';
                         break;
@@ -5273,7 +5274,7 @@ fn TypePair analyze_type(Thread* thread, Parser* parser, String src)
                         bit_size = (src.pointer[current_i] - '0') * 10 + (src.pointer[current_i + 1] - '0');
                         break;
                     default:
-                        fail();
+                        failed_execution();
                 }
                 parser->i += decimal_digit_count;
 
@@ -5281,23 +5282,23 @@ fn TypePair analyze_type(Thread* thread, Parser* parser, String src)
 
                 if (bit_size)
                 {
-                    auto bit_count = cast(u8, u64, bit_size);
+                    auto bit_count = cast_to(u8, u64, bit_size);
                     auto valid = MIN(MAX(8, round_up_to_next_power_of_2(MAX(bit_count, 1))), 64);
                     if (bit_count != valid)
                     {
-                        fail();
+                        failed_execution();
                     }
-                    auto bit_index = cast(u32, s32, __builtin_ctz(bit_count >> 3));
+                    auto bit_index = cast_to(u32, s32, __builtin_ctz(bit_count >> 3));
                     static_assert(array_length(thread->types.debug.integer.array) == 8);
                     auto index = signedness * 4 + bit_index;
                     auto debug_type_index = thread->types.debug.integer.array[index];
-                    BackendTypeId backend_type = cast(u8, u32, bit_index + 1);
+                    BackendTypeId backend_type = cast_to(u8, u32, bit_index + 1);
                     auto type_pair = type_pair_make(debug_type_index, backend_type);
                     return type_pair;
                 }
                 else
                 {
-                    fail();
+                    failed_execution();
                 }
             }
             else if (float_start)
@@ -5311,7 +5312,7 @@ fn TypePair analyze_type(Thread* thread, Parser* parser, String src)
         }
         else
         {
-            fail();
+            failed_execution();
         }
     }
 
@@ -5360,7 +5361,7 @@ fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, Function
         // }
         // else
         // {
-        //     fail();
+        //     failed_execution();
         // }
     }
     else if (is_digit)
@@ -5391,13 +5392,13 @@ fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, Function
                     case 'o': prefix = INTEGER_PREFIX_OCTAL; break;
                     case 'd': prefix = INTEGER_PREFIX_DECIMAL; break;
                     case 'b': prefix = INTEGER_PREFIX_BINARY; break;
-                    default: fail();
+                    default: failed_execution();
                 };
 
                 parser->i += 2;
 
             } else if (!is_valid_after_zero) {
-                fail();
+                failed_execution();
             }
         }
 
@@ -5786,7 +5787,7 @@ fn NodeIndex analyze_comparison(Thread* thread, Parser* parser, FunctionBuilder*
                 }
                 else
                 {
-                    fail();
+                    failed_execution();
                 }
                 break;
             case '<':
@@ -5929,7 +5930,7 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder,
             // auto left = scope_lookup(thread, builder, left_name);
             // if (!validi(left))
             // {
-            //     fail();
+            //     failed_execution();
             // }
             //
             // NodeIndex right;
@@ -5987,7 +5988,7 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder,
                         // auto result = scope_define(thread, builder, local_name, initial_value_node->type, initial_value_node_index);
                         // if (!validi(result))
                         // {
-                        //     fail();
+                        //     failed_execution();
                         // }
                     } break;
                 case block_start:
@@ -6044,7 +6045,7 @@ fn void analyze_file(Thread* thread, File* file)
                 skip_space(parser, src);
 
                 Function* restrict function = vb_add(&thread->buffer.functions, 1);
-                auto function_index = cast(u32, s64, function - thread->buffer.functions.pointer);
+                auto function_index = cast_to(u32, s64, function - thread->buffer.functions.pointer);
                 memset(function, 0, sizeof(Function));
                 builder->function = function;
                 function->line = start_line;
@@ -6074,7 +6075,7 @@ fn void analyze_file(Thread* thread, File* file)
                     if (argument_i == 255)
                     {
                         // Maximum arguments reached
-                        fail();
+                        failed_execution();
                     }
 
                     auto argument_name = parse_identifier(parser, src);
@@ -6836,11 +6837,11 @@ fn void analyze_file(Thread* thread, File* file)
 //     //     //         {
 //     //     //             if (projection_index == 0)
 //     //     //             {
-//     //     //                 fail();
+//     //     //                 failed_execution();
 //     //     //             }
 //     //     //             // if (projection_index > interpreter->arguments.length + 1)
 //     //     //             // {
-//     //     //             //     fail();
+//     //     //             //     failed_execution();
 //     //     //             // }
 //     //     //
 //     //     //             switch (projection_index)
@@ -7099,7 +7100,7 @@ fn void thread_init(Thread* thread)
         .mask = ((u16)0xffff & ~((u16)1 << RSP)), // & ~((u16)1 << RBP),
     };
 
-// global RegisterMask register_masks[] = {
+// global_variable RegisterMask register_masks[] = {
 //     {
 //     },
 //     {
@@ -7174,7 +7175,7 @@ fn void vb_align(VirtualBuffer(u8)* buffer, u64 alignment)
 {
     auto current_length = buffer->length;
     auto target_len = align_forward(current_length, alignment);
-    auto count = cast(u32, u64, target_len - current_length);
+    auto count = cast_to(u32, u64, target_len - current_length);
     auto* pointer = vb_add(buffer, count);
     memset(pointer, 0, count);
 }
@@ -7244,7 +7245,7 @@ fn u32 elf_get_string(VirtualBuffer(u8)* restrict buffer, String string)
         {
             if (s_equal(existing, string))
             {
-                return cast(u32, s64, existing.pointer - buffer->pointer);
+                return cast_to(u32, s64, existing.pointer - buffer->pointer);
             }
 
             existing.pointer += 1;
@@ -7255,7 +7256,7 @@ fn u32 elf_get_string(VirtualBuffer(u8)* restrict buffer, String string)
     }
 
     auto length = buffer->length;
-    auto* ptr = vb_add(buffer, cast(u32, u64, string.length + 1));
+    auto* ptr = vb_add(buffer, cast_to(u32, u64, string.length + 1));
     memcpy(ptr, string.pointer, string.length);
     *(ptr + string.length) = 0;
 
@@ -7809,7 +7810,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         auto* ptr = vb_add_scalar(&builder->file, ELFNoteHeader);
         *ptr = (ELFNoteHeader)
         {
-            .name_size = cast(u32, u64, vb_copy_string_zero_terminated(&builder->file, gnu_string)),
+            .name_size = cast_to(u32, u64, vb_copy_string_zero_terminated(&builder->file, gnu_string)),
             .descriptor_size = 16,
             .type = NT_GNU_PROPERTY_TYPE_0,
         };
@@ -7895,7 +7896,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
         auto* note_header = vb_add_scalar(&builder->file, ELFNoteHeader);
         *note_header = (ELFNoteHeader) {
-            .name_size = cast(u32, u64, vb_copy_string_zero_terminated(&builder->file, gnu_string)),
+            .name_size = cast_to(u32, u64, vb_copy_string_zero_terminated(&builder->file, gnu_string)),
             .descriptor_size = 16,
             .type = NT_GNU_ABI_TAG,
         };
@@ -7929,9 +7930,9 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
     auto gnu_build_id_abi_note_size = builder->file.length - gnu_build_id_abi_note_offset;
 
-    auto preliminar_section_count = cast(u16, u32, builder->section_headers.length + 1);
+    auto preliminar_section_count = cast_to(u16, u32, builder->section_headers.length + 1);
     auto dynamic_symbol_table_index = preliminar_section_count;
-    auto dynamic_string_table_index = cast(u16, u32, dynamic_symbol_table_index + 1);
+    auto dynamic_string_table_index = cast_to(u16, u32, dynamic_symbol_table_index + 1);
 
     u32 gnu_hash_offset = 0;
     {
@@ -8302,7 +8303,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
     auto code_offset = builder->file.length;
     auto init_offset = code_offset;
-    auto init_section_index = cast(u16, u32, builder->section_headers.length);
+    auto init_section_index = cast_to(u16, u32, builder->section_headers.length);
     VirtualBuffer(SymbolRelocation) symbol_relocations = {};
     String init_section_content = {};
     {
@@ -8340,7 +8341,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         };
 
         init_section_content.length = sizeof(data);
-        init_section_content.pointer = vb_add(&builder->file, cast(u32, u64, init_section_content.length));
+        init_section_content.pointer = vb_add(&builder->file, cast_to(u32, u64, init_section_content.length));
 
         memcpy(init_section_content.pointer, data, init_section_content.length);
 
@@ -8368,7 +8369,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
     u32 main_offset = 0;
     u32 main_size;
 
-    auto text_section_index = cast(u16, u32, builder->section_headers.length);
+    auto text_section_index = cast_to(u16, u32, builder->section_headers.length);
     {
         //.text
         auto* section_header = vb_add(&builder->section_headers, 1);
@@ -8607,7 +8608,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
         // TODO: fix this
         main_offset = builder->file.length;
-        main_size = cast(u32, u64, options.code.length);
+        main_size = cast_to(u32, u64, options.code.length);
 
         vb_copy_string(&builder->file, options.code);
 
@@ -8631,7 +8632,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
     }
 
     u32 fini_offset = 0; 
-    auto fini_section_index = cast(u16, u32, builder->section_headers.length);
+    auto fini_section_index = cast_to(u16, u32, builder->section_headers.length);
     {
         // .fini
         auto* section_header = vb_add(&builder->section_headers, 1);
@@ -8696,7 +8697,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
     auto read_only_offset = builder->file.length;
 
-    auto rodata_section_index = cast(u16, u32, builder->section_headers.length);
+    auto rodata_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 _IO_stdin_used_size = 0;
     u32 rodata_va = 0;
     {
@@ -8737,7 +8738,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
     u32 eh_frame_offset = 0;
     u32 eh_frame_size = 0;
     u64 eh_frame_alignment = 0;
-    auto eh_frame_hdr_section_index = cast(u16, u32, builder->section_headers.length);
+    auto eh_frame_hdr_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 eh_frame_header_entries = 0;
     EhFrameHeader* eh_frame_header = 0;
     {
@@ -8754,8 +8755,8 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
         // TODO: figure out a link between this and the code
         EhFrameHeaderEntry entries[] = {
-            { .pc = cast(s32, s64, (s64)_start_offset - (s64)offset), .fde = 0x34 },
-            { .pc = cast(s32, s64, (s64)main_offset - (s64)offset), .fde = 0x4c },
+            { .pc = cast_to(s32, s64, (s64)_start_offset - (s64)offset), .fde = 0x34 },
+            { .pc = cast_to(s32, s64, (s64)main_offset - (s64)offset), .fde = 0x4c },
         };
 
         eh_frame_header_entries = array_length(entries);
@@ -8795,7 +8796,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
             .pointer_encoding = elf_eh_frame_sdata4 | elf_eh_frame_pcrel,
             .count_encoding = elf_eh_frame_udata4 | elf_eh_frame_absptr,
             .table_encoding = elf_eh_frame_sdata4 | elf_eh_frame_datarel,
-            .frame_start = cast(u32, u64, offset - (cast(u64, s64, ((u8*)eh_frame_header - builder->file.pointer)) + offsetof(EhFrameHeader, frame_start))),
+            .frame_start = cast_to(u32, u64, offset - (cast_to(u64, s64, ((u8*)eh_frame_header - builder->file.pointer)) + offsetof(EhFrameHeader, frame_start))),
             .entry_count = eh_frame_header_entries,
         };
 
@@ -8855,7 +8856,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
             };
 
             // _start
-            s32 initial_location = cast(s32, s64, (s64)_start_offset - (s64)builder->file.length);
+            s32 initial_location = cast_to(s32, s64, (s64)_start_offset - (s64)builder->file.length);
             *(s32*)(vb_add(&builder->file, sizeof(s32))) = initial_location;
 
             *(u32*)(vb_add(&builder->file, sizeof(u32))) = _start_size;
@@ -8877,7 +8878,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 .length = 0x10,
                 .pointer = 0x34,
             };
-            s32 initial_location = cast(s32, s64, (s64)main_offset - (s64)builder->file.length);
+            s32 initial_location = cast_to(s32, s64, (s64)main_offset - (s64)builder->file.length);
             *(s32*)(vb_add(&builder->file, sizeof(s32))) = initial_location;
 
             *(u32*)(vb_add(&builder->file, sizeof(u32))) = main_size;
@@ -9035,7 +9036,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
     auto* __dso_handle_relocation = &dynamic_relocations[dynamic_relocation_count];
     dynamic_relocation_count += 1;
 
-    auto dynamic_section_index = cast(u16, u32, builder->section_headers.length);
+    auto dynamic_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 dynamic_va = 0;
     {
         // .dynamic
@@ -9175,7 +9176,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         };
     }
 
-    auto got_plt_section_index = cast(u16, u32, builder->section_headers.length);
+    auto got_plt_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 got_plt_va = 0;
     {
         // .got.plt
@@ -9288,7 +9289,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
     u32 data_va_start = 0;
     u32 data_va_end = 0;
-    auto data_section_index = cast(u16, u32, builder->section_headers.length);
+    auto data_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 __dso_handle_va;
     {
         // .data
@@ -9308,7 +9309,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         u32 size = sizeof(entries);
 
         vb_copy_any_array(&builder->file, entries);
-        data_va_end = cast(u32, u64, data_va_start + size);
+        data_va_end = cast_to(u32, u64, data_va_start + size);
 
         *section_header = (ELFSectionHeader) {
             .name_offset = name,
@@ -9334,7 +9335,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
     }
 
     u32 bss_size;
-    auto bss_section_index = cast(u16, u32, builder->section_headers.length);
+    auto bss_section_index = cast_to(u16, u32, builder->section_headers.length);
     u32 bss_end;
     u32 bss_start;
     {
@@ -9477,7 +9478,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 vb_copy_string_zero_terminated(&debug_str, string);
                 auto string_offset_index = debug_str_offsets.length;
                 *vb_add(&debug_str_offsets, 1) = string_offset;
-                *vb_add(&builder->file, 1) = cast(u8, u32, string_offset_index);
+                *vb_add(&builder->file, 1) = cast_to(u8, u32, string_offset_index);
             }
 
             // language: data2
@@ -9490,7 +9491,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 vb_copy_string_zero_terminated(&debug_str, string);
                 auto string_offset_index = debug_str_offsets.length;
                 *vb_add(&debug_str_offsets, 1) = string_offset;
-                *vb_add(&builder->file, 1) = cast(u8, u32, string_offset_index);
+                *vb_add(&builder->file, 1) = cast_to(u8, u32, string_offset_index);
             }
 
             // str_offsets_base: sec_offset
@@ -9506,7 +9507,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 vb_copy_string_zero_terminated(&debug_str, string);
                 auto string_offset_index = debug_str_offsets.length;
                 *vb_add(&debug_str_offsets, 1) = string_offset;
-                *vb_add(&builder->file, 1) = cast(u8, u32, string_offset_index);
+                *vb_add(&builder->file, 1) = cast_to(u8, u32, string_offset_index);
             }
 
             // low_pc: addrx
@@ -9544,7 +9545,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 vb_copy_string_zero_terminated(&builder->file, string);
                 auto string_offset_index = debug_str_offsets.length;
                 *vb_add(&debug_str_offsets, 1) = string_offset;
-                *vb_add(&builder->file, 1) = cast(u8, u32, string_offset_index);
+                *vb_add(&builder->file, 1) = cast_to(u8, u32, string_offset_index);
             }
             
             // file: data1
@@ -9572,7 +9573,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 vb_copy_string_zero_terminated(&builder->file, string);
                 auto string_offset_index = debug_str_offsets.length;
                 *vb_add(&debug_str_offsets, 1) = string_offset;
-                *vb_add(&builder->file, 1) = cast(u8, u32, string_offset_index);
+                *vb_add(&builder->file, 1) = cast_to(u8, u32, string_offset_index);
             }
 
             // encoding: data1
@@ -9588,7 +9589,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
         auto length_size = sizeof(compilation_unit->length);
         *compilation_unit = (DwarfCompilationUnit) {
-            .length = cast(u32, u64, size - length_size),
+            .length = cast_to(u32, u64, size - length_size),
             .version = 5,
             .type = DW_UT_compile,
             .address_size = 8,
@@ -9748,7 +9749,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 { DW_LCNT_path, DW_FORM_line_strp },
             };
 
-            auto directory_entry_format_count = cast(u8, u32, array_length(directory_entry_formats));
+            auto directory_entry_format_count = cast_to(u8, u32, array_length(directory_entry_formats));
             *vb_add(&builder->file, 1) = directory_entry_format_count;
 
             for (u8 i = 0; i < array_length(directory_entry_formats); i += 1)
@@ -9782,7 +9783,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
                 { DW_LCNT_MD5, DW_FORM_data16 },
             };
 
-            auto filename_entry_format_count = cast(u8, u32, array_length(filename_entry_formats));
+            auto filename_entry_format_count = cast_to(u8, u32, array_length(filename_entry_formats));
             *vb_add(&builder->file, 1) = filename_entry_format_count;
 
             for (u8 i = 0; i < filename_entry_format_count; i += 1)
@@ -9850,7 +9851,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
             // Advance PC by 3
             *vb_add(&builder->file, 1) = DW_LNS_advance_pc;
-            *vb_add(&builder->file, 1) = cast(u8, u32, main_size);
+            *vb_add(&builder->file, 1) = cast_to(u8, u32, main_size);
 
             {
                 // TODO: confirm this is the encoding of special opcodes?
@@ -9867,7 +9868,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
             .version = 5,
             .address_size = 8,
             .segment_selector_size = 0,
-            .header_length = cast(u32, u64, line_program_start_offset - after_header_length),
+            .header_length = cast_to(u32, u64, line_program_start_offset - after_header_length),
             .minimum_instruction_length = 1,
             .maximum_operations_per_instruction = 1,
             .default_is_stmt = 1,
@@ -9934,7 +9935,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         u64 addresses[] = { main_offset };
 
         auto header = (DwarfAddressTableHeader) {
-            .unit_length = cast(u32, u64, sizeof(DwarfAddressTableHeader) - length_size + sizeof(addresses)),
+            .unit_length = cast_to(u32, u64, sizeof(DwarfAddressTableHeader) - length_size + sizeof(addresses)),
             .version = 5,
             .address_size = 8,
             .segment_selector_size = 0,
@@ -10012,7 +10013,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
         u32 offset_array_size = debug_str_offsets.length * sizeof(*debug_str_offsets.pointer);
         auto header = (DwarfDebugStrOffsetsHeader) {
 
-            .length = cast(u32, u64, sizeof(DwarfDebugStrOffsetsHeader) - length_size + offset_array_size),
+            .length = cast_to(u32, u64, sizeof(DwarfDebugStrOffsetsHeader) - length_size + offset_array_size),
             .version = 5,
         };
         *vb_add_scalar(&builder->file, DwarfDebugStrOffsetsHeader) = header;
@@ -10360,7 +10361,7 @@ may_be_unused fn String write_elf(Thread* thread, ObjectOptions options)
 
     vb_align(&builder->file, alignof(ELFSectionHeader));
     auto section_header_offset = builder->file.length;
-    auto section_header_count = cast(u16, u32, builder->section_headers.length);
+    auto section_header_count = cast_to(u16, u32, builder->section_headers.length);
     memcpy(vb_add(&builder->file, sizeof(ELFSectionHeader) * section_header_count), builder->section_headers.pointer, builder->section_headers.length * sizeof(ELFSectionHeader));
 
     *elf_header = (ELFHeader)
@@ -10803,7 +10804,7 @@ static_assert(sizeof(PDBHeader) == 52);
 
 fn u32 pdb_size_to_block_count(u32 bytes, u32 block_size)
 {
-    return cast(u32, u64, ((u64)bytes + block_size - 1) / block_size);
+    return cast_to(u32, u64, ((u64)bytes + block_size - 1) / block_size);
 }
 
 fn u64 pdb_block_index_to_file_offset(u32 block_index, u32 block_size)
@@ -10844,12 +10845,12 @@ STRUCT(PDBStreamCreate)
     u32 size;
 };
 
-global const u32 nil_page_size = 0xffffffff;
-global const u16 nil_stream_index = 0xffff;
-global const u32 info_stream_index = 1;
-global const u32 tpi_stream_index = 2;
-global const u16 dbi_stream_index = 3;
-global const u16 ipi_stream_index = 4;
+global_variable const u32 nil_page_size = 0xffffffff;
+global_variable const u16 nil_stream_index = 0xffff;
+global_variable const u32 info_stream_index = 1;
+global_variable const u32 tpi_stream_index = 2;
+global_variable const u16 dbi_stream_index = 3;
+global_variable const u16 ipi_stream_index = 4;
 
 STRUCT(PDBCoalescedMSFStream)
 {
@@ -10909,7 +10910,7 @@ fn PDBStreamCreate pdb_setup_stream_creation(PDBFile pdb, u32 stream_index)
 {
     if (pdb_validate_stream_index(pdb, stream_index) != PDB_STREAM_INDEX_VALIDATION_SUCCESS)
     {
-        fail();
+        failed_execution();
     }
 
     return (PDBStreamCreate)
@@ -11064,7 +11065,7 @@ fn PDBDirectMSFStream pdb_direct_msf_stream_create(PDBStreamCreate create)
 {
     if (!is_power_of_two(create.block_size))
     {
-        fail();
+        failed_execution();
     }
 
     return create;
@@ -11126,7 +11127,7 @@ STRUCT(PDBDBIStreamHeader)
     u32 padding;
 };
 
-global const u32 dbi_stream_header_signature = 0xffffffff;
+global_variable const u32 dbi_stream_header_signature = 0xffffffff;
 
 STRUCT(PDBDBIStream)
 {
@@ -11141,18 +11142,18 @@ fn PDBDBIStream pdb_dbi_stream_create(PDBFile pdb)
 
     if (result.stream.size < sizeof(PDBDBIStreamHeader))
     {
-        fail();
+        failed_execution();
     }
 
     pdb_direct_msf_stream_read_at_offset(result.stream, scalar_to_bytes(result.header), 0);
 
     if (result.header.signature != dbi_stream_header_signature)
     {
-        fail();
+        failed_execution();
     }
     else if (result.header.version != PDB_DBI_VERSION_V70)
     {
-        fail();
+        failed_execution();
     }
 
     return result;
@@ -11293,8 +11294,8 @@ STRUCT(PDBHashTableHeader)
     u32 bucket_count;
 };
 
-global const u32 pdb_hash_table_signature = 0xffffffff;
-global const u32 pdb_hash_table_version = 0xeffe0000 + 19990810;
+global_variable const u32 pdb_hash_table_signature = 0xffffffff;
+global_variable const u32 pdb_hash_table_version = 0xeffe0000 + 19990810;
 
 typedef enum PDBSectionContributionVersion : u32
 {
@@ -11544,7 +11545,7 @@ fn void pdb_playground(Thread* thread)
 
     if (pdb_validate(pdb.content) != PDB_VALIDATION_SUCCESS)
     {
-        fail();
+        failed_execution();
     }
 
     pdb.header = (PDBHeader*)(pdb.content.pointer + 0);
@@ -11595,19 +11596,19 @@ fn void pdb_playground(Thread* thread)
     auto info_stream = pdb_info_stream_create(pdb);
     if (info_stream.uses_debug_fast_link)
     {
-        fail();
+        failed_execution();
     }
 
     // Symbol record stream validation
     if (dbi.header.symbol_record_stream_index == nil_stream_index)
     {
-        fail();
+        failed_execution();
     }
 
     // Public stream validation
     if (dbi.header.public_stream_index == nil_stream_index)
     {
-        fail();
+        failed_execution();
     }
 
     auto public_stream = pdb_direct_msf_stream_create(pdb_setup_stream_creation(pdb, dbi.header.public_stream_index));
@@ -11617,19 +11618,19 @@ fn void pdb_playground(Thread* thread)
 
         if (hash_header.signature != pdb_hash_table_signature)
         {
-            fail();
+            failed_execution();
         }
 
         if (hash_header.version != pdb_hash_table_version)
         {
-            fail();
+            failed_execution();
         }
     }
 
     // Global stream validation
     if (dbi.header.global_stream_index == nil_stream_index)
     {
-        fail();
+        failed_execution();
     }
 
     auto global_stream = pdb_direct_msf_stream_create(pdb_setup_stream_creation(pdb, dbi.header.global_stream_index));
@@ -11639,18 +11640,18 @@ fn void pdb_playground(Thread* thread)
 
         if (hash_header.signature != pdb_hash_table_signature)
         {
-            fail();
+            failed_execution();
         }
 
         if (hash_header.version != pdb_hash_table_version)
         {
-            fail();
+            failed_execution();
         }
     }
 
     if (dbi.header.section_contribution_size < sizeof(PDBSectionContributionVersion))
     {
-        fail();
+        failed_execution();
     }
 
     auto stream_offset = pdb_section_contribution_substream_offset(dbi.header);
@@ -11660,12 +11661,12 @@ fn void pdb_playground(Thread* thread)
 
     if (version != PDB_SECTION_CONTRIBUTION_V60)
     {
-        fail();
+        failed_execution();
     }
 
     if (dbi.header.optional_debug_header_size == 0)
     {
-        fail();
+        failed_execution();
     }
 
     auto debug_header_offset = pdb_debug_header_substream_offset(dbi.header);
@@ -11675,7 +11676,7 @@ fn void pdb_playground(Thread* thread)
 
     if (debug_header.section_header_stream_index == nil_stream_index)
     {
-        fail();
+        failed_execution();
     }
 
     auto tpi_stream = pdb_direct_msf_stream_create(pdb_setup_stream_creation(pdb, tpi_stream_index));
@@ -11685,7 +11686,7 @@ fn void pdb_playground(Thread* thread)
 
     if (header.version != TPI_STREAM_V80)
     {
-        fail();
+        failed_execution();
     }
 
     pdb_print_sizes(pdb, dbi);
@@ -20453,7 +20454,7 @@ fn String pdb_build(Thread* thread)
 
     if (pdb_file.length != array_length(pdb_image))
     {
-        fail();
+        failed_execution();
     }
 
     for (u32 i = 0; i < pdb_file.length; i += 1)
@@ -20464,7 +20465,7 @@ fn String pdb_build(Thread* thread)
         if (mine != original)
         {
             print("Diff at position {u32}\n", i);
-            fail();
+            failed_execution();
         }
     }
 
@@ -20510,7 +20511,7 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
 
     auto* section_headers = vb_add_any_array(&file, COFFSectionHeader, section_count);
     u16 section_i = 0;
-    auto headers_size = cast(u32, u64, align_forward(file.length, file_section_alignment));
+    auto headers_size = cast_to(u32, u64, align_forward(file.length, file_section_alignment));
     u32 rva = file.length;
 
     // .text
@@ -20518,7 +20519,7 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
     u32 entry_point_rva;
     section_i += 1;
     {
-        rva = cast(u32, u64, align_forward(rva, virtual_section_alignment));
+        rva = cast_to(u32, u64, align_forward(rva, virtual_section_alignment));
         vb_align(&file, file_section_alignment);
         auto file_offset = file.length;
         u8 text_content[] = { 0x48, 0x83, 0xEC, 0x28, 0x33, 0xC9, 0xFF, 0x15, 0xF4, 0x0F, 0x00, 0x00, 0x90, 0x48, 0x83, 0xC4, 0x28, 0xC3, };
@@ -20556,7 +20557,7 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
     u32 unwind_information_rva;
     {
         // .rdata
-        rva = cast(u32, u64, align_forward(rva, virtual_section_alignment));
+        rva = cast_to(u32, u64, align_forward(rva, virtual_section_alignment));
         assert(rva == 0x2000);
         vb_align(&file, file_section_alignment);
         auto file_offset = file.length;
@@ -20708,7 +20709,7 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
     {
         // .pdata content
         vb_align(&file, file_section_alignment);
-        rva = cast(u32, u64, align_forward(rva, virtual_section_alignment));
+        rva = cast_to(u32, u64, align_forward(rva, virtual_section_alignment));
 
         auto file_offset = file.length;
 
@@ -20744,7 +20745,7 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
     }
 
     vb_align(&file, file_section_alignment);
-    rva = cast(u32, u64, align_forward(rva, virtual_section_alignment));
+    rva = cast_to(u32, u64, align_forward(rva, virtual_section_alignment));
 
     assert(section_i == section_count);
 
@@ -20816,12 +20817,12 @@ may_be_unused fn String write_pe(Thread* thread, ObjectOptions options)
         path_without_extension,
         strlit(".pdb"),
     };
-    auto pdb_path = arena_join_string(thread->arena, (Slice(String))array_to_slice(to_join));
 
     auto pdb = pdb_build(thread);
 
     // TODO:
 #if _WIN32
+    auto pdb_path = arena_join_string(thread->arena, (Slice(String))array_to_slice(to_join));
     auto fd = os_file_open(strlit("mydbg.pdb"), (OSFileOpenFlags) {
         .write = 1,
         .truncate = 1,
@@ -21453,7 +21454,7 @@ fn s32 node_best_ready(Scheduler* restrict scheduler, u64 in_use_mask)
             continue;
         }
 
-        return cast(s32, u32, length);
+        return cast_to(s32, u32, length);
     }
 
     return -1;
@@ -21465,7 +21466,7 @@ fn RegisterMaskIndex register_mask_intern(Thread* thread, RegisterMask register_
 {
     auto* new_rm = vb_add(&thread->buffer.register_masks, 1);
     *new_rm = register_mask;
-    auto candidate_index = Index(RegisterMask, cast(u32, s64, new_rm - thread->buffer.register_masks.pointer));
+    auto candidate_index = Index(RegisterMask, cast_to(u32, s64, new_rm - thread->buffer.register_masks.pointer));
     auto result = ip_RegisterMask_get_or_put(&thread->interned.register_masks, thread, candidate_index);
     auto final_index = result.index;
     assert((!index_equal(candidate_index, final_index)) == result.existing);
@@ -21569,7 +21570,7 @@ fn RegisterMaskIndex node_constraint(Thread* thread, Node* node, Slice(RegisterM
             {
                 if (ins.length)
                 {
-                    const global s32 ret_gprs[] = { RAX, RDX };
+                    const global_variable s32 ret_gprs[] = { RAX, RDX };
 
                     ins.pointer[1] = empty_register_mask;
                     ins.pointer[2] = empty_register_mask;
@@ -22124,7 +22125,7 @@ fn u8 register_allocate(Thread* thread, VirtualBuffer(VirtualRegister) virtual_r
         {
             print("Interfere with active: {u32}\n", (s32)other->assigned);
             in_use |= ((u32)1 << other->assigned);
-            *vb_add(spills, 1) = cast(u32, u64, i);
+            *vb_add(spills, 1) = cast_to(u32, u64, i);
         }
     }
 
@@ -22170,7 +22171,7 @@ fn u8 register_allocate(Thread* thread, VirtualBuffer(VirtualRegister) virtual_r
     }
     else
     {
-        virtual_register->assigned = cast(s16, s32, __builtin_ffsll(~in_use) - 1);
+        virtual_register->assigned = cast_to(s16, s32, __builtin_ffsll(~in_use) - 1);
         print("Register assigned: {s}\n", gpr_to_string((GPR)virtual_register->assigned));
     }
 
@@ -22256,7 +22257,7 @@ fn void node_ready_up(Thread* thread, Scheduler* scheduler, NodeIndex node_index
 
     for (i = 0; i < count; i += 1)
     {
-        if (cast(s32, u64, priority) < scheduler->ready.pointer[i].priority)
+        if (cast_to(s32, u64, priority) < scheduler->ready.pointer[i].priority)
         {
             break;
         }
@@ -22270,7 +22271,7 @@ fn void node_ready_up(Thread* thread, Scheduler* scheduler, NodeIndex node_index
 
     scheduler->ready.pointer[i] = (ReadyNode) {
         .node_index = node_index,
-            .priority = cast(s32, u64, priority),
+            .priority = cast_to(s32, u64, priority),
             .unit_mask = unit_mask,
     };
 }
@@ -22357,32 +22358,6 @@ fn void cfg_builder_clear(CFGBuilder* restrict builder, Thread* restrict thread)
     builder->scheduled.length = 0;
 }
 
-typedef enum CpuArchitecture : u8
-{
-    CPU_ARCH_X86_64,
-    CPU_ARCH_AARCH64,
-} CpuArchitecture;
-
-typedef enum OperatingSystem : u8
-{
-    OPERATING_SYSTEM_LINUX,
-    OPERATING_SYSTEM_MAC,
-    OPERATING_SYSTEM_WINDOWS,
-} OperatingSystem;
-
-STRUCT(Target)
-{
-    CpuArchitecture cpu;
-    OperatingSystem os;
-};
-
-STRUCT(CodegenOptions)
-{
-    String test_name;
-    Target target;
-    CompilerBackend backend;
-};
-
 fn BasicBlockIndex cfg_get_predicate_basic_block(Thread* restrict thread, FixedBlockMap* map, NodeIndex arg_node_index, u16 i)
 {
     auto* arg_node = thread_node_get(thread, arg_node_index);
@@ -22677,12 +22652,12 @@ fn void cfg_global_schedule(CFGBuilder* restrict builder, Thread* restrict threa
         bitset_ensure_length(&basic_block->live_out, node_count);
     }
 
-    auto bb0 = Index(BasicBlock, cast(u32, s64, &builder->basic_blocks.pointer[0] - builder->basic_blocks.pointer));
+    auto bb0 = Index(BasicBlock, cast_to(u32, s64, &builder->basic_blocks.pointer[0] - builder->basic_blocks.pointer));
 
     for (u32 i = 0; i < builder->basic_blocks.length; i += 1)
     {
         auto* basic_block = &builder->basic_blocks.pointer[i];
-        auto bb_index = Index(BasicBlock, cast(u32, s64, basic_block - builder->basic_blocks.pointer));
+        auto bb_index = Index(BasicBlock, cast_to(u32, s64, basic_block - builder->basic_blocks.pointer));
         builder->scheduled.pointer[geti(basic_block->start)] = bb_index;
 
         if (i == 0)
@@ -22994,9 +22969,9 @@ fn void cfg_list_schedule(Thread* restrict thread, CFGBuilder* restrict builder,
             {
                 break;
             }
-            auto index = cast(u32, s32, signed_index);
+            auto index = cast_to(u32, s32, signed_index);
             auto available = scheduler.ready.pointer[index].unit_mask & ~in_use_mask;
-            auto unit_i = __builtin_ffsll(cast(s64, u64, available)) - 1;
+            auto unit_i = __builtin_ffsll(cast_to(s64, u64, available)) - 1;
 
             auto node_index = scheduler.ready.pointer[index].node_index;
             auto* node = thread_node_get(thread, node_index);
@@ -23015,7 +22990,7 @@ fn void cfg_list_schedule(Thread* restrict thread, CFGBuilder* restrict builder,
             *vb_add(&active, 1) = (InFlightNode)
             {
                 .node_index = node_index,
-                .end = cast(u32, u64, end_cycle),
+                .end = cast_to(u32, u64, end_cycle),
                 .unit_i = unit_i,
             };
 
@@ -23597,6 +23572,9 @@ may_be_unused fn String write_macho(Thread* restrict thread, ObjectOptions optio
 
 fn void code_generation(Thread* restrict thread, CodegenOptions options)
 {
+    // TODO: delete, this is testing
+    llvm_codegen(options);
+
     auto cfg_builder = cfg_builder_init(thread);
     auto* restrict builder = &cfg_builder;
     VirtualBuffer(u8) code = {};
@@ -23687,7 +23665,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
         memset(ins.pointer, 0, sizeof(RegisterMaskIndex) * max_ins);
 
         VirtualBuffer(VirtualRegister) virtual_registers = {};
-        vb_ensure_capacity(&virtual_registers, cast(u32, u64, round_up_to_next_power_of_2(virtual_register_count + 16)));
+        vb_ensure_capacity(&virtual_registers, cast_to(u32, u64, round_up_to_next_power_of_2(virtual_register_count + 16)));
         virtual_registers.length = virtual_register_count;
 
         for (u32 i = 0; i < builder->basic_blocks.length; i += 1)
@@ -23747,13 +23725,13 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
 
                 *vb_add(&virtual_registers, 1) = (VirtualRegister) {
                     .mask = mask,
-                    .class = cast(s16, u32, class),
-                    .assigned = cast(s16, u32, i),
+                    .class = cast_to(s16, u32, class),
+                    .assigned = cast_to(s16, u32, i),
                     .spill_cost = INFINITY,
                 };
             }
 
-            fixed[class] = cast(s32, u32, base);
+            fixed[class] = cast_to(s32, u32, base);
         }
 
         // Insert legalizing moves
@@ -23819,7 +23797,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
                                 if (shared_edge >= 0)
                                 {
                                     auto* input_node = thread_node_get(thread, input_index);
-                                    auto p_shared_edge = cast(u16, s32, shared_edge);
+                                    auto p_shared_edge = cast_to(u16, s32, shared_edge);
                                     assert(p_shared_edge < input_node->input_count);
                                     auto inputs = node_get_inputs(thread, input_node);
                                     for (u16 i = 1; i < input_node->input_count; i += 1)
@@ -24134,7 +24112,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
         for (u32 i = 0; i < builder->basic_blocks.length; i += 1)
         {
             auto* basic_block = &builder->basic_blocks.pointer[i];
-            auto basic_block_index = Index(BasicBlock, cast(u32, s64, basic_block - builder->basic_blocks.pointer));
+            auto basic_block_index = Index(BasicBlock, cast_to(u32, s64, basic_block - builder->basic_blocks.pointer));
             auto first_node = thread_node_get(thread, basic_block->items.pointer[0]);
             auto item_count = basic_block->items.length;
             u8 empty = 1;
@@ -24163,7 +24141,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
             }
             else
             {
-                basic_block->forward = cast(s32, u32, i);
+                basic_block->forward = cast_to(s32, u32, i);
 
                 auto* bb_end = thread_node_get(thread, basic_block->end);
                 if (!cfg_node_terminator(bb_end))
@@ -24217,7 +24195,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
         auto* order = arena_allocate(thread->arena, s32, builder->basic_blocks.length);
 
         u32 order_index = 0;
-        for (s32 i = 0; i < cast(s32, u32, builder->basic_blocks.length); i += 1)
+        for (s32 i = 0; i < cast_to(s32, u32, builder->basic_blocks.length); i += 1)
         {
             auto* basic_block = &builder->basic_blocks.pointer[i];
             if (basic_block->forward == i)
@@ -24231,7 +24209,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
             }
         }
 
-        for (s32 i = 0; i < cast(s32, u32, builder->basic_blocks.length); i += 1)
+        for (s32 i = 0; i < cast_to(s32, u32, builder->basic_blocks.length); i += 1)
         {
             auto* basic_block = &builder->basic_blocks.pointer[i];
             if (basic_block->forward == i)
@@ -24444,7 +24422,7 @@ fn void code_generation(Thread* restrict thread, CodegenOptions options)
 //                         );
 //                 unused(bufSize);
 //                 print("Error opening file \"{s}\": {cstr}\n", object_options.exe_path, lpMsgBuf);
-//                 fail();
+//                 failed_execution();
 //             }
 // #endif
             assert(os_file_descriptor_is_valid(fd));
@@ -24848,7 +24826,7 @@ void entry_point(int argc, char* argv[], char* envp[])
     Arena* global_arena = arena_init(MB(2), KB(64), KB(64));
 
     {
-        arguments.length = cast(u64, s32, argc);
+        arguments.length = cast_to(u64, s32, argc);
         arguments.pointer = arena_allocate(global_arena, String, arguments.length);
 
         for (int i = 0; i < argc; i += 1)
@@ -24869,7 +24847,7 @@ void entry_point(int argc, char* argv[], char* envp[])
 
     if (argc < 3)
     {
-        fail();
+        failed_execution();
     }
 
     String source_file_path = arguments.pointer[1];
@@ -24901,7 +24879,8 @@ void entry_point(int argc, char* argv[], char* envp[])
 
     if (thread->main_function == -1)
     {
-        fail();
+
+        failed_execution();
     }
 
     print("File path: {s}\n", source_file_path);
diff --git a/bootstrap/runner/runner.c b/bootstrap/runner/runner.c
index 7fe138d..a4e72a6 100644
--- a/bootstrap/runner/runner.c
+++ b/bootstrap/runner/runner.c
@@ -163,7 +163,7 @@ void entry_point(int argc, char* argv[], char* envp[])
     if (argc < 2)
     {
         print("Expected some arguments\n");
-        fail();
+        failed_execution();
     }
 
     Arena* arena = arena_init_default(KB(64));
@@ -187,7 +187,7 @@ void entry_point(int argc, char* argv[], char* envp[])
 
         if (string_starts_with(argument, strlit("build_type=")))
         {
-            auto release_start = cast(u32, s32, string_first_ch(argument, '=') + 1);
+            auto release_start = cast_to(u32, s32, string_first_ch(argument, '=') + 1);
             auto release_string = s_get_slice(u8, argument, release_start, argument.length);
 
             for (u64 i = 0; i < array_length(release_strings); i += 1)
@@ -261,7 +261,7 @@ void entry_point(int argc, char* argv[], char* envp[])
     if (command == COMMAND_COUNT && !source_file_path.pointer)
     {
         print("Expected a command\n");
-        fail();
+        failed_execution();
     }
 
     if (command == COMMAND_COUNT)
@@ -283,7 +283,6 @@ void entry_point(int argc, char* argv[], char* envp[])
         build_type = CMAKE_BUILD_TYPE_DEBUG;
     }
 
-    auto build_type_string = release_strings[build_type];
     String compiler_path = strlit("build/nest");
 
     switch (command)
@@ -291,7 +290,7 @@ void entry_point(int argc, char* argv[], char* envp[])
     case COMMAND_DEBUG:
         if (!source_file_path.pointer)
         {
-            fail();
+            failed_execution();
         }
 
         run(arena, envp, compiler_path, preferred_compiler_backend, 1, string_to_c(source_file_path));
diff --git a/bootstrap/std/base.c b/bootstrap/std/base.c
index 65f94e2..3dd62e0 100644
--- a/bootstrap/std/base.c
+++ b/bootstrap/std/base.c
@@ -320,14 +320,14 @@ u8 log2_alignment(u64 alignment)
     assert(alignment != 0);
     assert((alignment & (alignment - 1)) == 0);
     u64 left = (sizeof(alignment) * 8) - 1;
-    auto right = cast(u64, s32, __builtin_clzll(alignment));
-    auto result = cast(u8, u64, left - right);
+    auto right = cast_to(u64, s32, __builtin_clzll(alignment));
+    auto result = cast_to(u8, u64, left - right);
     return result;
 }
 
 // Lehmer's generator
 // https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/
-may_be_unused global __uint128_t rn_state;
+may_be_unused global_variable u128 rn_state;
 may_be_unused fn u64 generate_random_number()
 {
     rn_state *= 0xda942042e4dd58b5;
@@ -349,7 +349,7 @@ u64 round_up_to_next_power_of_2(u64 n)
 
 may_be_unused fn u64 absolute_int(s64 n)
 {
-    return n < 0 ? cast(u64, s64, -n) : cast(u64, s64, n);
+    return n < 0 ? cast_to(u64, s64, -n) : cast_to(u64, s64, n);
 }
 
 u64 parse_decimal(String string)
@@ -582,7 +582,7 @@ fn s32 pow5_bits(const s32 e)
 #define DOUBLE_POW5_INV_TABLE_SIZE 342
 #define DOUBLE_POW5_TABLE_SIZE 326
 
-global const u8 DIGIT_TABLE[200] = {
+global_variable const u8 DIGIT_TABLE[200] = {
   '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
   '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
   '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
@@ -595,7 +595,7 @@ global const u8 DIGIT_TABLE[200] = {
   '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
 };
 
-global const u64 DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] =
+global_variable const u64 DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] =
 {
     {                    1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u },
     {  5165088340638674453u, 1475739525896764129u }, {  7821419487252849886u, 1180591620717411303u },
@@ -802,7 +802,7 @@ fn u32 log10_pow5(const s32 e) {
   return (((u32) e) * 732923) >> 20;
 }
 
-global const u64 DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] =
+global_variable const u64 DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] =
 {
     {                    0u, 1152921504606846976u }, {                    0u, 1441151880758558720u },
     {                    0u, 1801439850948198400u }, {                    0u, 2251799813685248000u },
@@ -1288,7 +1288,7 @@ fn void write_float_decimal(String buffer, u64* value, u64 count)
 
     while (i + 2 < count)
     {
-        auto c = cast(u8, u64, *value % 100);
+        auto c = cast_to(u8, u64, *value % 100);
         *value /= 100;
         auto ptr = digits2(c);
         buffer.pointer[count - i - 1] = ptr[1];
@@ -1298,7 +1298,7 @@ fn void write_float_decimal(String buffer, u64* value, u64 count)
 
     while (i < count)
     {
-        auto c = cast(u8, u64, *value % 10);
+        auto c = cast_to(u8, u64, *value % 10);
         *value /= 10;
         buffer.pointer[count - i - 1] = '0' + c;
 
@@ -1494,7 +1494,7 @@ u64 format_float(String buffer, f64 value_double)
                 } break;
             case FLOAT_FORMAT_DECIMAL:
                 {
-                    auto dp_offset = result.exponent + cast(s32, u32, olength);
+                    auto dp_offset = result.exponent + cast_to(s32, u32, olength);
 
                     if (dp_offset <= 0)
                     {
@@ -1559,13 +1559,13 @@ u64 first_bit_set_64(u64 value)
 
 Hash32 hash32_fib_end(Hash32 hash)
 {
-    auto result = trunc(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32);
+    auto result = truncate_value(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32);
     return result;
 }
 
 Hash32 hash64_fib_end(Hash64 hash)
 {
-    auto result = trunc(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32);
+    auto result = truncate_value(Hash32, ((hash + 1) * 11400714819323198485ull) >> 32);
     return result;
 }
 
@@ -1637,7 +1637,7 @@ void* memmove(void* const dst, const void* const src, usize n)
 
 void* memset(void* dst, int n, usize size)
 {
-    u8 ch = cast(u8, s32, n);
+    u8 ch = cast_to(u8, s32, n);
     auto* destination = (u8*)dst;
     for (u64 i = 0; i < size; i += 1)
     {
diff --git a/bootstrap/std/md5.c b/bootstrap/std/md5.c
index 4a103ea..5568e77 100644
--- a/bootstrap/std/md5.c
+++ b/bootstrap/std/md5.c
@@ -19,12 +19,12 @@ STRUCT(MD5Context)
 #define MD5_H(X, Y, Z) (X ^ Y ^ Z)
 #define MD5_I(X, Y, Z) (Y ^ (X | ~Z))
 
-global u32 md5_s[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+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 u32 md5_k[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+global_variable u32 md5_k[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
                        0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
                        0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
                        0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
@@ -44,7 +44,7 @@ global u32 md5_k[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
 /*
  * Padding used to make the size (in bits) of the input congruent to 448 mod 512
  */
-global u8 md5_padding[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+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,
diff --git a/bootstrap/std/os.c b/bootstrap/std/os.c
index 5dd6ae1..77e3e7e 100644
--- a/bootstrap/std/os.c
+++ b/bootstrap/std/os.c
@@ -53,12 +53,12 @@ timestamp()
 
 
 #if _WIN32
-global u64 cpu_frequency;
+global_variable u64 cpu_frequency;
 #else
 #if LINK_LIBC
-global struct timespec cpu_resolution;
+global_variable struct timespec cpu_resolution;
 #else
-global u64 cpu_frequency;
+global_variable u64 cpu_frequency;
 #endif
 #endif
 
@@ -180,14 +180,14 @@ String path_base(String string)
     auto maybe_index = string_last_ch(string, '/');
     if (maybe_index != -1)
     {
-        auto index = cast(u64, s64, maybe_index);
+        auto index = cast_to(u64, s64, maybe_index);
         result = s_get_slice(u8, string, index + 1, string.length);
     }
 #if _WIN32
     if (!result.pointer)
     {
         auto maybe_index = string_last_ch(string, '\\');
-        auto index = cast(u64, s64, maybe_index);
+        auto index = cast_to(u64, s64, maybe_index);
         result = s_get_slice(u8, string, index + 1, string.length);
     }
 #endif
@@ -201,7 +201,7 @@ String path_no_extension(String string)
     auto maybe_index = string_last_ch(string, '.');
     if (maybe_index != -1)
     {
-        auto index = cast(u64, s64, maybe_index);
+        auto index = cast_to(u64, s64, maybe_index);
         result = s_get_slice(u8, string, 0, index);
     }
 
@@ -650,7 +650,7 @@ may_be_unused fn void* posix_mmap(void* address, size_t length, int protection_f
     return mmap(address, length, protection_flags, map_flags, fd, offset);
 #else 
 #ifdef __linux__
-    return (void*) syscall6(syscall_x86_64_mmap, (s64)address, cast(s64, u64, length), protection_flags, map_flags, fd, offset);
+    return (void*) syscall6(syscall_x86_64_mmap, (s64)address, cast_to(s64, u64, length), protection_flags, map_flags, fd, offset);
 #else
 #error "Unsupported operating system for static linking" 
 #endif
@@ -663,7 +663,7 @@ may_be_unused fn int syscall_mprotect(void *address, size_t length, int protecti
     return mprotect(address, length, protection_flags);
 #else 
 #ifdef __linux__
-    return cast(s32, s64, syscall3(syscall_x86_64_mprotect, (s64)address, cast(s64, u64, length), protection_flags));
+    return cast_to(s32, s64, syscall3(syscall_x86_64_mprotect, (s64)address, cast_to(s64, u64, length), protection_flags));
 #else
     return mprotect(address, length, protection_flags);
 #endif
@@ -676,7 +676,7 @@ may_be_unused fn int syscall_open(const char *file_path, int flags, int mode)
     return open(file_path, flags, mode);
 #else
 #ifdef __linux__
-    return cast(s32, s64, syscall3(syscall_x86_64_open, (s64)file_path, flags, mode));
+    return cast_to(s32, s64, syscall3(syscall_x86_64_open, (s64)file_path, flags, mode));
 #else
     return open(file_path, flags, mode);
 #endif
@@ -689,7 +689,7 @@ may_be_unused fn int syscall_close(int fd)
     return close(fd);
 #else
 #ifdef __linux__
-    return cast(s32, s64, syscall1(syscall_x86_64_close, fd));
+    return cast_to(s32, s64, syscall1(syscall_x86_64_close, fd));
 #else
     return close(fd);
 #endif
@@ -702,7 +702,7 @@ fn int syscall_fstat(int fd, struct stat *buffer)
     return fstat(fd, buffer);
 #else
 #ifdef __linux__
-    return cast(s32, s64, syscall2(syscall_x86_64_fstat, fd, (s64)buffer));
+    return cast_to(s32, s64, syscall2(syscall_x86_64_fstat, fd, (s64)buffer));
 #else
     return fstat(fd, buffer);
 #endif
@@ -741,7 +741,7 @@ may_be_unused fn int syscall_mkdir(String path, u32 mode)
 #if LINK_LIBC
     return mkdir((char*)path.pointer, mode);
 #else
-    return cast(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path.pointer, (s64)mode));
+    return cast_to(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path.pointer, (s64)mode));
 #endif
 }
 
@@ -751,7 +751,7 @@ may_be_unused fn int syscall_rmdir(String path)
 #if LINK_LIBC
     return rmdir((char*)path.pointer);
 #else
-    return cast(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path.pointer));
+    return cast_to(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path.pointer));
 #endif
 }
 
@@ -761,7 +761,7 @@ may_be_unused fn int syscall_unlink(String path)
 #if LINK_LIBC
     return unlink((char*)path.pointer);
 #else
-    return cast(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path.pointer));
+    return cast_to(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path.pointer));
 #endif
 }
 
@@ -770,7 +770,7 @@ may_be_unused fn pid_t syscall_fork()
 #if LINK_LIBC
     return fork();
 #else
-    return cast(s32, s64, syscall0(syscall_x86_64_fork));
+    return cast_to(s32, s64, syscall0(syscall_x86_64_fork));
 #endif
 
 }
@@ -789,7 +789,7 @@ may_be_unused fn pid_t syscall_waitpid(pid_t pid, int* status, int options)
 #if LINK_LIBC
     return waitpid(pid, status, options);
 #else
-    return cast(s32, s64, syscall4(syscall_x86_64_wait4, pid, (s64)status, options, 0));
+    return cast_to(s32, s64, syscall4(syscall_x86_64_wait4, pid, (s64)status, options, 0));
 #endif
 }
 
@@ -798,7 +798,7 @@ may_be_unused fn int syscall_gettimeofday(struct timeval* tv, struct timezone* t
 #if LINK_LIBC
     return gettimeofday(tv, tz);
 #else
-    return cast(s32, s64, syscall2(syscall_x86_64_gettimeofday, (s64)tv, (s64)tz));
+    return cast_to(s32, s64, syscall2(syscall_x86_64_gettimeofday, (s64)tv, (s64)tz));
 #endif
 }
 
@@ -831,7 +831,7 @@ may_be_unused fn u64 os_timer_get()
 #else
     struct timeval tv;
     syscall_gettimeofday(&tv, 0);
-    auto result = os_timer_freq() * cast(u64, s64, tv.tv_sec) + cast(u64, s64, tv.tv_usec);
+    auto result = os_timer_freq() * cast_to(u64, s64, tv.tv_sec) + cast_to(u64, s64, tv.tv_usec);
     return result;
 #endif
 }
@@ -898,7 +898,7 @@ u64 os_file_get_size(FileDescriptor fd)
     struct stat stat_buffer;
     int stat_result = syscall_fstat(fd, &stat_buffer);
     assert(stat_result == 0);
-    auto size = cast(u64, s64, stat_buffer.st_size);
+    auto size = cast_to(u64, s64, stat_buffer.st_size);
     return size;
 #endif
 }
@@ -907,11 +907,11 @@ void os_file_write(FileDescriptor fd, String content)
 {
 #if _WIN32
     DWORD bytes_written = 0;
-    BOOL result = WriteFile(fd, content.pointer, cast(u32, u64, content.length), &bytes_written, 0);
+    BOOL result = WriteFile(fd, content.pointer, cast_to(u32, u64, content.length), &bytes_written, 0);
     assert(result != 0);
 #else
     auto result = syscall_write(fd, content.pointer, content.length);
-    assert(cast(u64, s64, result) == content.length);
+    assert(cast_to(u64, s64, result) == content.length);
 #endif
 }
 
@@ -924,7 +924,7 @@ may_be_unused fn u64 os_file_read(FileDescriptor fd, String buffer, u64 byte_cou
     {
 #if _WIN32
         DWORD read = 0;
-        BOOL result = ReadFile(fd, buffer.pointer, cast(u32, u64, byte_count), &read, 0);
+        BOOL result = ReadFile(fd, buffer.pointer, cast_to(u32, u64, byte_count), &read, 0);
         assert(result != 0);
         bytes_read = read;
 #else
@@ -932,7 +932,7 @@ may_be_unused fn u64 os_file_read(FileDescriptor fd, String buffer, u64 byte_cou
         assert(result > 0);
         if (result > 0)
         {
-            bytes_read = cast(u64, s64, result);
+            bytes_read = cast_to(u64, s64, result);
         }
 #endif
     }
@@ -1090,22 +1090,22 @@ void print(const char* format, ...)
                                         it += 1;
                                         if (*it != '2')
                                         {
-                                            fail();
+                                            failed_execution();
                                         }
                                         it += 1;
-                                        fail();
+                                        failed_execution();
                                         break;
                                     case '6':
                                         it += 1;
                                         if (*it != '4')
                                         {
-                                            fail();
+                                            failed_execution();
                                         }
                                         it += 1;
                                         value_double = va_arg(args, f64);
                                         break;
                                     default:
-                                        fail();
+                                        failed_execution();
                                 }
 
                                 buffer_i += format_float(s_get_slice(u8, buffer, buffer_i, buffer.length), value_double);
@@ -1215,7 +1215,7 @@ void print(const char* format, ...)
 
                     if (*it != brace_close)
                     {
-                        fail();
+                        failed_execution();
                     }
 
                     it += 1;
@@ -1373,7 +1373,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
         if (argument)
         {
             auto string_len = strlen(argument);
-            length += cast(u32, u64, string_len + 1);
+            length += cast_to(u32, u64, string_len + 1);
         }
     }
 
@@ -1416,12 +1416,12 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
             print("Process ran with exit code: {u32:x}\n", exit_code);
             if (exit_code != 0)
             {
-                fail();
+                failed_execution();
             }
         }
         else
         {
-            fail();
+            failed_execution();
         }
 
         CloseHandle(process_information.hProcess);
@@ -1511,7 +1511,7 @@ void run_command(Arena* arena, CStringSlice arguments, char* envp[])
         if (!success)
         {
             print("Program failed to run!\n");
-            fail();
+            failed_execution();
         }
         auto ms = resolve_timestamp(start_timestamp, end_timestamp, TIME_UNIT_MILLISECONDS);
         auto ticks =
diff --git a/bootstrap/std/sha1.c b/bootstrap/std/sha1.c
index 411a6f4..f38cf4e 100644
--- a/bootstrap/std/sha1.c
+++ b/bootstrap/std/sha1.c
@@ -60,7 +60,7 @@
 
 // fn void sha1_process_block(Sha1Context* ctx)
 // {
-//     global const u32 k[4] =
+//     global_variable const u32 k[4] =
 //     {
 //         0x5A827999,
 //         0x6ED9EBA1,
diff --git a/bootstrap/std/string.c b/bootstrap/std/string.c
index c9234c5..f9e37de 100644
--- a/bootstrap/std/string.c
+++ b/bootstrap/std/string.c
@@ -24,7 +24,7 @@ s64 string_last_ch(String string, u8 ch)
         i -= 1;
         if (string.pointer[i] == ch)
         {
-            result = cast(s64, u64, i);
+            result = cast_to(s64, u64, i);
             break;
         }
     }
diff --git a/bootstrap/std/virtual_buffer.c b/bootstrap/std/virtual_buffer.c
index d03f018..8076569 100644
--- a/bootstrap/std/virtual_buffer.c
+++ b/bootstrap/std/virtual_buffer.c
@@ -13,8 +13,8 @@ void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_c
             vb->pointer = os_reserve(0, item_size * UINT32_MAX, (OSReserveProtectionFlags) {}, (OSReserveMapFlags) { .priv = 1, .anon = 1, .noreserve = 1 });
         }
 
-        u32 old_page_capacity = cast(u32, u64, align_forward(old_capacity * item_size, minimum_granularity));
-        u32 new_page_capacity = cast(u32, u64, align_forward(wanted_capacity * item_size, minimum_granularity));
+        u32 old_page_capacity = cast_to(u32, u64, align_forward(old_capacity * item_size, minimum_granularity));
+        u32 new_page_capacity = cast_to(u32, u64, align_forward(wanted_capacity * item_size, minimum_granularity));
 
         u32 commit_size = new_page_capacity - old_page_capacity;
         void* commit_pointer = vb->pointer + old_page_capacity;
@@ -42,7 +42,7 @@ u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count)
 
 u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes)
 {
-    auto len = cast(u32, u64, bytes.length);
+    auto len = cast_to(u32, u64, bytes.length);
     vb_generic_ensure_capacity(vb, sizeof(u8), len);
     auto* pointer = vb_generic_add_assume_capacity(vb, sizeof(u8), len);
     memcpy(pointer, bytes.pointer, len);
@@ -51,7 +51,7 @@ u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes)
 
 void vb_copy_string(VirtualBuffer(u8)* buffer, String string)
 {
-    auto length = cast(u32, u64, string.length);
+    auto length = cast_to(u32, u64, string.length);
     auto* pointer = vb_add(buffer, length);
     memcpy(pointer, string.pointer, length);
 }
diff --git a/project.sh b/project.sh
index 02f7c27..e497f8b 100755
--- a/project.sh
+++ b/project.sh
@@ -16,7 +16,7 @@ esac
 
 case "$OSTYPE" in
     linux*) cmake . -B$build_dir -G Ninja -DCMAKE_BUILD_TYPE="$release_mode" -DCMAKE_C_COMPILER="$CLANG_PREFIX/clang" -DCMAKE_CXX_COMPILER="$CLANG_PREFIX/clang++" -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" ;;
-    darwin*) cmake . -B$build_dir -G Ninja -DCMAKE_BUILD_TYPE="$release_mode" -DCMAKE_C_COMPILER="$CLANG_PREFIX/clang" -DCMAKE_CXX_COMPILER="$CLANG_PREFIX/clang++" -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) ;;
+    darwin*) cmake . -B$build_dir -G Ninja -DCMAKE_BUILD_TYPE="$release_mode" -DCMAKE_C_COMPILER="$CLANG_PREFIX/clang" -DCMAKE_CXX_COMPILER="$CLANG_PREFIX/clang++" "-DCMAKE_PREFIX_PATH=$(brew --prefix zstd);$(brew --prefix llvm)" ;;
     *)        exit 1 ;;
 esac