From 9571f8263c71ec29deff964ae1aadad804499fb8 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 3 Sep 2024 21:44:27 +0200 Subject: [PATCH] Integrate a TB-based backend --- .github/workflows/ci.yml | 18 +- bootstrap/build.c | 97 +- bootstrap/lib.h | 1352 ++++- bootstrap/main.c | 10711 ++++++++++++++++++++++++++----------- licenses/tb/LICENSE.txt | 21 + project.sh | 2 +- 6 files changed, 8997 insertions(+), 3204 deletions(-) create mode 100644 licenses/tb/LICENSE.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25bd0a8..c6f76dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,12 +18,12 @@ jobs: - name: Build and test run: | ./project.sh test all - macos_build_and_test: - runs-on: macos-latest - timeout-minutes: 15 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Build and test - run: | - ./project.sh test all + # macos_build_and_test: + # runs-on: macos-latest + # timeout-minutes: 15 + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # - name: Build and test + # run: | + # ./project.sh test all diff --git a/bootstrap/build.c b/bootstrap/build.c index 9053d90..9dde43b 100644 --- a/bootstrap/build.c +++ b/bootstrap/build.c @@ -103,6 +103,8 @@ fn void compile_c(const CompileOptions *const options, char** envp) unreachable(); } + *vb_add(args, 1) = "-march=native"; + if (options->error_on_warning) { *vb_add(args, 1) = "-Werror"; @@ -113,6 +115,7 @@ fn void compile_c(const CompileOptions *const options, char** envp) "-Wall", "-Wextra", "-Wpedantic", + "-Wconversion", "-Wno-nested-anon-types", "-Wno-keyword-macro", "-Wno-gnu-auto-type", @@ -133,7 +136,7 @@ fn void compile_c(const CompileOptions *const options, char** envp) if (options->linkage == LINKAGE_STATIC) { - char* static_options[] = { "-ffreestanding", "-nostdlib", "-static", "-DSTATIC", }; + char* static_options[] = { "-ffreestanding", "-nostdlib", "-static", "-DSTATIC", "-lgcc" }; memcpy(vb_add(args, array_length(static_options)), static_options, sizeof(static_options)); } @@ -217,6 +220,7 @@ typedef enum Command : u8 { COMMAND_DEBUG, COMMAND_RUN_TESTS, + COMMAND_COMPILE, COMMAND_COUNT, } Command; @@ -334,7 +338,6 @@ fn void run_tests(Arena* arena, TestOptions const * const test_options, char** e 0, }; - run_command((CStringSlice) array_to_slice(arguments), envp); if (compiler_backend != COMPILER_BACKEND_INTERPRETER) @@ -362,6 +365,7 @@ int main(int argc, char* argv[], char** envp) print("Expected some arguments\n"); return 1; } + // calibrate_cpu_timer(); CompilerBackend preferred_compiler_backend = COMPILER_BACKEND_COUNT; Command command = COMMAND_COUNT; @@ -392,15 +396,20 @@ int main(int argc, char* argv[], char** envp) { command = COMMAND_DEBUG; } + else if (s_equal(argument, strlit("compile"))) + { + command = COMMAND_COMPILE; + } else if (s_equal(argument, strlit("all"))) { test_every_config = 1; } } - if (argc > 2) + auto index = 2 - (command == COMMAND_COUNT); + if (argc > index) { - auto* c_argument = argv[2]; + auto* c_argument = argv[index]; auto argument = cstr(c_argument); String expected_starts[] = { strlit("tests/"), strlit("src/") }; @@ -419,12 +428,17 @@ int main(int argc, char* argv[], char** envp) } } - if (command == COMMAND_COUNT) + if (command == COMMAND_COUNT && !source_file_path.pointer) { print("Expected a command\n"); return 1; } + if (command == COMMAND_COUNT) + { + command = COMMAND_COMPILE; + } + if ((command == COMMAND_DEBUG) | ((command == COMMAND_RUN_TESTS) & (test_every_config == 0))) { if (preferred_compiler_backend == COMPILER_BACKEND_COUNT) @@ -441,46 +455,56 @@ int main(int argc, char* argv[], char** envp) fail(); } + Linkage linkage = +#if defined(__linux__) + LINKAGE_STATIC; +#else + LINKAGE_DYNAMIC; +#endif + compile_and_run(&(CompileOptions) { .in_path = compiler_source_path, - .out_path = build_dir "/" "nest_O0_static", + .out_path = linkage == LINKAGE_DYNAMIC ? (build_dir "/" "nest_O0_dynamic") : (build_dir "/" "nest_O0_static"), .compiler = default_compiler, .debug_info = 1, .error_on_warning = 0, .optimization_mode = O0, -#if defined(__linux__) - .linkage = LINKAGE_STATIC, -#else - .linkage = LINKAGE_DYNAMIC, -#endif + .linkage = linkage, }, envp, preferred_compiler_backend, 1, string_to_c(source_file_path)); break; case COMMAND_RUN_TESTS: { Arena* arena = arena_init_default(KB(64)); - Linkage all_linkages[] = { LINKAGE_DYNAMIC, LINKAGE_STATIC }; + Linkage all_linkages[] = { LINKAGE_STATIC, LINKAGE_DYNAMIC }; static_assert(array_length(all_linkages) == LINKAGE_COUNT); - OptimizationMode all_optimization_modes[] = { O0, O1, O2, O3, Os, Oz }; - static_assert(array_length(all_optimization_modes) == OPTIMIZATION_COUNT); + OptimizationMode all_optimization_modes[] = { + O0, + O1, + O2, + O3, + Os, + Oz + }; + // static_assert(array_length(all_optimization_modes) == OPTIMIZATION_COUNT); String every_single_test[] = { strlit("tests/first.nat"), - strlit("tests/add_sub.nat"), - strlit("tests/mul.nat"), - strlit("tests/div.nat"), - strlit("tests/and.nat"), - strlit("tests/or.nat"), - strlit("tests/xor.nat"), - strlit("tests/return_var.nat"), - strlit("tests/return_mod_scope.nat"), - strlit("tests/shift_left.nat"), - strlit("tests/shift_right.nat"), - strlit("tests/thousand_simple_functions.nat"), - strlit("tests/simple_arg.nat"), - strlit("tests/comparison.nat"), + // strlit("tests/add_sub.nat"), + // strlit("tests/mul.nat"), + // strlit("tests/div.nat"), + // strlit("tests/and.nat"), + // strlit("tests/or.nat"), + // strlit("tests/xor.nat"), + // strlit("tests/return_var.nat"), + // strlit("tests/return_mod_scope.nat"), + // strlit("tests/shift_left.nat"), + // strlit("tests/shift_right.nat"), + // strlit("tests/thousand_simple_functions.nat"), + // strlit("tests/simple_arg.nat"), + // strlit("tests/comparison.nat"), }; CompilerBackend all_compiler_backends[] = { - COMPILER_BACKEND_INTERPRETER, - COMPILER_BACKEND_C, + // COMPILER_BACKEND_INTERPRETER, + // COMPILER_BACKEND_C, #ifdef __linux__ COMPILER_BACKEND_MACHINE, #endif @@ -524,6 +548,21 @@ int main(int argc, char* argv[], char** envp) .compiler_backends = compiler_backend_selection, }, envp); } break; + case COMMAND_COMPILE: + compile_c(&(CompileOptions) { + .in_path = compiler_source_path, + .out_path = build_dir "/" "nest_O0_static", + .compiler = default_compiler, + .debug_info = 1, + .error_on_warning = 0, + .optimization_mode = O0, +#if defined(__linux__) + .linkage = LINKAGE_STATIC, +#else + .linkage = LINKAGE_DYNAMIC, +#endif + }, envp); + break; case COMMAND_COUNT: unreachable(); } diff --git a/bootstrap/lib.h b/bootstrap/lib.h index 2ccc384..bea5010 100644 --- a/bootstrap/lib.h +++ b/bootstrap/lib.h @@ -1,6 +1,10 @@ #include #include #include +#include +#if defined(__x86_64__) +#include +#endif #include #include @@ -24,17 +28,20 @@ #include #include #include +#include #endif typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; +typedef __uint128_t u128; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; +typedef __int128_t s128; typedef float f32; typedef double f64; @@ -42,14 +49,29 @@ typedef double f64; typedef u32 Hash32; typedef u64 Hash64; +#define FOR_N(it, start, end) \ +for (ptrdiff_t it = (start), end__ = (end); it < end__; ++it) + +#define FOR_REV_N(it, start, end) \ +for (ptrdiff_t it = (end), start__ = (start); (it--) > start__;) + +#define FOR_BIT(it, start, bits) \ +for (uint64_t _bits_ = (bits), it = (start); _bits_; _bits_ >>= 1, ++it) if (_bits_ & 1) + +#define FOREACH_SET(it, set) \ +FOR_N(_i, 0, ((set)->arr.capacity + 63) / 64) FOR_BIT(it, _i*64, (set)->arr.pointer[_i]) + #define 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 forceinline __attribute__((always_inline)) -#define expect(x, b) __builtin_expect(x, b) +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) #define breakpoint() __builtin_debugtrap() #define fail() trap() #define trap() bad_exit("Trap reached", __FILE__, __LINE__) @@ -60,11 +82,261 @@ typedef u64 Hash64; #define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024) #define unused(x) (void)(x) #define may_be_unused __attribute__((unused)) +#define truncate(Destination, source) (Destination)(source) +#define cast(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) \ + do {\ + auto temp = a;\ + a = b;\ + b = temp;\ + } while (0) + +fn u64 timestamp() +{ +#if defined(__x86_64__) + return __rdtsc(); +#else + return 0; +#endif +} + +may_be_unused fn void print(const char* format, ...); + +may_be_unused fn u16 cast_u32_to_u16(u32 source, const char* name, int line) +{ +#if _DEBUG + if (source > UINT16_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u16)source; + return result; +} + +may_be_unused fn s16 cast_u32_to_s16(u32 source, const char* name, int line) +{ +#if _DEBUG + if (source > INT16_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (s16)source; + return result; +} + +may_be_unused fn s32 cast_u32_to_s32(u32 source, const char* name, int line) +{ +#if _DEBUG + if (source > INT32_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (s32)source; + return result; +} + +may_be_unused fn u8 cast_u64_to_u8(u64 source, const char* name, int line) +{ +#if _DEBUG + if (source > UINT8_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u8)source; + return result; +} + +may_be_unused fn u16 cast_u64_to_u16(u64 source, const char* name, int line) +{ +#if _DEBUG + if (source > UINT16_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u16)source; + return result; +} + +may_be_unused fn u32 cast_u64_to_u32(u64 source, const char* name, int line) +{ +#if _DEBUG + if (source > UINT32_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u32)source; + return result; +} + +may_be_unused fn s32 cast_u64_to_s32(u64 source, const char* name, int line) +{ +#if _DEBUG + if (source > INT32_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (s32)source; + return result; +} + +may_be_unused fn s64 cast_u64_to_s64(u64 source, const char* name, int line) +{ +#if _DEBUG + if (source > INT64_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (s64)source; + return result; +} + +may_be_unused fn u8 cast_s32_to_u8(s32 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } + if ((u32)source > UINT8_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u8)source; + return result; +} + +may_be_unused fn u16 cast_s32_to_u16(s32 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } + if ((u32)source > UINT16_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u16)source; + return result; +} + +may_be_unused fn u32 cast_s32_to_u32(s32 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u32)source; + return result; +} + +may_be_unused fn u64 cast_s32_to_u64(s32 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u64)source; + return result; +} + +may_be_unused fn u16 cast_s64_to_u16(s64 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } + if (source > UINT16_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u16)source; + return result; +} + +may_be_unused fn u32 cast_s64_to_u32(s64 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u32)source; + return result; +} + +may_be_unused fn u64 cast_s64_to_u64(s64 source, const char* name, int line) +{ +#if _DEBUG + if (source < 0) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (u64)source; + return result; +} + +may_be_unused fn s32 cast_s64_to_s32(s64 source, const char* name, int line) +{ +#if _DEBUG + if (source < INT32_MIN) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } + + if (source > INT32_MAX) + { + print("Cast failed at {cstr}:{u32}\n", name, line); + trap(); + } +#endif + auto result = (s32)source; + return result; +} #if _DEBUG -#define assert(x) if (__builtin_expect(!(x), 0)) { bad_exit("Assert failed", __FILE__, __LINE__); } +#define assert(x) if (unlikely(!(x))) { bad_exit("Assert failed: \"" # x "\"", __FILE__, __LINE__); } #else -#define assert(x) __builtin_expect(!(x), 0) +#define assert(x) unlikely(!(x)) #endif #ifdef unreachable @@ -81,10 +353,8 @@ typedef u64 Hash64; #define alignof(x) _Alignof(x) #define auto __auto_type -#define bad_exit(message, file, line) do { print(message " at {cstr}:{u32}\n", file, line); __builtin_trap(); } while(0) #define todo() do { print("TODO at {cstr}:{u32}\n", __FILE__, __LINE__); __builtin_trap(); } while(0) -may_be_unused fn void print(const char* format, ...); #if __APPLE__ const global u64 page_size = KB(16); @@ -107,8 +377,8 @@ may_be_unused fn u8 log2_alignment(u64 alignment) assert(alignment != 0); assert((alignment & (alignment - 1)) == 0); u64 left = (sizeof(alignment) * 8) - 1; - u64 right = __builtin_clzl(alignment); - u8 result = left - right; + auto right = cast(u64, s32, __builtin_clzll(alignment)); + auto result = cast(u8, u64, left - right); return result; } @@ -134,8 +404,13 @@ may_be_unused fn u64 round_up_to_next_power_of_2(u64 n) return n; } +may_be_unused fn u64 absolute_int(s64 n) +{ + return n < 0 ? -n : n; +} + #if LINK_LIBC == 0 -void* memcpy(void* __restrict dst, void* __restrict src, u64 size) +void* memcpy(void* const restrict dst, const void* const restrict src, u64 size) { auto* destination = (u8*)dst; auto* source = (u8*)src; @@ -148,6 +423,40 @@ void* memcpy(void* __restrict dst, void* __restrict src, u64 size) return dst; } + +void* memmove(void* const dst, const void* const src, u64 n) +{ + // Implementation + // https://opensource.apple.com/source/network_cmds/network_cmds-481.20.1/unbound/compat/memmove.c.auto.html + uint8_t* from = (uint8_t*) src; + uint8_t* to = (uint8_t*) dst; + + if (from == to || n == 0) + return dst; + if (to > from && to-from < (int)n) { + /* to overlaps with from */ + /* */ + /* */ + /* copy in reverse, to avoid overwriting from */ + int i; + for(i=n-1; i>=0; i--) + to[i] = from[i]; + return dst; + } + if (from > to && from-to < (int)n) { + /* to overlaps with from */ + /* */ + /* */ + /* copy forwards, to avoid overwriting from */ + size_t i; + for(i=0; i 0) { i -= 1; if (string.pointer[i] == ch) { - result = i; + result = cast(s64, u64, i); break; } } @@ -260,9 +570,10 @@ fn s32 string_last_ch(String string, u8 ch) may_be_unused fn String string_base(String string) { String result = {}; - auto index = string_last_ch(string, '/'); - if (index != -1) + auto maybe_index = string_last_ch(string, '/'); + if (maybe_index != -1) { + auto index = cast(u64, s64, maybe_index); result = s_get_slice(u8, string, index + 1, string.length); } @@ -272,9 +583,10 @@ may_be_unused fn String string_base(String string) may_be_unused fn String string_no_extension(String string) { String result = {}; - auto index = string_last_ch(string, '.'); - if (index != -1) + auto maybe_index = string_last_ch(string, '.'); + if (maybe_index != -1) { + auto index = cast(u64, s64, maybe_index); result = s_get_slice(u8, string, 0, index); } @@ -396,21 +708,21 @@ may_be_unused fn Hash32 hash64_to_hash32(Hash64 hash64) #ifdef __linux__ may_be_unused fn forceinline long syscall0(long n) { - unsigned long ret; + long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall1(long n, long a1) { - unsigned long ret; + long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall2(long n, long a1, long a2) { - unsigned long ret; + long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2) : "rcx", "r11", "memory"); return ret; @@ -418,7 +730,7 @@ may_be_unused fn forceinline long syscall2(long n, long a1, long a2) may_be_unused fn forceinline long syscall3(long n, long a1, long a2, long a3) { - unsigned long ret; + long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3) : "rcx", "r11", "memory"); return ret; @@ -426,7 +738,7 @@ may_be_unused fn forceinline long syscall3(long n, long a1, long a2, long a3) may_be_unused fn forceinline long syscall4(long n, long a1, long a2, long a3, long a4) { - unsigned long ret; + long ret; register long r10 __asm__("r10") = a4; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10): "rcx", "r11", "memory"); @@ -435,7 +747,7 @@ may_be_unused fn forceinline long syscall4(long n, long a1, long a2, long a3, lo may_be_unused fn forceinline long syscall5(long n, long a1, long a2, long a3, long a4, long a5) { - unsigned long ret; + long ret; register long r10 __asm__("r10") = a4; register long r8 __asm__("r8") = a5; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), @@ -445,7 +757,7 @@ may_be_unused fn forceinline long syscall5(long n, long a1, long a2, long a3, lo may_be_unused fn forceinline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) { - unsigned long ret; + long ret; register long r10 __asm__("r10") = a4; register long r8 __asm__("r8") = a5; register long r9 __asm__("r9") = a6; @@ -833,7 +1145,7 @@ may_be_unused fn void* syscall_mmap(void* address, size_t length, int protection return mmap(address, length, protection_flags, map_flags, fd, offset); #else #ifdef __linux__ - return (void*) syscall6(syscall_x86_64_mmap, (unsigned long)address, length, protection_flags, map_flags, fd, offset); + return (void*) syscall6(syscall_x86_64_mmap, (s64)address, cast(s64, u64, length), protection_flags, map_flags, fd, offset); #else #error "Unsupported operating system for static linking" #endif @@ -846,7 +1158,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 syscall3(syscall_x86_64_mprotect, (unsigned long)address, length, protection_flags); + return cast(s32, s64, syscall3(syscall_x86_64_mprotect, (s64)address, cast(s64, u64, length), protection_flags)); #else return mprotect(address, length, protection_flags); #endif @@ -859,7 +1171,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 syscall3(syscall_x86_64_open, (unsigned long)file_path, flags, mode); + return cast(s32, s64, syscall3(syscall_x86_64_open, (s64)file_path, flags, mode)); #else return open(file_path, flags, mode); #endif @@ -872,7 +1184,7 @@ may_be_unused fn int syscall_close(int fd) return close(fd); #else #ifdef __linux__ - return syscall1(syscall_x86_64_close, fd); + return cast(s32, s64, syscall1(syscall_x86_64_close, fd)); #else return close(fd); #endif @@ -885,7 +1197,7 @@ fn int syscall_fstat(int fd, struct stat *buffer) return fstat(fd, buffer); #else #ifdef __linux__ - return syscall2(syscall_x86_64_fstat, fd, (unsigned long)buffer); + return cast(s32, s64, syscall2(syscall_x86_64_fstat, fd, (s64)buffer)); #else return fstat(fd, buffer); #endif @@ -897,7 +1209,7 @@ may_be_unused fn u64 file_get_size(int fd) struct stat stat_buffer; int stat_result = syscall_fstat(fd, &stat_buffer); assert(stat_result == 0); - u64 size = stat_buffer.st_size; + auto size = cast(u64, s64, stat_buffer.st_size); return size; } @@ -907,7 +1219,7 @@ may_be_unused fn ssize_t syscall_read(int fd, void* buffer, size_t bytes) return read(fd, buffer, bytes); #else #ifdef __linux__ - return syscall3(syscall_x86_64_read, fd, (unsigned long)buffer, bytes); + return syscall3(syscall_x86_64_read, fd, (s64)buffer, (s64)bytes); #else return read(fd, buffer, bytes); #endif @@ -920,7 +1232,7 @@ may_be_unused fn ssize_t syscall_write(int fd, const void *buffer, size_t bytes) return write(fd, buffer, bytes); #else #ifdef __linux__ - return syscall3(syscall_x86_64_write, fd, (unsigned long)buffer, bytes); + return syscall3(syscall_x86_64_write, fd, (s64)buffer, (s64)bytes); #else return write(fd, buffer, bytes); #endif @@ -932,7 +1244,7 @@ may_be_unused fn int syscall_mkdir(const char* path, u32 mode) #if LINK_LIBC return mkdir(path, mode); #else - return syscall2(syscall_x86_64_mkdir, (unsigned long)path, mode); + return cast(s32, s64, syscall2(syscall_x86_64_mkdir, (s64)path, (s64)mode)); #endif } @@ -941,7 +1253,7 @@ may_be_unused fn int syscall_rmdir(const char* path) #if LINK_LIBC return rmdir(path); #else - return syscall1(syscall_x86_64_rmdir, (unsigned long)path); + return cast(s32, s64, syscall1(syscall_x86_64_rmdir, (s64)path)); #endif } @@ -950,7 +1262,7 @@ may_be_unused fn int syscall_unlink(const char* path) #if LINK_LIBC return unlink(path); #else - return syscall1(syscall_x86_64_unlink, (unsigned long)path); + return cast(s32, s64, syscall1(syscall_x86_64_unlink, (s64)path)); #endif } @@ -959,7 +1271,7 @@ may_be_unused fn pid_t syscall_fork() #if LINK_LIBC return fork(); #else - return syscall0(syscall_x86_64_fork); + return cast(s32, s64, syscall0(syscall_x86_64_fork)); #endif } @@ -969,7 +1281,7 @@ may_be_unused fn signed long syscall_execve(const char* path, char *const argv[] #if LINK_LIBC return execve(path, argv, envp); #else - return syscall3(syscall_x86_64_execve, (unsigned long)path, (unsigned long)argv, (unsigned long)envp); + return syscall3(syscall_x86_64_execve, (s64)path, (s64)argv, (s64)envp); #endif } may_be_unused fn pid_t syscall_waitpid(pid_t pid, int* status, int options) @@ -977,7 +1289,16 @@ 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 syscall4(syscall_x86_64_wait4, pid, (unsigned long)status, options, 0); + return cast(s32, s64, syscall4(syscall_x86_64_wait4, pid, (s64)status, options, 0)); +#endif +} + +may_be_unused fn int syscall_gettimeofday(struct timeval* tv, struct timezone* tz) +{ +#if LINK_LIBC + return gettimeofday(tv, tz); +#else + return cast(s32, s64, syscall2(syscall_x86_64_gettimeofday, (s64)tv, (s64)tz)); #endif } @@ -995,6 +1316,41 @@ may_be_unused [[noreturn]] [[gnu::cold]] fn void syscall_exit(int status) #endif } +may_be_unused fn u64 os_timer_freq() +{ + return 1000 * 1000; +} + +may_be_unused fn u64 os_timer_get() +{ + struct timeval tv; + syscall_gettimeofday(&tv, 0); + auto result = os_timer_freq() * cast(u64, s64, tv.tv_sec) + cast(u64, s64, tv.tv_usec); + return result; +} + +global u64 cpu_frequency; + +may_be_unused fn void calibrate_cpu_timer() +{ + u64 miliseconds_to_wait = 100; + u64 cpu_start = timestamp(); + u64 os_frequency = os_timer_freq(); + u64 os_elapsed = 0; + u64 os_start = os_timer_get(); + u64 os_wait_time = os_frequency * miliseconds_to_wait / 1000; + + while (os_elapsed < os_wait_time) + { + auto os_end = os_timer_get(); + os_elapsed = os_end - os_start; + } + + u64 cpu_end = timestamp(); + u64 cpu_elapsed = cpu_end - cpu_start; + cpu_frequency = os_frequency * cpu_elapsed / os_elapsed; +} + fn u8* reserve(u64 size) { int protection_flags = PROT_NONE; @@ -1087,6 +1443,675 @@ fn u32 format_decimal(String buffer, u64 decimal) } #define SILENT (0) +struct SmallIntResult +{ + u64 mantissa; + s32 exponent; + u8 is_small_int; +}; +typedef struct SmallIntResult SmallIntResult; + +#define double_mantissa_bits 52 +#define double_exponent_bits 11 +#define double_bias 1023 + +#define double_pow5_bitcount 125 +#define double_pow5_inv_bitcount 125 + +// Returns floor(log_10(2^e)); requires 0 <= e <= 1650. +fn u32 log10_pow2(const s32 e) +{ + // The first value this approximation fails for is 2^1651 which is just greater than 10^297. + assert(e >= 0); + assert(e <= 1650); + return (((u32) e) * 78913) >> 18; +} + +// Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. +fn s32 pow5_bits(const s32 e) +{ + // This approximation works up to the point that the multiplication overflows at e = 3529. + // If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater + // than 2^9297. + assert(e >= 0); + assert(e <= 3528); + return (s32) (((((u32) e) * 1217359) >> 19) + 1); +} + +#define DOUBLE_POW5_INV_BITCOUNT 125 +#define DOUBLE_POW5_BITCOUNT 125 + +#define DOUBLE_POW5_INV_TABLE_SIZE 342 +#define DOUBLE_POW5_TABLE_SIZE 326 + +global 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', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '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] = +{ + { 1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u }, + { 5165088340638674453u, 1475739525896764129u }, { 7821419487252849886u, 1180591620717411303u }, + { 8824922364862649494u, 1888946593147858085u }, { 7059937891890119595u, 1511157274518286468u }, + { 13026647942995916322u, 1208925819614629174u }, { 9774590264567735146u, 1934281311383406679u }, + { 11509021026396098440u, 1547425049106725343u }, { 16585914450600699399u, 1237940039285380274u }, + { 15469416676735388068u, 1980704062856608439u }, { 16064882156130220778u, 1584563250285286751u }, + { 9162556910162266299u, 1267650600228229401u }, { 7281393426775805432u, 2028240960365167042u }, + { 16893161185646375315u, 1622592768292133633u }, { 2446482504291369283u, 1298074214633706907u }, + { 7603720821608101175u, 2076918743413931051u }, { 2393627842544570617u, 1661534994731144841u }, + { 16672297533003297786u, 1329227995784915872u }, { 11918280793837635165u, 2126764793255865396u }, + { 5845275820328197809u, 1701411834604692317u }, { 15744267100488289217u, 1361129467683753853u }, + { 3054734472329800808u, 2177807148294006166u }, { 17201182836831481939u, 1742245718635204932u }, + { 6382248639981364905u, 1393796574908163946u }, { 2832900194486363201u, 2230074519853062314u }, + { 5955668970331000884u, 1784059615882449851u }, { 1075186361522890384u, 1427247692705959881u }, + { 12788344622662355584u, 2283596308329535809u }, { 13920024512871794791u, 1826877046663628647u }, + { 3757321980813615186u, 1461501637330902918u }, { 10384555214134712795u, 1169201309864722334u }, + { 5547241898389809503u, 1870722095783555735u }, { 4437793518711847602u, 1496577676626844588u }, + { 10928932444453298728u, 1197262141301475670u }, { 17486291911125277965u, 1915619426082361072u }, + { 6610335899416401726u, 1532495540865888858u }, { 12666966349016942027u, 1225996432692711086u }, + { 12888448528943286597u, 1961594292308337738u }, { 17689456452638449924u, 1569275433846670190u }, + { 14151565162110759939u, 1255420347077336152u }, { 7885109000409574610u, 2008672555323737844u }, + { 9997436015069570011u, 1606938044258990275u }, { 7997948812055656009u, 1285550435407192220u }, + { 12796718099289049614u, 2056880696651507552u }, { 2858676849947419045u, 1645504557321206042u }, + { 13354987924183666206u, 1316403645856964833u }, { 17678631863951955605u, 2106245833371143733u }, + { 3074859046935833515u, 1684996666696914987u }, { 13527933681774397782u, 1347997333357531989u }, + { 10576647446613305481u, 2156795733372051183u }, { 15840015586774465031u, 1725436586697640946u }, + { 8982663654677661702u, 1380349269358112757u }, { 18061610662226169046u, 2208558830972980411u }, + { 10759939715039024913u, 1766847064778384329u }, { 12297300586773130254u, 1413477651822707463u }, + { 15986332124095098083u, 2261564242916331941u }, { 9099716884534168143u, 1809251394333065553u }, + { 14658471137111155161u, 1447401115466452442u }, { 4348079280205103483u, 1157920892373161954u }, + { 14335624477811986218u, 1852673427797059126u }, { 7779150767507678651u, 1482138742237647301u }, + { 2533971799264232598u, 1185710993790117841u }, { 15122401323048503126u, 1897137590064188545u }, + { 12097921058438802501u, 1517710072051350836u }, { 5988988032009131678u, 1214168057641080669u }, + { 16961078480698431330u, 1942668892225729070u }, { 13568862784558745064u, 1554135113780583256u }, + { 7165741412905085728u, 1243308091024466605u }, { 11465186260648137165u, 1989292945639146568u }, + { 16550846638002330379u, 1591434356511317254u }, { 16930026125143774626u, 1273147485209053803u }, + { 4951948911778577463u, 2037035976334486086u }, { 272210314680951647u, 1629628781067588869u }, + { 3907117066486671641u, 1303703024854071095u }, { 6251387306378674625u, 2085924839766513752u }, + { 16069156289328670670u, 1668739871813211001u }, { 9165976216721026213u, 1334991897450568801u }, + { 7286864317269821294u, 2135987035920910082u }, { 16897537898041588005u, 1708789628736728065u }, + { 13518030318433270404u, 1367031702989382452u }, { 6871453250525591353u, 2187250724783011924u }, + { 9186511415162383406u, 1749800579826409539u }, { 11038557946871817048u, 1399840463861127631u }, + { 10282995085511086630u, 2239744742177804210u }, { 8226396068408869304u, 1791795793742243368u }, + { 13959814484210916090u, 1433436634993794694u }, { 11267656730511734774u, 2293498615990071511u }, + { 5324776569667477496u, 1834798892792057209u }, { 7949170070475892320u, 1467839114233645767u }, + { 17427382500606444826u, 1174271291386916613u }, { 5747719112518849781u, 1878834066219066582u }, + { 15666221734240810795u, 1503067252975253265u }, { 12532977387392648636u, 1202453802380202612u }, + { 5295368560860596524u, 1923926083808324180u }, { 4236294848688477220u, 1539140867046659344u }, + { 7078384693692692099u, 1231312693637327475u }, { 11325415509908307358u, 1970100309819723960u }, + { 9060332407926645887u, 1576080247855779168u }, { 14626963555825137356u, 1260864198284623334u }, + { 12335095245094488799u, 2017382717255397335u }, { 9868076196075591040u, 1613906173804317868u }, + { 15273158586344293478u, 1291124939043454294u }, { 13369007293925138595u, 2065799902469526871u }, + { 7005857020398200553u, 1652639921975621497u }, { 16672732060544291412u, 1322111937580497197u }, + { 11918976037903224966u, 2115379100128795516u }, { 5845832015580669650u, 1692303280103036413u }, + { 12055363241948356366u, 1353842624082429130u }, { 841837113407818570u, 2166148198531886609u }, + { 4362818505468165179u, 1732918558825509287u }, { 14558301248600263113u, 1386334847060407429u }, + { 12225235553534690011u, 2218135755296651887u }, { 2401490813343931363u, 1774508604237321510u }, + { 1921192650675145090u, 1419606883389857208u }, { 17831303500047873437u, 2271371013423771532u }, + { 6886345170554478103u, 1817096810739017226u }, { 1819727321701672159u, 1453677448591213781u }, + { 16213177116328979020u, 1162941958872971024u }, { 14873036941900635463u, 1860707134196753639u }, + { 15587778368262418694u, 1488565707357402911u }, { 8780873879868024632u, 1190852565885922329u }, + { 2981351763563108441u, 1905364105417475727u }, { 13453127855076217722u, 1524291284333980581u }, + { 7073153469319063855u, 1219433027467184465u }, { 11317045550910502167u, 1951092843947495144u }, + { 12742985255470312057u, 1560874275157996115u }, { 10194388204376249646u, 1248699420126396892u }, + { 1553625868034358140u, 1997919072202235028u }, { 8621598323911307159u, 1598335257761788022u }, + { 17965325103354776697u, 1278668206209430417u }, { 13987124906400001422u, 2045869129935088668u }, + { 121653480894270168u, 1636695303948070935u }, { 97322784715416134u, 1309356243158456748u }, + { 14913111714512307107u, 2094969989053530796u }, { 8241140556867935363u, 1675975991242824637u }, + { 17660958889720079260u, 1340780792994259709u }, { 17189487779326395846u, 2145249268790815535u }, + { 13751590223461116677u, 1716199415032652428u }, { 18379969808252713988u, 1372959532026121942u }, + { 14650556434236701088u, 2196735251241795108u }, { 652398703163629901u, 1757388200993436087u }, + { 11589965406756634890u, 1405910560794748869u }, { 7475898206584884855u, 2249456897271598191u }, + { 2291369750525997561u, 1799565517817278553u }, { 9211793429904618695u, 1439652414253822842u }, + { 18428218302589300235u, 2303443862806116547u }, { 7363877012587619542u, 1842755090244893238u }, + { 13269799239553916280u, 1474204072195914590u }, { 10615839391643133024u, 1179363257756731672u }, + { 2227947767661371545u, 1886981212410770676u }, { 16539753473096738529u, 1509584969928616540u }, + { 13231802778477390823u, 1207667975942893232u }, { 6413489186596184024u, 1932268761508629172u }, + { 16198837793502678189u, 1545815009206903337u }, { 5580372605318321905u, 1236652007365522670u }, + { 8928596168509315048u, 1978643211784836272u }, { 18210923379033183008u, 1582914569427869017u }, + { 7190041073742725760u, 1266331655542295214u }, { 436019273762630246u, 2026130648867672343u }, + { 7727513048493924843u, 1620904519094137874u }, { 9871359253537050198u, 1296723615275310299u }, + { 4726128361433549347u, 2074757784440496479u }, { 7470251503888749801u, 1659806227552397183u }, + { 13354898832594820487u, 1327844982041917746u }, { 13989140502667892133u, 2124551971267068394u }, + { 14880661216876224029u, 1699641577013654715u }, { 11904528973500979224u, 1359713261610923772u }, + { 4289851098633925465u, 2175541218577478036u }, { 18189276137874781665u, 1740432974861982428u }, + { 3483374466074094362u, 1392346379889585943u }, { 1884050330976640656u, 2227754207823337509u }, + { 5196589079523222848u, 1782203366258670007u }, { 15225317707844309248u, 1425762693006936005u }, + { 5913764258841343181u, 2281220308811097609u }, { 8420360221814984868u, 1824976247048878087u }, + { 17804334621677718864u, 1459980997639102469u }, { 17932816512084085415u, 1167984798111281975u }, + { 10245762345624985047u, 1868775676978051161u }, { 4507261061758077715u, 1495020541582440929u }, + { 7295157664148372495u, 1196016433265952743u }, { 7982903447895485668u, 1913626293225524389u }, + { 10075671573058298858u, 1530901034580419511u }, { 4371188443704728763u, 1224720827664335609u }, + { 14372599139411386667u, 1959553324262936974u }, { 15187428126271019657u, 1567642659410349579u }, + { 15839291315758726049u, 1254114127528279663u }, { 3206773216762499739u, 2006582604045247462u }, + { 13633465017635730761u, 1605266083236197969u }, { 14596120828850494932u, 1284212866588958375u }, + { 4907049252451240275u, 2054740586542333401u }, { 236290587219081897u, 1643792469233866721u }, + { 14946427728742906810u, 1315033975387093376u }, { 16535586736504830250u, 2104054360619349402u }, + { 5849771759720043554u, 1683243488495479522u }, { 15747863852001765813u, 1346594790796383617u }, + { 10439186904235184007u, 2154551665274213788u }, { 15730047152871967852u, 1723641332219371030u }, + { 12584037722297574282u, 1378913065775496824u }, { 9066413911450387881u, 2206260905240794919u }, + { 10942479943902220628u, 1765008724192635935u }, { 8753983955121776503u, 1412006979354108748u }, + { 10317025513452932081u, 2259211166966573997u }, { 874922781278525018u, 1807368933573259198u }, + { 8078635854506640661u, 1445895146858607358u }, { 13841606313089133175u, 1156716117486885886u }, + { 14767872471458792434u, 1850745787979017418u }, { 746251532941302978u, 1480596630383213935u }, + { 597001226353042382u, 1184477304306571148u }, { 15712597221132509104u, 1895163686890513836u }, + { 8880728962164096960u, 1516130949512411069u }, { 10793931984473187891u, 1212904759609928855u }, + { 17270291175157100626u, 1940647615375886168u }, { 2748186495899949531u, 1552518092300708935u }, + { 2198549196719959625u, 1242014473840567148u }, { 18275073973719576693u, 1987223158144907436u }, + { 10930710364233751031u, 1589778526515925949u }, { 12433917106128911148u, 1271822821212740759u }, + { 8826220925580526867u, 2034916513940385215u }, { 7060976740464421494u, 1627933211152308172u }, + { 16716827836597268165u, 1302346568921846537u }, { 11989529279587987770u, 2083754510274954460u }, + { 9591623423670390216u, 1667003608219963568u }, { 15051996368420132820u, 1333602886575970854u }, + { 13015147745246481542u, 2133764618521553367u }, { 3033420566713364587u, 1707011694817242694u }, + { 6116085268112601993u, 1365609355853794155u }, { 9785736428980163188u, 2184974969366070648u }, + { 15207286772667951197u, 1747979975492856518u }, { 1097782973908629988u, 1398383980394285215u }, + { 1756452758253807981u, 2237414368630856344u }, { 5094511021344956708u, 1789931494904685075u }, + { 4075608817075965366u, 1431945195923748060u }, { 6520974107321544586u, 2291112313477996896u }, + { 1527430471115325346u, 1832889850782397517u }, { 12289990821117991246u, 1466311880625918013u }, + { 17210690286378213644u, 1173049504500734410u }, { 9090360384495590213u, 1876879207201175057u }, + { 18340334751822203140u, 1501503365760940045u }, { 14672267801457762512u, 1201202692608752036u }, + { 16096930852848599373u, 1921924308174003258u }, { 1809498238053148529u, 1537539446539202607u }, + { 12515645034668249793u, 1230031557231362085u }, { 1578287981759648052u, 1968050491570179337u }, + { 12330676829633449412u, 1574440393256143469u }, { 13553890278448669853u, 1259552314604914775u }, + { 3239480371808320148u, 2015283703367863641u }, { 17348979556414297411u, 1612226962694290912u }, + { 6500486015647617283u, 1289781570155432730u }, { 10400777625036187652u, 2063650512248692368u }, + { 15699319729512770768u, 1650920409798953894u }, { 16248804598352126938u, 1320736327839163115u }, + { 7551343283653851484u, 2113178124542660985u }, { 6041074626923081187u, 1690542499634128788u }, + { 12211557331022285596u, 1352433999707303030u }, { 1091747655926105338u, 2163894399531684849u }, + { 4562746939482794594u, 1731115519625347879u }, { 7339546366328145998u, 1384892415700278303u }, + { 8053925371383123274u, 2215827865120445285u }, { 6443140297106498619u, 1772662292096356228u }, + { 12533209867169019542u, 1418129833677084982u }, { 5295740528502789974u, 2269007733883335972u }, + { 15304638867027962949u, 1815206187106668777u }, { 4865013464138549713u, 1452164949685335022u }, + { 14960057215536570740u, 1161731959748268017u }, { 9178696285890871890u, 1858771135597228828u }, + { 14721654658196518159u, 1487016908477783062u }, { 4398626097073393881u, 1189613526782226450u }, + { 7037801755317430209u, 1903381642851562320u }, { 5630241404253944167u, 1522705314281249856u }, + { 814844308661245011u, 1218164251424999885u }, { 1303750893857992017u, 1949062802279999816u }, + { 15800395974054034906u, 1559250241823999852u }, { 5261619149759407279u, 1247400193459199882u }, + { 12107939454356961969u, 1995840309534719811u }, { 5997002748743659252u, 1596672247627775849u }, + { 8486951013736837725u, 1277337798102220679u }, { 2511075177753209390u, 2043740476963553087u }, + { 13076906586428298482u, 1634992381570842469u }, { 14150874083884549109u, 1307993905256673975u }, + { 4194654460505726958u, 2092790248410678361u }, { 18113118827372222859u, 1674232198728542688u }, + { 3422448617672047318u, 1339385758982834151u }, { 16543964232501006678u, 2143017214372534641u }, + { 9545822571258895019u, 1714413771498027713u }, { 15015355686490936662u, 1371531017198422170u }, + { 5577825024675947042u, 2194449627517475473u }, { 11840957649224578280u, 1755559702013980378u }, + { 16851463748863483271u, 1404447761611184302u }, { 12204946739213931940u, 2247116418577894884u }, + { 13453306206113055875u, 1797693134862315907u }, { 3383947335406624054u, 1438154507889852726u }, + { 16482362180876329456u, 2301047212623764361u }, { 9496540929959153242u, 1840837770099011489u }, + { 11286581558709232917u, 1472670216079209191u }, { 5339916432225476010u, 1178136172863367353u }, + { 4854517476818851293u, 1885017876581387765u }, { 3883613981455081034u, 1508014301265110212u }, + { 14174937629389795797u, 1206411441012088169u }, { 11611853762797942306u, 1930258305619341071u }, + { 5600134195496443521u, 1544206644495472857u }, { 15548153800622885787u, 1235365315596378285u }, + { 6430302007287065643u, 1976584504954205257u }, { 16212288050055383484u, 1581267603963364205u }, + { 12969830440044306787u, 1265014083170691364u }, { 9683682259845159889u, 2024022533073106183u }, + { 15125643437359948558u, 1619218026458484946u }, { 8411165935146048523u, 1295374421166787957u }, + { 17147214310975587960u, 2072599073866860731u }, { 10028422634038560045u, 1658079259093488585u }, + { 8022738107230848036u, 1326463407274790868u }, { 9147032156827446534u, 2122341451639665389u }, + { 11006974540203867551u, 1697873161311732311u }, { 5116230817421183718u, 1358298529049385849u }, + { 15564666937357714594u, 2173277646479017358u }, { 1383687105660440706u, 1738622117183213887u }, + { 12174996128754083534u, 1390897693746571109u }, { 8411947361780802685u, 2225436309994513775u }, + { 6729557889424642148u, 1780349047995611020u }, { 5383646311539713719u, 1424279238396488816u }, + { 1235136468979721303u, 2278846781434382106u }, { 15745504434151418335u, 1823077425147505684u }, + { 16285752362063044992u, 1458461940118004547u }, { 5649904260166615347u, 1166769552094403638u }, + { 5350498001524674232u, 1866831283351045821u }, { 591049586477829062u, 1493465026680836657u }, + { 11540886113407994219u, 1194772021344669325u }, { 18673707743239135u, 1911635234151470921u }, + { 14772334225162232601u, 1529308187321176736u }, { 8128518565387875758u, 1223446549856941389u }, + { 1937583260394870242u, 1957514479771106223u }, { 8928764237799716840u, 1566011583816884978u }, + { 14521709019723594119u, 1252809267053507982u }, { 8477339172590109297u, 2004494827285612772u }, + { 17849917782297818407u, 1603595861828490217u }, { 6901236596354434079u, 1282876689462792174u }, + { 18420676183650915173u, 2052602703140467478u }, { 3668494502695001169u, 1642082162512373983u }, + { 10313493231639821582u, 1313665730009899186u }, { 9122891541139893884u, 2101865168015838698u }, + { 14677010862395735754u, 1681492134412670958u }, { 673562245690857633u, 1345193707530136767u } +}; +// Best case: use 128-bit type. +fn u64 mul_shift_64(const u64 m, const u64* const mul, const s32 j) +{ + const u128 b0 = ((u128) m) * mul[0]; + const u128 b2 = ((u128) m) * mul[1]; + return (u64) (((b0 >> 64) + b2) >> (j - 64)); +} + +fn u64 mul_shift_all_64(const u64 m, const u64* const mul, const s32 j, u64* const vp, u64* const vm, const u32 mmShift) +{ + *vp = mul_shift_64(4 * m + 2, mul, j); + *vm = mul_shift_64(4 * m - 1 - mmShift, mul, j); + return mul_shift_64(4 * m, mul, j); +} + +// Returns e == 0 ? 1 : [log_2(5^e)]; requires 0 <= e <= 3528. +// fn s32 log2_pow5(const s32 e) +// { +// // This approximation works up to the point that the multiplication overflows at e = 3529. +// // If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater +// // than 2^9297. +// assert(e >= 0); +// assert(e <= 3528); +// return (s32) ((((u32) e) * 1217359) >> 19); +// } +// Returns floor(log_10(5^e)); requires 0 <= e <= 2620. +fn u32 log10_pow5(const s32 e) { + // The first value this approximation fails for is 5^2621 which is just greater than 10^1832. + assert(e >= 0); + assert(e <= 2620); + return (((u32) e) * 732923) >> 20; +} + +global const u64 DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] = +{ + { 0u, 1152921504606846976u }, { 0u, 1441151880758558720u }, + { 0u, 1801439850948198400u }, { 0u, 2251799813685248000u }, + { 0u, 1407374883553280000u }, { 0u, 1759218604441600000u }, + { 0u, 2199023255552000000u }, { 0u, 1374389534720000000u }, + { 0u, 1717986918400000000u }, { 0u, 2147483648000000000u }, + { 0u, 1342177280000000000u }, { 0u, 1677721600000000000u }, + { 0u, 2097152000000000000u }, { 0u, 1310720000000000000u }, + { 0u, 1638400000000000000u }, { 0u, 2048000000000000000u }, + { 0u, 1280000000000000000u }, { 0u, 1600000000000000000u }, + { 0u, 2000000000000000000u }, { 0u, 1250000000000000000u }, + { 0u, 1562500000000000000u }, { 0u, 1953125000000000000u }, + { 0u, 1220703125000000000u }, { 0u, 1525878906250000000u }, + { 0u, 1907348632812500000u }, { 0u, 1192092895507812500u }, + { 0u, 1490116119384765625u }, { 4611686018427387904u, 1862645149230957031u }, + { 9799832789158199296u, 1164153218269348144u }, { 12249790986447749120u, 1455191522836685180u }, + { 15312238733059686400u, 1818989403545856475u }, { 14528612397897220096u, 2273736754432320594u }, + { 13692068767113150464u, 1421085471520200371u }, { 12503399940464050176u, 1776356839400250464u }, + { 15629249925580062720u, 2220446049250313080u }, { 9768281203487539200u, 1387778780781445675u }, + { 7598665485932036096u, 1734723475976807094u }, { 274959820560269312u, 2168404344971008868u }, + { 9395221924704944128u, 1355252715606880542u }, { 2520655369026404352u, 1694065894508600678u }, + { 12374191248137781248u, 2117582368135750847u }, { 14651398557727195136u, 1323488980084844279u }, + { 13702562178731606016u, 1654361225106055349u }, { 3293144668132343808u, 2067951531382569187u }, + { 18199116482078572544u, 1292469707114105741u }, { 8913837547316051968u, 1615587133892632177u }, + { 15753982952572452864u, 2019483917365790221u }, { 12152082354571476992u, 1262177448353618888u }, + { 15190102943214346240u, 1577721810442023610u }, { 9764256642163156992u, 1972152263052529513u }, + { 17631875447420442880u, 1232595164407830945u }, { 8204786253993389888u, 1540743955509788682u }, + { 1032610780636961552u, 1925929944387235853u }, { 2951224747111794922u, 1203706215242022408u }, + { 3689030933889743652u, 1504632769052528010u }, { 13834660704216955373u, 1880790961315660012u }, + { 17870034976990372916u, 1175494350822287507u }, { 17725857702810578241u, 1469367938527859384u }, + { 3710578054803671186u, 1836709923159824231u }, { 26536550077201078u, 2295887403949780289u }, + { 11545800389866720434u, 1434929627468612680u }, { 14432250487333400542u, 1793662034335765850u }, + { 8816941072311974870u, 2242077542919707313u }, { 17039803216263454053u, 1401298464324817070u }, + { 12076381983474541759u, 1751623080406021338u }, { 5872105442488401391u, 2189528850507526673u }, + { 15199280947623720629u, 1368455531567204170u }, { 9775729147674874978u, 1710569414459005213u }, + { 16831347453020981627u, 2138211768073756516u }, { 1296220121283337709u, 1336382355046097823u }, + { 15455333206886335848u, 1670477943807622278u }, { 10095794471753144002u, 2088097429759527848u }, + { 6309871544845715001u, 1305060893599704905u }, { 12499025449484531656u, 1631326116999631131u }, + { 11012095793428276666u, 2039157646249538914u }, { 11494245889320060820u, 1274473528905961821u }, + { 532749306367912313u, 1593091911132452277u }, { 5277622651387278295u, 1991364888915565346u }, + { 7910200175544436838u, 1244603055572228341u }, { 14499436237857933952u, 1555753819465285426u }, + { 8900923260467641632u, 1944692274331606783u }, { 12480606065433357876u, 1215432671457254239u }, + { 10989071563364309441u, 1519290839321567799u }, { 9124653435777998898u, 1899113549151959749u }, + { 8008751406574943263u, 1186945968219974843u }, { 5399253239791291175u, 1483682460274968554u }, + { 15972438586593889776u, 1854603075343710692u }, { 759402079766405302u, 1159126922089819183u }, + { 14784310654990170340u, 1448908652612273978u }, { 9257016281882937117u, 1811135815765342473u }, + { 16182956370781059300u, 2263919769706678091u }, { 7808504722524468110u, 1414949856066673807u }, + { 5148944884728197234u, 1768687320083342259u }, { 1824495087482858639u, 2210859150104177824u }, + { 1140309429676786649u, 1381786968815111140u }, { 1425386787095983311u, 1727233711018888925u }, + { 6393419502297367043u, 2159042138773611156u }, { 13219259225790630210u, 1349401336733506972u }, + { 16524074032238287762u, 1686751670916883715u }, { 16043406521870471799u, 2108439588646104644u }, + { 803757039314269066u, 1317774742903815403u }, { 14839754354425000045u, 1647218428629769253u }, + { 4714634887749086344u, 2059023035787211567u }, { 9864175832484260821u, 1286889397367007229u }, + { 16941905809032713930u, 1608611746708759036u }, { 2730638187581340797u, 2010764683385948796u }, + { 10930020904093113806u, 1256727927116217997u }, { 18274212148543780162u, 1570909908895272496u }, + { 4396021111970173586u, 1963637386119090621u }, { 5053356204195052443u, 1227273366324431638u }, + { 15540067292098591362u, 1534091707905539547u }, { 14813398096695851299u, 1917614634881924434u }, + { 13870059828862294966u, 1198509146801202771u }, { 12725888767650480803u, 1498136433501503464u }, + { 15907360959563101004u, 1872670541876879330u }, { 14553786618154326031u, 1170419088673049581u }, + { 4357175217410743827u, 1463023860841311977u }, { 10058155040190817688u, 1828779826051639971u }, + { 7961007781811134206u, 2285974782564549964u }, { 14199001900486734687u, 1428734239102843727u }, + { 13137066357181030455u, 1785917798878554659u }, { 11809646928048900164u, 2232397248598193324u }, + { 16604401366885338411u, 1395248280373870827u }, { 16143815690179285109u, 1744060350467338534u }, + { 10956397575869330579u, 2180075438084173168u }, { 6847748484918331612u, 1362547148802608230u }, + { 17783057643002690323u, 1703183936003260287u }, { 17617136035325974999u, 2128979920004075359u }, + { 17928239049719816230u, 1330612450002547099u }, { 17798612793722382384u, 1663265562503183874u }, + { 13024893955298202172u, 2079081953128979843u }, { 5834715712847682405u, 1299426220705612402u }, + { 16516766677914378815u, 1624282775882015502u }, { 11422586310538197711u, 2030353469852519378u }, + { 11750802462513761473u, 1268970918657824611u }, { 10076817059714813937u, 1586213648322280764u }, + { 12596021324643517422u, 1982767060402850955u }, { 5566670318688504437u, 1239229412751781847u }, + { 2346651879933242642u, 1549036765939727309u }, { 7545000868343941206u, 1936295957424659136u }, + { 4715625542714963254u, 1210184973390411960u }, { 5894531928393704067u, 1512731216738014950u }, + { 16591536947346905892u, 1890914020922518687u }, { 17287239619732898039u, 1181821263076574179u }, + { 16997363506238734644u, 1477276578845717724u }, { 2799960309088866689u, 1846595723557147156u }, + { 10973347230035317489u, 1154122327223216972u }, { 13716684037544146861u, 1442652909029021215u }, + { 12534169028502795672u, 1803316136286276519u }, { 11056025267201106687u, 2254145170357845649u }, + { 18439230838069161439u, 1408840731473653530u }, { 13825666510731675991u, 1761050914342066913u }, + { 3447025083132431277u, 2201313642927583642u }, { 6766076695385157452u, 1375821026829739776u }, + { 8457595869231446815u, 1719776283537174720u }, { 10571994836539308519u, 2149720354421468400u }, + { 6607496772837067824u, 1343575221513417750u }, { 17482743002901110588u, 1679469026891772187u }, + { 17241742735199000331u, 2099336283614715234u }, { 15387775227926763111u, 1312085177259197021u }, + { 5399660979626290177u, 1640106471573996277u }, { 11361262242960250625u, 2050133089467495346u }, + { 11712474920277544544u, 1281333180917184591u }, { 10028907631919542777u, 1601666476146480739u }, + { 7924448521472040567u, 2002083095183100924u }, { 14176152362774801162u, 1251301934489438077u }, + { 3885132398186337741u, 1564127418111797597u }, { 9468101516160310080u, 1955159272639746996u }, + { 15140935484454969608u, 1221974545399841872u }, { 479425281859160394u, 1527468181749802341u }, + { 5210967620751338397u, 1909335227187252926u }, { 17091912818251750210u, 1193334516992033078u }, + { 12141518985959911954u, 1491668146240041348u }, { 15176898732449889943u, 1864585182800051685u }, + { 11791404716994875166u, 1165365739250032303u }, { 10127569877816206054u, 1456707174062540379u }, + { 8047776328842869663u, 1820883967578175474u }, { 836348374198811271u, 2276104959472719343u }, + { 7440246761515338900u, 1422565599670449589u }, { 13911994470321561530u, 1778206999588061986u }, + { 8166621051047176104u, 2222758749485077483u }, { 2798295147690791113u, 1389224218428173427u }, + { 17332926989895652603u, 1736530273035216783u }, { 17054472718942177850u, 2170662841294020979u }, + { 8353202440125167204u, 1356664275808763112u }, { 10441503050156459005u, 1695830344760953890u }, + { 3828506775840797949u, 2119787930951192363u }, { 86973725686804766u, 1324867456844495227u }, + { 13943775212390669669u, 1656084321055619033u }, { 3594660960206173375u, 2070105401319523792u }, + { 2246663100128858359u, 1293815875824702370u }, { 12031700912015848757u, 1617269844780877962u }, + { 5816254103165035138u, 2021587305976097453u }, { 5941001823691840913u, 1263492066235060908u }, + { 7426252279614801142u, 1579365082793826135u }, { 4671129331091113523u, 1974206353492282669u }, + { 5225298841145639904u, 1233878970932676668u }, { 6531623551432049880u, 1542348713665845835u }, + { 3552843420862674446u, 1927935892082307294u }, { 16055585193321335241u, 1204959932551442058u }, + { 10846109454796893243u, 1506199915689302573u }, { 18169322836923504458u, 1882749894611628216u }, + { 11355826773077190286u, 1176718684132267635u }, { 9583097447919099954u, 1470898355165334544u }, + { 11978871809898874942u, 1838622943956668180u }, { 14973589762373593678u, 2298278679945835225u }, + { 2440964573842414192u, 1436424174966147016u }, { 3051205717303017741u, 1795530218707683770u }, + { 13037379183483547984u, 2244412773384604712u }, { 8148361989677217490u, 1402757983365377945u }, + { 14797138505523909766u, 1753447479206722431u }, { 13884737113477499304u, 2191809349008403039u }, + { 15595489723564518921u, 1369880843130251899u }, { 14882676136028260747u, 1712351053912814874u }, + { 9379973133180550126u, 2140438817391018593u }, { 17391698254306313589u, 1337774260869386620u }, + { 3292878744173340370u, 1672217826086733276u }, { 4116098430216675462u, 2090272282608416595u }, + { 266718509671728212u, 1306420176630260372u }, { 333398137089660265u, 1633025220787825465u }, + { 5028433689789463235u, 2041281525984781831u }, { 10060300083759496378u, 1275800953740488644u }, + { 12575375104699370472u, 1594751192175610805u }, { 1884160825592049379u, 1993438990219513507u }, + { 17318501580490888525u, 1245899368887195941u }, { 7813068920331446945u, 1557374211108994927u }, + { 5154650131986920777u, 1946717763886243659u }, { 915813323278131534u, 1216698602428902287u }, + { 14979824709379828129u, 1520873253036127858u }, { 9501408849870009354u, 1901091566295159823u }, + { 12855909558809837702u, 1188182228934474889u }, { 2234828893230133415u, 1485227786168093612u }, + { 2793536116537666769u, 1856534732710117015u }, { 8663489100477123587u, 1160334207943823134u }, + { 1605989338741628675u, 1450417759929778918u }, { 11230858710281811652u, 1813022199912223647u }, + { 9426887369424876662u, 2266277749890279559u }, { 12809333633531629769u, 1416423593681424724u }, + { 16011667041914537212u, 1770529492101780905u }, { 6179525747111007803u, 2213161865127226132u }, + { 13085575628799155685u, 1383226165704516332u }, { 16356969535998944606u, 1729032707130645415u }, + { 15834525901571292854u, 2161290883913306769u }, { 2979049660840976177u, 1350806802445816731u }, + { 17558870131333383934u, 1688508503057270913u }, { 8113529608884566205u, 2110635628821588642u }, + { 9682642023980241782u, 1319147268013492901u }, { 16714988548402690132u, 1648934085016866126u }, + { 11670363648648586857u, 2061167606271082658u }, { 11905663298832754689u, 1288229753919426661u }, + { 1047021068258779650u, 1610287192399283327u }, { 15143834390605638274u, 2012858990499104158u }, + { 4853210475701136017u, 1258036869061940099u }, { 1454827076199032118u, 1572546086327425124u }, + { 1818533845248790147u, 1965682607909281405u }, { 3442426662494187794u, 1228551629943300878u }, + { 13526405364972510550u, 1535689537429126097u }, { 3072948650933474476u, 1919611921786407622u }, + { 15755650962115585259u, 1199757451116504763u }, { 15082877684217093670u, 1499696813895630954u }, + { 9630225068416591280u, 1874621017369538693u }, { 8324733676974063502u, 1171638135855961683u }, + { 5794231077790191473u, 1464547669819952104u }, { 7242788847237739342u, 1830684587274940130u }, + { 18276858095901949986u, 2288355734093675162u }, { 16034722328366106645u, 1430222333808546976u }, + { 1596658836748081690u, 1787777917260683721u }, { 6607509564362490017u, 2234722396575854651u }, + { 1823850468512862308u, 1396701497859909157u }, { 6891499104068465790u, 1745876872324886446u }, + { 17837745916940358045u, 2182346090406108057u }, { 4231062170446641922u, 1363966306503817536u }, + { 5288827713058302403u, 1704957883129771920u }, { 6611034641322878003u, 2131197353912214900u }, + { 13355268687681574560u, 1331998346195134312u }, { 16694085859601968200u, 1664997932743917890u }, + { 11644235287647684442u, 2081247415929897363u }, { 4971804045566108824u, 1300779634956185852u }, + { 6214755056957636030u, 1625974543695232315u }, { 3156757802769657134u, 2032468179619040394u }, + { 6584659645158423613u, 1270292612261900246u }, { 17454196593302805324u, 1587865765327375307u }, + { 17206059723201118751u, 1984832206659219134u }, { 6142101308573311315u, 1240520129162011959u }, + { 3065940617289251240u, 1550650161452514949u }, { 8444111790038951954u, 1938312701815643686u }, + { 665883850346957067u, 1211445438634777304u }, { 832354812933696334u, 1514306798293471630u }, + { 10263815553021896226u, 1892883497866839537u }, { 17944099766707154901u, 1183052186166774710u }, + { 13206752671529167818u, 1478815232708468388u }, { 16508440839411459773u, 1848519040885585485u }, + { 12623618533845856310u, 1155324400553490928u }, { 15779523167307320387u, 1444155500691863660u }, + { 1277659885424598868u, 1805194375864829576u }, { 1597074856780748586u, 2256492969831036970u }, + { 5609857803915355770u, 1410308106144398106u }, { 16235694291748970521u, 1762885132680497632u }, + { 1847873790976661535u, 2203606415850622041u }, { 12684136165428883219u, 1377254009906638775u }, + { 11243484188358716120u, 1721567512383298469u }, { 219297180166231438u, 2151959390479123087u }, + { 7054589765244976505u, 1344974619049451929u }, { 13429923224983608535u, 1681218273811814911u }, + { 12175718012802122765u, 2101522842264768639u }, { 14527352785642408584u, 1313451776415480399u }, + { 13547504963625622826u, 1641814720519350499u }, { 12322695186104640628u, 2052268400649188124u }, + { 16925056528170176201u, 1282667750405742577u }, { 7321262604930556539u, 1603334688007178222u }, + { 18374950293017971482u, 2004168360008972777u }, { 4566814905495150320u, 1252605225005607986u }, + { 14931890668723713708u, 1565756531257009982u }, { 9441491299049866327u, 1957195664071262478u }, + { 1289246043478778550u, 1223247290044539049u }, { 6223243572775861092u, 1529059112555673811u }, + { 3167368447542438461u, 1911323890694592264u }, { 1979605279714024038u, 1194577431684120165u }, + { 7086192618069917952u, 1493221789605150206u }, { 18081112809442173248u, 1866527237006437757u }, + { 13606538515115052232u, 1166579523129023598u }, { 7784801107039039482u, 1458224403911279498u }, + { 507629346944023544u, 1822780504889099373u }, { 5246222702107417334u, 2278475631111374216u }, + { 3278889188817135834u, 1424047269444608885u }, { 8710297504448807696u, 1780059086805761106u } +}; + +// Returns true if value is divisible by 2^p. +fn u8 multiple_of_power_of_2(const u64 value, const u32 p) { + assert(value != 0); + assert(p < 64); + // __builtin_ctzll doesn't appear to be faster here. + return (value & ((1ull << p) - 1)) == 0; +} + +static inline uint64_t div5(const uint64_t x) { + return x / 5; +} + +static inline uint64_t div10(const uint64_t x) { + return x / 10; +} + +static inline uint64_t div100(const uint64_t x) { + return x / 100; +} + +static inline uint64_t div1e8(const uint64_t x) { + return x / 100000000; +} + +static inline uint64_t div1e9(const uint64_t x) { + return x / 1000000000; +} + +static inline uint32_t mod1e9(const uint64_t x) { + return (uint32_t) (x - 1000000000 * div1e9(x)); +} + +struct Double +{ + u64 mantissa; + s32 exponent; +}; +typedef struct Double Double; + +may_be_unused fn Double double_transform(u64 ieee_mantissa, u32 ieee_exponent) +{ + u64 m2; + s32 e2; + if (ieee_exponent) + { + m2 = ((u64)1 << double_mantissa_bits) | ieee_mantissa; + e2 = (s32)(ieee_exponent) - double_bias - double_mantissa_bits - 2; + } + else + { + m2 = ieee_mantissa; + e2 = 1 - double_bias - double_mantissa_bits - 2; + } + + // u8 is_even = (m2 & 1) == 0; + // auto accept_bounds = is_even; + + u64 mv = 4 * m2; + u32 mm_shift = (ieee_mantissa != 0) | (ieee_exponent <= 1); + + u64 vr, vp, vm; + s32 e10; + u8 vm_is_trailing_zeroes = 0; + u8 vr_is_trailing_zeroes = 0; + + if (e2 >= 0) + { + u32 q = log10_pow2(e2) - (e2 > 3); + e10 = (s32)q; + s32 k = double_pow5_inv_bitcount + pow5_bits((s32)q) - 1; + s32 i = -e2 + (s32)q + k; + vr = mul_shift_all_64(m2, DOUBLE_POW5_INV_SPLIT[q], i, &vp, &vm, mm_shift); + if (q <= 21) + { + __builtin_trap(); + } + } + else + { + u32 q = log10_pow5(-e2) - (-e2 > 1); + e10 = (s32) q + e2; + s32 i = -e2 - (s32)q; + s32 k = pow5_bits(i) - double_pow5_bitcount; + s32 j = (s32)q - k; + vr = mul_shift_all_64(m2, DOUBLE_POW5_SPLIT[i], j, &vp, &vm, mm_shift); + + if (q <= 1) + { + __builtin_trap(); + } + else if (q < 63) + { + vr_is_trailing_zeroes = multiple_of_power_of_2(mv, q); + } + } + + s32 removed = 0; + u64 output; + + if (vm_is_trailing_zeroes | vr_is_trailing_zeroes) + { + __builtin_trap(); + } + else + { + u8 round_up = 0; + u64 vp_div100 = div100(vp); + u64 vm_div100 = div100(vm); + + if (vp_div100 > vm_div100) + { + u64 vr_div100 = div100(vr); + u32 vr_mod100 = ((u32)vr) - 100 * ((u32)vr_div100); + round_up = vr_mod100 >= 50; + vr = vr_div100; + vp = vp_div100; + vm = vm_div100; + removed += 2; + } + + while (1) + { + u64 vp_div10 = div10(vp); + u64 vm_div10 = div10(vm); + if (vp_div10 <= vm_div10) + { + break; + } + u64 vr_div10 = div10(vr); + u32 vr_mod10 = ((u32)vr) - 10 * ((u32) vr_div10); + round_up = vr_mod10 >= 5; + vr = vr_div10; + vp = vp_div10; + vm = vm_div10; + removed += 1; + } + + output = vr + ((vr == vm) | round_up); + } + + s32 exp = e10 + removed; + + return (Double) + { + .mantissa = output, + .exponent = exp, + }; +} + +may_be_unused fn SmallIntResult small_int(u64 ieee_mantissa, u32 ieee_exponent) +{ + SmallIntResult result = {}; + auto m2 = ((u64)1 << double_mantissa_bits) | ieee_mantissa; + auto e2 = (s32)ieee_exponent - double_bias - double_mantissa_bits; + + if (e2 > 0) + { + return result; + } + + if (e2 < -52) + { + return result; + } + + u64 mask = ((u64)1 << -e2) - 1; + u64 fraction = m2 & mask; + if (fraction != 0) + { + return result; + } + + result.mantissa = m2 >> -e2; + result.exponent = 0; + result.is_small_int = 1; + + return result; +} + +fn u32 decimalLength17(const u64 v) { + // This is slightly faster than a loop. + // The average output length is 16.38 digits, so we check high-to-low. + // Function precondition: v is not an 18, 19, or 20-digit number. + // (17 digits are sufficient for round-tripping.) + assert(v < 100000000000000000L); + if (v >= 10000000000000000L) { return 17; } + if (v >= 1000000000000000L) { return 16; } + if (v >= 100000000000000L) { return 15; } + if (v >= 10000000000000L) { return 14; } + if (v >= 1000000000000L) { return 13; } + if (v >= 100000000000L) { return 12; } + if (v >= 10000000000L) { return 11; } + if (v >= 1000000000L) { return 10; } + if (v >= 100000000L) { return 9; } + if (v >= 10000000L) { return 8; } + if (v >= 1000000L) { return 7; } + if (v >= 100000L) { return 6; } + if (v >= 10000L) { return 5; } + if (v >= 1000L) { return 4; } + if (v >= 100L) { return 3; } + if (v >= 10L) { return 2; } + return 1; +} + +// A floating decimal representing m * 10^e. +typedef struct floating_decimal_64 { + uint64_t mantissa; + // Decimal exponent's range is -324 to 308 + // inclusive, and can fit in a short if needed. + int32_t exponent; +} floating_decimal_64; + +fn u8* digits2(u64 value) +{ + auto str = strlit("00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"); + return str.pointer + (value * 2); +} + +fn void write_float_decimal(String buffer, u64* value, u64 count) +{ + u64 i = 0; + + while (i + 2 < count) + { + auto c = cast(u8, u64, *value % 100); + *value /= 100; + auto ptr = digits2(c); + buffer.pointer[count - i - 1] = ptr[1]; + buffer.pointer[count - i - 2] = ptr[0]; + i += 2; + } + + while (i < count) + { + auto c = cast(u8, u64, *value % 10); + *value /= 10; + buffer.pointer[count - i - 1] = '0' + c; + + i += 1; + } +} may_be_unused fn void print(const char* format, ...) { @@ -1110,7 +2135,7 @@ may_be_unused fn void print(const char* format, ...) if (*it == brace_open) { it += 1; - char next_ch = *it; + auto next_ch = *it; if (next_ch == brace_open) { @@ -1134,7 +2159,7 @@ may_be_unused fn void print(const char* format, ...) { it += 1; done = 1; - auto* cstring = va_arg(args, const char*); + auto* cstring = va_arg(args, const u8*); while (*cstring) { buffer.pointer[buffer_i] = *cstring; @@ -1146,6 +2171,209 @@ may_be_unused fn void print(const char* format, ...) } assert(done); } break; + case 'f': + { + it += 1; + f64 value_double; + switch (*it) + { + case '3': + it += 1; + if (*it != '2') + { + fail(); + } + it += 1; + fail(); + break; + case '6': + it += 1; + if (*it != '4') + { + fail(); + } + it += 1; + value_double = va_arg(args, f64); + break; + default: + fail(); + } + + { + auto value_int = *(u64*)&value_double; + + const u8 ieee_sign = ((value_int >> (double_mantissa_bits + double_exponent_bits)) & 1) != 0; + const auto ieee_mantissa = value_int & (((u64)1 << double_mantissa_bits) - 1); + const auto ieee_exponent = (u32)((value_int >> double_mantissa_bits) & (((u32)1 << double_exponent_bits) - 1)); + + if (ieee_exponent == (((u32)1 << double_exponent_bits) - 1) || (ieee_exponent == 0 && ieee_mantissa == 0)) + { + __builtin_trap(); + } + + auto small_int_result = small_int(ieee_mantissa, ieee_exponent); + Double result; + if (small_int_result.is_small_int) + { + __builtin_trap(); + } + else + { + result = double_transform(ieee_mantissa, ieee_exponent); + } + + typedef enum FloatFormat + { + FLOAT_FORMAT_DECIMAL, + FLOAT_FORMAT_SCIENTIFIC, + } FloatFormat; + + FloatFormat format = FLOAT_FORMAT_DECIMAL; + u64 output = result.mantissa; + u32 olength = decimalLength17(output); + + // Sign + buffer.pointer[buffer_i] = '-'; + buffer_i += ieee_sign; + + switch (format) + { + case FLOAT_FORMAT_SCIENTIFIC: + { + u32 i = 0; + + if ((output >> 32) != 0) + { + u64 q = div1e8(output); + u32 output2 = ((u32)output) - 100000000 * ((u32)q); + output = q; + + u32 c = output % 10000; + output2 /= 10000; + + const uint32_t d = output2 % 10000; + const uint32_t c0 = (c % 100) << 1; + const uint32_t c1 = (c / 100) << 1; + const uint32_t d0 = (d % 100) << 1; + const uint32_t d1 = (d / 100) << 1; + + auto base_index = buffer_i + olength; + auto base = buffer.pointer + base_index; + memcpy(base - 1, DIGIT_TABLE + c0, 2); + memcpy(base - 3, DIGIT_TABLE + c1, 2); + memcpy(base - 5, DIGIT_TABLE + d0, 2); + memcpy(base - 7, DIGIT_TABLE + d1, 2); + + i += 8; + } + + auto output2 = (u32) output; + + while (output2 >= 10000) + { +#ifdef __clang__ + const u32 c = output2 - 10000 * (output2 / 10000); +#else + const uint32_t c = output2 % 10000; +#endif + output2 /= 10000; + const u32 c0 = (c % 100) << 1; + const u32 c1 = (c / 100) << 1; + auto base_index = buffer_i + olength - i; + memcpy(buffer.pointer + base_index - 1, DIGIT_TABLE + c0, 2); + memcpy(buffer.pointer + base_index - 3, DIGIT_TABLE + c1, 2); + + i += 4; + } + + if (output2 >= 100) + { + const u32 c = (output2 % 100) << 1; + output2 /= 100; + memcpy(buffer.pointer + buffer_i + olength - i - 1, DIGIT_TABLE + c, 2); + i += 2; + } + + if (output2 >= 10) + { + const uint32_t c = output2 << 1; + // We can't use memcpy here: the decimal dot goes between these two digits. + buffer.pointer[buffer_i + olength - i] = DIGIT_TABLE[c + 1]; + buffer.pointer[buffer_i] = DIGIT_TABLE[c]; + } + else + { + buffer.pointer[buffer_i] = (u8)output2 + '0'; + } + + // Print decimal point if needed. + if (olength > 1) + { + buffer.pointer[buffer_i + 1] = '.'; + buffer_i += olength + 1; + } else { + buffer_i += 1; + } + + // Print the exponent. + buffer.pointer[buffer_i] = 'E'; + buffer_i += 1; + int32_t exp = result.exponent + (int32_t) olength - 1; + if (exp < 0) { + buffer.pointer[buffer_i] = '-'; + buffer_i += 1; + exp = -exp; + } + + if (exp >= 100) + { + const int32_t c = exp % 10; + memcpy(buffer.pointer + buffer_i, DIGIT_TABLE + 2 * (exp / 10), 2); + buffer.pointer[buffer_i + 2] = (u8)c + '0'; + buffer_i += 3; + } + else if (exp >= 10) + { + memcpy(buffer.pointer + buffer_i, DIGIT_TABLE + 2 * exp, 2); + buffer_i += 2; + } + else + { + buffer.pointer[buffer_i] = (u8)exp + '0'; + buffer_i += 1; + } + } break; + case FLOAT_FORMAT_DECIMAL: + { + if (result.exponent == (((u32)1 << double_exponent_bits) - 1)) + { + __builtin_trap(); + } + + auto dp_offset = result.exponent + cast(s32, u32, olength); + + if (dp_offset <= 0) + { + buffer.pointer[buffer_i] = '0'; + buffer.pointer[buffer_i + 1] = '.'; + buffer_i += 2; + + // auto dp_index = buffer_i; + + auto dp_poffset = (u32)(-dp_offset); + memset(buffer.pointer + buffer_i, '0', dp_poffset); + buffer_i += dp_poffset; + write_float_decimal(s_get_slice(u8, buffer, buffer_i, buffer.length), &output, olength); + buffer_i += olength; + } + else + { + __builtin_trap(); + } + } break; + } + } + } break; case 's': { it += 1; @@ -1364,11 +2592,14 @@ fn void run_command(CStringSlice arguments, char* envp[]) print("\n"); pid_t pid = syscall_fork(); + if (pid == -1) { trap(); } + u64 start_ns = timestamp(); + if (pid == 0) { // close(pipes[0]); @@ -1387,6 +2618,7 @@ fn void run_command(CStringSlice arguments, char* envp[]) int status = 0; int options = 0; pid_t result = syscall_waitpid(pid, &status, options); + u64 end_ns = timestamp(); int success = 0; if (result == pid) { @@ -1409,6 +2641,15 @@ fn void run_command(CStringSlice arguments, char* envp[]) print("Program failed to run!\n"); fail(); } + + if (cpu_frequency) + { + auto ticks = end_ns - start_ns; + auto ticks_f = (f64)ticks; + auto time_frequency_f = (f64)cpu_frequency; + auto s = ticks_f / time_frequency_f; + print("Command run successfully in {f64} s\n", s); + } } } @@ -1429,6 +2670,7 @@ typedef struct StructName StructName decl_vb(u8); decl_vbp(u8); +decl_vb(s32); fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) { @@ -1442,8 +2684,8 @@ fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 ite vb->pointer = reserve(item_size * UINT32_MAX); } - u32 old_page_capacity = align_forward(old_capacity * item_size, minimum_granularity); - u32 new_page_capacity = align_forward(wanted_capacity * item_size, minimum_granularity); + 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 commit_size = new_page_capacity - old_page_capacity; void* commit_pointer = vb->pointer + old_page_capacity; @@ -1478,18 +2720,26 @@ fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) may_be_unused fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes) { - vb_generic_ensure_capacity(vb, sizeof(u8), bytes.length); - auto* pointer = vb_generic_add_assume_capacity(vb, sizeof(u8), bytes.length); - memcpy(pointer, bytes.pointer, bytes.length); + auto len = cast(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); return pointer; } -// fn u8* vb_generic_append(VirtualBuffer(u8)* vb, void* item_pointer, u32 item_size, u32 item_count) -// { -// vb_generic_ensure_capacity(vb, item_size, item_count); -// return vb_generic_append_assume_capacity(vb, item_pointer, item_size, item_count); -// } - #define vb_add(a, count) (typeof((a)->pointer)) vb_generic_add((VirtualBuffer(u8)*)(a), sizeof(*((a)->pointer)), count) #define vb_append_one(a, item) (typeof((a)->pointer)) vb_generic_append((VirtualBuffer(u8)*)(a), &(item), sizeof(*((a)->pointer)), 1) #define vb_to_bytes(vb) (Slice(u8)) { .pointer = (u8*)((vb).pointer), .length = sizeof(*((vb).pointer)) * (vb).length, } +#define vb_ensure_capacity(a, count) vb_generic_ensure_capacity((VirtualBuffer(u8)*)(a), sizeof(*((a)->pointer)), count) + +may_be_unused fn Hash32 hash32_fib_end(Hash32 hash) +{ + auto result = truncate(Hash32, (hash * 11400714819323198485ull) >> 32); + return result; +} + +may_be_unused fn Hash32 hash64_fib_end(Hash64 hash) +{ + auto result = truncate(Hash32, (hash * 11400714819323198485ull) >> 32); + return result; +} diff --git a/bootstrap/main.c b/bootstrap/main.c index e3dbc83..b315d09 100644 --- a/bootstrap/main.c +++ b/bootstrap/main.c @@ -1,6 +1,29 @@ #include "lib.h" #define clang_path "/usr/bin/clang" +#define RawIndex(T, i) (T ## Index) { .index = (i) } +#define Index(T, i) RawIndex(T, (i) + 1) +#define geti(i) ((i).index - 1) +#define validi(i) ((i).index != 0) +#define invalidi(T) RawIndex(T, 0) + +#define InternPool(T) InternPool_ ## T +#define GetOrPut(T) T ## GetOrPut +#define declare_ip(T) \ +struct InternPool(T) \ +{ \ + T ## Index * pointer; \ + u32 length;\ + u32 capacity;\ +}; \ +typedef struct InternPool(T) InternPool(T);\ +struct GetOrPut(T) \ +{\ + T ## Index index; \ + u8 existing;\ +};\ +typedef struct GetOrPut(T) GetOrPut(T) + struct StringMapValue { String string; @@ -16,53 +39,53 @@ struct StringMap }; typedef struct StringMap StringMap; -fn StringMapValue* string_map_values(StringMap* map) -{ - assert(map->pointer); - return (StringMapValue*)(map->pointer + map->capacity); -} +// fn StringMapValue* string_map_values(StringMap* map) +// { +// assert(map->pointer); +// return (StringMapValue*)(map->pointer + map->capacity); +// } -fn s32 string_map_find_slot(StringMap* map, u32 original_index, String key) -{ - s32 result = -1; - - if (map->pointer) - { - auto it_index = original_index; - auto existing_capacity = map->capacity; - auto* values = string_map_values(map); - - for (u32 i = 0; i < existing_capacity; i += 1) - { - auto index = it_index & (existing_capacity - 1); - u32 existing_key = map->pointer[index]; - - // Not set - if (existing_key == 0) - { - result = index; - break; - } - else - { - auto pair = &values[index]; - if (s_equal(pair->string, key)) - { - result = index; - break; - } - else - { - trap(); - } - } - - it_index += 1; - } - } - - return result; -} +// fn s32 string_map_find_slot(StringMap* map, u32 original_index, String key) +// { +// s32 result = -1; +// +// if (map->pointer) +// { +// auto it_index = original_index; +// auto existing_capacity = map->capacity; +// auto* values = string_map_values(map); +// +// for (u32 i = 0; i < existing_capacity; i += 1) +// { +// auto index = it_index & (existing_capacity - 1); +// u32 existing_key = map->pointer[index]; +// +// // Not set +// if (existing_key == 0) +// { +// result = cast(s32, u32, index); +// break; +// } +// else +// { +// auto pair = &values[index]; +// if (s_equal(pair->string, key)) +// { +// result = cast(s32, u32, index); +// break; +// } +// else +// { +// trap(); +// } +// } +// +// it_index += 1; +// } +// } +// +// return result; +// } struct StringMapPut { @@ -71,201 +94,165 @@ struct StringMapPut }; typedef struct StringMapPut StringMapPut; - -fn void string_map_ensure_capacity(StringMap* map, Arena* arena, u32 additional) -{ - auto current_capacity = map->capacity; - auto half_capacity = current_capacity >> 1; - auto destination_length = map->length + additional; - - if (destination_length > half_capacity) - { - u32 new_capacity = MAX(round_up_to_next_power_of_2(destination_length), 32); - auto new_capacity_bytes = sizeof(u32) * new_capacity + new_capacity * sizeof(StringMapValue); - - void* ptr = arena_allocate_bytes(arena, new_capacity_bytes, MAX(alignof(u32), alignof(StringMapValue))); - memset(ptr, 0, new_capacity_bytes); - - auto* keys = (u32*)ptr; - auto* values = (StringMapValue*)(keys + new_capacity); - - auto* old_keys = map->pointer; - auto old_capacity = map->capacity; - auto* old_values = (StringMapValue*)(map->pointer + current_capacity); - - map->length = 0; - map->pointer = keys; - map->capacity = new_capacity; - - for (u32 i = 0; i < old_capacity; i += 1) - { - auto key = old_keys[i]; - if (key) - { - unused(values); - unused(old_values); - trap(); - } - } - - for (u32 i = 0; i < old_capacity; i += 1) - { - trap(); - } - } -} - -fn StringMapPut string_map_put_at_assume_not_existent_assume_capacity(StringMap* map, u32 hash, String key, u32 value, u32 index) -{ - u32 existing_hash = map->pointer[index]; - map->pointer[index] = hash; - auto* values = string_map_values(map); - auto existing_value = values[index]; - values[index] = (StringMapValue) { - .value = value, - .string = key, - }; - map->length += 1; - assert(existing_hash ? s_equal(existing_value.string, key) : 1); - - return (StringMapPut) - { - .value = existing_value.value, - .existing = existing_hash != 0, - }; -} - -fn StringMapPut string_map_put_assume_not_existent_assume_capacity(StringMap* map, u32 hash, String key, u32 value) -{ - assert(map->length < map->capacity); - auto index = hash & (map->capacity - 1); - - return string_map_put_at_assume_not_existent_assume_capacity(map, hash, key, value, index); -} - -fn StringMapPut string_map_put_assume_not_existent(StringMap* map, Arena* arena, u32 hash, String key, u32 value) -{ - string_map_ensure_capacity(map, arena, 1); - return string_map_put_assume_not_existent_assume_capacity(map, hash, key, value); -} - -fn StringMapPut string_map_get(StringMap* map, String key) -{ - u32 value = 0; - auto long_hash = hash_bytes(key); - static_assert(sizeof(long_hash) == sizeof(u64)); - auto hash = hash64_to_hash32(long_hash); - static_assert(sizeof(hash) == sizeof(u32)); - assert(hash); - auto index = hash & (map->capacity - 1); - auto slot = string_map_find_slot(map, index, key); - u8 existing = slot != -1; - if (existing) - { - existing = map->pointer[slot] != 0; - auto* value_pair = &string_map_values(map)[slot]; - value = value_pair->value; - } - - return (StringMapPut) { - .value = value, - .existing = existing, - }; -} - -fn StringMapPut string_map_put(StringMap* map, Arena* arena, String key, u32 value) -{ - auto long_hash = hash_bytes(key); - static_assert(sizeof(long_hash) == sizeof(u64)); - auto hash = hash64_to_hash32(long_hash); - static_assert(sizeof(hash) == sizeof(u32)); - assert(hash); - auto index = hash & (map->capacity - 1); - auto slot = string_map_find_slot(map, index, key); - if (slot != -1) - { - auto* values = string_map_values(map); - auto* key_pointer = &map->pointer[slot]; - auto old_key_pointer = *key_pointer; - *key_pointer = hash; - values[slot].string = key; - values[slot].value = value; - return (StringMapPut) { - .value = value, - .existing = old_key_pointer != 0, - }; - } - else - { - if (map->length < map->capacity) - { - trap(); - } - else if (map->length == map->capacity) - { - auto result = string_map_put_assume_not_existent(map, arena, hash, key, value); - assert(!result.existing); - return result; - } - else - { - trap(); - } - } -} - -// fn void string_map_get_or_put(StringMap* map, Arena* arena, String key, u32 value) +// fn void string_map_ensure_capacity(StringMap* map, Arena* arena, u32 additional) // { -// assert(value); -// auto hash = hash_bytes(key); +// auto current_capacity = map->capacity; +// auto half_capacity = current_capacity >> 1; +// auto destination_length = map->length + additional; +// +// 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_bytes = sizeof(u32) * new_capacity + new_capacity * sizeof(StringMapValue); +// +// void* ptr = arena_allocate_bytes(arena, new_capacity_bytes, MAX(alignof(u32), alignof(StringMapValue))); +// memset(ptr, 0, new_capacity_bytes); +// +// auto* keys = (u32*)ptr; +// auto* values = (StringMapValue*)(keys + new_capacity); +// +// auto* old_keys = map->pointer; +// auto old_capacity = map->capacity; +// auto* old_values = (StringMapValue*)(map->pointer + current_capacity); +// +// map->length = 0; +// map->pointer = keys; +// map->capacity = new_capacity; +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// auto key = old_keys[i]; +// if (key) +// { +// unused(values); +// unused(old_values); +// trap(); +// } +// } +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// trap(); +// } +// } +// } + +// fn StringMapPut string_map_put_at_assume_not_existent_assume_capacity(StringMap* map, u32 hash, String key, u32 value, u32 index) +// { +// u32 existing_hash = map->pointer[index]; +// map->pointer[index] = hash; +// auto* values = string_map_values(map); +// auto existing_value = values[index]; +// values[index] = (StringMapValue) { +// .value = value, +// .string = key, +// }; +// map->length += 1; +// assert(existing_hash ? s_equal(existing_value.string, key) : 1); +// +// return (StringMapPut) +// { +// .value = existing_value.value, +// .existing = existing_hash != 0, +// }; +// } + +// fn StringMapPut string_map_put_assume_not_existent_assume_capacity(StringMap* map, u32 hash, String key, u32 value) +// { +// assert(map->length < map->capacity); +// auto index = hash & (map->capacity - 1); +// +// return string_map_put_at_assume_not_existent_assume_capacity(map, hash, key, value, index); +// } + +// fn StringMapPut string_map_put_assume_not_existent(StringMap* map, Arena* arena, u32 hash, String key, u32 value) +// { +// string_map_ensure_capacity(map, arena, 1); +// return string_map_put_assume_not_existent_assume_capacity(map, hash, key, value); +// } + +// fn StringMapPut string_map_get(StringMap* map, String key) +// { +// u32 value = 0; +// auto long_hash = hash_bytes(key); +// static_assert(sizeof(long_hash) == sizeof(u64)); +// auto hash = hash64_to_hash32(long_hash); +// static_assert(sizeof(hash) == sizeof(u32)); +// assert(hash); +// auto index = hash & (map->capacity - 1); +// auto slot = string_map_find_slot(map, index, key); +// u8 existing = slot != -1; +// if (existing) +// { +// existing = map->pointer[slot] != 0; +// auto* value_pair = &string_map_values(map)[slot]; +// value = value_pair->value; +// } +// +// return (StringMapPut) { +// .value = value, +// .existing = existing, +// }; +// } + +// fn StringMapPut string_map_put(StringMap* map, Arena* arena, String key, u32 value) +// { +// auto long_hash = hash_bytes(key); +// static_assert(sizeof(long_hash) == sizeof(u64)); +// auto hash = hash64_to_hash32(long_hash); +// static_assert(sizeof(hash) == sizeof(u32)); +// assert(hash); // auto index = hash & (map->capacity - 1); // auto slot = string_map_find_slot(map, index, key); // if (slot != -1) // { // auto* values = string_map_values(map); // auto* key_pointer = &map->pointer[slot]; -// todo(); -// // auto old_key_pointer = *key_pointer; -// // *key_pointer = hash; -// // values[slot].string = key; -// // values[slot].value = value; -// // return (StringMapPut) { -// // .value = value, -// // .existing = old_key_pointer != 0, -// // }; +// auto old_key_pointer = *key_pointer; +// *key_pointer = hash; +// values[slot].string = key; +// values[slot].value = value; +// return (StringMapPut) { +// .value = value, +// .existing = old_key_pointer != 0, +// }; // } // else // { // if (map->length < map->capacity) // { -// todo(); +// trap(); // } // else if (map->length == map->capacity) // { -// todo(); -// // auto result = string_map_put_assume_not_existent(map, arena, hash, key, value); -// // assert(!result.existing); -// // return result; +// auto result = string_map_put_assume_not_existent(map, arena, hash, key, value); +// assert(!result.existing); +// return result; // } // else // { -// todo(); +// trap(); // } // } // } -fn int file_write(String file_path, String file_data) +// fn int file_write(String file_path, String file_data) +// { +// int file_descriptor = syscall_open(string_to_c(file_path), O_WRONLY | O_CREAT | O_TRUNC, 0644); +// assert(file_descriptor != -1); +// +// auto bytes = syscall_write(file_descriptor, file_data.pointer, file_data.length); +// assert(bytes >= 0); +// assert((u64)bytes == file_data.length); +// +// int close_result = syscall_close(file_descriptor); +// assert(close_result == 0); +// return 0; +// } + +fn int dir_make(const char* path) { - int file_descriptor = syscall_open(string_to_c(file_path), O_WRONLY | O_CREAT | O_TRUNC, 0644); - assert(file_descriptor != -1); - - auto bytes = syscall_write(file_descriptor, file_data.pointer, file_data.length); - assert(bytes >= 0); - assert((u64)bytes == file_data.length); - - int close_result = syscall_close(file_descriptor); - assert(close_result == 0); - return 0; + return syscall_mkdir(path, 0755); } fn String file_read(Arena* arena, String path) @@ -278,7 +265,7 @@ fn String file_read(Arena* arena, String path) int stat_result = syscall_fstat(file_descriptor, &stat_buffer); assert(stat_result == 0); - u64 file_size = stat_buffer.st_size; + auto file_size = cast(u64, s64, stat_buffer.st_size); result = (String){ .pointer = arena_allocate_bytes(arena, file_size, 64), @@ -461,7 +448,7 @@ decl_vb(ELFSymbol); static_assert(sizeof(ELFSymbol) == 24); // DWARF -struct DWARFCompilationUnit +struct DwarfCompilationUnit { u32 length; u16 version; @@ -469,9 +456,9 @@ struct DWARFCompilationUnit u8 address_size; u32 debug_abbreviation_offset; }; -typedef struct DWARFCompilationUnit DWARFCompilationUnit; +typedef struct DwarfCompilationUnit DwarfCompilationUnit; -struct NameReference +struct StringReference { u32 offset; u32 length; @@ -495,7 +482,6 @@ typedef enum TypeId : u32 TYPE_COUNT, } TypeId; -typedef struct BackendType BackendType; struct TypeIndex { u32 index; @@ -506,12 +492,6 @@ typedef struct TypeIndex TypeIndex; static_assert(sizeof(TypeIndex) == sizeof(u32)); declare_slice(TypeIndex); -#define RawIndex(T, i) (T ## Index) { .index = (i) } -#define Index(T, i) RawIndex(T, (i) + 1) -#define geti(i) ((i).index - 1) -#define validi(i) ((i).index != 0) -#define invalidi(T) RawIndex(T, 0) - struct TypeInteger { u64 constant; @@ -544,6 +524,86 @@ typedef struct Type Type; static_assert(offsetof(Type, hash) == 0); decl_vb(Type); +struct DebugTypeIndex +{ + u32 index; +}; +typedef struct DebugTypeIndex DebugTypeIndex; + +struct DebugTypeInteger +{ + u8 bit_count:7; + u8 signedness:1; +}; + +typedef struct DebugTypeInteger DebugTypeInteger; + +typedef enum DebugTypeId : u8 +{ + DEBUG_TYPE_VOID = 0, + DEBUG_TYPE_INTEGER, +} DebugTypeId; + +struct DebugType +{ + union + { + DebugTypeInteger integer; + }; + DebugTypeId id; +}; +typedef struct DebugType DebugType; +decl_vb(DebugType); +declare_ip(DebugType); + +typedef enum BackendTypeId +{ + BACKEND_TYPE_VOID = 0x00, + BACKEND_TYPE_INTEGER_8 = 0x01, + BACKEND_TYPE_INTEGER_16 = 0x02, + BACKEND_TYPE_INTEGER_32 = 0x03, + BACKEND_TYPE_INTEGER_64 = 0x03, + BACKEND_TYPE_POINTER = 0x04, + BACKEND_TYPE_SCALAR_LAST = BACKEND_TYPE_POINTER, + BACKEND_TYPE_TUPLE, + BACKEND_TYPE_MEMORY, + BACKEND_TYPE_CONTROL, + BACKEND_TYPE_REGION, +} BackendTypeId; + +struct TypePair +{ + u32 raw; +}; +typedef struct TypePair TypePair; +decl_vb(TypePair); +global const TypePair type_pair_invalid; + +global const u32 debug_mask = 0xffffff; + +fn TypePair type_pair_make(DebugTypeIndex debug_type, BackendTypeId backend_type) +{ + u32 value = backend_type; + value <<= 24; + auto debug_raw = *(u32*)&debug_type; + assert(debug_raw <= debug_mask); + value |= debug_raw; + + return (TypePair){ .raw = value }; +} + +// fn DebugTypeIndex type_pair_get_debug(TypePair type_pair) +// { +// return (DebugTypeIndex) { +// .index = type_pair.raw & debug_mask, +// }; +// } + +fn BackendTypeId type_pair_get_backend(TypePair type_pair) +{ + return type_pair.raw >> 24; +} + struct NodeIndex { u32 index; @@ -555,159 +615,238 @@ decl_vb(NodeIndex); struct Function { String name; - NodeIndex start; - NodeIndex stop; - TypeIndex return_type; + NodeIndex root; + TypePair return_type; }; typedef struct Function Function; decl_vb(Function); typedef enum NodeId : u8 { - NODE_START, - NODE_STOP, - NODE_CONTROL_PROJECTION, - NODE_DEAD_CONTROL, - NODE_SCOPE, - NODE_PROJECTION, - NODE_RETURN, - NODE_REGION, - NODE_REGION_LOOP, - NODE_IF, - NODE_PHI, + IR_ROOT, + IR_PROJECTION, + IR_RETURN, + IR_REGION, + IR_PHI, + IR_SYMBOL_TABLE, - NODE_INTEGER_ADD, - NODE_INTEGER_SUBSTRACT, - NODE_INTEGER_MULTIPLY, - NODE_INTEGER_UNSIGNED_DIVIDE, - NODE_INTEGER_SIGNED_DIVIDE, - NODE_INTEGER_UNSIGNED_REMAINDER, - NODE_INTEGER_SIGNED_REMAINDER, - NODE_INTEGER_UNSIGNED_SHIFT_LEFT, - NODE_INTEGER_SIGNED_SHIFT_LEFT, - NODE_INTEGER_UNSIGNED_SHIFT_RIGHT, - NODE_INTEGER_SIGNED_SHIFT_RIGHT, - NODE_INTEGER_AND, - NODE_INTEGER_OR, - NODE_INTEGER_XOR, - NODE_INTEGER_NEGATION, + // Binary integer + IR_INTEGER_ADD, + IR_INTEGER_SUBSTRACT, + IR_INTEGER_MULTIPLY, + IR_INTEGER_DIVIDE, + IR_INTEGER_REMAINDER, - NODE_INTEGER_COMPARE_EQUAL, - NODE_INTEGER_COMPARE_NOT_EQUAL, + IR_INTEGER_SHIFT_LEFT, + IR_INTEGER_SHIFT_RIGHT, + IR_INTEGER_AND, + IR_INTEGER_OR, + IR_INTEGER_XOR, - NODE_CONSTANT, + IR_INTEGER_COMPARE_EQUAL, + IR_INTEGER_COMPARE_NOT_EQUAL, + + // Unary integer + IR_INTEGER_NEGATION, + + IR_INTEGER_CONSTANT, + + MACHINE_COPY, + MACHINE_MOVE, + MACHINE_JUMP, NODE_COUNT, } NodeId; -struct NodeCFG -{ - s32 immediate_dominator_tree_depth; - s32 loop_depth; - s32 anti_dependency; -}; -typedef struct NodeCFG NodeCFG; - -struct NodeConstant -{ - TypeIndex type; -}; -typedef struct NodeConstant NodeConstant; - -struct NodeStart -{ - NodeCFG cfg; - TypeIndex arguments; - Function* function; -}; -typedef struct NodeStart NodeStart; - -struct NodeStop -{ - NodeCFG cfg; -}; -typedef struct NodeStop NodeStop; - -struct ScopePair -{ - StringMap values; - StringMap types; -}; -typedef struct ScopePair ScopePair; - -struct StackScope -{ - ScopePair* pointer; - u32 length; - u32 capacity; -}; -typedef struct StackScope StackScope; - -struct NodeScope -{ - StackScope stack; -}; -typedef struct NodeScope NodeScope; +// struct NodeCFG +// { +// s32 immediate_dominator_tree_depth; +// s32 loop_depth; +// s32 anti_dependency; +// }; +// typedef struct NodeCFG NodeCFG; +// struct NodeConstant +// { +// TypeIndex type; +// }; +// typedef struct NodeConstant NodeConstant; +// +// struct NodeStart +// { +// // NodeCFG cfg; +// TypeIndex arguments; +// Function* function; +// }; +// typedef struct NodeStart NodeStart; +// +// // struct NodeStop +// // { +// // // NodeCFG cfg; +// // }; +// // typedef struct NodeStop NodeStop; +// +// struct ScopePair +// { +// StringMap values; +// StringMap types; +// }; +// typedef struct ScopePair ScopePair; +// +// struct StackScope +// { +// ScopePair* pointer; +// u32 length; +// u32 capacity; +// }; +// typedef struct StackScope StackScope; +// +// struct NodeScope +// { +// StackScope stack; +// }; +// typedef struct NodeScope NodeScope; +// +// +// struct NodeControlProjection +// { +// NodeProjection projection; +// // NodeCFG cfg; +// }; +// typedef struct NodeControlProjection NodeControlProjection; +// +// struct NodeReturn +// { +// // NodeCFG cfg; +// }; +// typedef struct NodeReturn NodeReturn; +// +// struct NodeDeadControl +// { +// // NodeCFG cfg; +// }; +// typedef struct NodeDeadControl NodeDeadControl; struct NodeProjection { - String label; u32 index; }; typedef struct NodeProjection NodeProjection; -struct NodeControlProjection +struct NodeRoot { - NodeProjection projection; - NodeCFG cfg; + u32 function_index; }; -typedef struct NodeControlProjection NodeControlProjection; -struct NodeReturn -{ - NodeCFG cfg; -}; -typedef struct NodeReturn NodeReturn; +typedef struct NodeRoot NodeRoot; -struct NodeDeadControl +struct NodeRegion { - NodeCFG cfg; + NodeIndex in_mem; }; -typedef struct NodeDeadControl NodeDeadControl; + +typedef struct NodeRegion NodeRegion; + +union NodeIntegerConstant +{ + s64 signed_value; + u64 unsigned_value; +}; + +typedef union NodeIntegerConstant NodeIntegerConstant; + +struct RegisterMaskIndex +{ + u32 index; +}; +typedef struct RegisterMaskIndex RegisterMaskIndex; +declare_slice(RegisterMaskIndex); + +struct NodeMachineCopy +{ + RegisterMaskIndex use_mask; + RegisterMaskIndex def_mask; +}; +typedef struct NodeMachineCopy NodeMachineCopy; struct Node { - Hash64 hash; u32 input_offset; u32 output_offset; - u32 dependency_offset; u16 output_count; u16 input_count; - u16 dependency_count; u16 input_capacity; u16 output_capacity; - u16 dependency_capacity; - u16 thread_id; - TypeIndex type; - NodeId id; + TypePair type; + NodeId id:8; + u32 interned:1; + u32 reserved:23; + NodeIndex next_free; + union { - NodeConstant constant; - NodeStart start; - NodeStop stop; - NodeScope scope; - NodeControlProjection control_projection; NodeProjection projection; - NodeReturn return_node; - NodeDeadControl dead_control; + NodeRoot root; + NodeRegion region; + NodeIntegerConstant integer_constant; + NodeMachineCopy machine_copy; }; + // union + // { + // NodeConstant constant; + // NodeStart start; + // NodeStop stop; + // NodeScope scope; + // NodeControlProjection control_projection; + // NodeProjection projection; + // NodeReturn return_node; + // NodeDeadControl dead_control; + // }; }; typedef struct Node Node; -declare_slice_p(Node); -static_assert(offsetof(Node, hash) == 0); +// See above bitset +static_assert(sizeof(NodeId) == 1); +declare_slice_p(Node); decl_vb(Node); decl_vbp(Node); +declare_ip(Node); + +fn u8 node_is_control_projection(Node* restrict node) +{ + return node->id == IR_PROJECTION && type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE; +} + +fn u8 node_is_cfg_fork(Node* restrict node) +{ + switch (node->id) + { + case IR_ROOT: + case IR_PROJECTION: + case IR_RETURN: + case IR_REGION: + case IR_PHI: + case IR_SYMBOL_TABLE: + case IR_INTEGER_ADD: + case IR_INTEGER_SUBSTRACT: + case IR_INTEGER_MULTIPLY: + case IR_INTEGER_DIVIDE: + case IR_INTEGER_REMAINDER: + case IR_INTEGER_SHIFT_LEFT: + case IR_INTEGER_SHIFT_RIGHT: + case IR_INTEGER_AND: + case IR_INTEGER_OR: + case IR_INTEGER_XOR: + case IR_INTEGER_COMPARE_EQUAL: + case IR_INTEGER_COMPARE_NOT_EQUAL: + case IR_INTEGER_NEGATION: + case IR_INTEGER_CONSTANT: + case MACHINE_COPY: + case MACHINE_MOVE: + case MACHINE_JUMP: + case NODE_COUNT: + return 0; + } +} struct ArrayReference { @@ -717,7 +856,6 @@ struct ArrayReference typedef struct ArrayReference ArrayReference; decl_vb(ArrayReference); - struct File { String path; @@ -731,18 +869,17 @@ struct FunctionBuilder { Function* function; File* file; - NodeIndex scope; - NodeIndex dead_control; + NodeIndex current; }; typedef struct FunctionBuilder FunctionBuilder; -struct InternPool -{ - u32* pointer; - u32 length; - u32 capacity; -}; -typedef struct InternPool InternPool; +// struct InternPool +// { +// u32* pointer; +// u32 length; +// u32 capacity; +// }; +// typedef struct InternPool InternPool; typedef u64 BitsetElement; decl_vb(BitsetElement); @@ -761,7 +898,7 @@ fn u8 bitset_get(Bitset* bitset, u64 index) if (element_index < bitset->arr.length) { auto bit_index = index % element_bitsize; - auto result = (bitset->arr.pointer[element_index] & (1 << bit_index)) != 0; + u8 result = (bitset->arr.pointer[element_index] & (1 << bit_index)) != 0; return result; } @@ -774,7 +911,7 @@ fn void bitset_ensure_length(Bitset* bitset, u64 max) auto old_length = bitset->arr.length; if (old_length < length) { - auto new_element_count = length - old_length; + auto new_element_count = cast(u32, u64, length - old_length); unused(vb_add(&bitset->arr, new_element_count)); } } @@ -784,7 +921,19 @@ fn void bitset_set_value(Bitset* bitset, u64 index, u8 value) bitset_ensure_length(bitset, index + 1); auto element_index = index / element_bitsize; auto bit_index = index % element_bitsize; - bitset->arr.pointer[element_index] |= (!!value) << bit_index; + auto mask = ~((u64)1 << bit_index); + bitset->arr.pointer[element_index] = (bitset->arr.pointer[element_index] & mask) | ((u64)(!!value) << bit_index); + + if (value) + { + bitset->length += 1; + } + else + { + bitset->length -= 1; + } + + assert(bitset_get(bitset, index) == value); } fn void bitset_clear(Bitset* bitset) @@ -803,6 +952,66 @@ struct WorkList }; typedef struct WorkList WorkList; +enum +{ + REGISTER_CLASS_STACK = 0, +}; + +typedef enum x86_64_RegisterClass : u8 +{ + REGISTER_CLASS_X86_64_GPR = 1, + REGISTER_CLASS_X86_64_XMM, + REGISTER_CLASS_X86_64_COUNT +} x86_64_RegisterClass; + +const global u8 register_count_per_class[] = { + [0] = 0, + [REGISTER_CLASS_X86_64_GPR] = 16, + [REGISTER_CLASS_X86_64_XMM] = 16, +}; +static_assert(array_length(register_count_per_class) == REGISTER_CLASS_X86_64_COUNT); + +typedef enum GPR : u8 +{ + RAX = 0, + RCX = 1, + RDX = 2, + RBX = 3, + RSP = 4, + RBP = 5, + RSI = 6, + RDI = 7, + + R8 = 8 + 0, + R9 = 8 + 1, + R10 = 8 + 2, + R11 = 8 + 3, + R12 = 8 + 4, + R13 = 8 + 5, + R14 = 8 + 6, + R15 = 8 + 7, + + GPR_NONE = -1 +} GPR; + +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); + +struct RegisterMask +{ + u32 mask; + u32 class:3; + u32 may_spill:1; + u32 reserved:28; +}; +typedef struct RegisterMask RegisterMask; +decl_vb(RegisterMask); +declare_ip(RegisterMask); + struct Thread { Arena* arena; @@ -810,17 +1019,24 @@ struct Thread { VirtualBuffer(Type) types; VirtualBuffer(Node) nodes; + VirtualBuffer(DebugType) debug_types; VirtualBuffer(NodeIndex) uses; - VirtualBuffer(u8) name_buffer; VirtualBuffer(ArrayReference) use_free_list; VirtualBuffer(Function) functions; + VirtualBuffer(u8) string; + VirtualBuffer(RegisterMask) register_masks; } buffer; struct { - InternPool types; - InternPool nodes; + InternPool(Node) nodes; + InternPool(DebugType) debug_types; + InternPool(RegisterMask) register_masks; } interned; struct + { + NodeIndex nodes; + } free_list; + struct { TypeIndex bottom; TypeIndex top; @@ -840,60 +1056,106 @@ struct Thread TypeIndex s32; TypeIndex s64; } integer; + struct + { + union + { + struct + { + DebugTypeIndex u8; + DebugTypeIndex u16; + DebugTypeIndex u32; + DebugTypeIndex u64; + DebugTypeIndex s8; + DebugTypeIndex s16; + DebugTypeIndex s32; + DebugTypeIndex s64; + }; + DebugTypeIndex array[8]; + } integer; + } debug; } types; - WorkList worklist; s64 main_function; - struct - { - u64 total; - u64 nop; - } iteration; + WorkList worklists[8]; + u64 worklist_bitset:3; + u64 reserved:61; }; typedef struct Thread Thread; -fn NodeIndex thread_worklist_push(Thread* thread, NodeIndex node_index) +struct WorkListHandle { - if (validi(node_index)) - { - if (!bitset_get(&thread->worklist.bitset, geti(node_index))) - { - bitset_set_value(&thread->worklist.bitset, geti(node_index), 1); - *vb_add(&thread->worklist.nodes, 1) = node_index; - } - } + u8 index:3; + u8 is_valid:1; + u8 reserved:4; +}; - return node_index; +typedef struct WorkListHandle WorkListHandle; + +fn WorkListHandle thread_worklist_acquire(Thread* thread) +{ + u8 bitset = thread->worklist_bitset; + if (bitset) + { + auto index = cast(u8, s32, __builtin_ctz(~thread->worklist_bitset)); + thread->worklist_bitset |= (1 << index); + return (WorkListHandle) + { + .index = index, + .is_valid = 1, + }; + } + else + { + thread->worklist_bitset |= (1 << 0); + return (WorkListHandle) + { + .index = 0, + .is_valid = 1, + }; + } } -fn NodeIndex thread_worklist_pop(Thread* thread) +fn u32 thread_worklist_length(Thread* thread, WorkListHandle handle) { - auto result = invalidi(Node); + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + return thread->worklists[handle.index].nodes.length; +} - auto len = thread->worklist.nodes.length; - if (len) +fn NodeIndex thread_worklist_get(Thread* thread, WorkListHandle handle, u32 index) +{ + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + auto* worklist = &thread->worklists[handle.index]; + assert(index < worklist->nodes.length); + return worklist->nodes.pointer[index]; +} + +fn u8 thread_worklist_test(Thread* thread, WorkListHandle handle, NodeIndex node_index) +{ + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + + u8 result = 0; + if (validi(node_index)) { - auto index = len - 1; - auto node_index = thread->worklist.nodes.pointer[index]; - thread->worklist.nodes.length = index; - bitset_set_value(&thread->worklist.bitset, index, 0); - result = node_index; + WorkList* restrict worklist = &thread->worklists[handle.index]; + result = bitset_get(&worklist->bitset, geti(node_index)); } return result; } -fn void thread_worklist_clear(Thread* thread) +fn u8 thread_worklist_test_and_set(Thread* thread, WorkListHandle handle, NodeIndex node_index) { - bitset_clear(&thread->worklist.visited); - bitset_clear(&thread->worklist.bitset); - thread->worklist.nodes.length = 0; -} + auto result = thread_worklist_test(thread, handle, node_index); + if (!result) + { + WorkList* restrict worklist = &thread->worklists[handle.index]; + bitset_set_value(&worklist->bitset, geti(node_index), 1); + } -fn Type* thread_type_get(Thread* thread, TypeIndex type_index) -{ - assert(validi(type_index)); - auto* type = &thread->buffer.types.pointer[geti(type_index)]; - return type; + return result; } fn Node* thread_node_get(Thread* thread, NodeIndex node_index) @@ -903,44 +1165,173 @@ fn Node* thread_node_get(Thread* thread, NodeIndex node_index) return node; } +may_be_unused fn String node_id_to_string(NodeId node_id) +{ + switch (node_id) + { + case_to_name(IR_, ROOT); + case_to_name(IR_, PROJECTION); + case_to_name(IR_, RETURN); + case_to_name(IR_, REGION); + case_to_name(IR_, PHI); + case_to_name(IR_, SYMBOL_TABLE); + case_to_name(IR_, INTEGER_ADD); + case_to_name(IR_, INTEGER_SUBSTRACT); + case_to_name(IR_, INTEGER_MULTIPLY); + case_to_name(IR_, INTEGER_DIVIDE); + case_to_name(IR_, INTEGER_REMAINDER); + case_to_name(IR_, INTEGER_SHIFT_LEFT); + case_to_name(IR_, INTEGER_SHIFT_RIGHT); + case_to_name(IR_, INTEGER_AND); + case_to_name(IR_, INTEGER_OR); + case_to_name(IR_, INTEGER_XOR); + case_to_name(IR_, INTEGER_COMPARE_EQUAL); + case_to_name(IR_, INTEGER_COMPARE_NOT_EQUAL); + case_to_name(IR_, INTEGER_NEGATION); + case_to_name(IR_, INTEGER_CONSTANT); + case_to_name(MACHINE_, COPY); + case_to_name(MACHINE_, MOVE); + case_to_name(MACHINE_, JUMP); + case NODE_COUNT: unreachable(); + break; + } +} + +fn void thread_worklist_push(Thread* thread, WorkListHandle handle, NodeIndex node_index) +{ + // print("Pushing node #{u32} ({s})\n", geti(node_index), node_id_to_string(thread_node_get(thread, node_index)->id)); + if (!thread_worklist_test_and_set(thread, handle, node_index)) + { + WorkList* restrict worklist = &thread->worklists[handle.index]; + *vb_add(&worklist->nodes, 1) = node_index; + } +} + +fn void thread_worklist_push_array(Thread* thread, WorkListHandle handle, NodeIndex node_index) +{ + assert(handle.is_valid); + + auto* worklist = &thread->worklists[handle.index]; + *vb_add(&worklist->nodes, 1) = node_index; +} + +fn NodeIndex thread_worklist_pop_array(Thread* thread, WorkListHandle handle) +{ + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + auto result = invalidi(Node); + + assert(handle.is_valid); + auto* worklist = &thread->worklists[handle.index]; + auto len = worklist->nodes.length; + if (len) + { + auto index = len - 1; + result = worklist->nodes.pointer[index]; + worklist->nodes.length = index; + } + + return result; +} + +fn NodeIndex thread_worklist_pop(Thread* thread, WorkListHandle handle) +{ + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + auto result = invalidi(Node); + + assert(handle.is_valid); + auto* worklist = &thread->worklists[handle.index]; + auto len = worklist->nodes.length; + if (len) + { + auto index = len - 1; + auto node_index = worklist->nodes.pointer[index]; + worklist->nodes.length = index; + bitset_set_value(&worklist->bitset, index, 0); + result = node_index; + } + + return result; +} + +fn void thread_worklist_clear(Thread* thread, WorkListHandle handle) +{ + assert(handle.is_valid); + assert((thread->worklist_bitset & (1 << handle.index)) != 0); + auto* restrict worklist = &thread->worklists[handle.index]; + + bitset_clear(&worklist->visited); + bitset_clear(&worklist->bitset); + worklist->nodes.length = 0; +} + +// fn void thread_worklist_release(Thread* thread, WorkListHandle* handle) +// { +// thread_worklist_clear(thread, *handle); +// handle->is_valid = 0; +// } + +fn Type* thread_type_get(Thread* thread, TypeIndex type_index) +{ + assert(validi(type_index)); + auto* type = &thread->buffer.types.pointer[geti(type_index)]; + return type; +} + + +fn DebugType* thread_debug_type_get(Thread* thread, DebugTypeIndex debug_type_index) +{ + assert(validi(debug_type_index)); + auto* type = &thread->buffer.debug_types.pointer[geti(debug_type_index)]; + return type; +} + +fn RegisterMask* thread_register_mask_get(Thread* thread, RegisterMaskIndex register_mask_index) +{ + assert(validi(register_mask_index)); + auto* register_mask = &thread->buffer.register_masks.pointer[geti(register_mask_index)]; + return register_mask; +} + fn void thread_node_set_use(Thread* thread, u32 offset, u16 index, NodeIndex new_use) { thread->buffer.uses.pointer[offset + index] = new_use; } -fn NodeIndex thread_node_get_use(Thread* thread, u32 offset, u16 index) -{ - NodeIndex i = thread->buffer.uses.pointer[offset + index]; - return i; -} +// fn NodeIndex thread_node_get_use(Thread* thread, u32 offset, u16 index) +// { +// NodeIndex i = thread->buffer.uses.pointer[offset + index]; +// return i; +// } -fn NodeIndex node_input_get(Thread* thread, Node* node, u16 index) -{ - assert(index < node->input_count); - NodeIndex result = thread_node_get_use(thread, node->input_offset, index); - return result; -} +// fn NodeIndex node_input_get(Thread* thread, Node* node, u16 index) +// { +// assert(index < node->input_count); +// NodeIndex result = thread_node_get_use(thread, node->input_offset, index); +// return result; +// } +// +// fn NodeIndex node_output_get(Thread* thread, Node* node, u16 index) +// { +// assert(index < node->output_count); +// NodeIndex result = thread_node_get_use(thread, node->output_offset, index); +// return result; +// } -fn NodeIndex node_output_get(Thread* thread, Node* node, u16 index) -{ - assert(index < node->output_count); - NodeIndex result = thread_node_get_use(thread, node->output_offset, index); - return result; -} +// fn NodeIndex scope_get_control(Thread* thread, Node* node) +// { +// assert(node->id == NODE_SCOPE); +// auto control = node_input_get(thread, node, 0); +// return control; +// } -fn NodeIndex scope_get_control(Thread* thread, Node* node) -{ - assert(node->id == NODE_SCOPE); - auto control = node_input_get(thread, node, 0); - return control; -} - -fn NodeIndex builder_get_control_node_index(Thread* thread, FunctionBuilder* builder) -{ - auto* scope_node = thread_node_get(thread, builder->scope); - auto result = scope_get_control(thread, scope_node); - return result; -} +// fn NodeIndex builder_get_control_node_index(Thread* thread, FunctionBuilder* builder) +// { +// auto* scope_node = thread_node_get(thread, builder->scope); +// auto result = scope_get_control(thread, scope_node); +// return result; +// } typedef struct NodeDualReference NodeDualReference; @@ -975,12 +1366,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 = current_length + additional; + auto desired_capacity = cast(u16, u32, current_length + additional); if (desired_capacity > current_capacity) { auto* ptr = vb_add(&thread->buffer.uses, desired_capacity); - u32 new_offset = ptr - thread->buffer.uses.pointer; + u32 new_offset = cast(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; @@ -1010,43 +1401,9 @@ fn NodeIndex node_add_output(Thread* thread, NodeIndex node_index, NodeIndex out return node_index; } -fn NodeIndex intern_pool_remove_node(Thread* thread, NodeIndex node_index); -fn void node_unlock(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - if (node->hash) - { - auto old_node_index = intern_pool_remove_node(thread, node_index); - assert(index_equal(old_node_index, node_index)); - node->hash = 0; - } -} +// fn NodeIndex intern_pool_remove_node(Thread* thread, NodeIndex node_index); -fn s32 node_find(Slice(NodeIndex) nodes, NodeIndex node_index) -{ - s32 result = -1; - for (u32 i = 0; i < nodes.length; i += 1) - { - if (index_equal(nodes.pointer[i], node_index)) - { - result = i; - break; - } - } - return result; -} - -fn void thread_node_remove_use(Thread* thread, u32 offset, u16* length, u16 index) -{ - auto current_length = *length; - assert(index < current_length); - auto item_to_remove = &thread->buffer.uses.pointer[offset + index]; - auto substitute = &thread->buffer.uses.pointer[offset + current_length - 1]; - *item_to_remove = *substitute; - *length = current_length - 1; -} - -fn Slice(NodeIndex) node_get_inputs(Thread* thread, Node* node) +fn Slice(NodeIndex) node_get_inputs(Thread* thread, const Node * restrict const node) { auto result = (Slice(NodeIndex)) { .pointer = &thread->buffer.uses.pointer[node->input_offset], @@ -1064,112 +1421,192 @@ fn Slice(NodeIndex) node_get_outputs(Thread* thread, Node* node) return result; } +fn u8 node_is_constant(const Node* const restrict node) +{ + switch (node->id) + { + case IR_INTEGER_CONSTANT: + return 1; + default: + return 0; + } +} + +fn Hash32 node_hash(Thread* thread, const Node* restrict const node) +{ + Hash32 hash = 0; + hash += node->id; + hash += sizeof(u8); + + auto inputs = node_get_inputs(thread, node); + + // Constants are allowed to live across functions + if (!node_is_constant(node)) + { + u32 valid_input_count = 0; + + for (u16 i = 0; i < inputs.length; i += 1) + { + auto input = inputs.pointer[i]; + if (validi(input)) + { + valid_input_count += 1; + hash += geti(input); + hash += sizeof(input); + } + } + + hash += valid_input_count; + hash += sizeof(u16); + } + + auto* union_start = (u8*)&node->projection; + auto* union_end = union_start + size_until_end(Node, projection); + for (auto* it = union_start; it < union_end; it += 1) + { + hash += *it; + } + hash += union_end - union_start; + + auto result = hash32_fib_end(hash); + return result; +} + +fn Hash32 node_hash_index(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + auto hash = node_hash(thread, node); + return hash; +} + +fn Hash32 register_mask_hash(Thread* thread, const RegisterMask* const restrict mask) +{ + unused(thread); + static_assert(sizeof(RegisterMask) == sizeof(u64)); + auto hash = *(Hash64*)mask; + + auto result = hash64_fib_end(hash); + return result; +} + +fn Hash32 register_mask_hash_index(Thread* thread, RegisterMaskIndex register_mask_index) +{ + auto* mask = thread_register_mask_get(thread, register_mask_index); + auto hash = register_mask_hash(thread, mask); + return hash; +} + +fn void node_gvn_remove(Thread* thread, NodeIndex node_index); + +fn void node_unlock(Thread* thread, NodeIndex node_index) +{ + unused(thread); + unused(node_index); + // auto* node = thread_node_get(thread, node_index); + // if (node->interned) + // { + // auto r = node_gvn_remove(thread, node_index); + // assert(index_equal(r, node_index)); + // } +} + +fn s64 node_find(Slice(NodeIndex) nodes, NodeIndex node_index) +{ + s64 result = -1; + + for (u64 i = 0; i < nodes.length; i += 1) + { + if (index_equal(nodes.pointer[i], node_index)) + { + result = cast(s64, u64, i); + break; + } + } + + return result; +} + +fn void thread_node_remove_use(Thread* thread, u32 offset, u16* length, u16 index) +{ + auto current_length = *length; + assert(index < current_length); + auto item_to_remove = &thread->buffer.uses.pointer[offset + index]; + auto substitute = &thread->buffer.uses.pointer[offset + current_length - 1]; + *item_to_remove = *substitute; + *length = current_length - 1; +} + fn u8 node_remove_output(Thread* thread, NodeIndex node_index, NodeIndex use_index) { auto* node = thread_node_get(thread, node_index); auto outputs = node_get_outputs(thread, node); - auto index = node_find(outputs, use_index); - assert(index != -1); + auto maybe_index = node_find(outputs, use_index); + assert(maybe_index != -1); + auto index = cast(u16, s64, maybe_index); thread_node_remove_use(thread, node->output_offset, &node->output_count, index); return node->output_count == 0; } -fn void move_dependencies_to_worklist(Thread* thread, Node* node) -{ - assert(node->dependency_count == 0); - for (u32 i = 0; i < node->dependency_count; i += 1) - { - unused(thread); - trap(); - } -} +// fn void move_dependencies_to_worklist(Thread* thread, Node* node) +// { +// assert(node->dependency_count == 0); +// for (u32 i = 0; i < node->dependency_count; i += 1) +// { +// unused(thread); +// trap(); +// } +// } -fn String node_id_to_string(Node* node) -{ - switch (node->id) - { - case_to_name(NODE_, START); - case_to_name(NODE_, STOP); - case_to_name(NODE_, CONTROL_PROJECTION); - case_to_name(NODE_, DEAD_CONTROL); - case_to_name(NODE_, SCOPE); - case_to_name(NODE_, PROJECTION); - case_to_name(NODE_, RETURN); - case_to_name(NODE_, REGION); - case_to_name(NODE_, REGION_LOOP); - case_to_name(NODE_, IF); - case_to_name(NODE_, PHI); - case_to_name(NODE_, INTEGER_ADD); - case_to_name(NODE_, INTEGER_SUBSTRACT); - case_to_name(NODE_, INTEGER_MULTIPLY); - case_to_name(NODE_, INTEGER_UNSIGNED_DIVIDE); - case_to_name(NODE_, INTEGER_SIGNED_DIVIDE); - case_to_name(NODE_, INTEGER_UNSIGNED_REMAINDER); - case_to_name(NODE_, INTEGER_SIGNED_REMAINDER); - case_to_name(NODE_, INTEGER_UNSIGNED_SHIFT_LEFT); - case_to_name(NODE_, INTEGER_SIGNED_SHIFT_LEFT); - case_to_name(NODE_, INTEGER_UNSIGNED_SHIFT_RIGHT); - case_to_name(NODE_, INTEGER_SIGNED_SHIFT_RIGHT); - case_to_name(NODE_, INTEGER_AND); - case_to_name(NODE_, INTEGER_OR); - case_to_name(NODE_, INTEGER_XOR); - case_to_name(NODE_, INTEGER_NEGATION); - case_to_name(NODE_, CONSTANT); - case_to_name(NODE_, COUNT); - case_to_name(NODE_, INTEGER_COMPARE_EQUAL); - case_to_name(NODE_, INTEGER_COMPARE_NOT_EQUAL); - break; - } -} +// fn u8 node_is_unused(Node* node) +// { +// return node->output_count == 0; +// } -fn u8 node_is_unused(Node* node) -{ - return node->output_count == 0; -} +// fn u8 node_is_dead(Node* node) +// { +// return node_is_unused(node) & ((node->input_count == 0) & (!validi(node->type))); +// } -fn u8 node_is_dead(Node* node) -{ - return node_is_unused(node) & ((node->input_count == 0) & (!validi(node->type))); -} - -fn void node_kill(Thread* thread, NodeIndex node_index) -{ - node_unlock(thread, node_index); - auto* node = thread_node_get(thread, node_index); - // print("[NODE KILLING] (#{u32}, {s}) START\n", node_index.index, node_id_to_string(node)); - assert(node_is_unused(node)); - node->type = invalidi(Type); - - auto inputs = node_get_inputs(thread, node); - while (node->input_count > 0) - { - auto input_index = node->input_count - 1; - node->input_count = input_index; - auto old_input_index = inputs.pointer[input_index]; - - // print("[NODE KILLING] (#{u32}, {s}) Removing input #{u32} at slot {u32}\n", node_index.index, node_id_to_string(node), old_input_index.index, input_index); - if (validi(old_input_index)) - { - thread_worklist_push(thread, old_input_index); - u8 no_more_outputs = node_remove_output(thread, old_input_index, node_index); - if (no_more_outputs) - { - // print("[NODE KILLING] (#{u32}, {s}) (NO MORE OUTPUTS - KILLING) Input #{u32}\n", node_index.index, node_id_to_string(node), old_input_index.index); - node_kill(thread, old_input_index); - } - } - } - - assert(node_is_dead(node)); - // print("[NODE KILLING] (#{u32}, {s}) END\n", node_index.index, node_id_to_string(node)); -} +// fn void node_kill(Thread* thread, NodeIndex node_index) +// { +// node_unlock(thread, node_index); +// auto* node = thread_node_get(thread, node_index); +// // print("[NODE KILLING] (#{u32}, {s}) START\n", node_index.index, node_id_to_string(node)); +// assert(node_is_unused(node)); +// todo(); +// // node->type = invalidi(TypePair); +// +// auto inputs = node_get_inputs(thread, node); +// while (node->input_count > 0) +// { +// auto input_index = cast(u16, u32, node->input_count - 1); +// node->input_count = input_index; +// auto old_input_index = inputs.pointer[input_index]; +// +// // print("[NODE KILLING] (#{u32}, {s}) Removing input #{u32} at slot {u32}\n", node_index.index, node_id_to_string(node), old_input_index.index, input_index); +// if (validi(old_input_index)) +// { +// thread_worklist_push(thread, old_input_index); +// u8 no_more_outputs = node_remove_output(thread, old_input_index, node_index); +// if (no_more_outputs) +// { +// // print("[NODE KILLING] (#{u32}, {s}) (NO MORE OUTPUTS - KILLING) Input #{u32}\n", node_index.index, node_id_to_string(node), old_input_index.index); +// node_kill(thread, old_input_index); +// } +// } +// } +// +// assert(node_is_dead(node)); +// // print("[NODE KILLING] (#{u32}, {s}) END\n", node_index.index, node_id_to_string(node)); +// } fn NodeIndex node_set_input(Thread* thread, NodeIndex node_index, u16 index, NodeIndex new_input) { auto* node = thread_node_get(thread, node_index); assert(index < node->input_count); node_unlock(thread, node_index); - auto old_input = node_input_get(thread, node, index); + auto inputs = node_get_inputs(thread, node); + auto old_input = inputs.pointer[index]; if (!index_equal(old_input, new_input)) { @@ -1184,20 +1621,21 @@ fn NodeIndex node_set_input(Thread* thread, NodeIndex node_index, u16 index, Nod { if (node_remove_output(thread, old_input, node_index)) { - node_kill(thread, old_input); + // todo(); + // node_kill(thread, old_input); } } - move_dependencies_to_worklist(thread, node); + // move_dependencies_to_worklist(thread, node); } return new_input; } -fn NodeIndex builder_set_control(Thread* thread, FunctionBuilder* builder, NodeIndex node_index) -{ - return node_set_input(thread, builder->scope, 0, node_index); -} +// fn NodeIndex builder_set_control(Thread* thread, FunctionBuilder* builder, NodeIndex node_index) +// { +// return node_set_input(thread, builder->scope, 0, node_index); +// } fn NodeIndex node_add_input(Thread* thread, NodeIndex node_index, NodeIndex input_index) { @@ -1212,35 +1650,34 @@ fn NodeIndex node_add_input(Thread* thread, NodeIndex node_index, NodeIndex inpu return input_index; } -fn NodeIndex builder_add_return(Thread* thread, FunctionBuilder* builder, NodeIndex node_index) -{ - return node_add_input(thread, builder->function->stop, node_index); -} - struct NodeCreate { - NodeId id; Slice(NodeIndex) inputs; + TypePair type_pair; + NodeId id; }; typedef struct NodeCreate NodeCreate; - fn NodeIndex thread_node_add(Thread* thread, NodeCreate data) { - auto input_result = thread_get_node_reference_array(thread, data.inputs.length); - memcpy(input_result.pointer, data.inputs.pointer, sizeof(NodeIndex) * data.inputs.length); + auto input_count = cast(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, node - thread->buffer.nodes.pointer); + auto node_index = Index(Node, cast(u32, s64, node - thread->buffer.nodes.pointer)); memset(node, 0, sizeof(Node)); node->id = data.id; node->input_offset = input_result.index; - node->input_count = data.inputs.length; - node->type = invalidi(Type); + node->input_count = input_count; + node->input_capacity = input_count; + node->type = type_pair_invalid; + // node->type = invalidi(TypePair); + node->type = data.type_pair; // print("[NODE CREATION] #{u32} {s} | INPUTS: { ", node_index.index, node_id_to_string(node)); - for (u32 i = 0; i < data.inputs.length; i += 1) + for (u16 i = 0; i < input_count; i += 1) { NodeIndex input = data.inputs.pointer[i]; // print("{u32} ", input.index); @@ -1255,183 +1692,181 @@ fn NodeIndex thread_node_add(Thread* thread, NodeCreate data) return node_index; } -fn void node_pop_inputs(Thread* thread, NodeIndex node_index, u16 input_count) -{ - node_unlock(thread, node_index); - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - for (u16 i = 0; i < input_count; i += 1) - { - auto old_input = inputs.pointer[node->input_count - 1]; - node->input_count -= 1; - if (validi(old_input)) - { - if (node_remove_output(thread, old_input, node_index)) - { - trap(); - } - } - } -} +// fn void node_pop_inputs(Thread* thread, NodeIndex node_index, u16 input_count) +// { +// node_unlock(thread, node_index); +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// for (u16 i = 0; i < input_count; i += 1) +// { +// auto old_input = inputs.pointer[node->input_count - 1]; +// node->input_count -= 1; +// if (validi(old_input)) +// { +// if (node_remove_output(thread, old_input, node_index)) +// { +// trap(); +// } +// } +// } +// } -fn void scope_push(Thread* thread, FunctionBuilder* builder) -{ - auto* scope = thread_node_get(thread, builder->scope); - auto current_length = scope->scope.stack.length; - auto desired_length = current_length + 1; - auto current_capacity = scope->scope.stack.capacity; +// fn void scope_push(Thread* thread, FunctionBuilder* builder) +// { +// auto* scope = thread_node_get(thread, builder->scope); +// auto current_length = scope->scope.stack.length; +// auto desired_length = current_length + 1; +// auto current_capacity = scope->scope.stack.capacity; +// +// if (current_capacity < desired_length) +// { +// auto optimal_capacity = MAX(round_up_to_next_power_of_2(desired_length), 8); +// auto* new_pointer = arena_allocate(thread->arena, ScopePair, optimal_capacity); +// memcpy(new_pointer, scope->scope.stack.pointer, current_length * sizeof(ScopePair)); +// scope->scope.stack.capacity = optimal_capacity; +// scope->scope.stack.pointer = new_pointer; +// } +// +// memset(&scope->scope.stack.pointer[current_length], 0, sizeof(ScopePair)); +// scope->scope.stack.length = current_length + 1; +// } - if (current_capacity < desired_length) - { - auto optimal_capacity = MAX(round_up_to_next_power_of_2(desired_length), 8); - auto* new_pointer = arena_allocate(thread->arena, ScopePair, optimal_capacity); - memcpy(new_pointer, scope->scope.stack.pointer, current_length * sizeof(ScopePair)); - scope->scope.stack.capacity = optimal_capacity; - scope->scope.stack.pointer = new_pointer; - } +// fn void scope_pop(Thread* thread, FunctionBuilder* builder) +// { +// auto scope_index = builder->scope; +// auto* scope = thread_node_get(thread, scope_index); +// auto index = scope->scope.stack.length - 1; +// auto popped_scope = scope->scope.stack.pointer[index]; +// scope->scope.stack.length = index; +// auto input_count = popped_scope.values.length; +// node_pop_inputs(thread, scope_index, input_count); +// } - memset(&scope->scope.stack.pointer[current_length], 0, sizeof(ScopePair)); - scope->scope.stack.length = current_length + 1; -} +// fn ScopePair* scope_get_last(Node* node) +// { +// assert(node->id == NODE_SCOPE); +// return &node->scope.stack.pointer[node->scope.stack.length - 1]; +// } -fn void scope_pop(Thread* thread, FunctionBuilder* builder) -{ - auto scope_index = builder->scope; - auto* scope = thread_node_get(thread, scope_index); - auto index = scope->scope.stack.length - 1; - auto popped_scope = scope->scope.stack.pointer[index]; - scope->scope.stack.length = index; - auto input_count = popped_scope.values.length; - node_pop_inputs(thread, scope_index, input_count); -} +// fn NodeIndex scope_define(Thread* thread, FunctionBuilder* builder, String name, TypeIndex type_index, NodeIndex node_index) +// { +// auto scope_node_index = builder->scope; +// auto* scope_node = thread_node_get(thread, scope_node_index); +// auto* last = scope_get_last(scope_node); +// string_map_put(&last->types, thread->arena, name, geti(type_index)); +// +// auto existing = string_map_put(&last->values, thread->arena, name, scope_node->input_count).existing; +// NodeIndex result; +// +// if (existing) +// { +// result = invalidi(Node); +// } +// else +// { +// result = node_add_input(thread, scope_node_index, node_index); +// } +// +// return result; +// } -fn ScopePair* scope_get_last(Node* node) -{ - assert(node->id == NODE_SCOPE); - return &node->scope.stack.pointer[node->scope.stack.length - 1]; -} +// fn NodeIndex scope_update_extended(Thread* thread, FunctionBuilder* builder, String name, NodeIndex node_index, s32 nesting_level) +// { +// NodeIndex result = invalidi(Node); +// +// if (nesting_level >= 0) +// { +// auto* scope_node = thread_node_get(thread, builder->scope); +// auto* string_map = &scope_node->scope.stack.pointer[nesting_level].values; +// auto lookup_result = string_map_get(string_map, name); +// if (lookup_result.existing) +// { +// auto index = lookup_result.value; +// auto old_index = node_input_get(thread, scope_node, index); +// auto* old_node = thread_node_get(thread, old_index); +// +// // if (old_node->id == NODE_SCOPE) +// // { +// // trap(); +// // } +// +// if (validi(node_index)) +// { +// auto result = node_set_input(thread, builder->scope, index, node_index); +// return result; +// } +// else +// { +// return old_index; +// } +// } +// else +// { +// return scope_update_extended(thread, builder, name, node_index, nesting_level - 1); +// } +// } +// +// return result; +// } -fn NodeIndex scope_define(Thread* thread, FunctionBuilder* builder, String name, TypeIndex type_index, NodeIndex node_index) -{ - auto scope_node_index = builder->scope; - auto* scope_node = thread_node_get(thread, scope_node_index); - auto* last = scope_get_last(scope_node); - string_map_put(&last->types, thread->arena, name, geti(type_index)); +// fn NodeIndex scope_lookup(Thread* thread, FunctionBuilder* builder, String name) +// { +// auto* scope_node = thread_node_get(thread, builder->scope); +// return scope_update_extended(thread, builder, name, invalidi(Node), scope_node->scope.stack.length - 1); +// } - auto existing = string_map_put(&last->values, thread->arena, name, scope_node->input_count).existing; - NodeIndex result; +// fn NodeIndex scope_update(Thread* thread, FunctionBuilder* builder, String name, NodeIndex value_node_index) +// { +// auto* scope_node = thread_node_get(thread, builder->scope); +// auto result = scope_update_extended(thread, builder, name, value_node_index, scope_node->scope.stack.length - 1); +// return result; +// } - if (existing) - { - result = invalidi(Node); - } - else - { - result = node_add_input(thread, scope_node_index, node_index); - } - - return result; -} - -fn NodeIndex scope_update_extended(Thread* thread, FunctionBuilder* builder, String name, NodeIndex node_index, s32 nesting_level) -{ - NodeIndex result = invalidi(Node); - - if (nesting_level >= 0) - { - auto* scope_node = thread_node_get(thread, builder->scope); - auto* string_map = &scope_node->scope.stack.pointer[nesting_level].values; - auto lookup_result = string_map_get(string_map, name); - if (lookup_result.existing) - { - auto index = lookup_result.value; - auto old_index = node_input_get(thread, scope_node, index); - auto* old_node = thread_node_get(thread, old_index); - - if (old_node->id == NODE_SCOPE) - { - trap(); - } - - if (validi(node_index)) - { - auto result = node_set_input(thread, builder->scope, index, node_index); - return result; - } - else - { - return old_index; - } - } - else - { - return scope_update_extended(thread, builder, name, node_index, nesting_level - 1); - } - } - - return result; -} - -fn NodeIndex scope_lookup(Thread* thread, FunctionBuilder* builder, String name) -{ - auto* scope_node = thread_node_get(thread, builder->scope); - return scope_update_extended(thread, builder, name, invalidi(Node), scope_node->scope.stack.length - 1); -} - -fn NodeIndex scope_update(Thread* thread, FunctionBuilder* builder, String name, NodeIndex value_node_index) -{ - auto* scope_node = thread_node_get(thread, builder->scope); - auto result = scope_update_extended(thread, builder, name, value_node_index, scope_node->scope.stack.length - 1); - return result; -} - -fn u8 type_equal(Type* a, Type* b) -{ - u8 result = 0; - if (a == b) - { - result = 1; - } - else - { - assert(a->hash); - assert(b->hash); - if ((a->hash == b->hash) & (a->id == b->id)) - { - switch (a->id) - { - case TYPE_INTEGER: - { - result = - ((a->integer.constant == b->integer.constant) & (a->integer.bit_count == b->integer.bit_count)) - & - ((a->integer.is_signed == b->integer.is_signed) & (a->integer.is_constant == b->integer.is_constant)); - } break; - case TYPE_TUPLE: - { - result = a->tuple.types.length == b->tuple.types.length; - - if (result) - { - for (u32 i = 0; i < a->tuple.types.length; i += 1) - { - if (!index_equal(a->tuple.types.pointer[i], b->tuple.types.pointer[i])) - { - todo(); - } - } - } - } break; - default: - trap(); - } - } - } - - return result; -} - -fn Hash64 hash_type(Thread* thread, Type* type); +// fn u8 type_equal(Type* a, Type* b) +// { +// u8 result = 0; +// if (a == b) +// { +// result = 1; +// } +// else +// { +// assert(a->hash); +// assert(b->hash); +// if ((a->hash == b->hash) & (a->id == b->id)) +// { +// switch (a->id) +// { +// case TYPE_INTEGER: +// { +// result = +// ((a->integer.constant == b->integer.constant) & (a->integer.bit_count == b->integer.bit_count)) +// & +// ((a->integer.is_signed == b->integer.is_signed) & (a->integer.is_constant == b->integer.is_constant)); +// } break; +// case TYPE_TUPLE: +// { +// result = a->tuple.types.length == b->tuple.types.length; +// +// if (result) +// { +// for (u32 i = 0; i < a->tuple.types.length; i += 1) +// { +// if (!index_equal(a->tuple.types.pointer[i], b->tuple.types.pointer[i])) +// { +// todo(); +// } +// } +// } +// } break; +// default: +// trap(); +// } +// } +// } +// +// return result; +// } fn Hash64 node_get_hash_default(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) { @@ -1441,116 +1876,593 @@ fn Hash64 node_get_hash_default(Thread* thread, Node* node, NodeIndex node_index return hash; } -fn Hash64 node_get_hash_projection(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +// fn Hash64 node_get_hash_projection(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +// { +// unused(thread); +// unused(node_index); +// auto projection_index = node->projection.index; +// auto proj_index_bytes = struct_to_bytes(projection_index); +// for (u32 i = 0; i < proj_index_bytes.length; i += 1) +// { +// hash = hash_byte(hash, proj_index_bytes.pointer[i]); +// } +// +// return hash; +// } + +// fn Hash64 node_get_hash_control_projection(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +// { +// unused(thread); +// unused(node_index); +// auto projection_index = node->control_projection.projection.index; +// auto proj_index_bytes = struct_to_bytes(projection_index); +// for (u32 i = 0; i < proj_index_bytes.length; i += 1) +// { +// hash = hash_byte(hash, proj_index_bytes.pointer[i]); +// } +// +// return hash; +// } + +// fn Hash64 node_get_hash_constant(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +// { +// unused(node_index); +// unused(thread); +// unused(node); +// assert(hash == fnv_offset); +// todo(); +// // auto type_index = node->type; +// // auto* type = thread_type_get(thread, node->type); +// // auto type_hash = hash_type(thread, type); +// // print("Hashing node #{u32} (constant) (type: #{u32}) (hash: {u64:x})\n", node_index.index, type_index.index, type_hash); +// // return type_hash; +// } + +// fn Hash64 node_get_hash_scope(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +// { +// unused(thread); +// unused(node); +// unused(node_index); +// return hash; +// } + +// fn NodeIndex node_idealize_substract(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// auto left_node_index = inputs.pointer[1]; +// auto right_node_index = inputs.pointer[2]; +// auto* left = thread_node_get(thread, left_node_index); +// auto* right = thread_node_get(thread, right_node_index); +// if (index_equal(left_node_index, right_node_index)) +// { +// trap(); +// } +// else if (right->id == IR_INTEGER_NEGATION) +// { +// trap(); +// } +// else if (left->id == IR_INTEGER_NEGATION) +// { +// trap(); +// } +// else +// { +// return invalidi(Node); +// } +// } + +// fn NodeIndex node_idealize_compare(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// auto left_node_index = inputs.pointer[1]; +// auto right_node_index = inputs.pointer[2]; +// auto* left = thread_node_get(thread, left_node_index); +// auto* right = thread_node_get(thread, right_node_index); +// if (index_equal(left_node_index, right_node_index)) +// { +// trap(); +// } +// +// if (node->id == IR_INTEGER_COMPARE_EQUAL) +// { +// if (right->id != IR_CONSTANT) +// { +// if (left->id == IR_CONSTANT) +// { +// todo(); +// } +// else if (left_node_index.index > right_node_index.index) +// { +// todo(); +// } +// } +// +// // TODO: null pointer +// if (index_equal(right->type, thread->types.integer.zero)) +// { +// todo(); +// } +// } +// +// // TODO: phi constant +// +// return invalidi(Node); +// } + +fn Hash32 debug_type_hash(Thread* thread, const DebugType* const restrict type) { unused(thread); - unused(node_index); - auto projection_index = node->projection.index; - auto proj_index_bytes = struct_to_bytes(projection_index); - for (u32 i = 0; i < proj_index_bytes.length; i += 1) + auto* start = (const u8*) type; + Hash32 hash = 0; + for (auto* it = start; it < start + sizeof(*type); it += 1) { - hash = hash_byte(hash, proj_index_bytes.pointer[i]); + hash += *it; } - - return hash; + auto result = hash32_fib_end(hash); + return result; } -fn Hash64 node_get_hash_control_projection(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +fn Hash32 debug_type_hash_index(Thread* thread, DebugTypeIndex type_index) { - unused(thread); - unused(node_index); - auto projection_index = node->control_projection.projection.index; - auto proj_index_bytes = struct_to_bytes(projection_index); - for (u32 i = 0; i < proj_index_bytes.length; i += 1) - { - hash = hash_byte(hash, proj_index_bytes.pointer[i]); - } - - return hash; + auto* type = thread_debug_type_get(thread, type_index); + return debug_type_hash(thread, type); } -fn Hash64 node_get_hash_constant(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +global const u64 intern_pool_min_capacity = 64; +struct GenericInternPool { - unused(node_index); - assert(hash == fnv_offset); - // auto type_index = node->type; - auto* type = thread_type_get(thread, node->type); - auto type_hash = hash_type(thread, type); - // print("Hashing node #{u32} (constant) (type: #{u32}) (hash: {u64:x})\n", node_index.index, type_index.index, type_hash); - return type_hash; -} + u32* pointer; + u32 length; + u32 capacity; +}; +typedef struct GenericInternPool GenericInternPool; -fn Hash64 node_get_hash_scope(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash) +struct GenericInternPoolBufferResult { - unused(thread); - unused(node); - unused(node_index); - return hash; -} + void* pointer; + u32 index; +}; +typedef struct GenericInternPoolBufferResult GenericInternPoolBufferResult; -fn NodeIndex node_idealize_substract(Thread* thread, NodeIndex node_index) +struct GenericGetOrPut { - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - auto left_node_index = inputs.pointer[1]; - auto right_node_index = inputs.pointer[2]; - auto* left = thread_node_get(thread, left_node_index); - auto* right = thread_node_get(thread, right_node_index); - if (index_equal(left_node_index, right_node_index)) - { - trap(); - } - else if (right->id == NODE_INTEGER_NEGATION) - { - trap(); - } - else if (left->id == NODE_INTEGER_NEGATION) - { - trap(); - } - else - { - return invalidi(Node); - } -} + u32 index; + u8 existing; +}; +typedef struct GenericGetOrPut GenericGetOrPut; -fn NodeIndex node_idealize_compare(Thread* thread, NodeIndex node_index) +typedef s64 FindSlotCallback(GenericInternPool* pool, Thread* thread, Hash32 hash, u32 raw_item_index, u32 saved_index, u32 slots_ahead); +typedef GenericInternPoolBufferResult AddToBufferCallback(Thread* thread); +// typedef s64 Find + +struct InternPoolInterface { - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - auto left_node_index = inputs.pointer[1]; - auto right_node_index = inputs.pointer[2]; - auto* left = thread_node_get(thread, left_node_index); - auto* right = thread_node_get(thread, right_node_index); - if (index_equal(left_node_index, right_node_index)) - { - trap(); - } + FindSlotCallback * const find_slot; + AddToBufferCallback* const add_to_buffer; +}; +typedef struct InternPoolInterface InternPoolInterface; +fn s64 ip_find_slot_debug_type(GenericInternPool* generic_pool, Thread* thread, Hash32 hash, u32 raw_item_index, u32 saved_index, u32 slots_ahead) +{ + auto* pool = (InternPool(DebugType)*)generic_pool; + assert(pool == &thread->interned.debug_types); + auto* ptr = pool->pointer; - if (node->id == NODE_INTEGER_COMPARE_EQUAL) - { - if (right->id != NODE_CONSTANT) - { - if (left->id == NODE_CONSTANT) - { - todo(); - } - else if (left_node_index.index > right_node_index.index) - { - todo(); - } - } + s64 result = -1; - // TODO: null pointer - if (index_equal(right->type, thread->types.integer.zero)) + unused(raw_item_index); + + for (auto index = saved_index; index < saved_index + slots_ahead; index += 1) + { + auto typed_index = ptr[index]; + auto debug_type = thread_debug_type_get(thread, typed_index); + auto existing_hash = debug_type_hash(thread, debug_type); + if (existing_hash == hash) { todo(); } } - // TODO: phi constant - - return invalidi(Node); + return result; +} + +fn s64 ip_generic_find_slot(GenericInternPool* pool, Thread* thread, u32 item_index, Hash32 hash, const InternPoolInterface* restrict const interface) +{ + auto* pointer = pool->pointer; + auto existing_capacity = pool->capacity; + auto original_index = hash & (existing_capacity - 1); + auto it_index = original_index; + s64 result = -1; + + while (1) + { + auto index = it_index & (existing_capacity - 1); + auto* ptr = &pointer[index]; + if (!*ptr) + { + result = index; + break; + } + +#if (__AVX2__) +#if (__AVX512F__) + auto chunk = _mm512_loadu_epi32(ptr); + auto is_zero = _mm512_cmpeq_epi32_mask(chunk, _mm512_setzero_epi32()); +#elif (__AVX2__) + auto chunk = _mm256_loadu_si256(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(is_zero)); +#else + u32 occupied_slots_ahead = 0; + for (u32 fake_i = it_index; fake_i < it_index + existing_capacity; fake_i += 1) + { + auto i = fake_i & (existing_capacity - 1); + auto item = pointer[i]; + if (item == 0) + { + break; + } + occupied_slots_ahead += 1; + } +#endif + + auto cap_ahead = existing_capacity - index; + auto slots_ahead = MIN(occupied_slots_ahead, cap_ahead); + auto slot = interface->find_slot(pool, thread, hash, item_index, index, slots_ahead); + + if (slot != -1) + { + assert(pointer[slot] != 0); + result = slot; + break; + } + + if (occupied_slots_ahead < cap_ahead) + { + result = index + occupied_slots_ahead; + break; + } + + it_index += slots_ahead; + } + + return result; +} + + +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 type_index = Index(DebugType, buffer_index); + static_assert(sizeof(type_index) == sizeof(u32)); + return (GenericInternPoolBufferResult) { + .pointer = result, + .index = *(u32*)&type_index, + }; +} + +fn u32 ip_generic_put_new_at_assume_not_existent_assume_capacity(GenericInternPool* pool, Thread* thread, u32 item_index, const void* restrict const item_pointer, u32 item_size, u32 pool_index, const InternPoolInterface* restrict const interface) +{ + if (!item_index) + { + auto buffer_result = interface->add_to_buffer(thread); + assert(buffer_result.index); + memcpy(buffer_result.pointer, item_pointer, item_size); + item_index = buffer_result.index; + } + auto* ptr = &pool->pointer[pool_index]; + *ptr = item_index; + pool->length += 1; + return item_index; +} + +fn u32 intern_pool_put_new_assume_not_existent_assume_capacity(GenericInternPool* pool, Thread* thread, u32 item_index, const void* restrict const item_pointer, u32 item_size, Hash32 hash, const InternPoolInterface* restrict const interface) +{ + auto capacity = pool->capacity; + assert(pool->length < capacity); + assert(hash); + auto pool_index = hash & (capacity - 1); + auto result = ip_generic_put_new_at_assume_not_existent_assume_capacity(pool, thread, item_index, item_pointer, item_size, pool_index, interface); + return result; +} + +fn void ip_generic_ensure_capacity(GenericInternPool* pool, Thread* thread, u32 additional) +{ + auto current_length = pool->length; + auto current_capacity = pool->capacity; + auto half_capacity = current_capacity >> 1; + auto destination_length = current_length + additional; + + 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_array = arena_allocate(thread->arena, u32, new_capacity); + memset(new_array, 0, sizeof(u32) * new_capacity); + + auto old_capacity = current_capacity; + + pool->pointer = new_array; + pool->length = 0; + pool->capacity = new_capacity; + + if (old_capacity) + { + todo(); + } + } +} + +fn u32 ip_generic_put_new_assume_not_existent(GenericInternPool* pool, Thread* thread, u32 item_index, const void* item_pointer, u32 item_size, Hash32 hash, const InternPoolInterface* const restrict interface) +{ + ip_generic_ensure_capacity(pool, thread, 1); + auto result = intern_pool_put_new_assume_not_existent_assume_capacity(pool, thread, item_index, item_pointer, item_size, hash, interface); + return result; +} + +fn GenericGetOrPut ip_generic_get_or_put(GenericInternPool* pool, Thread* thread, u32 item_index, const void* const restrict item_pointer, u32 item_size, Hash32 hash, const InternPoolInterface* const restrict interface) +{ + assert(hash); + auto length = pool->length; + auto capacity = pool->capacity; + + if (capacity) + { + 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 element = pool->pointer[index]; + u8 is_valid_or_existing = element != 0; + if (!is_valid_or_existing) + { + element = ip_generic_put_new_at_assume_not_existent_assume_capacity(pool, thread, item_index, item_pointer, item_size, index, interface); + assert(element != 0); + } + + return (GenericGetOrPut) { + .index = element, + .existing = is_valid_or_existing, + }; + } + } + + if (length < capacity) + { + todo(); + } + else if (length == capacity) + { + auto index = ip_generic_put_new_assume_not_existent(pool, thread, item_index, item_pointer, item_size, hash, interface); + return (GenericGetOrPut) + { + .index = index, + .existing = 0, + }; + } + else + { + unreachable(); + } +} + +// This assumes the indices are not equal +fn u8 node_equal(Thread* thread, NodeIndex a_index, NodeIndex b_index) +{ + u8 result = 0; + auto a_hash = node_hash_index(thread, a_index); + auto b_hash = node_hash_index(thread, b_index); + auto* a = thread_node_get(thread, a_index); + auto* b = thread_node_get(thread, b_index); + assert(!index_equal(a_index, b_index)); + assert(a != b); + + if (((a->id == b->id) & (a_hash == b_hash)) & (a->input_count == b->input_count)) + { + auto inputs_a = node_get_inputs(thread, a); + auto inputs_b = node_get_inputs(thread, b); + result = 1; + + for (u16 i = 0; i < a->input_count; i += 1) + { + if (!index_equal(inputs_a.pointer[i], inputs_b.pointer[i])) + { + result = 0; + break; + } + } + + if (result) + { + todo(); + // switch (a->id) + // { + // case IR_CONSTANT: + // todo(); + // // result = index_equal(a->constant.type, b->constant.type); + // break; + // case IR_START: + // todo(); + // // result = a->start.function == b->start.function; + // break; + // default: + // trap(); + // } + } + } + + return result; +} + +fn u8 node_index_equal(Thread* thread, NodeIndex a, NodeIndex b) +{ + u8 result = 0; + result = index_equal(a, b) || node_equal(thread, a, b); + + return result; +} + +fn s64 ip_find_slot_node(GenericInternPool* generic_pool, Thread* thread, Hash32 hash, u32 raw_item_index, u32 saved_index, u32 slots_ahead) +{ + auto* pool = (InternPool(Node)*)generic_pool; + assert(pool == &thread->interned.nodes); + auto* ptr = pool->pointer; + auto item_index = *(NodeIndex*)&raw_item_index; + unused(hash); + + s64 result = -1; + + for (auto index = saved_index; index < saved_index + slots_ahead; index += 1) + { + auto typed_index = ptr[index]; + if (node_index_equal(thread, item_index, typed_index)) + { + result = index; + break; + } + } + + return result; +} + +fn s64 ip_find_slot_register_mask(GenericInternPool* generic_pool, Thread* thread, Hash32 hash, u32 raw_item_index, u32 saved_index, u32 slots_ahead) +{ + auto* pool = (InternPool(RegisterMask)*)generic_pool; + assert(pool == &thread->interned.register_masks); + auto* ptr = pool->pointer; + auto item_index = *(RegisterMaskIndex*)&raw_item_index; + unused(hash); + + s64 result = -1; + + RegisterMask rm = *thread_register_mask_get(thread, item_index); + + for (auto index = saved_index; index < saved_index + slots_ahead; index += 1) + { + auto typed_index = ptr[index]; + static_assert(sizeof(RegisterMaskIndex) == sizeof(u32)); + if (index_equal(item_index, typed_index)) + { + result = index; + break; + } + + auto register_mask = thread_register_mask_get(thread, typed_index); + static_assert(sizeof(RegisterMask) == sizeof(u64)); + if (*(u64*)register_mask == *(u64*)&rm) + { + result = index; + break; + } + } + + return result; +} + +global 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) { + .find_slot = &ip_find_slot_node, +}; + +global const auto ip_interface_register_mask = (InternPoolInterface) { + .find_slot = &ip_find_slot_register_mask, +}; + +#define declare_ip_functions(T, lower) \ +fn Hash32 lower ## _hash_index(Thread* thread, T ## Index item_index); \ +fn Hash32 lower ## _hash(Thread* thread, const T * const restrict item); \ +\ +may_be_unused fn T ## GetOrPut ip_ ## T ## _get_or_put(InternPool(T)* pool, Thread* thread, T ## Index item_index) \ +{ \ + auto hash = lower ## _hash_index(thread, item_index); \ + auto* item = thread_ ## lower ## _get(thread, item_index); \ + static_assert(sizeof(item_index) == sizeof(u32));\ + auto raw_item_index = *(u32*)&item_index;\ + auto result = ip_generic_get_or_put((GenericInternPool*)pool, thread, raw_item_index, (void*)item, sizeof(T), hash, &ip_interface_ ## lower); \ + return (T ## GetOrPut)\ + {\ + .index = *(T ## Index*)&result.index,\ + .existing = result.existing,\ + };\ +}\ +may_be_unused fn T ## GetOrPut ip_ ## T ## _get_or_put_new(InternPool(T)* pool, Thread* thread, const T* item) \ +{ \ + auto hash = lower ## _hash(thread, item); \ + auto result = ip_generic_get_or_put((GenericInternPool*)pool, thread, 0, (void*)item, sizeof(T), hash, &ip_interface_ ## lower); \ + return (T ## GetOrPut)\ + {\ + .index = *(T ## Index*)&result.index,\ + .existing = result.existing,\ + };\ +}\ +may_be_unused fn T ## Index ip_ ## T ## _remove(InternPool(T)* pool, Thread* thread, T ## Index item_index)\ +{\ + auto existing_capacity = pool->capacity;\ + auto* item = thread_ ## lower ## _get(thread, item_index);\ + auto hash = lower ## _hash(thread, item);\ + static_assert(sizeof(item_index) == sizeof(u32));\ + auto raw_item_index = *(u32*)&item_index;\ + auto maybe_slot = ip_generic_find_slot((GenericInternPool*)pool, thread, raw_item_index, hash, &ip_interface_ ## lower);\ + \ + if (maybe_slot != -1)\ + {\ + auto i = cast(u32, s64, maybe_slot);\ + auto* slot_pointer = &pool->pointer[i];\ + auto old_item_index = *slot_pointer;\ + assert(validi(old_item_index));\ + pool->length -= 1;\ + *slot_pointer = invalidi(T);\ + auto j = i;\ + \ + while (1)\ + {\ + j = (j + 1) & (existing_capacity - 1);\ +\ + auto existing = pool->pointer[j];\ + if (!validi(existing))\ + {\ + break;\ + }\ +\ + auto existing_item_index = *(T ## Index*)&existing;\ + auto* existing_item = thread_ ## lower ## _get(thread, existing_item_index);\ + auto existing_item_hash = lower ## _hash(thread, existing_item);\ + auto k = existing_item_hash & (existing_capacity - 1);\ +\ + if (i <= j)\ + {\ + if ((i < k) & (k <= j))\ + {\ + continue;\ + }\ + }\ + else\ + {\ + if ((k <= j) | (i < k))\ + {\ + continue;\ + }\ + }\ +\ + pool->pointer[i] = pool->pointer[j];\ + pool->pointer[j] = invalidi(T);\ +\ + i = j;\ + }\ +\ + \ + return old_item_index;\ + }\ + else\ + {\ + todo();\ + }\ } struct TypeGetOrPut @@ -1561,57 +2473,61 @@ struct TypeGetOrPut typedef struct TypeGetOrPut TypeGetOrPut; -fn TypeGetOrPut intern_pool_get_or_put_new_type(Thread* thread, Type* type); +// fn TypeGetOrPut intern_pool_get_or_put_new_type(Thread* thread, Type* type); typedef NodeIndex NodeIdealize(Thread* thread, NodeIndex node_index); typedef TypeIndex NodeComputeType(Thread* thread, NodeIndex node_index); typedef Hash64 TypeGetHash(Thread* thread, Type* type); typedef Hash64 NodeGetHash(Thread* thread, Node* node, NodeIndex node_index, Hash64 hash); -fn TypeIndex thread_get_integer_type(Thread* thread, TypeInteger type_integer) -{ - Type type; - memset(&type, 0, sizeof(Type)); - type.integer = type_integer; - type.id = TYPE_INTEGER; - - auto result = intern_pool_get_or_put_new_type(thread, &type); - return result.index; -} +// fn TypeIndex thread_get_integer_type(Thread* thread, TypeInteger type_integer) +// { +// Type type; +// memset(&type, 0, sizeof(Type)); +// type.integer = type_integer; +// type.id = TYPE_INTEGER; +// +// auto result = intern_pool_get_or_put_new_type(thread, &type); +// return result.index; +// } fn NodeIndex peephole(Thread* thread, Function* function, NodeIndex node_index); -fn NodeIndex constant_int_create_with_type(Thread* thread, Function* function, TypeIndex type_index) -{ - auto node_index = thread_node_add(thread, (NodeCreate){ - .id = NODE_CONSTANT, - .inputs = array_to_slice(((NodeIndex []) { - function->start, - })) - }); - auto* node = thread_node_get(thread, node_index); +// fn NodeIndex constant_int_create_with_type(Thread* thread, Function* function, TypeIndex type_index) +// { +// auto node_index = thread_node_add(thread, (NodeCreate){ +// .id = IR_CONSTANT, +// .inputs = array_to_slice(((NodeIndex []) { +// // function->start, +// })) +// }); +// auto* node = thread_node_get(thread, node_index); +// unused(node); +// unused(type_index); +// +// todo(); +// +// // node->constant = (NodeConstant) { +// // .type = type_index, +// // }; +// // +// // // print("Creating constant integer node #{u32} with value: {u64:x}\n", node_index.index, thread_type_get(thread, type_index)->integer.constant); +// // +// // auto result = peephole(thread, function, node_index); +// // return result; +// } - node->constant = (NodeConstant) { - .type = type_index, - }; - - // print("Creating constant integer node #{u32} with value: {u64:x}\n", node_index.index, thread_type_get(thread, type_index)->integer.constant); - - auto result = peephole(thread, function, node_index); - return result; -} - -fn NodeIndex constant_int_create(Thread* thread, Function* function, u64 value) -{ - auto type_index = thread_get_integer_type(thread, (TypeInteger){ - .constant = value, - .bit_count = 0, - .is_constant = 1, - .is_signed = 0, - }); - - auto constant_int = constant_int_create_with_type(thread, function, type_index); - return constant_int; -} +// fn NodeIndex constant_int_create(Thread* thread, Function* function, u64 value) +// { +// auto type_index = thread_get_integer_type(thread, (TypeInteger){ +// .constant = value, +// .bit_count = 0, +// .is_constant = 1, +// .is_signed = 0, +// }); +// +// auto constant_int = constant_int_create_with_type(thread, function, type_index); +// return constant_int; +// } struct NodeVirtualTable { @@ -1628,19 +2544,20 @@ struct TypeVirtualTable typedef struct TypeVirtualTable TypeVirtualTable; fn Hash64 hash_type(Thread* thread, Type* type); -fn NodeIndex idealize_null(Thread* thread, NodeIndex node_index) -{ - unused(thread); - unused(node_index); - return invalidi(Node); -} +// fn NodeIndex idealize_null(Thread* thread, NodeIndex node_index) +// { +// unused(thread); +// unused(node_index); +// return invalidi(Node); +// } -fn TypeIndex compute_type_constant(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - assert(node->id == NODE_CONSTANT); - return node->constant.type; -} +// fn TypeIndex compute_type_constant(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// assert(node->id == IR_CONSTANT); +// todo(); +// // return node->constant.type; +// } fn Hash64 type_get_hash_default(Thread* thread, Type* type) { @@ -1678,465 +2595,635 @@ fn Hash64 type_get_hash_tuple(Thread* thread, Type* type) return hash; } -fn u8 node_is_projection(Node* n) -{ - return (n->id == NODE_CONTROL_PROJECTION) | (n->id == NODE_PROJECTION); -} +// fn void intern_pool_ensure_capacity(InternPool(T)* pool, Thread* thread, u32 additional) \ +// {\ +// auto current_capacity = pool->capacity; \ +// auto current_length = pool->length; \ +// assert(current_capacity % 2 == 0); \ +// auto half_capacity = current_capacity >> 1; \ +// auto destination_length = current_length + additional; \ +// \ +// if (destination_length > half_capacity) \ +// {\ +// auto new_capacity = cast(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); \ +// \ +// auto* old_pointer = pool->pointer;\ +// auto old_capacity = current_capacity;\ +// auto old_length = current_length;\ +// \ +// pool->length = 0; +// pool->pointer = new_array; +// pool->capacity = new_capacity; +// +// u8* buffer; +// u64 stride; +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// buffer = (u8*)thread->buffer.types.pointer; +// stride = sizeof(Type); +// assert(pool == &thread->interned.types); +// break; +// case INTERN_POOL_KIND_NODE: +// buffer = (u8*)thread->buffer.nodes.pointer; +// stride = sizeof(Node); +// assert(pool == &thread->interned.nodes); +// break; +// } +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// auto key = old_pointer[i]; +// if (key) +// { +// auto hash = *(Hash64*)(buffer + (stride * (key - 1))); +// assert(hash); +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// { +// auto type_index = *(TypeIndex*)&key; +// auto* type = thread_type_get(thread, type_index); +// assert(type->hash == hash); +// } break; +// case INTERN_POOL_KIND_NODE: +// { +// auto node_index = *(NodeIndex*)&key; +// auto* node = thread_node_get(thread, node_index); +// todo(); +// // assert(node->hash == hash); +// // intern_pool_put_node_assume_not_existent_assume_capacity(thread, hash, node_index); +// } break; +// } +// +// } +// } +// +// assert(old_length == pool->length); +// assert(pool->capacity == new_capacity); +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// auto key = old_pointer[i]; +// if (key) +// { +// auto hash = *(Hash64*)(buffer + (stride * (key - 1))); +// assert(hash); +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// { +// auto type_index = *(TypeIndex*)&key; +// unused(type_index); +// trap(); +// } break; +// case INTERN_POOL_KIND_NODE: +// { +// auto node_index = *(NodeIndex*)&key; +// auto* node = thread_node_get(thread, node_index); +// todo(); +// // assert(node->hash == hash); +// // auto result = intern_pool_get_node(thread, node_index, hash); +// // assert(validi(node_index)); +// // assert(index_equal(node_index, result)); +// } break; +// } +// } +// } +// } +// } +// fn u8 node_is_projection(Node* n) +// { +// return (n->id == IR_CONTROL_PROJECTION) | (n->id == IR_PROJECTION); +// } -fn NodeIndex projection_get_control(Thread* thread, Node* node) -{ - assert(node_is_projection(node)); - auto node_index = node_input_get(thread, node, 0); - return node_index; -} +// fn NodeIndex projection_get_control(Thread* thread, Node* node) +// { +// assert(node_is_projection(node)); +// auto node_index = node_input_get(thread, node, 0); +// return node_index; +// } -fn s32 projection_get_index(Node* node) -{ - assert(node_is_projection(node)); +// fn s32 projection_get_index(Node* node) +// { +// assert(node_is_projection(node)); +// +// switch (node->id) +// { +// case IR_CONTROL_PROJECTION: +// return node->control_projection.projection.index; +// case IR_PROJECTION: +// return node->projection.index; +// default: +// trap(); +// } +// } - switch (node->id) - { - case NODE_CONTROL_PROJECTION: - return node->control_projection.projection.index; - case NODE_PROJECTION: - return node->projection.index; - default: - trap(); - } -} +// fn TypeIndex compute_type_projection(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// assert(node_is_projection(node)); +// auto control_node_index = projection_get_control(thread, node); +// auto* control_node = thread_node_get(thread, control_node_index); +// auto* control_type = thread_type_get(thread, control_node->type); +// +// if (control_type->id == TYPE_TUPLE) +// { +// auto index = projection_get_index(node); +// auto type_index = control_type->tuple.types.pointer[index]; +// return type_index; +// } +// else +// { +// return thread->types.bottom; +// } +// } -fn TypeIndex compute_type_projection(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - assert(node_is_projection(node)); - auto control_node_index = projection_get_control(thread, node); - auto* control_node = thread_node_get(thread, control_node_index); - auto* control_type = thread_type_get(thread, control_node->type); - - if (control_type->id == TYPE_TUPLE) - { - auto index = projection_get_index(node); - auto type_index = control_type->tuple.types.pointer[index]; - return type_index; - } - else - { - return thread->types.bottom; - } -} - -fn NodeIndex idealize_control_projection(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - assert(node->id == NODE_CONTROL_PROJECTION); - auto control_node_index = projection_get_control(thread, node); - auto* control_node = thread_node_get(thread, control_node_index); - auto* control_type = thread_type_get(thread, control_node->type); - auto index = node->control_projection.projection.index; - - if (control_type->id == TYPE_TUPLE) - { - if (index_equal(control_type->tuple.types.pointer[index], thread->types.dead_control)) - { - trap(); - } - if (control_node->id == NODE_IF) - { - trap(); - } - } - - if (control_node->id == NODE_IF) - { - trap(); - } - - return invalidi(Node); -} +// fn NodeIndex idealize_control_projection(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// assert(node->id == IR_CONTROL_PROJECTION); +// auto control_node_index = projection_get_control(thread, node); +// auto* control_node = thread_node_get(thread, control_node_index); +// auto* control_type = thread_type_get(thread, control_node->type); +// auto index = node->control_projection.projection.index; +// +// if (control_type->id == TYPE_TUPLE) +// { +// if (index_equal(control_type->tuple.types.pointer[index], thread->types.dead_control)) +// { +// trap(); +// } +// if (control_node->id == IR_IF) +// { +// trap(); +// } +// } +// +// if (control_node->id == IR_IF) +// { +// trap(); +// } +// +// return invalidi(Node); +// } fn NodeIndex return_get_control(Thread* thread, Node* node) { - return node_input_get(thread, node, 0); + return node_get_inputs(thread, node).pointer[0]; } fn NodeIndex return_get_value(Thread* thread, Node* node) { - return node_input_get(thread, node, 1); + return node_get_inputs(thread, node).pointer[1]; } -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 = result - thread->buffer.types.pointer; - auto type_index = Index(Type, buffer_index); - *result = *type; +// 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 type_index = Index(Type, buffer_index); +// *result = *type; +// +// u32 raw_type = *(u32*)&type_index; +// thread->interned.types.pointer[index] = raw_type; +// assert(raw_type); +// thread->interned.types.length += 1; +// +// return type_index; +// } - u32 raw_type = *(u32*)&type_index; - thread->interned.types.pointer[index] = raw_type; - assert(raw_type); - thread->interned.types.length += 1; +// fn TypeIndex intern_pool_put_new_type_assume_not_existent_assume_capacity(Thread* thread, Type* type) +// { +// 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)); +// +// return intern_pool_put_new_type_at_assume_not_existent_assume_capacity(thread, type, index); +// } +// +// typedef enum InternPoolKind +// { +// INTERN_POOL_KIND_TYPE, +// INTERN_POOL_KIND_NODE, +// } InternPoolKind; - return type_index; -} +// [[gnu::hot]] fn s64 intern_pool_find_node_slot(Thread* thread, u32 original_index, NodeIndex node_index) +// { +// assert(validi(node_index)); +// auto it_index = original_index; +// auto existing_capacity = thread->interned.nodes.capacity; +// s64 result = -1; +// // auto* node = thread_node_get(thread, node_index); +// +// for (u32 i = 0; i < existing_capacity; i += 1) +// { +// auto index = it_index & (existing_capacity - 1); +// u32 key = thread->interned.nodes.pointer[index]; +// +// if (key == 0) +// { +// assert(thread->interned.nodes.length < thread->interned.nodes.capacity); +// result = index; +// break; +// } +// else +// { +// NodeIndex existing_node_index = *(NodeIndex*)&key; +// // Exhaustive comparation, shortcircuit when possible +// if (node_index_equal(thread, existing_node_index, node_index)) +// { +// result = index; +// break; +// } +// } +// +// it_index += 1; +// } +// +// return result; +// } -fn TypeIndex intern_pool_put_new_type_assume_not_existent_assume_capacity(Thread* thread, Type* type) -{ - assert(thread->interned.types.length < thread->interned.types.capacity); - Hash64 hash = type->hash; - assert(hash); - auto index = hash & (thread->interned.types.capacity - 1); +// 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 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* pointer_to_slot = &thread->interned.nodes.pointer[slot]; +// node_index = *(NodeIndex*)pointer_to_slot; +// } +// +// return node_index; +// } - return intern_pool_put_new_type_at_assume_not_existent_assume_capacity(thread, type, index); -} +// fn NodeIndex intern_pool_put_node_at_assume_not_existent_assume_capacity(Thread* thread, NodeIndex node, u32 index) +// { +// u32 raw_node = *(u32*)&node; +// assert(raw_node); +// thread->interned.nodes.pointer[index] = raw_node; +// thread->interned.nodes.length += 1; +// +// return node; +// } -typedef enum InternPoolKind -{ - INTERN_POOL_KIND_TYPE, - INTERN_POOL_KIND_NODE, -} InternPoolKind; +// fn NodeIndex intern_pool_put_node_assume_not_existent_assume_capacity(Thread* thread, Hash64 hash, NodeIndex node) +// { +// auto capacity = thread->interned.nodes.capacity; +// assert(thread->interned.nodes.length < capacity); +// auto original_index = cast(u32, u64, hash & (capacity - 1)); +// +// auto slot = intern_pool_find_node_slot(thread, original_index, node); +// if (slot == -1) +// { +// fail(); +// } +// auto index = (u32)slot; +// +// return intern_pool_put_node_at_assume_not_existent_assume_capacity(thread, node, index); +// } -// This assumes the indices are not equal -fn u8 node_equal(Thread* thread, Node* a, Node* b) -{ - u8 result = 0; - assert(a != b); - assert(a->hash); - assert(b->hash); - - if (((a->id == b->id) & (a->hash == b->hash)) & (a->input_count == b->input_count)) - { - auto inputs_a = node_get_inputs(thread, a); - auto inputs_b = node_get_inputs(thread, b); - result = 1; - - for (u16 i = 0; i < a->input_count; i += 1) - { - if (!index_equal(inputs_a.pointer[i], inputs_b.pointer[i])) - { - result = 0; - break; - } - } - - if (result) - { - switch (a->id) - { - case NODE_CONSTANT: - result = index_equal(a->constant.type, b->constant.type); - break; - case NODE_START: - result = a->start.function == b->start.function; - break; - default: - trap(); - } - } - } - - return result; -} - -fn u8 node_index_equal(Thread* thread, NodeIndex a, NodeIndex b) -{ - u8 result = 0; - auto* node_a = thread_node_get(thread, a); - auto* node_b = thread_node_get(thread, b); - result = index_equal(a, b) || node_equal(thread, node_a, node_b); - - return result; -} - -[[gnu::hot]] fn s32 intern_pool_find_node_slot(Thread* thread, u32 original_index, NodeIndex node_index) -{ - assert(validi(node_index)); - auto it_index = original_index; - auto existing_capacity = thread->interned.nodes.capacity; - s32 result = -1; - // auto* node = thread_node_get(thread, node_index); - - for (u32 i = 0; i < existing_capacity; i += 1) - { - auto index = it_index & (existing_capacity - 1); - u32 key = thread->interned.nodes.pointer[index]; - - if (key == 0) - { - assert(thread->interned.nodes.length < thread->interned.nodes.capacity); - result = index; - break; - } - else - { - NodeIndex existing_node_index = *(NodeIndex*)&key; - // Exhaustive comparation, shortcircuit when possible - if (node_index_equal(thread, existing_node_index, node_index)) - { - result = index; - break; - } - } - - it_index += 1; - } - - return result; -} - -fn NodeIndex intern_pool_get_node(Thread* thread, NodeIndex key, Hash64 hash) -{ - auto original_index = hash & (thread->interned.nodes.capacity - 1); - auto slot = intern_pool_find_node_slot(thread, original_index, key); - auto* pointer_to_slot = &thread->interned.nodes.pointer[slot]; - return *(NodeIndex*)pointer_to_slot; -} - -fn NodeIndex intern_pool_put_node_at_assume_not_existent_assume_capacity(Thread* thread, NodeIndex node, u32 index) -{ - u32 raw_node = *(u32*)&node; - assert(raw_node); - thread->interned.nodes.pointer[index] = raw_node; - thread->interned.nodes.length += 1; - - return node; -} - -fn NodeIndex intern_pool_put_node_assume_not_existent_assume_capacity(Thread* thread, Hash64 hash, NodeIndex node) -{ - auto capacity = thread->interned.nodes.capacity; - assert(thread->interned.nodes.length < capacity); - auto original_index = hash & (capacity - 1); - - auto slot = intern_pool_find_node_slot(thread, original_index, node); - if (slot == -1) - { - fail(); - } - auto index = (u32)slot; - - return intern_pool_put_node_at_assume_not_existent_assume_capacity(thread, node, index); -} - -fn void intern_pool_ensure_capacity(InternPool* pool, Thread* thread, u32 additional, InternPoolKind kind) -{ - auto current_capacity = pool->capacity; - auto current_length = pool->length; - auto half_capacity = current_capacity >> 1; - auto destination_length = current_length + additional; - - if (destination_length > half_capacity) - { - u32 new_capacity = MAX(round_up_to_next_power_of_2(destination_length), 32); - u32* new_array = arena_allocate(thread->arena, u32, new_capacity); - memset(new_array, 0, sizeof(u32) * new_capacity); - - auto* old_pointer = pool->pointer; - auto old_capacity = current_capacity; - auto old_length = current_length; - - pool->length = 0; - pool->pointer = new_array; - pool->capacity = new_capacity; - - u8* buffer; - u64 stride; - switch (kind) - { - case INTERN_POOL_KIND_TYPE: - buffer = (u8*)thread->buffer.types.pointer; - stride = sizeof(Type); - assert(pool == &thread->interned.types); - break; - case INTERN_POOL_KIND_NODE: - buffer = (u8*)thread->buffer.nodes.pointer; - stride = sizeof(Node); - assert(pool == &thread->interned.nodes); - break; - } - - for (u32 i = 0; i < old_capacity; i += 1) - { - auto key = old_pointer[i]; - if (key) - { - auto hash = *(Hash64*)(buffer + (stride * (key - 1))); - assert(hash); - switch (kind) - { - case INTERN_POOL_KIND_TYPE: - { - auto type_index = *(TypeIndex*)&key; - auto* type = thread_type_get(thread, type_index); - assert(type->hash == hash); - } break; - case INTERN_POOL_KIND_NODE: - { - auto node_index = *(NodeIndex*)&key; - auto* node = thread_node_get(thread, node_index); - assert(node->hash == hash); - intern_pool_put_node_assume_not_existent_assume_capacity(thread, hash, node_index); - } break; - } - - } - } - - assert(old_length == pool->length); - assert(pool->capacity == new_capacity); - - for (u32 i = 0; i < old_capacity; i += 1) - { - auto key = old_pointer[i]; - if (key) - { - auto hash = *(Hash64*)(buffer + (stride * (key - 1))); - assert(hash); - switch (kind) - { - case INTERN_POOL_KIND_TYPE: - { - auto type_index = *(TypeIndex*)&key; - unused(type_index); - trap(); - } break; - case INTERN_POOL_KIND_NODE: - { - auto node_index = *(NodeIndex*)&key; - auto* node = thread_node_get(thread, node_index); - assert(node->hash == hash); - auto result = intern_pool_get_node(thread, node_index, hash); - assert(validi(node_index)); - assert(index_equal(node_index, result)); - } break; - } - } - } - } -} - -fn TypeIndex intern_pool_put_new_type_assume_not_existent(Thread* thread, Type* type) -{ - intern_pool_ensure_capacity(&thread->interned.types, thread, 1, INTERN_POOL_KIND_TYPE); - return intern_pool_put_new_type_assume_not_existent_assume_capacity(thread, type); -} - -fn s32 intern_pool_find_type_slot(Thread* thread, u32 original_index, Type* type) -{ - auto it_index = original_index; - auto existing_capacity = thread->interned.types.capacity; - s32 result = -1; - - for (u32 i = 0; i < existing_capacity; i += 1) - { - auto index = it_index & (existing_capacity - 1); - u32 key = thread->interned.types.pointer[index]; - - // Not set - if (key == 0) - { - result = index; - break; - } - else - { - TypeIndex existing_type_index = *(TypeIndex*)&key; - Type* existing_type = thread_type_get(thread, existing_type_index); - if (type_equal(existing_type, type)) - { - result = index; - break; - } - } - - it_index += 1; - } - - return result; -} - -fn TypeGetOrPut intern_pool_get_or_put_new_type(Thread* thread, Type* type) -{ - auto existing_capacity = thread->interned.types.capacity; - auto hash = hash_type(thread, type); - auto original_index = hash & (existing_capacity - 1); - - auto slot = intern_pool_find_type_slot(thread, original_index, type); - if (slot != -1) - { - u32 index = slot; - TypeIndex type_index = *(TypeIndex*)&thread->interned.types.pointer[index]; - u8 existing = validi(type_index); - if (!existing) - { - type_index = intern_pool_put_new_type_at_assume_not_existent_assume_capacity(thread, type, index); - } - - return (TypeGetOrPut) { - .index = type_index, - .existing = existing, - }; - } - else - { - if (thread->interned.types.length < existing_capacity) - { - trap(); - } - else if (thread->interned.types.length == existing_capacity) - { - auto result = intern_pool_put_new_type_assume_not_existent(thread, type); - return (TypeGetOrPut) { - .index = result, - .existing = 0, - }; - } - else - { - trap(); - } - } -} +// fn void intern_pool_ensure_capacity(InternPool* pool, Thread* thread, u32 additional, InternPoolKind kind) +// { +// auto current_capacity = pool->capacity; +// auto current_length = pool->length; +// auto half_capacity = current_capacity >> 1; +// auto destination_length = current_length + additional; +// +// if (destination_length > half_capacity) +// { +// auto new_capacity = cast(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); +// +// auto* old_pointer = pool->pointer; +// auto old_capacity = current_capacity; +// auto old_length = current_length; +// +// pool->length = 0; +// pool->pointer = new_array; +// pool->capacity = new_capacity; +// +// u8* buffer; +// u64 stride; +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// buffer = (u8*)thread->buffer.types.pointer; +// stride = sizeof(Type); +// assert(pool == &thread->interned.types); +// break; +// case INTERN_POOL_KIND_NODE: +// buffer = (u8*)thread->buffer.nodes.pointer; +// stride = sizeof(Node); +// assert(pool == &thread->interned.nodes); +// break; +// } +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// auto key = old_pointer[i]; +// if (key) +// { +// auto hash = *(Hash64*)(buffer + (stride * (key - 1))); +// assert(hash); +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// { +// auto type_index = *(TypeIndex*)&key; +// auto* type = thread_type_get(thread, type_index); +// assert(type->hash == hash); +// } break; +// case INTERN_POOL_KIND_NODE: +// { +// auto node_index = *(NodeIndex*)&key; +// auto* node = thread_node_get(thread, node_index); +// todo(); +// // assert(node->hash == hash); +// // intern_pool_put_node_assume_not_existent_assume_capacity(thread, hash, node_index); +// } break; +// } +// +// } +// } +// +// assert(old_length == pool->length); +// assert(pool->capacity == new_capacity); +// +// for (u32 i = 0; i < old_capacity; i += 1) +// { +// auto key = old_pointer[i]; +// if (key) +// { +// auto hash = *(Hash64*)(buffer + (stride * (key - 1))); +// assert(hash); +// switch (kind) +// { +// case INTERN_POOL_KIND_TYPE: +// { +// auto type_index = *(TypeIndex*)&key; +// unused(type_index); +// trap(); +// } break; +// case INTERN_POOL_KIND_NODE: +// { +// auto node_index = *(NodeIndex*)&key; +// auto* node = thread_node_get(thread, node_index); +// todo(); +// // assert(node->hash == hash); +// // auto result = intern_pool_get_node(thread, node_index, hash); +// // assert(validi(node_index)); +// // assert(index_equal(node_index, result)); +// } break; +// } +// } +// } +// } +// } +// +// fn TypeIndex intern_pool_put_new_type_assume_not_existent(Thread* thread, Type* type) +// { +// intern_pool_ensure_capacity(&thread->interned.types, thread, 1, INTERN_POOL_KIND_TYPE); +// return intern_pool_put_new_type_assume_not_existent_assume_capacity(thread, type); +// } +// +// fn s64 intern_pool_find_type_slot(Thread* thread, u32 original_index, Type* type) +// { +// auto it_index = original_index; +// auto existing_capacity = thread->interned.types.capacity; +// s64 result = -1; +// +// for (u32 i = 0; i < existing_capacity; i += 1) +// { +// auto index = it_index & (existing_capacity - 1); +// u32 key = thread->interned.types.pointer[index]; +// +// // Not set +// if (key == 0) +// { +// result = index; +// break; +// } +// else +// { +// TypeIndex existing_type_index = *(TypeIndex*)&key; +// Type* existing_type = thread_type_get(thread, existing_type_index); +// if (type_equal(existing_type, type)) +// { +// result = index; +// break; +// } +// } +// +// it_index += 1; +// } +// +// return result; +// } -fn TypeGetOrPut type_make_tuple(Thread* thread, Slice(TypeIndex) types) -{ - Type type; - memset(&type, 0, sizeof(Type)); - type.tuple = (TypeTuple){ - .types = types, - }; - type.id = TYPE_TUPLE; - auto result = intern_pool_get_or_put_new_type(thread, &type); - return result; -} +// fn s64 intern_pool_find_debug_type_slot(Thread* thread, const DebugType* type, Hash32 hash) +// { +// auto it_index = original_index; +// auto existing_capacity = thread->interned.types.capacity; +// s64 result = -1; +// +// for (u32 i = 0; i < existing_capacity; i += 1) +// { +// auto index = it_index & (existing_capacity - 1); +// u32 key = thread->interned.types.pointer[index]; +// +// // Not set +// if (key == 0) +// { +// result = index; +// break; +// } +// else +// { +// auto existing_type_index = *(DebugTypeIndex*)&key; +// DebugType* existing_type = thread_debug_type_get(thread, existing_type_index); +// auto existing_hash = hash_debug_type(existing_type); +// trap(); +// // if (type_equal(existing_type, type)) +// // { +// // result = index; +// // break; +// // } +// } +// +// it_index += 1; +// } +// +// return result; +// } -fn TypeIndex type_make_tuple_allocate(Thread* thread, Slice(TypeIndex) types) -{ - auto gop = type_make_tuple(thread, types); - // Need to reallocate the type array - if (!gop.existing) - { - auto* type = thread_type_get(thread, gop.index); - assert(type->tuple.types.pointer == types.pointer); - assert(type->tuple.types.length == types.length); - type->tuple.types = arena_allocate_slice(thread->arena, TypeIndex, types.length); - memcpy(type->tuple.types.pointer, types.pointer, sizeof(TypeIndex) * types.length); - } +// 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 type_index = Index(DebugType, buffer_index); +// *result = *type; +// +// u32 raw_type = *(u32*)&type_index; +// thread->interned.types.pointer[index] = raw_type; +// assert(raw_type); +// thread->interned.types.length += 1; +// +// return type_index; +// } - return gop.index; -} +// fn DebugTypeIndex intern_pool_put_new_debug_type_assume_not_existent_assume_capacity(Thread* thread, const DebugType* type, Hash32 hash) +// { +// assert(thread->interned.types.length < thread->interned.types.capacity); +// assert(hash); +// auto index = hash & (thread->interned.types.capacity - 1); +// +// return intern_pool_put_new_debug_type_at_assume_not_existent_assume_capacity(thread, type, index); +// } + +// fn DebugTypeIndex intern_pool_put_new_debug_type_assume_not_existent(Thread* thread, const DebugType* type, Hash32 hash) +// { +// intern_pool_ensure_capacity(&thread->interned.types, thread, 1, INTERN_POOL_KIND_TYPE); +// return intern_pool_put_new_debug_type_assume_not_existent_assume_capacity(thread, type, hash); +// } + +// fn DebugTypeGetOrPut intern_pool_get_or_put_new_debug_type(Thread* thread, const DebugType* type) +// { +// auto existing_capacity = thread->interned.types.capacity; +// auto hash = hash_debug_type(type); +// auto original_index = cast(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 type_index = *(DebugTypeIndex*)&thread->interned.types.pointer[index]; +// u8 existing = validi(type_index); +// if (!existing) +// { +// type_index = intern_pool_put_new_debug_type_at_assume_not_existent_assume_capacity(thread, type, index); +// } +// +// return (DebugTypeGetOrPut) { +// .index = type_index, +// .existing = existing, +// }; +// } +// else +// { +// if (thread->interned.types.length < existing_capacity) +// { +// trap(); +// } +// else if (thread->interned.types.length == existing_capacity) +// { +// auto result = intern_pool_put_new_debug_type_assume_not_existent(thread, type, hash); +// return (DebugTypeGetOrPut) { +// .index = result, +// .existing = 0, +// }; +// } +// else +// { +// trap(); +// } +// } +// } + +// fn TypeGetOrPut intern_pool_get_or_put_new_type(Thread* thread, Type* type) +// { +// auto existing_capacity = thread->interned.types.capacity; +// auto hash = hash_type(thread, type); +// auto original_index = cast(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); +// TypeIndex type_index = *(TypeIndex*)&thread->interned.types.pointer[index]; +// u8 existing = validi(type_index); +// if (!existing) +// { +// type_index = intern_pool_put_new_type_at_assume_not_existent_assume_capacity(thread, type, index); +// } +// +// return (TypeGetOrPut) { +// .index = type_index, +// .existing = existing, +// }; +// } +// else +// { +// if (thread->interned.types.length < existing_capacity) +// { +// trap(); +// } +// else if (thread->interned.types.length == existing_capacity) +// { +// auto result = intern_pool_put_new_type_assume_not_existent(thread, type); +// return (TypeGetOrPut) { +// .index = result, +// .existing = 0, +// }; +// } +// else +// { +// trap(); +// } +// } +// } + + +// fn TypeGetOrPut type_make_tuple(Thread* thread, Slice(TypeIndex) types) +// { +// Type type; +// memset(&type, 0, sizeof(Type)); +// type.tuple = (TypeTuple){ +// .types = types, +// }; +// type.id = TYPE_TUPLE; +// auto result = intern_pool_get_or_put_new_type(thread, &type); +// return result; +// } + +// fn TypeIndex type_make_tuple_allocate(Thread* thread, Slice(TypeIndex) types) +// { +// auto gop = type_make_tuple(thread, types); +// // Need to reallocate the type array +// if (!gop.existing) +// { +// auto* type = thread_type_get(thread, gop.index); +// assert(type->tuple.types.pointer == types.pointer); +// assert(type->tuple.types.length == types.length); +// type->tuple.types = arena_allocate_slice(thread->arena, TypeIndex, types.length); +// memcpy(type->tuple.types.pointer, types.pointer, sizeof(TypeIndex) * types.length); +// } +// +// return gop.index; +// } fn TypeIndex compute_type_return(Thread* thread, NodeIndex node_index) { Node* node = thread_node_get(thread, node_index); auto control_type = thread_node_get(thread, return_get_control(thread, node))->type; + unused(control_type); auto return_type = thread_node_get(thread, return_get_value(thread, node))->type; - Slice(TypeIndex) types = array_to_slice(((TypeIndex[]) { - control_type, - return_type, - })); - auto result = type_make_tuple_allocate(thread, types); - return result; + unused(return_type); + todo(); + // Slice(TypeIndex) types = array_to_slice(((TypeIndex[]) { + // control_type, + // return_type, + // })); + // auto result = type_make_tuple_allocate(thread, types); + // return result; } fn NodeIndex idealize_return(Thread* thread, NodeIndex node_index) @@ -2144,268 +3231,272 @@ fn NodeIndex idealize_return(Thread* thread, NodeIndex node_index) auto* node = thread_node_get(thread, node_index); auto control_node_index = return_get_control(thread, node); auto* control_node = thread_node_get(thread, control_node_index); - if (index_equal(control_node->type, thread->types.dead_control)) - { - return control_node_index; - } - else - { - return invalidi(Node); - } + unused(control_node); + // if (index_equal(control_node->type, thread->types.dead_control)) + // { + // return control_node_index; + // } + // else + // { + // return invalidi(Node); + // } + todo(); } -fn TypeIndex compute_type_dead_control(Thread* thread, NodeIndex node_index) -{ - unused(node_index); - return thread->types.dead_control; -} +// fn TypeIndex compute_type_dead_control(Thread* thread, NodeIndex node_index) +// { +// unused(node_index); +// return thread->types.dead_control; +// } -fn TypeIndex compute_type_bottom(Thread* thread, NodeIndex node_index) -{ - unused(node_index); - return thread->types.bottom; -} +// fn TypeIndex compute_type_bottom(Thread* thread, NodeIndex node_index) +// { +// unused(node_index); +// return thread->types.bottom; +// } -fn NodeIndex idealize_stop(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - auto original_input_count = node->input_count; - for (u16 i = 0; i < node->input_count; i += 1) - { - auto input_node_index = node_input_get(thread, node, i); - auto* input_node = thread_node_get(thread, input_node_index); - if (index_equal(input_node->type, thread->types.dead_control)) - { - trap(); - } - } +// fn NodeIndex idealize_stop(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// auto original_input_count = node->input_count; +// for (u16 i = 0; i < node->input_count; i += 1) +// { +// auto input_node_index = node_input_get(thread, node, i); +// auto* input_node = thread_node_get(thread, input_node_index); +// if (index_equal(input_node->type, thread->types.dead_control)) +// { +// trap(); +// } +// } +// +// if (node->input_count != original_input_count) +// { +// return node_index; +// } +// else +// { +// return invalidi(Node); +// } +// } - if (node->input_count != original_input_count) - { - return node_index; - } - else - { - return invalidi(Node); - } -} +// fn TypeIndex compute_type_start(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// unused(node); +// todo(); +// // return node->start.arguments; +// } -fn TypeIndex compute_type_start(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - return node->start.arguments; -} +// fn u8 type_is_constant(Type* type) +// { +// switch (type->id) +// { +// case TYPE_INTEGER: +// return type->integer.is_constant; +// default: +// return 0; +// } +// } -fn u8 type_is_constant(Type* type) -{ - switch (type->id) - { - case TYPE_INTEGER: - return type->integer.is_constant; - default: - return 0; - } -} +// fn u8 type_is_simple(Type* type) +// { +// return type->id <= TYPE_DEAD_CONTROL; +// } -fn u8 type_is_simple(Type* type) -{ - return type->id <= TYPE_DEAD_CONTROL; -} +// fn TypeIndex type_meet(Thread* thread, TypeIndex a, TypeIndex b) +// { +// TypeIndex result = invalidi(Type); +// if (index_equal(a, b)) +// { +// result = a; +// } +// else +// { +// Type* a_type = thread_type_get(thread, a); +// Type* b_type = thread_type_get(thread, b); +// TypeIndex left = invalidi(Type); +// TypeIndex right = invalidi(Type); +// +// assert(a_type != b_type); +// if (a_type->id == b_type->id) +// { +// left = a; +// right = b; +// } +// else if (type_is_simple(a_type)) +// { +// left = a; +// right = b; +// } +// else if (type_is_simple(b_type)) +// { +// trap(); +// } +// else +// { +// result = thread->types.bottom; +// } +// +// assert(!!validi(left) == !!validi(right)); +// assert((validi(left) & validi(right)) | (validi(result))); +// +// if (validi(left)) +// { +// assert(!validi(result)); +// auto* left_type = thread_type_get(thread, left); +// auto* right_type = thread_type_get(thread, right); +// +// switch (left_type->id) +// { +// case TYPE_INTEGER: +// { +// // auto integer_bot = thread->types.integer.bottom; +// // auto integer_top = thread->types.integer.top; +// // if (index_equal(left, integer_bot)) +// // { +// // result = left; +// // } +// // else if (index_equal(right, integer_bot)) +// // { +// // result = right; +// // } +// // else if (index_equal(right, integer_top)) +// // { +// // result = left; +// // } +// // else if (index_equal(left, integer_top)) +// // { +// // result = right; +// // } +// // else +// // { +// // result = integer_bot; +// // } +// if (left_type->integer.bit_count == right_type->integer.bit_count) +// { +// todo(); +// } +// else +// { +// if ((!left_type->integer.is_constant & !!left_type->integer.bit_count) & (right_type->integer.is_constant & !right_type->integer.bit_count)) +// { +// result = left; +// } +// else if ((left_type->integer.is_constant & !left_type->integer.bit_count) & (!right_type->integer.is_constant & !!right_type->integer.bit_count)) +// { +// trap(); +// } +// } +// } break; +// case TYPE_BOTTOM: +// { +// assert(type_is_simple(left_type)); +// if ((left_type->id == TYPE_BOTTOM) | (right_type->id == TYPE_TOP)) +// { +// result = left; +// } +// else if ((left_type->id == TYPE_TOP) | (right_type->id == TYPE_BOTTOM)) +// { +// result = right; +// } +// else if (!type_is_simple(right_type)) +// { +// result = thread->types.bottom; +// } +// else if (left_type->id == TYPE_LIVE_CONTROL) +// { +// result = thread->types.live_control; +// } +// else +// { +// result = thread->types.dead_control; +// } +// } break; +// default: +// trap(); +// } +// } +// } +// +// assert(validi(result)); +// +// return result; +// } -fn TypeIndex type_meet(Thread* thread, TypeIndex a, TypeIndex b) -{ - TypeIndex result = invalidi(Type); - if (index_equal(a, b)) - { - result = a; - } - else - { - Type* a_type = thread_type_get(thread, a); - Type* b_type = thread_type_get(thread, b); - TypeIndex left = invalidi(Type); - TypeIndex right = invalidi(Type); +// fn u8 type_is_a(Thread* thread, TypeIndex a, TypeIndex b) +// { +// auto m = type_meet(thread, a, b); +// return index_equal(m, b); +// } - assert(a_type != b_type); - if (a_type->id == b_type->id) - { - left = a; - right = b; - } - else if (type_is_simple(a_type)) - { - left = a; - right = b; - } - else if (type_is_simple(b_type)) - { - trap(); - } - else - { - result = thread->types.bottom; - } - - assert(!!validi(left) == !!validi(right)); - assert((validi(left) & validi(right)) | (validi(result))); - - if (validi(left)) - { - assert(!validi(result)); - auto* left_type = thread_type_get(thread, left); - auto* right_type = thread_type_get(thread, right); - - switch (left_type->id) - { - case TYPE_INTEGER: - { - // auto integer_bot = thread->types.integer.bottom; - // auto integer_top = thread->types.integer.top; - // if (index_equal(left, integer_bot)) - // { - // result = left; - // } - // else if (index_equal(right, integer_bot)) - // { - // result = right; - // } - // else if (index_equal(right, integer_top)) - // { - // result = left; - // } - // else if (index_equal(left, integer_top)) - // { - // result = right; - // } - // else - // { - // result = integer_bot; - // } - if (left_type->integer.bit_count == right_type->integer.bit_count) - { - todo(); - } - else - { - if ((!left_type->integer.is_constant & !!left_type->integer.bit_count) & (right_type->integer.is_constant & !right_type->integer.bit_count)) - { - result = left; - } - else if ((left_type->integer.is_constant & !left_type->integer.bit_count) & (!right_type->integer.is_constant & !!right_type->integer.bit_count)) - { - trap(); - } - } - } break; - case TYPE_BOTTOM: - { - assert(type_is_simple(left_type)); - if ((left_type->id == TYPE_BOTTOM) | (right_type->id == TYPE_TOP)) - { - result = left; - } - else if ((left_type->id == TYPE_TOP) | (right_type->id == TYPE_BOTTOM)) - { - result = right; - } - else if (!type_is_simple(right_type)) - { - result = thread->types.bottom; - } - else if (left_type->id == TYPE_LIVE_CONTROL) - { - result = thread->types.live_control; - } - else - { - result = thread->types.dead_control; - } - } break; - default: - trap(); - } - } - } - - assert(validi(result)); - - return result; -} - -fn u8 type_is_a(Thread* thread, TypeIndex a, TypeIndex b) -{ - auto m = type_meet(thread, a, b); - return index_equal(m, b); -} - -fn TypeIndex compute_type_integer_binary(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - auto* left = thread_node_get(thread, inputs.pointer[1]); - auto* right = thread_node_get(thread, inputs.pointer[2]); - assert(!node_is_dead(left)); - assert(!node_is_dead(right)); - auto* left_type = thread_type_get(thread, left->type); - auto* right_type = thread_type_get(thread, right->type); - - if (((left_type->id == TYPE_INTEGER) & (right_type->id == TYPE_INTEGER)) & (type_is_constant(left_type) & type_is_constant(right_type))) - { - auto left_value = left_type->integer.constant; - auto right_value = right_type->integer.constant; - assert(left_type->integer.bit_count == 0); - assert(right_type->integer.bit_count == 0); - assert(!left_type->integer.is_signed); - assert(!right_type->integer.is_signed); - - u64 result; - TypeInteger type_integer = left_type->integer; - - switch (node->id) - { - case NODE_INTEGER_ADD: - result = left_value + right_value; - break; - case NODE_INTEGER_SUBSTRACT: - result = left_value - right_value; - break; - case NODE_INTEGER_MULTIPLY: - result = left_value * right_value; - break; - case NODE_INTEGER_SIGNED_DIVIDE: - result = left_value * right_value; - break; - case NODE_INTEGER_AND: - result = left_value & right_value; - break; - case NODE_INTEGER_OR: - result = left_value | right_value; - break; - case NODE_INTEGER_XOR: - result = left_value ^ right_value; - break; - case NODE_INTEGER_SIGNED_SHIFT_LEFT: - result = left_value << right_value; - break; - case NODE_INTEGER_SIGNED_SHIFT_RIGHT: - result = left_value >> right_value; - break; - default: - trap(); - } - - type_integer.constant = result; - - auto new_type = thread_get_integer_type(thread, type_integer); - return new_type; - } - else - { - auto result = type_meet(thread, left->type, right->type); - return result; - } -} +// fn TypeIndex compute_type_integer_binary(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// auto* left = thread_node_get(thread, inputs.pointer[1]); +// auto* right = thread_node_get(thread, inputs.pointer[2]); +// assert(!node_is_dead(left)); +// assert(!node_is_dead(right)); +// auto* left_type = thread_type_get(thread, left->type); +// auto* right_type = thread_type_get(thread, right->type); +// +// if (((left_type->id == TYPE_INTEGER) & (right_type->id == TYPE_INTEGER)) & (type_is_constant(left_type) & type_is_constant(right_type))) +// { +// auto left_value = left_type->integer.constant; +// auto right_value = right_type->integer.constant; +// assert(left_type->integer.bit_count == 0); +// assert(right_type->integer.bit_count == 0); +// assert(!left_type->integer.is_signed); +// assert(!right_type->integer.is_signed); +// +// u64 result; +// TypeInteger type_integer = left_type->integer; +// +// switch (node->id) +// { +// case IR_INTEGER_ADD: +// result = left_value + right_value; +// break; +// case IR_INTEGER_SUBSTRACT: +// result = left_value - right_value; +// break; +// case IR_INTEGER_MULTIPLY: +// result = left_value * right_value; +// break; +// case IR_INTEGER_SIGNED_DIVIDE: +// result = left_value * right_value; +// break; +// case IR_INTEGER_AND: +// result = left_value & right_value; +// break; +// case IR_INTEGER_OR: +// result = left_value | right_value; +// break; +// case IR_INTEGER_XOR: +// result = left_value ^ right_value; +// break; +// case IR_INTEGER_SIGNED_SHIFT_LEFT: +// result = left_value << right_value; +// break; +// case IR_INTEGER_SIGNED_SHIFT_RIGHT: +// result = left_value >> right_value; +// break; +// default: +// trap(); +// } +// +// type_integer.constant = result; +// +// auto new_type = thread_get_integer_type(thread, type_integer); +// return new_type; +// } +// else +// { +// auto result = type_meet(thread, left->type, right->type); +// return result; +// } +// } global const TypeVirtualTable type_functions[TYPE_COUNT] = { [TYPE_BOTTOM] = { .get_hash = &type_get_hash_default }, @@ -2417,90 +3508,90 @@ global const TypeVirtualTable type_functions[TYPE_COUNT] = { }; global const NodeVirtualTable node_functions[NODE_COUNT] = { - [NODE_START] = { - .compute_type = &compute_type_start, - .idealize = &idealize_null, - .get_hash = &node_get_hash_default, - }, - [NODE_STOP] = { - .compute_type = &compute_type_bottom, - .idealize = &idealize_stop, - .get_hash = &node_get_hash_default, - }, - [NODE_CONTROL_PROJECTION] = { - .compute_type = &compute_type_projection, - .idealize = &idealize_control_projection, - .get_hash = &node_get_hash_control_projection, - }, - [NODE_DEAD_CONTROL] = { - .compute_type = &compute_type_dead_control, - .idealize = &idealize_null, - .get_hash = &node_get_hash_default, - }, - [NODE_RETURN] = { + // [NODE_START] = { + // .compute_type = &compute_type_start, + // .idealize = &idealize_null, + // .get_hash = &node_get_hash_default, + // }, + // [NODE_STOP] = { + // .compute_type = &compute_type_bottom, + // .idealize = &idealize_stop, + // .get_hash = &node_get_hash_default, + // }, + // [NODE_CONTROL_PROJECTION] = { + // .compute_type = &compute_type_projection, + // .idealize = &idealize_control_projection, + // .get_hash = &node_get_hash_control_projection, + // }, + // [NODE_DEAD_CONTROL] = { + // .compute_type = &compute_type_dead_control, + // .idealize = &idealize_null, + // .get_hash = &node_get_hash_default, + // }, + [IR_RETURN] = { .compute_type = &compute_type_return, .idealize = &idealize_return, .get_hash = &node_get_hash_default, }, - [NODE_PROJECTION] = { - .compute_type = &compute_type_projection, - .idealize = &idealize_null, - .get_hash = &node_get_hash_projection, - }, - [NODE_SCOPE] = { - .compute_type = &compute_type_bottom, - .idealize = &idealize_null, - .get_hash = &node_get_hash_scope, - }, + // [NODE_PROJECTION] = { + // .compute_type = &compute_type_projection, + // .idealize = &idealize_null, + // .get_hash = &node_get_hash_projection, + // }, + // [NODE_SCOPE] = { + // .compute_type = &compute_type_bottom, + // .idealize = &idealize_null, + // .get_hash = &node_get_hash_scope, + // }, // Integer operations - [NODE_INTEGER_ADD] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_SUBSTRACT] = { - .compute_type = &compute_type_integer_binary, - .idealize = &node_idealize_substract, - .get_hash = &node_get_hash_default, - }, - [NODE_INTEGER_SIGNED_DIVIDE] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_MULTIPLY] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_AND] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_OR] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_XOR] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_SIGNED_SHIFT_LEFT] = { - .compute_type = &compute_type_integer_binary, - }, - [NODE_INTEGER_SIGNED_SHIFT_RIGHT] = { - .compute_type = &compute_type_integer_binary, - }, - - [NODE_INTEGER_COMPARE_EQUAL] = { - .compute_type = &compute_type_integer_binary, - .idealize = &node_idealize_compare, - .get_hash = &node_get_hash_default, - }, - [NODE_INTEGER_COMPARE_NOT_EQUAL] = { - .compute_type = &compute_type_integer_binary, - .idealize = &node_idealize_compare, - .get_hash = &node_get_hash_default, - }, - - // Constant - [NODE_CONSTANT] = { - .compute_type = &compute_type_constant, - .idealize = &idealize_null, - .get_hash = &node_get_hash_constant, - }, + // [NODE_INTEGER_ADD] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_SUBSTRACT] = { + // .compute_type = &compute_type_integer_binary, + // .idealize = &node_idealize_substract, + // .get_hash = &node_get_hash_default, + // }, + // [NODE_INTEGER_SIGNED_DIVIDE] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_MULTIPLY] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_AND] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_OR] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_XOR] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_SIGNED_SHIFT_LEFT] = { + // .compute_type = &compute_type_integer_binary, + // }, + // [NODE_INTEGER_SIGNED_SHIFT_RIGHT] = { + // .compute_type = &compute_type_integer_binary, + // }, + // + // [NODE_INTEGER_COMPARE_EQUAL] = { + // .compute_type = &compute_type_integer_binary, + // .idealize = &node_idealize_compare, + // .get_hash = &node_get_hash_default, + // }, + // [NODE_INTEGER_COMPARE_NOT_EQUAL] = { + // .compute_type = &compute_type_integer_binary, + // .idealize = &node_idealize_compare, + // .get_hash = &node_get_hash_default, + // }, + // + // // Constant + // [NODE_CONSTANT] = { + // .compute_type = &compute_type_constant, + // .idealize = &idealize_null, + // .get_hash = &node_get_hash_constant, + // }, }; may_be_unused fn String type_id_to_string(Type* type) @@ -2517,6 +3608,7 @@ may_be_unused fn String type_id_to_string(Type* type) } } + fn Hash64 hash_type(Thread* thread, Type* type) { Hash64 hash = type->hash; @@ -2533,165 +3625,158 @@ fn Hash64 hash_type(Thread* thread, Type* type) return hash; } -fn NodeIndex intern_pool_put_node_assume_not_existent(Thread* thread, Hash64 hash, NodeIndex node) -{ - intern_pool_ensure_capacity(&thread->interned.nodes, thread, 1, INTERN_POOL_KIND_NODE); - return intern_pool_put_node_assume_not_existent_assume_capacity(thread, hash, node); -} +// fn NodeIndex intern_pool_put_node_assume_not_existent(Thread* thread, Hash64 hash, NodeIndex node) +// { +// intern_pool_ensure_capacity(&thread->interned.nodes, thread, 1, INTERN_POOL_KIND_NODE); +// return intern_pool_put_node_assume_not_existent_assume_capacity(thread, hash, node); +// } -struct NodeGetOrPut -{ - NodeIndex index; - u8 existing; -}; -typedef struct NodeGetOrPut NodeGetOrPut; +// fn Hash64 hash_node(Thread* thread, Node* node, NodeIndex node_index) +// { +// auto hash = node->hash; +// if (!hash) +// { +// hash = fnv_offset; +// hash = node_functions[node->id].get_hash(thread, node, node_index, hash); +// // print("[HASH #{u32}] Received hash from callback: {u64:x}\n", node_index.index, hash); +// +// hash = hash_byte(hash, node->id); +// +// auto inputs = node_get_inputs(thread, node); +// for (u32 i = 0; i < inputs.length; i += 1) +// { +// auto input_index = inputs.pointer[i]; +// if (validi(input_index)) +// { +// for (u8* it = (u8*)&input_index; it < (u8*)(&input_index + 1); it += 1) +// { +// hash = hash_byte(hash, *it); +// } +// } +// } +// +// // print("[HASH] Node #{u32}, {s}: {u64:x}\n", node_index.index, node_id_to_string(node), hash); +// +// node->hash = hash; +// } +// +// assert(hash); +// +// return hash; +// } -fn Hash64 hash_node(Thread* thread, Node* node, NodeIndex node_index) -{ - auto hash = node->hash; - if (!hash) - { - hash = fnv_offset; - hash = node_functions[node->id].get_hash(thread, node, node_index, hash); - // print("[HASH #{u32}] Received hash from callback: {u64:x}\n", node_index.index, hash); +// fn NodeGetOrPut intern_pool_get_or_put_node(Thread* thread, NodeIndex node_index) +// { +// assert(thread->interned.nodes.length <= thread->interned.nodes.capacity); +// auto existing_capacity = thread->interned.nodes.capacity; +// auto* node = &thread->buffer.nodes.pointer[geti(node_index)]; +// auto hash = hash_node(thread, node, node_index); +// auto original_index = hash & (existing_capacity - 1); +// +// auto slot = intern_pool_find_node_slot(thread, original_index, node_index); +// if (slot != -1) +// { +// u32 index = slot; +// auto* existing_ptr = &thread->interned.nodes.pointer[index]; +// NodeIndex existing_value = *(NodeIndex*)existing_ptr; +// u8 existing = validi(existing_value); +// NodeIndex new_value = existing_value; +// if (!existing) +// { +// assert(thread->interned.nodes.length < thread->interned.nodes.capacity); +// new_value = intern_pool_put_node_at_assume_not_existent_assume_capacity(thread, node_index, index); +// assert(!index_equal(new_value, existing_value)); +// assert(index_equal(new_value, node_index)); +// } +// return (NodeGetOrPut) { +// .index = new_value, +// .existing = existing, +// }; +// } +// else +// { +// if (thread->interned.nodes.length < existing_capacity) +// { +// trap(); +// } +// else if (thread->interned.nodes.length == existing_capacity) +// { +// auto result = intern_pool_put_node_assume_not_existent(thread, hash, node_index); +// return (NodeGetOrPut) { +// .index = result, +// .existing = 0, +// }; +// } +// else +// { +// trap(); +// } +// } +// } - hash = hash_byte(hash, node->id); - - auto inputs = node_get_inputs(thread, node); - for (u32 i = 0; i < inputs.length; i += 1) - { - auto input_index = inputs.pointer[i]; - if (validi(input_index)) - { - for (u8* it = (u8*)&input_index; it < (u8*)(&input_index + 1); it += 1) - { - hash = hash_byte(hash, *it); - } - } - } - - // print("[HASH] Node #{u32}, {s}: {u64:x}\n", node_index.index, node_id_to_string(node), hash); - - node->hash = hash; - } - - assert(hash); - - return hash; -} - -fn NodeGetOrPut intern_pool_get_or_put_node(Thread* thread, NodeIndex node_index) -{ - assert(thread->interned.nodes.length <= thread->interned.nodes.capacity); - auto existing_capacity = thread->interned.nodes.capacity; - auto* node = &thread->buffer.nodes.pointer[geti(node_index)]; - auto hash = hash_node(thread, node, node_index); - auto original_index = hash & (existing_capacity - 1); - - auto slot = intern_pool_find_node_slot(thread, original_index, node_index); - if (slot != -1) - { - u32 index = slot; - auto* existing_ptr = &thread->interned.nodes.pointer[index]; - NodeIndex existing_value = *(NodeIndex*)existing_ptr; - u8 existing = validi(existing_value); - NodeIndex new_value = existing_value; - if (!existing) - { - assert(thread->interned.nodes.length < thread->interned.nodes.capacity); - new_value = intern_pool_put_node_at_assume_not_existent_assume_capacity(thread, node_index, index); - assert(!index_equal(new_value, existing_value)); - assert(index_equal(new_value, node_index)); - } - return (NodeGetOrPut) { - .index = new_value, - .existing = existing, - }; - } - else - { - if (thread->interned.nodes.length < existing_capacity) - { - trap(); - } - else if (thread->interned.nodes.length == existing_capacity) - { - auto result = intern_pool_put_node_assume_not_existent(thread, hash, node_index); - return (NodeGetOrPut) { - .index = result, - .existing = 0, - }; - } - else - { - trap(); - } - } -} - -fn NodeIndex intern_pool_remove_node(Thread* thread, NodeIndex node_index) -{ - auto existing_capacity = thread->interned.nodes.capacity; - auto* node = thread_node_get(thread, node_index); - auto hash = hash_node(thread, node, node_index); - - auto original_index = hash & (existing_capacity - 1); - auto slot = intern_pool_find_node_slot(thread, original_index, node_index); - - if (slot != -1) - { - auto i = (u32)slot; - auto* slot_pointer = &thread->interned.nodes.pointer[i]; - auto old_node_index = *(NodeIndex*)slot_pointer; - assert(validi(old_node_index)); - thread->interned.nodes.length -= 1; - *slot_pointer = 0; - - auto j = i; - - while (1) - { - j = (j + 1) & (existing_capacity - 1); - - auto existing = thread->interned.nodes.pointer[j]; - if (existing == 0) - { - break; - } - - auto existing_node_index = *(NodeIndex*)&existing; - auto* existing_node = thread_node_get(thread, existing_node_index); - auto existing_node_hash = hash_node(thread, existing_node, existing_node_index); - auto k = existing_node_hash & (existing_capacity - 1); - - if (i <= j) - { - if ((i < k) & (k <= j)) - { - continue; - } - } - else - { - if ((k <= j) | (i < k)) - { - continue; - } - } - - thread->interned.nodes.pointer[i] = thread->interned.nodes.pointer[j]; - thread->interned.nodes.pointer[j] = 0; - - i = j; - } - - return old_node_index; - } - else - { - trap(); - } -} +// fn NodeIndex intern_pool_remove_node(Thread* thread, NodeIndex node_index) +// { +// auto existing_capacity = thread->interned.nodes.capacity; +// auto* node = thread_node_get(thread, node_index); +// 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); +// +// if (slot != -1) +// { +// auto i = (u32)slot; +// auto* slot_pointer = &thread->interned.nodes.pointer[i]; +// auto old_node_index = *(NodeIndex*)slot_pointer; +// assert(validi(old_node_index)); +// thread->interned.nodes.length -= 1; +// *slot_pointer = 0; +// +// auto j = i; +// +// while (1) +// { +// j = (j + 1) & (existing_capacity - 1); +// +// auto existing = thread->interned.nodes.pointer[j]; +// if (existing == 0) +// { +// break; +// } +// +// auto existing_node_index = *(NodeIndex*)&existing; +// auto* existing_node = thread_node_get(thread, existing_node_index); +// auto existing_node_hash = hash_node(thread, existing_node, existing_node_index); +// auto k = existing_node_hash & (existing_capacity - 1); +// +// if (i <= j) +// { +// if ((i < k) & (k <= j)) +// { +// continue; +// } +// } +// else +// { +// if ((k <= j) | (i < k)) +// { +// continue; +// } +// } +// +// thread->interned.nodes.pointer[i] = thread->interned.nodes.pointer[j]; +// thread->interned.nodes.pointer[j] = 0; +// +// i = j; +// } +// +// return old_node_index; +// } +// else +// { +// trap(); +// } +// } struct Parser { @@ -2718,7 +3803,8 @@ typedef struct Parser Parser; if (new_line) { - parser->column = index + 1; + // TODO: is this a bug? + parser->column = cast(u32, u64, index + 1); } if (!is_space(ch, get_next_ch_safe(src, parser->i))) @@ -2752,11 +3838,10 @@ typedef struct Parser Parser; [[gnu::hot]] fn void expect_character(Parser* parser, String src, u8 expected_ch) { u64 index = parser->i; - if (expect(index < src.length, 1)) + if (likely(index < src.length)) { u8 ch = src.pointer[index]; - u64 matches = ch == expected_ch; - expect(matches, 1); + auto matches = cast(u64, s64, likely(ch == expected_ch)); parser->i += matches; if (!matches) { @@ -2786,18 +3871,17 @@ typedef struct Parser Parser; u64 is_valid_identifier_start = is_identifier_start(identifier_start_ch); parser->i += is_valid_identifier_start; - if (expect(is_valid_identifier_start, 1)) + if (likely(is_valid_identifier_start)) { while (parser->i < src.length) { u8 ch = src.pointer[parser->i]; - u64 is_identifier = is_identifier_ch(ch); - expect(is_identifier, 1); + auto is_identifier = cast(u64, s64, likely(is_identifier_ch(ch))); parser->i += is_identifier; if (!is_identifier) { - if (expect(is_string_literal, 0)) + if (unlikely(is_string_literal)) { expect_character(parser, src, '"'); } @@ -2828,21 +3912,21 @@ typedef struct Parser Parser; #define pointer_sign '*' -fn void thread_add_job(Thread* thread, NodeIndex node_index) -{ - unused(thread); - unused(node_index); - trap(); -} +// fn void thread_add_job(Thread* thread, NodeIndex node_index) +// { +// unused(thread); +// unused(node_index); +// trap(); +// } -fn void thread_add_jobs(Thread* thread, Slice(NodeIndex) nodes) -{ - for (u32 i = 0; i < nodes.length; i += 1) - { - NodeIndex node_index = nodes.pointer[i]; - thread_add_job(thread, node_index); - } -} +// fn void thread_add_jobs(Thread* thread, Slice(NodeIndex) nodes) +// { +// for (u32 i = 0; i < nodes.length; i += 1) +// { +// NodeIndex node_index = nodes.pointer[i]; +// thread_add_job(thread, node_index); +// } +// } union NodePair { @@ -2855,16 +3939,16 @@ union NodePair }; typedef union NodePair NodePair; -fn NodeIndex node_keep(Thread* thread, NodeIndex node_index) -{ - return node_add_output(thread, node_index, invalidi(Node)); -} +// fn NodeIndex node_keep(Thread* thread, NodeIndex node_index) +// { +// return node_add_output(thread, node_index, invalidi(Node)); +// } -fn NodeIndex node_unkeep(Thread* thread, NodeIndex node_index) -{ - node_remove_output(thread, node_index, invalidi(Node)); - return node_index; -} +// fn NodeIndex node_unkeep(Thread* thread, NodeIndex node_index) +// { +// node_remove_output(thread, node_index, invalidi(Node)); +// return node_index; +// } fn NodeIndex dead_code_elimination(Thread* thread, NodePair nodes) { @@ -2875,64 +3959,68 @@ fn NodeIndex dead_code_elimination(Thread* thread, NodePair nodes) { // print("[DCE] old: #{u32} != new: #{u32}. Proceeding to eliminate\n", old.index, new.index); auto* old_node = thread_node_get(thread, old); - if (node_is_unused(old_node) & !node_is_dead(old_node)) - { - node_keep(thread, new); - node_kill(thread, old); - node_unkeep(thread, new); - } + unused(old_node); + todo(); + // if (node_is_unused(old_node) & !node_is_dead(old_node)) + // { + // node_keep(thread, new); + // todo(); + // // node_kill(thread, old); + // // node_unkeep(thread, new); + // } } return new; } -fn u8 type_is_high_or_const(Thread* thread, TypeIndex type_index) -{ - u8 result = index_equal(type_index, thread->types.top) | index_equal(type_index, thread->types.dead_control); - if (!result) - { - Type* type = thread_type_get(thread, type_index); - switch (type->id) - { - case TYPE_INTEGER: - result = type->integer.is_constant | ((type->integer.constant == 0) & (type->integer.bit_count == 0)); - break; - default: - break; - } - } +// fn u8 type_is_high_or_const(Thread* thread, TypeIndex type_index) +// { +// u8 result = index_equal(type_index, thread->types.top) | index_equal(type_index, thread->types.dead_control); +// if (!result) +// { +// Type* type = thread_type_get(thread, type_index); +// switch (type->id) +// { +// case TYPE_INTEGER: +// result = type->integer.is_constant | ((type->integer.constant == 0) & (type->integer.bit_count == 0)); +// break; +// default: +// break; +// } +// } +// +// return result; +// } - return result; -} +// fn TypeIndex type_join(Thread* thread, TypeIndex a, TypeIndex b) +// { +// TypeIndex result; +// if (index_equal(a, b)) +// { +// result = a; +// } +// else +// { +// unused(thread); +// trap(); +// } +// +// return result; +// } -fn TypeIndex type_join(Thread* thread, TypeIndex a, TypeIndex b) -{ - TypeIndex result; - if (index_equal(a, b)) - { - result = a; - } - else - { - unused(thread); - trap(); - } - - return result; -} - -fn void node_set_type(Thread* thread, Node* node, TypeIndex new_type) -{ - auto old_type = node->type; - assert(!validi(old_type) || type_is_a(thread, new_type, old_type)); - if (!index_equal(old_type, new_type)) - { - node->type = new_type; - auto outputs = node_get_outputs(thread, node); - thread_add_jobs(thread, outputs); - move_dependencies_to_worklist(thread, node); - } -} +// fn void node_set_type(Thread* thread, Node* node, TypeIndex new_type) +// { +// todo(); +// // auto old_type = node->type; +// // assert(!validi(old_type) || type_is_a(thread, new_type, old_type)); +// // if (!index_equal(old_type, new_type)) +// // { +// // node->type = new_type; +// // auto outputs = node_get_outputs(thread, node); +// // thread_add_jobs(thread, outputs); +// // // move_dependencies_to_worklist(thread, node); +// // } +// } global auto enable_peephole = 1; @@ -2944,67 +4032,72 @@ fn NodeIndex peephole_optimize(Thread* thread, Function* function, NodeIndex nod // print("Peepholing node #{u32} ({s})\n", node_index.index, node_id_to_string(node)); auto old_type = node->type; auto new_type = node_functions[node->id].compute_type(thread, node_index); + unused(new_type); + unused(old_type); if (enable_peephole) { - thread->iteration.total += 1; - node_set_type(thread, node, new_type); - - if (node->id != NODE_CONSTANT && node->id != NODE_DEAD_CONTROL && type_is_high_or_const(thread, node->type)) - { - if (index_equal(node->type, thread->types.dead_control)) - { - trap(); - } - else - { - auto constant_node = constant_int_create_with_type(thread, function, node->type); - return constant_node; - } - } - - auto idealize = 1; - if (!node->hash) - { - auto gop = intern_pool_get_or_put_node(thread, node_index); - idealize = !gop.existing; - - if (gop.existing) - { - auto interned_node_index = gop.index; - auto* interned_node = thread_node_get(thread, interned_node_index); - auto new_type = type_join(thread, interned_node->type, node->type); - node_set_type(thread, interned_node, new_type); - node->hash = 0; - // print("[peephole_optimize] Eliminating #{u32} because an existing node was found: #{u32}\n", node_index.index, interned_node_index.index); - auto dce_node = dead_code_elimination(thread, (NodePair) { - .old = node_index, - .new = interned_node_index, - }); - - result = dce_node; - } - } - - if (idealize) - { - auto idealized_node = node_functions[node->id].idealize(thread, node_index); - if (validi(idealized_node)) - { - result = idealized_node; - } - else - { - u64 are_types_equal = index_equal(new_type, old_type); - thread->iteration.nop += are_types_equal; - - result = are_types_equal ? invalidi(Node) : node_index; - } - } + unused(function); + // thread->iteration.total += 1; + // node_set_type(thread, node, new_type); + // + // if (node->id != NODE_CONSTANT && node->id != NODE_DEAD_CONTROL && type_is_high_or_const(thread, node->type)) + // { + // if (index_equal(node->type, thread->types.dead_control)) + // { + // trap(); + // } + // else + // { + // auto constant_node = constant_int_create_with_type(thread, function, node->type); + // return constant_node; + // } + // } + // + // auto idealize = 1; + // if (!node->hash) + // { + // auto gop = intern_pool_get_or_put_node(thread, node_index); + // idealize = !gop.existing; + // + // if (gop.existing) + // { + // auto interned_node_index = gop.index; + // auto* interned_node = thread_node_get(thread, interned_node_index); + // auto new_type = type_join(thread, interned_node->type, node->type); + // node_set_type(thread, interned_node, new_type); + // node->hash = 0; + // // print("[peephole_optimize] Eliminating #{u32} because an existing node was found: #{u32}\n", node_index.index, interned_node_index.index); + // auto dce_node = dead_code_elimination(thread, (NodePair) { + // .old = node_index, + // .new = interned_node_index, + // }); + // + // result = dce_node; + // } + // } + // + // if (idealize) + // { + // auto idealized_node = node_functions[node->id].idealize(thread, node_index); + // if (validi(idealized_node)) + // { + // result = idealized_node; + // } + // else + // { + // u64 are_types_equal = index_equal(new_type, old_type); + // thread->iteration.nop += are_types_equal; + // + // result = are_types_equal ? invalidi(Node) : node_index; + // } + // } + todo(); } else { - node->type = new_type; + todo(); + // node->type = new_type; } return result; @@ -3037,15 +4130,34 @@ fn NodeIndex peephole(Thread* thread, Function* function, NodeIndex node_index) { auto* node = thread_node_get(thread, node_index); auto new_type = node_functions[node->id].compute_type(thread, node_index); - node->type = new_type; - result = node_index; + unused(new_type); + todo(); + // node->type = new_type; + // result = node_index; } return result; } +fn NodeIndex node_project(Thread* thread, NodeIndex node_index, TypePair type, u32 index) +{ + auto* node = thread_node_get(thread, node_index); + assert(type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE); + auto projection_node_index = thread_node_add(thread, (NodeCreate) + { + .id = IR_PROJECTION, + .inputs = array_to_slice(((NodeIndex[]) { node_index })), + .type_pair = type, + }); + auto* projection = thread_node_get(thread, projection_node_index); + projection->projection = (NodeProjection) + { + .index = index, + }; + return projection_node_index; +} -fn TypeIndex analyze_type(Thread* thread, Parser* parser, String src) +fn TypePair analyze_type(Thread* thread, Parser* parser, String src) { u64 start_index = parser->i; u8 start_ch = src.pointer[start_index]; @@ -3080,11 +4192,11 @@ fn TypeIndex analyze_type(Thread* thread, Parser* parser, String src) while (i < top) { u8 ch = src.pointer[i]; - u32 is_digit = is_decimal_digit(ch); + auto is_digit = is_decimal_digit(ch); decimal_digit_count += is_digit; if (!is_digit) { - u32 is_alpha = is_alphabetic(ch); + auto is_alpha = is_alphabetic(ch); if (is_alpha) { decimal_digit_count = 0; @@ -3102,7 +4214,7 @@ fn TypeIndex analyze_type(Thread* thread, Parser* parser, String src) if (integer_start) { - u64 signedness = s_start; + auto signedness = cast(u8, u64, s_start); u64 bit_size; u64 current_i = parser->i; assert(src.pointer[current_i] >= '0' & src.pointer[current_i] <= '9'); @@ -3124,13 +4236,19 @@ fn TypeIndex analyze_type(Thread* thread, Parser* parser, String src) if (bit_size) { - auto type_index = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .bit_count = bit_size, - .is_constant = 0, - .is_signed = signedness, - }); - return type_index; + auto bit_count = cast(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(); + } + auto bit_index = cast(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 = bit_index + 1; + auto type_pair = type_pair_make(debug_type_index, backend_type); + return type_pair; } else { @@ -3155,6 +4273,30 @@ fn TypeIndex analyze_type(Thread* thread, Parser* parser, String src) trap(); } +declare_ip_functions(Node, node) + +// TODO: +fn NodeIndex node_gvn_intern(Thread* thread, NodeIndex node_index) +{ + auto result = ip_Node_get_or_put(&thread->interned.nodes, thread, node_index); + if (result.existing) + { + assert(thread_node_get(thread, result.index)->interned); + } + else + { + thread_node_get(thread, node_index)->interned = 1; + } + return result.index; +} + +fn void node_gvn_remove(Thread* thread, NodeIndex node_index) +{ + auto result = ip_Node_remove(&thread->interned.nodes, thread, node_index); + assert(index_equal(result, node_index)); + thread_node_get(thread, node_index)->interned = 0; +} + fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, FunctionBuilder* builder, String src) { u8 starting_ch = src.pointer[parser->i]; @@ -3164,15 +4306,17 @@ fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, Function if (is_identifier) { String identifier = parse_identifier(parser, src); - auto node_index = scope_lookup(thread, builder, identifier); - if (validi(node_index)) - { - return node_index; - } - else - { - fail(); - } + unused(identifier); + todo(); + // auto node_index = scope_lookup(thread, builder, identifier); + // if (validi(node_index)) + // { + // return node_index; + // } + // else + // { + // fail(); + // } } else if (is_digit) { @@ -3214,7 +4358,8 @@ fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, Function auto start = parser->i; - switch (prefix) { + switch (prefix) + { case INTEGER_PREFIX_HEXADECIMAL: { // while (is_hex_digit(src[parser->i])) { @@ -3235,13 +4380,31 @@ fn NodeIndex analyze_primary_expression(Thread* thread, Parser* parser, Function value = parse_decimal(s_get_slice(u8, src, start, parser->i)); } break; case INTEGER_PREFIX_OCTAL: - trap(); + { + trap(); + } break; case INTEGER_PREFIX_BINARY: - trap(); + { + trap(); + } break; } - auto node_index = constant_int_create(thread, builder->function, value); - return node_index; + auto node_index = thread_node_add(thread, (NodeCreate){ + .inputs = array_to_slice(((NodeIndex []) { + builder->function->root, + })), + .type_pair = type_pair_make(thread->types.debug.integer.u64, BACKEND_TYPE_INTEGER_64), + .id = IR_INTEGER_CONSTANT, + }); + + auto* node = thread_node_get(thread, node_index); + node->integer_constant = (NodeIntegerConstant) { + .unsigned_value = value, + }; + + auto new_node_index = node_gvn_intern(thread, node_index); + + return new_node_index; } else { @@ -3322,15 +4485,15 @@ fn NodeIndex analyze_multiplication(Thread* thread, Parser* parser, FunctionBuil skip_space(parser, src); NodeId node_id; - auto skip_count = 1; + u64 skip_count = 1; switch (src.pointer[parser->i]) { case '*': - node_id = NODE_INTEGER_MULTIPLY; + node_id = IR_INTEGER_MULTIPLY; break; case '/': - node_id = NODE_INTEGER_SIGNED_DIVIDE; + node_id = IR_INTEGER_DIVIDE; break; case '%': todo(); @@ -3359,10 +4522,12 @@ fn NodeIndex analyze_multiplication(Thread* thread, Parser* parser, FunctionBuil // print("Before right: LEFT is #{u32}\n", left.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); auto right = analyze_multiplication(thread, parser, builder, src); + unused(right); // print("Addition: left: #{u32}, right: #{u32}\n", left.index, right.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); - node_set_input(thread, new_node_index, 2, right); + todo(); + // node_set_input(thread, new_node_index, 2, right); // print("Addition new node #{u32}\n", new_node_index.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); @@ -3388,10 +4553,10 @@ fn NodeIndex analyze_addition(Thread* thread, Parser* parser, FunctionBuilder* b switch (src.pointer[parser->i]) { case '+': - node_id = NODE_INTEGER_ADD; + node_id = IR_INTEGER_ADD; break; case '-': - node_id = NODE_INTEGER_SUBSTRACT; + node_id = IR_INTEGER_SUBSTRACT; break; default: node_id = NODE_COUNT; @@ -3418,10 +4583,13 @@ fn NodeIndex analyze_addition(Thread* thread, Parser* parser, FunctionBuilder* b // print("Before right: LEFT is #{u32}\n", left.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); auto right = analyze_multiplication(thread, parser, builder, src); + unused(right); // print("Addition: left: #{u32}, right: #{u32}\n", left.index, right.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); - node_set_input(thread, new_node_index, 2, right); + // node_set_input(thread, new_node_index, 2, right); + todo(); + // print("Addition new node #{u32}\n", new_node_index.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); @@ -3446,11 +4614,11 @@ fn NodeIndex analyze_shift(Thread* thread, Parser* parser, FunctionBuilder* buil if ((src.pointer[parser->i] == '<') & (src.pointer[parser->i + 1] == '<')) { - node_id = NODE_INTEGER_SIGNED_SHIFT_LEFT; + node_id = IR_INTEGER_SHIFT_LEFT; } else if ((src.pointer[parser->i] == '>') & (src.pointer[parser->i + 1] == '>')) { - node_id = NODE_INTEGER_SIGNED_SHIFT_RIGHT; + node_id = IR_INTEGER_SHIFT_RIGHT; } else { @@ -3472,10 +4640,12 @@ fn NodeIndex analyze_shift(Thread* thread, Parser* parser, FunctionBuilder* buil // print("Before right: LEFT is #{u32}\n", left.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); auto right = analyze_addition(thread, parser, builder, src); + unused(right); // print("Addition: left: #{u32}, right: #{u32}\n", left.index, right.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); - node_set_input(thread, new_node_index, 2, right); + // node_set_input(thread, new_node_index, 2, right); + todo(); // print("Addition new node #{u32}\n", new_node_index.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); @@ -3495,18 +4665,18 @@ fn NodeIndex analyze_bitwise_binary(Thread* thread, Parser* parser, FunctionBuil skip_space(parser, src); NodeId node_id; - auto skip_count = 1; + u64 skip_count = 1; switch (src.pointer[parser->i]) { case '&': - node_id = NODE_INTEGER_AND; + node_id = IR_INTEGER_AND; break; case '|': - node_id = NODE_INTEGER_OR; + node_id = IR_INTEGER_OR; break; case '^': - node_id = NODE_INTEGER_XOR; + node_id = IR_INTEGER_XOR; break; default: node_id = NODE_COUNT; @@ -3533,10 +4703,12 @@ fn NodeIndex analyze_bitwise_binary(Thread* thread, Parser* parser, FunctionBuil // print("Before right: LEFT is #{u32}\n", left.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); auto right = analyze_shift(thread, parser, builder, src); + unused(right); // print("Addition: left: #{u32}, right: #{u32}\n", left.index, right.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); - node_set_input(thread, new_node_index, 2, right); + // node_set_input(thread, new_node_index, 2, right); + todo(); // print("Addition new node #{u32}\n", new_node_index.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); @@ -3556,7 +4728,7 @@ fn NodeIndex analyze_comparison(Thread* thread, Parser* parser, FunctionBuilder* skip_space(parser, src); NodeId node_id; - auto skip_count = 1; + u64 skip_count = 1; switch (src.pointer[parser->i]) { @@ -3566,7 +4738,7 @@ fn NodeIndex analyze_comparison(Thread* thread, Parser* parser, FunctionBuilder* if (src.pointer[parser->i + 1] == '=') { skip_count = 2; - node_id = NODE_INTEGER_COMPARE_NOT_EQUAL; + node_id = IR_INTEGER_COMPARE_NOT_EQUAL; } else { @@ -3602,10 +4774,12 @@ fn NodeIndex analyze_comparison(Thread* thread, Parser* parser, FunctionBuilder* // print("Before right: LEFT is #{u32}\n", left.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); auto right = analyze_bitwise_binary(thread, parser, builder, src); + unused(right); // print("Addition: left: #{u32}, right: #{u32}\n", left.index, right.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); - node_set_input(thread, new_node_index, 2, right); + // node_set_input(thread, new_node_index, 2, right); + todo(); // print("Addition new node #{u32}\n", new_node_index.index); // print("Left code:\n```\n{s}\n```\n", s_get_slice(u8, src, parser->i, src.length)); @@ -3616,7 +4790,7 @@ fn NodeIndex analyze_comparison(Thread* thread, Parser* parser, FunctionBuilder* return left; } -fn NodeIndex analyze_expression(Thread* thread, Parser* parser, FunctionBuilder* builder, String src, TypeIndex result_type) +fn NodeIndex analyze_expression(Thread* thread, Parser* parser, FunctionBuilder* builder, String src, TypePair result_type) { NodeIndex result = analyze_comparison(thread, parser, builder, src); // TODO: typecheck @@ -3628,7 +4802,8 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, { expect_character(parser, src, block_start); - scope_push(thread, builder); + // TODO + // scope_push(thread, builder); Function* function = builder->function; @@ -3653,30 +4828,31 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, skip_space(parser, src); expect_character(parser, src, ';'); - auto return_node_index = thread_node_add(thread, (NodeCreate) { - .id = NODE_RETURN, - .inputs = array_to_slice(((NodeIndex[]) { - builder_get_control_node_index(thread, builder), - return_value, - })), - }); + auto* current_node = thread_node_get(thread, builder->current); + auto current_inputs = node_get_inputs(thread, current_node); + auto mem_state = current_inputs.pointer[2]; - if (validi(builder->scope)) - { - // TODO: - // Look for memory slices - // todo(); - } + auto return_node_index = node_get_inputs(thread, thread_node_get(thread, builder->function->root)).pointer[1]; + auto* return_node = thread_node_get(thread, return_node_index); + assert(return_node->input_count >= 4); + auto return_inputs = node_get_inputs(thread, return_node); - return_node_index = peephole(thread, function, return_node_index); + node_add_input(thread, return_inputs.pointer[1], mem_state); - builder_add_return(thread, builder, return_node_index); + node_add_input(thread, return_inputs.pointer[3], return_value); - builder_set_control(thread, builder, builder->dead_control); + auto control = return_inputs.pointer[0]; + assert(thread_node_get(thread, control)->id == IR_REGION); + + assert(validi(current_inputs.pointer[0])); + node_add_input(thread, control, current_inputs.pointer[0]); + + builder->current = invalidi(Node); continue; } String left_name = statement_start_identifier; + unused(left_name); skip_space(parser, src); @@ -3695,28 +4871,32 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, default: trap(); } + unused(assignment_operator); skip_space(parser, src); - NodeIndex initial_right = analyze_expression(thread, parser, builder, src, invalidi(Type)); + NodeIndex initial_right = analyze_expression(thread, parser, builder, src, type_pair_invalid); + unused(initial_right); expect_character(parser, src, ';'); - auto left = scope_lookup(thread, builder, left_name); - if (!validi(left)) - { - fail(); - } + todo(); - NodeIndex right; - switch (assignment_operator) - { - case ASSIGNMENT_OPERATOR_NONE: - right = initial_right; - break; - } - - scope_update(thread, builder, left_name, right); + // auto left = scope_lookup(thread, builder, left_name); + // if (!validi(left)) + // { + // fail(); + // } + // + // NodeIndex right; + // switch (assignment_operator) + // { + // case ASSIGNMENT_OPERATOR_NONE: + // right = initial_right; + // break; + // } + // + // scope_update(thread, builder, left_name, right); } else { @@ -3728,10 +4908,11 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, skip_space(parser, src); String local_name = parse_identifier(parser, src); + unused(local_name); skip_space(parser, src); - TypeIndex type = invalidi(Type); + auto type = type_pair_invalid; u8 has_type_declaration = src.pointer[parser->i] == ':'; if (has_type_declaration) @@ -3754,14 +4935,16 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, expect_character(parser, src, ';'); auto* initial_value_node = thread_node_get(thread, initial_value_node_index); + unused(initial_value_node); // TODO: typecheck + todo(); - auto result = scope_define(thread, builder, local_name, initial_value_node->type, initial_value_node_index); - if (!validi(result)) - { - fail(); - } + // auto result = scope_define(thread, builder, local_name, initial_value_node->type, initial_value_node_index); + // if (!validi(result)) + // { + // fail(); + // } } break; case block_start: analyze_block(thread, parser, builder, src); @@ -3775,7 +4958,7 @@ fn void analyze_block(Thread* thread, Parser* parser, FunctionBuilder* builder, expect_character(parser, src, block_end); - scope_pop(thread, builder); + // scope_pop(thread, builder); } fn void analyze_file(Thread* thread, File* file) @@ -3814,8 +4997,8 @@ fn void analyze_file(Thread* thread, File* file) skip_space(parser, src); - - Function* function = vb_add(&thread->buffer.functions, 1); + Function* restrict function = vb_add(&thread->buffer.functions, 1); + auto function_index = cast(u32, s64, function - thread->buffer.functions.pointer); memset(function, 0, sizeof(Function)); builder->function = function; @@ -3830,16 +5013,8 @@ fn void analyze_file(Thread* thread, File* file) // Parse arguments expect_character(parser, src, argument_start); - // Create the start node early since it is needed as a dependency for control and arguments - function->start = thread_node_add(thread, (NodeCreate) { - .id = NODE_START, - .inputs = {}, - }); - TypeIndex tuple = invalidi(Type); - TypeIndex start_argument_type_buffer[256]; + u32 argument_i = 0; String argument_names[255]; - start_argument_type_buffer[0] = thread->types.live_control; - u32 argument_i = 1; while (1) { @@ -3849,21 +5024,22 @@ fn void analyze_file(Thread* thread, File* file) break; } - if (argument_i == 256) + if (argument_i == 255) { // Maximum arguments reached fail(); } auto argument_name = parse_identifier(parser, src); - argument_names[argument_i - 1] = argument_name; + argument_names[argument_i] = argument_name; skip_space(parser, src); expect_character(parser, src, ':'); skip_space(parser, src); auto type_index = analyze_type(thread, parser, src); - start_argument_type_buffer[argument_i] = type_index; + unused(type_index); + // start_argument_type_buffer[argument_i] = type_index; argument_i += 1; skip_space(parser, src); @@ -3880,94 +5056,109 @@ fn void analyze_file(Thread* thread, File* file) expect_character(parser, src, argument_end); skip_space(parser, src); - auto start_argument_types = s_get_slice(TypeIndex, (Slice(TypeIndex)) array_to_slice(start_argument_type_buffer), 0, argument_i); - tuple = type_make_tuple_allocate(thread, start_argument_types); - - auto argument_count = argument_i - 1; - - auto* start_node = thread_node_get(thread, function->start); - assert(validi(tuple)); - start_node->type = tuple; - start_node->start.arguments = tuple; - start_node->start.function = function; - - - // Create stop node - { - function->stop = thread_node_add(thread, (NodeCreate) { - .id = NODE_STOP, - .inputs = {}, - }); - } - - auto dead_control = thread_node_add(thread, (NodeCreate) { - .id = NODE_DEAD_CONTROL, - .inputs = { .pointer = &function->start, .length = 1 }, - }); - dead_control = peephole(thread, function, dead_control); - - builder->dead_control = node_keep(thread, dead_control); - - // Create the function scope node - { - auto scope_node_index = thread_node_add(thread, (NodeCreate) - { - .id = NODE_SCOPE, - .inputs = {}, - }); - auto* scope_node = thread_node_get(thread, scope_node_index); - scope_node->type = thread->types.bottom; - builder->scope = scope_node_index; - - scope_push(thread, builder); - auto control_node_index = thread_node_add(thread, (NodeCreate){ - .id = NODE_CONTROL_PROJECTION, - .inputs = { - .pointer = &function->start, - .length = 1, - }, - }); - auto* control_node = thread_node_get(thread, control_node_index); - auto control_name = strlit("$control"); - control_node->control_projection.projection = (NodeProjection) { - .label = control_name, - .index = 0, - }; - control_node_index = peephole(thread, function, control_node_index); - scope_define(thread, builder, control_name, thread->types.live_control, control_node_index); - } - - for (u32 i = 0; i < argument_count; i += 1) - { - TypeIndex argument_type = start_argument_types.pointer[i + 1]; - String argument_name = argument_names[i]; - - auto argument_node_index = thread_node_add(thread, (NodeCreate){ - .id = NODE_PROJECTION, - .inputs = { - .pointer = &function->start, - .length = 1, - }, - }); - auto* argument_node = thread_node_get(thread, argument_node_index); - argument_node->projection = (NodeProjection) { - .index = i + 1, - .label = argument_name, - }; - - argument_node_index = peephole(thread, function, argument_node_index); - - scope_define(thread, builder, argument_name, argument_type, argument_node_index); - } - function->return_type = analyze_type(thread, parser, src); + function->root = thread_node_add(thread, (NodeCreate) + { + .type_pair = type_pair_make(invalidi(DebugType), BACKEND_TYPE_TUPLE), + .id = IR_ROOT, + .inputs = array_to_slice(((NodeIndex[]){ + invalidi(Node), // TODO: add callgraph node + invalidi(Node), // return node + })), + }); + + auto* root_node = thread_node_get(thread, function->root); + root_node->root = (NodeRoot) + { + .function_index = function_index, + }; + + auto control = node_project(thread, function->root, type_pair_make(invalidi(DebugType), BACKEND_TYPE_CONTROL), 0); + auto memory = node_project(thread, function->root, type_pair_make(invalidi(DebugType), BACKEND_TYPE_MEMORY), 1); + auto pointer = node_project(thread, function->root, type_pair_make(invalidi(DebugType), BACKEND_TYPE_POINTER), 2); + + if (argument_i > 0) + { + // TODO: project arguments + todo(); + } + + NodeIndex fake[256] = {}; + auto slice = (Slice(NodeIndex)) array_to_slice(fake); + slice.length = 4; + auto return_node_index = thread_node_add(thread, (NodeCreate) + { + .id = IR_RETURN, + .inputs = slice, + .type_pair = type_pair_make(invalidi(DebugType), BACKEND_TYPE_CONTROL), + }); + + node_set_input(thread, function->root, 1, return_node_index); + + auto region = thread_node_add(thread, (NodeCreate) + { + .id = IR_REGION, + .inputs = {}, + .type_pair = type_pair_make(invalidi(DebugType), BACKEND_TYPE_CONTROL), + }); + + auto memory_phi = thread_node_add(thread, (NodeCreate) + { + .id = IR_PHI, + .inputs = array_to_slice(((NodeIndex[]) { + region, + })), + .type_pair = type_pair_make(invalidi(DebugType), BACKEND_TYPE_MEMORY), + }); + + node_set_input(thread, return_node_index, 0, region); + node_set_input(thread, return_node_index, 1, memory_phi); + node_set_input(thread, return_node_index, 2, pointer); + + auto ret_phi = thread_node_add(thread, (NodeCreate) + { + .id = IR_PHI, + .inputs = array_to_slice(((NodeIndex[]) { + region, + })), + .type_pair = function->return_type, + }); + node_set_input(thread, ret_phi, 0, region); + node_set_input(thread, return_node_index, 3, ret_phi); + + thread_node_get(thread, region)->region = (NodeRegion) + { + .in_mem = memory_phi, + }; + + node_gvn_intern(thread, function->root); + node_gvn_intern(thread, control); + node_gvn_intern(thread, memory); + node_gvn_intern(thread, pointer); + skip_space(parser, src); + auto symbol_table = thread_node_add(thread, (NodeCreate) + { + .id = IR_SYMBOL_TABLE, + .inputs = array_to_slice(((NodeIndex[]) + { + control, + control, + memory, + pointer, + })), + }); + builder->current = symbol_table; + analyze_block(thread, parser, builder, src); - scope_pop(thread, builder); - function->stop = peephole(thread, function, function->stop); + node_gvn_intern(thread, return_node_index); + node_gvn_intern(thread, region); + node_gvn_intern(thread, memory_phi); + node_gvn_intern(thread, ret_phi); + node_gvn_intern(thread, symbol_table); } else { @@ -3981,727 +5172,735 @@ fn void analyze_file(Thread* thread, File* file) } } -typedef NodeIndex NodeCallback(Thread* thread, Function* function, NodeIndex node_index); - -fn NodeIndex node_walk_internal(Thread* thread, Function* function, NodeIndex node_index, NodeCallback* callback) -{ - if (bitset_get(&thread->worklist.visited, geti(node_index))) - { - return invalidi(Node); - } - else - { - bitset_set_value(&thread->worklist.visited, geti(node_index), 1); - auto callback_result = callback(thread, function, node_index); - if (validi(callback_result)) - { - return callback_result; - } - - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - auto outputs = node_get_outputs(thread, node); - - for (u64 i = 0; i < inputs.length; i += 1) - { - auto n = inputs.pointer[i]; - if (validi(n)) - { - auto n_result = node_walk_internal(thread, function, n, callback); - if (validi(n_result)) - { - return n_result; - } - } - } - - for (u64 i = 0; i < outputs.length; i += 1) - { - auto n = outputs.pointer[i]; - if (validi(n)) - { - auto n_result = node_walk_internal(thread, function, n, callback); - if (validi(n_result)) - { - return n_result; - } - } - } - - return invalidi(Node); - } -} - -fn NodeIndex node_walk(Thread* thread, Function* function, NodeIndex node_index, NodeCallback* callback) -{ - assert(thread->worklist.visited.length == 0); - NodeIndex result = node_walk_internal(thread, function, node_index, callback); - bitset_clear(&thread->worklist.visited); - return result; -} - -fn NodeIndex progress_on_list_callback(Thread* thread, Function* function, NodeIndex node_index) -{ - if (bitset_get(&thread->worklist.bitset, geti(node_index))) - { - return invalidi(Node); - } - else - { - NodeIndex new_node = peephole_optimize(thread, function, node_index); - return new_node; - } -} - -fn u8 progress_on_list(Thread* thread, Function* function, NodeIndex stop_node_index) -{ - thread->worklist.mid_assert = 1; - - NodeIndex changed = node_walk(thread, function, stop_node_index, &progress_on_list_callback); - - thread->worklist.mid_assert = 0; - - return !validi(changed); -} - -fn void iterate_peepholes(Thread* thread, Function* function, NodeIndex stop_node_index) -{ - assert(progress_on_list(thread, function, stop_node_index)); - if (thread->worklist.nodes.length > 0) - { - while (1) - { - auto node_index = thread_worklist_pop(thread); - if (!validi(node_index)) - { - break; - } - - auto* node = thread_node_get(thread, node_index); - if (!node_is_dead(node)) - { - auto new_node_index = peephole_optimize(thread, function, node_index); - if (validi(new_node_index)) - { - trap(); - } - } - } - } - - thread_worklist_clear(thread); -} - -fn u8 node_is_cfg(Node* node) -{ - switch (node->id) - { - case NODE_START: - case NODE_DEAD_CONTROL: - case NODE_CONTROL_PROJECTION: - case NODE_RETURN: - case NODE_STOP: - return 1; - case NODE_SCOPE: - case NODE_CONSTANT: - case NODE_PROJECTION: - return 0; - default: - trap(); - } -} - -fn void rpo_cfg(Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - if (node_is_cfg(node) && !bitset_get(&thread->worklist.visited, geti(node_index))) - { - bitset_set_value(&thread->worklist.visited, geti(node_index), 1); - auto outputs = node_get_outputs(thread, node); - for (u64 i = 0; i < outputs.length; i += 1) - { - auto output = outputs.pointer[i]; - if (validi(output)) - { - rpo_cfg(thread, output); - } - } - - *vb_add(&thread->worklist.nodes, 1) = node_index; - } -} - -fn s32 node_loop_depth(Thread* thread, Node* node) -{ - assert(node_is_cfg(node)); - s32 loop_depth; - - switch (node->id) - { - case NODE_START: - { - loop_depth = node->start.cfg.loop_depth; - if (!loop_depth) - { - loop_depth = node->start.cfg.loop_depth = 1; - } - } break; - case NODE_STOP: - { - loop_depth = node->stop.cfg.loop_depth; - if (!loop_depth) - { - loop_depth = node->stop.cfg.loop_depth = 1; - } - } break; - case NODE_RETURN: - { - loop_depth = node->return_node.cfg.loop_depth; - if (!loop_depth) - { - auto input_index = node_input_get(thread, node, 0); - auto input = thread_node_get(thread, input_index); - node->return_node.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); - } - } break; - case NODE_CONTROL_PROJECTION: - { - loop_depth = node->control_projection.cfg.loop_depth; - if (!loop_depth) - { - auto input_index = node_input_get(thread, node, 0); - auto input = thread_node_get(thread, input_index); - node->control_projection.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); - } - } break; - case NODE_DEAD_CONTROL: - { - loop_depth = node->dead_control.cfg.loop_depth; - if (!loop_depth) - { - auto input_index = node_input_get(thread, node, 0); - auto input = thread_node_get(thread, input_index); - node->dead_control.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); - } - } break; - default: - trap(); - } - - return loop_depth; -} - -fn u8 node_is_region(Node* node) -{ - return (node->id == NODE_REGION) | (node->id == NODE_REGION_LOOP); -} - -fn u8 node_is_pinned(Node* node) -{ - switch (node->id) - { - case NODE_PROJECTION: - case NODE_START: - return 1; - case NODE_CONSTANT: - case NODE_INTEGER_SUBSTRACT: - case NODE_INTEGER_COMPARE_EQUAL: - case NODE_INTEGER_COMPARE_NOT_EQUAL: - return 0; - default: - trap(); - } -} - -fn s32 node_cfg_get_immediate_dominator_tree_depth(Node* node) -{ - assert(node_is_cfg(node)); - switch (node->id) - { - case NODE_START: - return 0; - case NODE_DEAD_CONTROL: - todo(); - case NODE_CONTROL_PROJECTION: - todo(); - case NODE_RETURN: - todo(); - case NODE_STOP: - todo(); - default: - trap(); - } -} - -fn void schedule_early(Thread* thread, NodeIndex node_index, NodeIndex start_node) -{ - if (validi(node_index) && !bitset_get(&thread->worklist.visited, geti(node_index))) - { - bitset_set_value(&thread->worklist.visited, geti(node_index), 1); - - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - - for (u64 i = 0; i < inputs.length; i += 1) - { - auto input = inputs.pointer[i]; - - if (validi(input)) - { - auto* input_node = thread_node_get(thread, input); - if (!node_is_pinned(input_node)) - { - schedule_early(thread, node_index, start_node); - } - } - } - - if (!node_is_pinned(node)) - { - auto early = start_node; - - for (u64 i = 1; i < inputs.length; i += 1) - { - auto input_index = inputs.pointer[i]; - auto input_node = thread_node_get(thread, input_index); - auto control_input_index = node_input_get(thread, input_node, 0); - auto* control_input_node = thread_node_get(thread, control_input_index); - auto* early_node = thread_node_get(thread, early); - auto input_depth = node_cfg_get_immediate_dominator_tree_depth(control_input_node); - auto early_depth = node_cfg_get_immediate_dominator_tree_depth(early_node); - if (input_depth > early_depth) - { - early = control_input_index; - trap(); - } - } - - node_set_input(thread, node_index, 0, early); - } - } -} - -fn u8 node_cfg_block_head(Node* node) -{ - assert(node_is_cfg(node)); - switch (node->id) - { - case NODE_START: - return 1; - default: - trap(); - } -} - -fn u8 is_forwards_edge(Thread* thread, NodeIndex output_index, NodeIndex input_index) -{ - u8 result = validi(output_index) & validi(input_index); - if (result) - { - auto* output = thread_node_get(thread, output_index); - result = output->input_count > 2; - if (result) - { - auto input_index2 = node_input_get(thread, output, 2); - - result = index_equal(input_index2, input_index); - - if (result) - { - trap(); - } - } - } - - return result; -} - -fn void schedule_late(Thread* thread, NodeIndex node_index, Slice(NodeIndex) nodes, Slice(NodeIndex) late) -{ - if (!validi(late.pointer[geti(node_index)])) - { - auto* node = thread_node_get(thread, node_index); - - if (node_is_cfg(node)) - { - late.pointer[geti(node_index)] = node_cfg_block_head(node) ? node_index : node_input_get(thread, node, 0); - } - - if (node->id == NODE_PHI) - { - trap(); - } - - auto outputs = node_get_outputs(thread, node); - - for (u32 i = 0; i < outputs.length; i += 1) - { - NodeIndex output = outputs.pointer[i]; - if (is_forwards_edge(thread, output, node_index)) - { - trap(); - } - } - - for (u32 i = 0; i < outputs.length; i += 1) - { - NodeIndex output = outputs.pointer[i]; - if (is_forwards_edge(thread, output, node_index)) - { - trap(); - } - } - - if (!node_is_pinned(node)) - { - unused(nodes); - trap(); - } - } -} - -fn void gcm_build_cfg(Thread* thread, NodeIndex start_node_index, NodeIndex stop_node_index) -{ - unused(stop_node_index); - // Fix loops - { - // TODO: - } - - // Schedule early - rpo_cfg(thread, start_node_index); - - u32 i = thread->worklist.nodes.length; - while (i > 0) - { - i -= 1; - auto node_index = thread->worklist.nodes.pointer[i]; - auto* node = thread_node_get(thread, node_index); - node_loop_depth(thread, node); - auto inputs = node_get_inputs(thread, node); - for (u64 i = 0; i < inputs.length; i += 1) - { - auto input = inputs.pointer[i]; - schedule_early(thread, input, start_node_index); - } - - if (node_is_region(node)) - { - trap(); - } - } - - // Schedule late - - auto max_node_count = thread->buffer.nodes.length; - auto* alloc = arena_allocate(thread->arena, NodeIndex, max_node_count * 2); - auto late = (Slice(NodeIndex)) { - .pointer = alloc, - .length = max_node_count, - }; - auto nodes = (Slice(NodeIndex)) { - .pointer = alloc + max_node_count, - .length = max_node_count, - }; - - schedule_late(thread, start_node_index, nodes, late); - - for (u32 i = 0; i < late.length; i += 1) - { - auto node_index = nodes.pointer[i]; - if (validi(node_index)) - { - trap(); - auto late_node_index = late.pointer[i]; - node_set_input(thread, node_index, 0, late_node_index); - } - } -} - -may_be_unused fn void print_function(Thread* thread, Function* function) -{ - print("fn {s}\n====\n", function->name); - VirtualBuffer(NodeIndex) nodes = {}; - *vb_add(&nodes, 1) = function->stop; - - while (1) - { - auto node_index = nodes.pointer[nodes.length - 1]; - auto* node = thread_node_get(thread, node_index); - - if (node->input_count) - { - for (u32 i = 1; i < node->input_count; i += 1) - { - *vb_add(&nodes, 1) = node_input_get(thread, node, 1); - } - *vb_add(&nodes, 1) = node_input_get(thread, node, 0); - } - else - { - break; - } - } - - u32 i = nodes.length; - while (i > 0) - { - i -= 1; - - auto node_index = nodes.pointer[i]; - auto* node = thread_node_get(thread, node_index); - auto* type = thread_type_get(thread, node->type); - print("%{u32} - {s} - {s} ", geti(node_index), type_id_to_string(type), node_id_to_string(node)); - auto inputs = node_get_inputs(thread, node); - auto outputs = node_get_outputs(thread, node); - - print("(INPUTS: { "); - for (u32 i = 0; i < inputs.length; i += 1) - { - auto input_index = inputs.pointer[i]; - print("%{u32} ", geti(input_index)); - } - print("} OUTPUTS: { "); - for (u32 i = 0; i < outputs.length; i += 1) - { - auto output_index = outputs.pointer[i]; - print("%{u32} ", geti(output_index)); - } - print_string(strlit("})\n")); - } - - - print("====\n", function->name); -} - -struct CBackend -{ - VirtualBuffer(u8) buffer; - Function* function; -}; - -typedef struct CBackend CBackend; - -fn void c_lower_append_string(CBackend* backend, String string) -{ - vb_append_bytes(&backend->buffer, string); -} - -fn void c_lower_append_ch(CBackend* backend, u8 ch) -{ - *vb_add(&backend->buffer, 1) = ch; -} - -fn void c_lower_append_ch_repeated(CBackend* backend, u8 ch, u32 times) -{ - u8* pointer = vb_add(&backend->buffer, times); - memset(pointer, ch, times); -} - -fn void c_lower_append_space(CBackend* backend) -{ - c_lower_append_ch(backend, ' '); -} - -fn void c_lower_append_space_margin(CBackend* backend, u32 times) -{ - c_lower_append_ch_repeated(backend, ' ', times * 4); -} - -fn void c_lower_type(CBackend* backend, Thread* thread, TypeIndex type_index) -{ - Type* type = thread_type_get(thread, type_index); - switch (type->id) - { - case TYPE_INTEGER: - { - u8 ch[] = { 'u', 's' }; - auto integer = &type->integer; - u8 signedness_ch = ch[type->integer.is_signed]; - c_lower_append_ch(backend, signedness_ch); - u8 upper_digit = integer->bit_count / 10; - u8 lower_digit = integer->bit_count % 10; - if (upper_digit) - { - c_lower_append_ch(backend, upper_digit + '0'); - } - c_lower_append_ch(backend, lower_digit + '0'); - } break; - default: - trap(); - } -} - -fn void c_lower_node(CBackend* backend, Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - auto* type = thread_type_get(thread, node->type); - auto inputs = node_get_inputs(thread, node); - - switch (node->id) - { - case NODE_CONSTANT: - { - switch (type->id) - { - case TYPE_INTEGER: - { - assert(type->integer.bit_count == 0); - assert(type->integer.is_constant); - assert(!type->integer.is_signed); - vb_generic_ensure_capacity(&backend->buffer, 1, 64); - auto current_length = backend->buffer.length; - auto buffer_slice = (String){ .pointer = backend->buffer.pointer + current_length, .length = backend->buffer.capacity - current_length, }; - auto written_characters = format_hexadecimal(buffer_slice, type->integer.constant); - backend->buffer.length = current_length + written_characters; - } break; - trap(); - default: - trap(); - } - } break; - case NODE_INTEGER_SUBSTRACT: - { - auto left = inputs.pointer[1]; - auto right = inputs.pointer[2]; - c_lower_node(backend, thread, left); - c_lower_append_string(backend, strlit(" - ")); - c_lower_node(backend, thread, right); - } break; - case NODE_INTEGER_COMPARE_EQUAL: - { - auto left = inputs.pointer[1]; - auto right = inputs.pointer[2]; - c_lower_node(backend, thread, left); - c_lower_append_string(backend, strlit(" == ")); - c_lower_node(backend, thread, right); - } break; - case NODE_INTEGER_COMPARE_NOT_EQUAL: - { - auto left = inputs.pointer[1]; - auto right = inputs.pointer[2]; - c_lower_node(backend, thread, left); - c_lower_append_string(backend, strlit(" != ")); - c_lower_node(backend, thread, right); - } break; - case NODE_PROJECTION: - { - auto projected_node_index = inputs.pointer[0]; - auto projection_index = node->projection.index; - - if (index_equal(projected_node_index, backend->function->start)) - { - if (projection_index == 0) - { - fail(); - } - // if (projection_index > interpreter->arguments.length + 1) - // { - // fail(); - // } - - switch (projection_index) - { - case 1: - c_lower_append_string(backend, strlit("argc")); - break; - // return interpreter->arguments.length; - case 2: - trap(); - default: - trap(); - } - } - else - { - trap(); - } - } break; - default: - trap(); - } -} - -fn String c_lower(Thread* thread) -{ - CBackend backend_stack = {}; - CBackend* backend = &backend_stack; - auto program_epilogue = strlit("#include \n" - "typedef uint8_t u8;\n" - "typedef uint16_t u16;\n" - "typedef uint32_t u32;\n" - "typedef uint64_t u64;\n" - "typedef int8_t s8;\n" - "typedef int16_t s16;\n" - "typedef int32_t s32;\n" - "typedef int64_t s64;\n" - ); - c_lower_append_string(backend, program_epilogue); - - for (u32 function_i = 0; function_i < thread->buffer.functions.length; function_i += 1) - { - auto* function = &thread->buffer.functions.pointer[function_i]; - backend->function = function; - c_lower_type(backend, thread, function->return_type); - c_lower_append_space(backend); - - c_lower_append_string(backend, function->name); - c_lower_append_ch(backend, argument_start); - if (s_equal(function->name, strlit("main"))) - { - c_lower_append_string(backend, strlit("int argc, char* argv[]")); - } - - c_lower_append_ch(backend, argument_end); - c_lower_append_ch(backend, '\n'); - c_lower_append_ch(backend, block_start); - c_lower_append_ch(backend, '\n'); - - auto start_node_index = function->start; - auto* start_node = thread_node_get(thread, start_node_index); - assert(start_node->output_count > 0); - auto stop_node_index = function->stop; - - auto proj_node_index = node_output_get(thread, start_node, 1); - auto it_node_index = proj_node_index; - auto current_statement_margin = 1; - - while (!index_equal(it_node_index, stop_node_index)) - { - auto* it_node = thread_node_get(thread, it_node_index); - auto outputs = node_get_outputs(thread, it_node); - auto inputs = node_get_inputs(thread, it_node); - - switch (it_node->id) - { - case NODE_CONTROL_PROJECTION: - break; - case NODE_RETURN: - { - c_lower_append_space_margin(backend, current_statement_margin); - c_lower_append_string(backend, strlit("return ")); - assert(inputs.length > 1); - assert(inputs.length == 2); - auto input = inputs.pointer[1]; - c_lower_node(backend, thread, input); - c_lower_append_ch(backend, ';'); - c_lower_append_ch(backend, '\n'); - } break; - case NODE_STOP: - break; - default: - trap(); - } - - assert(outputs.length == 1); - it_node_index = outputs.pointer[0]; - } - - c_lower_append_ch(backend, block_end); - } - - return (String) { .pointer = backend->buffer.pointer, .length = backend->buffer.length }; -} +// typedef NodeIndex NodeCallback(Thread* thread, Function* function, NodeIndex node_index); +// +// fn NodeIndex node_walk_internal(Thread* thread, Function* function, NodeIndex node_index, NodeCallback* callback) +// { +// if (bitset_get(&thread->worklist.visited, geti(node_index))) +// { +// return invalidi(Node); +// } +// else +// { +// bitset_set_value(&thread->worklist.visited, geti(node_index), 1); +// auto callback_result = callback(thread, function, node_index); +// if (validi(callback_result)) +// { +// return callback_result; +// } +// +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// auto outputs = node_get_outputs(thread, node); +// +// for (u64 i = 0; i < inputs.length; i += 1) +// { +// auto n = inputs.pointer[i]; +// if (validi(n)) +// { +// auto n_result = node_walk_internal(thread, function, n, callback); +// if (validi(n_result)) +// { +// return n_result; +// } +// } +// } +// +// for (u64 i = 0; i < outputs.length; i += 1) +// { +// auto n = outputs.pointer[i]; +// if (validi(n)) +// { +// auto n_result = node_walk_internal(thread, function, n, callback); +// if (validi(n_result)) +// { +// return n_result; +// } +// } +// } +// +// return invalidi(Node); +// } +// } +// +// fn NodeIndex node_walk(Thread* thread, Function* function, NodeIndex node_index, NodeCallback* callback) +// { +// assert(thread->worklist.visited.length == 0); +// NodeIndex result = node_walk_internal(thread, function, node_index, callback); +// bitset_clear(&thread->worklist.visited); +// return result; +// } +// +// fn NodeIndex progress_on_list_callback(Thread* thread, Function* function, NodeIndex node_index) +// { +// if (bitset_get(&thread->worklist.bitset, geti(node_index))) +// { +// return invalidi(Node); +// } +// else +// { +// NodeIndex new_node = peephole_optimize(thread, function, node_index); +// return new_node; +// } +// } +// +// fn u8 progress_on_list(Thread* thread, Function* function, NodeIndex stop_node_index) +// { +// thread->worklist.mid_assert = 1; +// +// NodeIndex changed = node_walk(thread, function, stop_node_index, &progress_on_list_callback); +// +// thread->worklist.mid_assert = 0; +// +// return !validi(changed); +// } +// +// fn void iterate_peepholes(Thread* thread, Function* function, NodeIndex stop_node_index) +// { +// assert(progress_on_list(thread, function, stop_node_index)); +// if (thread->worklist.nodes.length > 0) +// { +// while (1) +// { +// auto node_index = thread_worklist_pop(thread); +// if (!validi(node_index)) +// { +// break; +// } +// +// auto* node = thread_node_get(thread, node_index); +// todo(); +// // if (!node_is_dead(node)) +// // { +// // auto new_node_index = peephole_optimize(thread, function, node_index); +// // if (validi(new_node_index)) +// // { +// // trap(); +// // } +// // } +// } +// } +// +// thread_worklist_clear(thread); +// } + +// fn u8 node_is_cfg(Node* node) +// { +// switch (node->id) +// { +// case IR_START: +// case IR_DEAD_CONTROL: +// case IR_CONTROL_PROJECTION: +// case IR_RETURN: +// case IR_STOP: +// return 1; +// case IR_SCOPE: +// case IR_CONSTANT: +// case IR_PROJECTION: +// return 0; +// default: +// trap(); +// } +// } + +// fn void rpo_cfg(Thread* thread, NodeIndex node_index) +// { +// auto* node = thread_node_get(thread, node_index); +// if (node_is_cfg(node) && !bitset_get(&thread->worklist.visited, geti(node_index))) +// { +// bitset_set_value(&thread->worklist.visited, geti(node_index), 1); +// auto outputs = node_get_outputs(thread, node); +// for (u64 i = 0; i < outputs.length; i += 1) +// { +// auto output = outputs.pointer[i]; +// if (validi(output)) +// { +// rpo_cfg(thread, output); +// } +// } +// +// *vb_add(&thread->worklist.nodes, 1) = node_index; +// } +// } + +// fn s32 node_loop_depth(Thread* thread, Node* node) +// { +// assert(node_is_cfg(node)); +// s32 loop_depth; +// +// switch (node->id) +// { +// case IR_START: +// { +// loop_depth = node->start.cfg.loop_depth; +// if (!loop_depth) +// { +// loop_depth = node->start.cfg.loop_depth = 1; +// } +// } break; +// case IR_STOP: +// { +// loop_depth = node->stop.cfg.loop_depth; +// if (!loop_depth) +// { +// loop_depth = node->stop.cfg.loop_depth = 1; +// } +// } break; +// case IR_RETURN: +// { +// loop_depth = node->return_node.cfg.loop_depth; +// if (!loop_depth) +// { +// auto input_index = node_input_get(thread, node, 0); +// auto input = thread_node_get(thread, input_index); +// node->return_node.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); +// } +// } break; +// case IR_CONTROL_PROJECTION: +// { +// loop_depth = node->control_projection.cfg.loop_depth; +// if (!loop_depth) +// { +// auto input_index = node_input_get(thread, node, 0); +// auto input = thread_node_get(thread, input_index); +// node->control_projection.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); +// } +// } break; +// case IR_DEAD_CONTROL: +// { +// loop_depth = node->dead_control.cfg.loop_depth; +// if (!loop_depth) +// { +// auto input_index = node_input_get(thread, node, 0); +// auto input = thread_node_get(thread, input_index); +// node->dead_control.cfg.loop_depth = loop_depth = node_loop_depth(thread, input); +// } +// } break; +// default: +// trap(); +// } +// +// return loop_depth; +// } + +// fn u8 node_is_region(Node* node) +// { +// return (node->id == IR_REGION) | (node->id == IR_REGION_LOOP); +// } +// +// fn u8 node_is_pinned(Node* node) +// { +// switch (node->id) +// { +// case IR_PROJECTION: +// case IR_START: +// return 1; +// case IR_CONSTANT: +// case IR_INTEGER_SUBSTRACT: +// case IR_INTEGER_COMPARE_EQUAL: +// case IR_INTEGER_COMPARE_NOT_EQUAL: +// return 0; +// default: +// trap(); +// } +// } + +// fn s32 node_cfg_get_immediate_dominator_tree_depth(Node* node) +// { +// assert(node_is_cfg(node)); +// switch (node->id) +// { +// case IR_START: +// return 0; +// case IR_DEAD_CONTROL: +// todo(); +// case IR_CONTROL_PROJECTION: +// todo(); +// case IR_RETURN: +// todo(); +// case IR_STOP: +// todo(); +// default: +// trap(); +// } +// } + +// fn void schedule_early(Thread* thread, NodeIndex node_index, NodeIndex start_node) +// { +// if (validi(node_index) && !bitset_get(&thread->worklist.visited, geti(node_index))) +// { +// bitset_set_value(&thread->worklist.visited, geti(node_index), 1); +// +// auto* node = thread_node_get(thread, node_index); +// auto inputs = node_get_inputs(thread, node); +// +// for (u64 i = 0; i < inputs.length; i += 1) +// { +// auto input = inputs.pointer[i]; +// +// if (validi(input)) +// { +// auto* input_node = thread_node_get(thread, input); +// if (!node_is_pinned(input_node)) +// { +// schedule_early(thread, node_index, start_node); +// } +// } +// } +// +// if (!node_is_pinned(node)) +// { +// auto early = start_node; +// +// for (u64 i = 1; i < inputs.length; i += 1) +// { +// auto input_index = inputs.pointer[i]; +// auto input_node = thread_node_get(thread, input_index); +// auto control_input_index = node_input_get(thread, input_node, 0); +// auto* control_input_node = thread_node_get(thread, control_input_index); +// auto* early_node = thread_node_get(thread, early); +// auto input_depth = node_cfg_get_immediate_dominator_tree_depth(control_input_node); +// auto early_depth = node_cfg_get_immediate_dominator_tree_depth(early_node); +// if (input_depth > early_depth) +// { +// early = control_input_index; +// trap(); +// } +// } +// +// node_set_input(thread, node_index, 0, early); +// } +// } +// } +// +// fn u8 node_cfg_block_head(Node* node) +// { +// assert(node_is_cfg(node)); +// switch (node->id) +// { +// case IR_START: +// return 1; +// default: +// trap(); +// } +// } +// +// fn u8 is_forwards_edge(Thread* thread, NodeIndex output_index, NodeIndex input_index) +// { +// u8 result = validi(output_index) & validi(input_index); +// if (result) +// { +// auto* output = thread_node_get(thread, output_index); +// result = output->input_count > 2; +// if (result) +// { +// auto input_index2 = node_input_get(thread, output, 2); +// +// result = index_equal(input_index2, input_index); +// +// if (result) +// { +// trap(); +// } +// } +// } +// +// return result; +// } +// +// fn void schedule_late(Thread* thread, NodeIndex node_index, Slice(NodeIndex) nodes, Slice(NodeIndex) late) +// { +// if (!validi(late.pointer[geti(node_index)])) +// { +// auto* node = thread_node_get(thread, node_index); +// +// if (node_is_cfg(node)) +// { +// late.pointer[geti(node_index)] = node_cfg_block_head(node) ? node_index : node_input_get(thread, node, 0); +// } +// +// if (node->id == IR_PHI) +// { +// trap(); +// } +// +// auto outputs = node_get_outputs(thread, node); +// +// for (u32 i = 0; i < outputs.length; i += 1) +// { +// NodeIndex output = outputs.pointer[i]; +// if (is_forwards_edge(thread, output, node_index)) +// { +// trap(); +// } +// } +// +// for (u32 i = 0; i < outputs.length; i += 1) +// { +// NodeIndex output = outputs.pointer[i]; +// if (is_forwards_edge(thread, output, node_index)) +// { +// trap(); +// } +// } +// +// if (!node_is_pinned(node)) +// { +// unused(nodes); +// trap(); +// } +// } +// } + +// fn void gcm_build_cfg(Thread* thread, NodeIndex start_node_index, NodeIndex stop_node_index) +// { +// unused(stop_node_index); +// // Fix loops +// { +// // TODO: +// } +// +// // Schedule early +// rpo_cfg(thread, start_node_index); +// +// u32 i = thread->worklist.nodes.length; +// while (i > 0) +// { +// i -= 1; +// auto node_index = thread->worklist.nodes.pointer[i]; +// auto* node = thread_node_get(thread, node_index); +// node_loop_depth(thread, node); +// auto inputs = node_get_inputs(thread, node); +// for (u64 i = 0; i < inputs.length; i += 1) +// { +// auto input = inputs.pointer[i]; +// schedule_early(thread, input, start_node_index); +// } +// +// if (node_is_region(node)) +// { +// trap(); +// } +// } +// +// // Schedule late +// +// auto max_node_count = thread->buffer.nodes.length; +// auto* alloc = arena_allocate(thread->arena, NodeIndex, max_node_count * 2); +// auto late = (Slice(NodeIndex)) { +// .pointer = alloc, +// .length = max_node_count, +// }; +// auto nodes = (Slice(NodeIndex)) { +// .pointer = alloc + max_node_count, +// .length = max_node_count, +// }; +// +// schedule_late(thread, start_node_index, nodes, late); +// +// for (u32 i = 0; i < late.length; i += 1) +// { +// auto node_index = nodes.pointer[i]; +// if (validi(node_index)) +// { +// trap(); +// auto late_node_index = late.pointer[i]; +// node_set_input(thread, node_index, 0, late_node_index); +// } +// } +// } + +// may_be_unused fn void print_function(Thread* thread, Function* function) +// { +// print("fn {s}\n====\n", function->name); +// VirtualBuffer(NodeIndex) nodes = {}; +// *vb_add(&nodes, 1) = function->stop; +// +// while (1) +// { +// auto node_index = nodes.pointer[nodes.length - 1]; +// auto* node = thread_node_get(thread, node_index); +// +// if (node->input_count) +// { +// for (u32 i = 1; i < node->input_count; i += 1) +// { +// *vb_add(&nodes, 1) = node_input_get(thread, node, 1); +// } +// *vb_add(&nodes, 1) = node_input_get(thread, node, 0); +// } +// else +// { +// break; +// } +// } +// +// u32 i = nodes.length; +// while (i > 0) +// { +// i -= 1; +// +// auto node_index = nodes.pointer[i]; +// auto* node = thread_node_get(thread, node_index); +// unused(node); +// todo(); +// // auto* type = thread_type_get(thread, node->type); +// // print("%{u32} - {s} - {s} ", geti(node_index), type_id_to_string(type), node_id_to_string(node)); +// // auto inputs = node_get_inputs(thread, node); +// // auto outputs = node_get_outputs(thread, node); +// // +// // print("(INPUTS: { "); +// // for (u32 i = 0; i < inputs.length; i += 1) +// // { +// // auto input_index = inputs.pointer[i]; +// // print("%{u32} ", geti(input_index)); +// // } +// // print("} OUTPUTS: { "); +// // for (u32 i = 0; i < outputs.length; i += 1) +// // { +// // auto output_index = outputs.pointer[i]; +// // print("%{u32} ", geti(output_index)); +// // } +// // print_string(strlit("})\n")); +// } +// +// +// print("====\n", function->name); +// } + +// struct CBackend +// { +// VirtualBuffer(u8) buffer; +// Function* function; +// }; +// +// typedef struct CBackend CBackend; +// +// fn void c_lower_append_string(CBackend* backend, String string) +// { +// vb_append_bytes(&backend->buffer, string); +// } +// +// fn void c_lower_append_ch(CBackend* backend, u8 ch) +// { +// *vb_add(&backend->buffer, 1) = ch; +// } +// +// fn void c_lower_append_ch_repeated(CBackend* backend, u8 ch, u32 times) +// { +// u8* pointer = vb_add(&backend->buffer, times); +// memset(pointer, ch, times); +// } +// +// fn void c_lower_append_space(CBackend* backend) +// { +// c_lower_append_ch(backend, ' '); +// } +// +// fn void c_lower_append_space_margin(CBackend* backend, u32 times) +// { +// c_lower_append_ch_repeated(backend, ' ', times * 4); +// } + +// fn void c_lower_type(CBackend* backend, Thread* thread, TypeIndex type_index) +// { +// Type* type = thread_type_get(thread, type_index); +// switch (type->id) +// { +// case TYPE_INTEGER: +// { +// u8 ch[] = { 'u', 's' }; +// auto integer = &type->integer; +// u8 signedness_ch = ch[type->integer.is_signed]; +// c_lower_append_ch(backend, signedness_ch); +// u8 upper_digit = integer->bit_count / 10; +// u8 lower_digit = integer->bit_count % 10; +// if (upper_digit) +// { +// c_lower_append_ch(backend, upper_digit + '0'); +// } +// c_lower_append_ch(backend, lower_digit + '0'); +// } break; +// default: +// trap(); +// } +// } + +// fn void c_lower_node(CBackend* backend, Thread* thread, NodeIndex node_index) +// { +// unused(backend); +// auto* node = thread_node_get(thread, node_index); +// unused(node); +// // auto* type = thread_type_get(thread, node->type); +// // auto inputs = node_get_inputs(thread, node); +// // +// // switch (node->id) +// // { +// // case IR_CONSTANT: +// // { +// // switch (type->id) +// // { +// // case TYPE_INTEGER: +// // { +// // assert(type->integer.bit_count == 0); +// // assert(type->integer.is_constant); +// // assert(!type->integer.is_signed); +// // vb_generic_ensure_capacity(&backend->buffer, 1, 64); +// // auto current_length = backend->buffer.length; +// // auto buffer_slice = (String){ .pointer = backend->buffer.pointer + current_length, .length = backend->buffer.capacity - current_length, }; +// // auto written_characters = format_hexadecimal(buffer_slice, type->integer.constant); +// // backend->buffer.length = current_length + written_characters; +// // } break; +// // trap(); +// // default: +// // trap(); +// // } +// // } break; +// // case IR_INTEGER_SUBSTRACT: +// // { +// // auto left = inputs.pointer[1]; +// // auto right = inputs.pointer[2]; +// // c_lower_node(backend, thread, left); +// // c_lower_append_string(backend, strlit(" - ")); +// // c_lower_node(backend, thread, right); +// // } break; +// // case IR_INTEGER_COMPARE_EQUAL: +// // { +// // auto left = inputs.pointer[1]; +// // auto right = inputs.pointer[2]; +// // c_lower_node(backend, thread, left); +// // c_lower_append_string(backend, strlit(" == ")); +// // c_lower_node(backend, thread, right); +// // } break; +// // case IR_INTEGER_COMPARE_NOT_EQUAL: +// // { +// // auto left = inputs.pointer[1]; +// // auto right = inputs.pointer[2]; +// // c_lower_node(backend, thread, left); +// // c_lower_append_string(backend, strlit(" != ")); +// // c_lower_node(backend, thread, right); +// // } break; +// // // case IR_PROJECTION: +// // // { +// // // auto projected_node_index = inputs.pointer[0]; +// // // auto projection_index = node->projection.index; +// // // +// // // if (index_equal(projected_node_index, backend->function->start)) +// // // { +// // // if (projection_index == 0) +// // // { +// // // fail(); +// // // } +// // // // if (projection_index > interpreter->arguments.length + 1) +// // // // { +// // // // fail(); +// // // // } +// // // +// // // switch (projection_index) +// // // { +// // // case 1: +// // // c_lower_append_string(backend, strlit("argc")); +// // // break; +// // // // return interpreter->arguments.length; +// // // case 2: +// // // trap(); +// // // default: +// // // trap(); +// // // } +// // // } +// // // else +// // // { +// // // trap(); +// // // } +// // // } break; +// // default: +// // trap(); +// // } +// todo(); +// } + +// fn String c_lower(Thread* thread) +// { +// CBackend backend_stack = {}; +// CBackend* backend = &backend_stack; +// auto program_epilogue = strlit("#include \n" +// "typedef uint8_t u8;\n" +// "typedef uint16_t u16;\n" +// "typedef uint32_t u32;\n" +// "typedef uint64_t u64;\n" +// "typedef int8_t s8;\n" +// "typedef int16_t s16;\n" +// "typedef int32_t s32;\n" +// "typedef int64_t s64;\n" +// ); +// c_lower_append_string(backend, program_epilogue); +// +// for (u32 function_i = 0; function_i < thread->buffer.functions.length; function_i += 1) +// { +// auto* function = &thread->buffer.functions.pointer[function_i]; +// backend->function = function; +// c_lower_type(backend, thread, function->return_type); +// c_lower_append_space(backend); +// +// c_lower_append_string(backend, function->name); +// c_lower_append_ch(backend, argument_start); +// if (s_equal(function->name, strlit("main"))) +// { +// c_lower_append_string(backend, strlit("int argc, char* argv[]")); +// } +// +// c_lower_append_ch(backend, argument_end); +// c_lower_append_ch(backend, '\n'); +// c_lower_append_ch(backend, block_start); +// c_lower_append_ch(backend, '\n'); +// +// auto start_node_index = function->start; +// auto* start_node = thread_node_get(thread, start_node_index); +// assert(start_node->output_count > 0); +// auto stop_node_index = function->stop; +// +// auto proj_node_index = node_output_get(thread, start_node, 1); +// auto it_node_index = proj_node_index; +// u32 current_statement_margin = 1; +// +// while (!index_equal(it_node_index, stop_node_index)) +// { +// auto* it_node = thread_node_get(thread, it_node_index); +// auto outputs = node_get_outputs(thread, it_node); +// auto inputs = node_get_inputs(thread, it_node); +// +// switch (it_node->id) +// { +// // case IR_CONTROL_PROJECTION: +// // break; +// case IR_RETURN: +// { +// c_lower_append_space_margin(backend, current_statement_margin); +// c_lower_append_string(backend, strlit("return ")); +// assert(inputs.length > 1); +// assert(inputs.length == 2); +// auto input = inputs.pointer[1]; +// c_lower_node(backend, thread, input); +// c_lower_append_ch(backend, ';'); +// c_lower_append_ch(backend, '\n'); +// } break; +// // case IR_STOP: +// // break; +// default: +// todo(); +// } +// +// assert(outputs.length == 1); +// it_node_index = outputs.pointer[0]; +// } +// +// c_lower_append_ch(backend, block_end); +// } +// +// return (String) { .pointer = backend->buffer.pointer, .length = backend->buffer.length }; +// } + +declare_ip_functions(DebugType, debug_type) fn void thread_init(Thread* thread) { @@ -4709,87 +5908,157 @@ fn void thread_init(Thread* thread) thread->arena = arena_init_default(KB(64)); thread->main_function = -1; - Type top, bot, live_control, dead_control; - memset(&top, 0, sizeof(Type)); - top.id = TYPE_TOP; - memset(&bot, 0, sizeof(Type)); - bot.id = TYPE_BOTTOM; - memset(&live_control, 0, sizeof(Type)); - live_control.id = TYPE_LIVE_CONTROL; - memset(&dead_control, 0, sizeof(Type)); - dead_control.id = TYPE_DEAD_CONTROL; + // This assertion is here to make the pertinent changes in the reserve syscall - thread->types.top = intern_pool_get_or_put_new_type(thread, &top).index; - thread->types.bottom = intern_pool_get_or_put_new_type(thread, &bot).index; - thread->types.live_control = intern_pool_get_or_put_new_type(thread, &live_control).index; - thread->types.dead_control = intern_pool_get_or_put_new_type(thread, &dead_control).index; + // UINT32_MAX so they can be indexed via an unsigned integer of 32 bits + const u64 offsets[] = { + align_forward(sizeof(Type) * UINT32_MAX, page_size), + align_forward(sizeof(Node) * UINT32_MAX, page_size), + align_forward(sizeof(DebugType) * UINT32_MAX, page_size), + align_forward(sizeof(NodeIndex) * UINT32_MAX, page_size), + align_forward(sizeof(ArrayReference) * UINT32_MAX, page_size), + align_forward(sizeof(Function) * UINT32_MAX, page_size), + align_forward(sizeof(u8) * UINT32_MAX, page_size), + align_forward(sizeof(RegisterMask) * UINT32_MAX, page_size), + }; + static_assert(sizeof(thread->buffer) / sizeof(VirtualBuffer(u8)) == array_length(offsets)); + + // Compute the total size (this is optimized out into a constant + u64 total_size = 0; + for (u32 i = 0; i < array_length(offsets); i += 1) + { + total_size += offsets[i]; + } + + // Actually make the syscall + auto* ptr = reserve(total_size); + assert(ptr); + + auto* buffer_it = (VirtualBuffer(u8)*)&thread->buffer; + for (u32 i = 0; i < array_length(offsets); i += 1) + { + buffer_it->pointer = ptr; + ptr += offsets[i]; + } + + DebugType integer_type; + memset(&integer_type, 0, sizeof(u8)); + auto* it = &thread->types.debug.integer.array[0]; + + for (u8 signedness = 0; signedness <= 1; signedness += 1) + { + integer_type.integer.signedness = signedness; + + for (u8 bit_count = 8; bit_count <= 64; bit_count *= 2, it += 1) + { + integer_type.integer.bit_count = bit_count; + auto put_result = ip_DebugType_get_or_put_new(&thread->interned.debug_types, thread, &integer_type); + assert(!put_result.existing); + assert(validi(put_result.index)); + *it = put_result.index; + } + } + + // Type top, bot, live_control, dead_control; + // memset(&top, 0, sizeof(Type)); + // top.id = TYPE_TOP; + // memset(&bot, 0, sizeof(Type)); + // bot.id = TYPE_BOTTOM; + // memset(&live_control, 0, sizeof(Type)); + // live_control.id = TYPE_LIVE_CONTROL; + // memset(&dead_control, 0, sizeof(Type)); + // dead_control.id = TYPE_DEAD_CONTROL; + // + // thread->types.top = intern_pool_get_or_put_new_type(thread, &top).index; + // thread->types.bottom = intern_pool_get_or_put_new_type(thread, &bot).index; + // thread->types.live_control = intern_pool_get_or_put_new_type(thread, &live_control).index; + // thread->types.dead_control = intern_pool_get_or_put_new_type(thread, &dead_control).index; + // + // thread->types.integer.top = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 0, + // }); + // thread->types.integer.bottom = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 1, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 0, + // }); + // thread->types.integer.zero = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 1, + // .is_signed = 0, + // .bit_count = 0, + // }); + // thread->types.integer.u8 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 8, + // }); + // thread->types.integer.u16 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 16, + // }); + // thread->types.integer.u32 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 32, + // }); + // thread->types.integer.u64 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 0, + // .bit_count = 64, + // }); + // thread->types.integer.s8 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 1, + // .bit_count = 8, + // }); + // thread->types.integer.s16 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 1, + // .bit_count = 16, + // }); + // thread->types.integer.s32 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 1, + // .bit_count = 32, + // }); + // thread->types.integer.s64 = thread_get_integer_type(thread, (TypeInteger) { + // .constant = 0, + // .is_constant = 0, + // .is_signed = 1, + // .bit_count = 64, + // }); + + *vb_add(&thread->buffer.register_masks, 1) = (RegisterMask) { + .class = 1, + .may_spill = 0, + .mask = 0, + }; + *vb_add(&thread->buffer.register_masks, 1) = (RegisterMask) { + .class = REGISTER_CLASS_X86_64_GPR, + .may_spill = 0, + .mask = ((u16)0xffff & ~((u16)1 << RSP)), // & ~((u16)1 << RBP), + }; + +// global RegisterMask register_masks[] = { +// { +// }, +// { +// }, +// }; - thread->types.integer.top = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 0, - .bit_count = 0, - }); - thread->types.integer.bottom = thread_get_integer_type(thread, (TypeInteger) { - .constant = 1, - .is_constant = 0, - .is_signed = 0, - .bit_count = 0, - }); - thread->types.integer.zero = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 1, - .is_signed = 0, - .bit_count = 0, - }); - thread->types.integer.u8 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 0, - .bit_count = 8, - }); - thread->types.integer.u16 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 0, - .bit_count = 16, - }); - thread->types.integer.u32 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 0, - .bit_count = 32, - }); - thread->types.integer.u64 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 0, - .bit_count = 64, - }); - thread->types.integer.s8 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 1, - .bit_count = 8, - }); - thread->types.integer.s16 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 1, - .bit_count = 16, - }); - thread->types.integer.s32 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 1, - .bit_count = 32, - }); - thread->types.integer.s64 = thread_get_integer_type(thread, (TypeInteger) { - .constant = 0, - .is_constant = 0, - .is_signed = 1, - .bit_count = 64, - }); } fn void thread_clear(Thread* thread) @@ -4824,132 +6093,6 @@ struct Interpreter }; typedef struct Interpreter Interpreter; -fn Interpreter* interpreter_create(Thread* thread) -{ - auto* interpreter = arena_allocate(thread->arena, Interpreter, 1); - *interpreter = (Interpreter){}; - return interpreter; -} - -fn s32 emit_node(Interpreter* interpreter, Thread* thread, NodeIndex node_index) -{ - auto* node = thread_node_get(thread, node_index); - auto inputs = node_get_inputs(thread, node); - s32 result = -1; - - switch (node->id) - { - case NODE_STOP: - case NODE_CONTROL_PROJECTION: - break; - case NODE_RETURN: - { - auto return_value = emit_node(interpreter, thread, inputs.pointer[1]); - result = return_value; - } break; - case NODE_CONSTANT: - { - auto constant_type_index = node->constant.type; - auto* constant_type = thread_type_get(thread, constant_type_index); - switch (constant_type->id) - { - case TYPE_INTEGER: - { - assert(constant_type->integer.is_constant); - result = constant_type->integer.constant; - } break; - default: - trap(); - } - } break; - case NODE_INTEGER_SUBSTRACT: - { - auto left = emit_node(interpreter, thread, inputs.pointer[1]); - auto right = emit_node(interpreter, thread, inputs.pointer[2]); - result = left - right; - } break; - case NODE_PROJECTION: - { - auto projected_node_index = inputs.pointer[0]; - auto projection_index = node->projection.index; - - if (index_equal(projected_node_index, interpreter->function->start)) - { - if (projection_index == 0) - { - fail(); - } - if (projection_index > interpreter->arguments.length + 1) - { - fail(); - } - - switch (projection_index) - { - case 1: - return interpreter->arguments.length; - case 2: - trap(); - default: - trap(); - } - trap(); - } - else - { - trap(); - } - - } break; - case NODE_INTEGER_COMPARE_EQUAL: - { - auto left = emit_node(interpreter, thread, inputs.pointer[1]); - auto right = emit_node(interpreter, thread, inputs.pointer[2]); - result = left == right; - } break; - case NODE_INTEGER_COMPARE_NOT_EQUAL: - { - auto left = emit_node(interpreter, thread, inputs.pointer[1]); - auto right = emit_node(interpreter, thread, inputs.pointer[2]); - result = left != right; - } break; - default: - trap(); - } - - return result; -} - -fn s32 interpreter_run(Interpreter* interpreter, Thread* thread) -{ - Function* function = interpreter->function; - auto start_node_index = function->start; - auto* start_node = thread_node_get(thread, start_node_index); - assert(start_node->output_count > 0); - auto stop_node_index = function->stop; - - auto proj_node_index = node_output_get(thread, start_node, 1); - auto it_node_index = proj_node_index; - - s32 result = -1; - - while (!index_equal(it_node_index, stop_node_index)) - { - auto* it_node = thread_node_get(thread, it_node_index); - auto outputs = node_get_outputs(thread, it_node); - auto this_result = emit_node(interpreter, thread, it_node_index); - if (this_result != -1) - { - result = this_result; - } - - assert(outputs.length == 1); - it_node_index = outputs.pointer[0]; - } - - return result; -} - struct ELFOptions { char* object_path; @@ -4990,7 +6133,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 = target_len - current_length; + auto count = cast(u32, u64, target_len - current_length); auto* pointer = vb_add(buffer, count); memset(pointer, 0, count); } @@ -5011,8 +6154,9 @@ fn ELFSectionHeader* elf_builder_add_section(ELFBuilder* builder, ELFSectionHead return section_header; } -fn void write_elf(Thread* thread, char** envp, const ELFOptions* const options) +may_be_unused fn void write_elf(Thread* thread, char** envp, const ELFOptions* const options) { + unused(thread); // { // auto main_c_content = strlit("int main()\n{\n return 0;\n}"); // int fd = syscall_open("main.c", O_WRONLY | O_CREAT | O_TRUNC, 0644); @@ -5154,8 +6298,8 @@ fn void write_elf(Thread* thread, char** envp, const ELFOptions* const options) .program_header_size = 0, .program_header_count = 0, .section_header_size = sizeof(ELFSectionHeader), - .section_header_count = builder->section_table.length, - .section_header_string_table_index = builder->section_table.length - 1, + .section_header_count = cast(u16, u64, builder->section_table.length), + .section_header_string_table_index = cast(u16, u64, builder->section_table.length - 1), }; auto object_path_z = options->object_path; @@ -5177,6 +6321,3399 @@ fn void write_elf(Thread* thread, char** envp, const ELFOptions* const options) run_command((CStringSlice) array_to_slice(command), envp); } +void subsume_node_without_killing(Thread* thread, NodeIndex old_node_index, NodeIndex new_node_index) +{ + assert(!index_equal(old_node_index, new_node_index)); + auto* old = thread_node_get(thread, old_node_index); + auto* new = thread_node_get(thread, new_node_index); + auto old_node_outputs = node_get_outputs(thread, old); + + u8 allow_cycle = old->id == IR_PHI || old->id == IR_REGION || new->id == IR_REGION; + + for (auto i = old->output_count; i > 0; i -= 1) + { + auto output = old_node_outputs.pointer[i - 1]; + old->output_count -= 1; + if (!allow_cycle && index_equal(output, new_node_index)) + { + continue; + } + + auto* output_node = thread_node_get(thread, output); + auto output_inputs = node_get_inputs(thread, output_node); + + u16 output_input_index; + for (output_input_index = 0; output_input_index < output_inputs.length; output_input_index += 1) + { + auto output_input = output_inputs.pointer[output_input_index]; + if (index_equal(output_input, old_node_index)) + { + output_inputs.pointer[output_input_index] = new_node_index; + node_add_output(thread, new_node_index, output); + break; + } + } + + assert(output_input_index < output_inputs.length); + } +} + +fn NodeIndex function_get_control_start(Thread* thread, Function* function) +{ + auto* root = thread_node_get(thread, function->root); + auto outputs = node_get_outputs(thread, root); + auto result = outputs.pointer[0]; + return result; +} + +fn u8 cfg_is_control(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + auto backend_type = type_pair_get_backend(node->type); + if (backend_type == BACKEND_TYPE_CONTROL) + { + return 1; + } + else if (backend_type == BACKEND_TYPE_TUPLE) + { + if (node->id == IR_ROOT) + { + return 1; + } + else + { + todo(); + } + } + + return 0; +} + +fn u8 cfg_node_terminator(Node* node) +{ + u8 is_terminator; + switch (node->id) + { + case IR_PROJECTION: + case IR_REGION: + is_terminator = 0; + break; + case IR_RETURN: + case IR_ROOT: + is_terminator = 1; + break; + default: + todo(); + } + + return is_terminator; +} + +fn NodeIndex basic_block_end(Thread* thread, NodeIndex start_index) +{ + auto node_index = start_index; + while (1) + { + auto* node = thread_node_get(thread, node_index); + u8 is_terminator = cfg_node_terminator(node); + + if (is_terminator) + { + break; + } + + auto outputs = node_get_outputs(thread, node); + auto new_node_index = node_index; + + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + auto* output = thread_node_get(thread, output_index); + auto output_inputs = node_get_inputs(thread, output); + if (index_equal(output_inputs.pointer[0], node_index) && cfg_is_control(thread, output_index)) + { + if (output->id == IR_REGION) + { + return node_index; + } + + new_node_index = output_index; + break; + } + } + + if (index_equal(node_index, new_node_index)) + { + break; + } + + node_index = new_node_index; + } + + return node_index; +} + +struct Block +{ + NodeIndex start; + NodeIndex end; + NodeIndex successors[2]; + u32 successor_count; + struct Block* parent; +}; +typedef struct Block Block; + +fn NodeIndex cfg_next_control(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + auto outputs = node_get_outputs(thread, node); + + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + if (cfg_is_control(thread, output_index)) + { + return output_index; + } + } + + return invalidi(Node); +} + +fn NodeIndex cfg_next_user(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + auto outputs = node_get_outputs(thread, node); + + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + if (cfg_is_control(thread, output_index)) + { + return output_index; + } + } + + return invalidi(Node); +} + +fn u8 cfg_is_endpoint(Thread* thread, Node* node) +{ + unused(thread); + switch (node->id) + { + case IR_ROOT: + case IR_RETURN: + return 1; + default: + return 0; + } +} + +fn Block* create_block(Thread* thread, NodeIndex node_index) +{ + auto end_of_basic_block_index = basic_block_end(thread, node_index); + auto* end_node = thread_node_get(thread, end_of_basic_block_index); + + u32 successor_count = 0; + // Branch + auto is_endpoint = cfg_is_endpoint(thread, end_node); + auto is_branch = 0; + if (is_branch) + { + todo(); + } + else if (type_pair_get_backend(end_node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + else if (!is_endpoint) + { + successor_count = 1; + } + + auto* block = arena_allocate(thread->arena, Block, 1); + *block = (Block) + { + .start = node_index, + .end = end_of_basic_block_index, + .successor_count = successor_count, + }; + + if (node_is_cfg_fork(end_node)) + { + todo(); + } + else if (!is_endpoint) + { + block->successors[0] = cfg_next_user(thread, end_of_basic_block_index); + } + + return block; +} + +fn NodeIndex node_select_instruction(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + switch (node->id) + { + case IR_PROJECTION: + return node_index; + case IR_ROOT: + return node_index; + case IR_PHI: + { + auto backend_type = type_pair_get_backend(node->type); + if (backend_type <= BACKEND_TYPE_SCALAR_LAST) + { + { + auto copy_index = thread_node_add(thread, (NodeCreate) + { + .id = MACHINE_COPY, + .type_pair = node->type, + .inputs = array_to_slice(((NodeIndex[]) { + invalidi(Node), + invalidi(Node), + })), + }); + thread_node_get(thread, copy_index)->machine_copy = (NodeMachineCopy) { + .use_mask = Index(RegisterMask, REGISTER_MASK_GPR), + .def_mask = Index(RegisterMask, REGISTER_MASK_GPR), + }; + subsume_node_without_killing(thread, node_index, copy_index); + node_set_input(thread, copy_index, 1, node_index); + node_gvn_intern(thread, copy_index); + } + + { + auto inputs = node_get_inputs(thread, node); + + for (u16 i = 1; i < inputs.length; i += 1) + { + auto input_index = inputs.pointer[i]; + auto input = thread_node_get(thread, input_index); + assert(input->id != MACHINE_MOVE); + auto move_index = thread_node_add(thread, (NodeCreate) + { + .id = MACHINE_MOVE, + .type_pair = input->type, + .inputs = array_to_slice(((NodeIndex[]) { + invalidi(Node), + invalidi(Node), + })), + }); + node_set_input(thread, move_index, 1, input_index); + node_set_input(thread, node_index, i, move_index); + node_gvn_intern(thread, move_index); + } + } + } + + return node_index; + } break; + case IR_RETURN: + case IR_REGION: + case IR_INTEGER_CONSTANT: + + case MACHINE_MOVE: + case MACHINE_COPY: + return invalidi(Node); + default: + todo(); + } +} + +struct BasicBlockIndex +{ + u32 index; +}; +typedef struct BasicBlockIndex BasicBlockIndex; +decl_vb(BasicBlockIndex); + +struct BasicBlock +{ + VirtualBuffer(NodeIndex) items; + Bitset gen; + Bitset kill; + Bitset live_in; + Bitset live_out; + NodeIndex start; + NodeIndex end; + s32 dominator_depth; + BasicBlockIndex dominator; + s32 forward; +}; +typedef struct BasicBlock BasicBlock; +decl_vb(BasicBlock); +decl_vbp(BasicBlock); + +fn u8 node_is_pinned(Node* node) +{ + switch (node->id) + { + case IR_ROOT: + case IR_PROJECTION: + case IR_RETURN: + case IR_REGION: + case IR_PHI: + return 1; + case IR_SYMBOL_TABLE: + case IR_INTEGER_ADD: + case IR_INTEGER_SUBSTRACT: + case IR_INTEGER_MULTIPLY: + case IR_INTEGER_DIVIDE: + case IR_INTEGER_REMAINDER: + case IR_INTEGER_SHIFT_LEFT: + case IR_INTEGER_SHIFT_RIGHT: + case IR_INTEGER_AND: + case IR_INTEGER_OR: + case IR_INTEGER_XOR: + case IR_INTEGER_COMPARE_EQUAL: + case IR_INTEGER_COMPARE_NOT_EQUAL: + case IR_INTEGER_NEGATION: + case IR_INTEGER_CONSTANT: + case MACHINE_COPY: + case MACHINE_MOVE: + case NODE_COUNT: + return 0; + case MACHINE_JUMP: + todo(); + } +} + +fn u8 node_has_memory_out(NodeId id) +{ + switch (id) + { + case IR_ROOT: + case IR_PROJECTION: + case IR_RETURN: + case IR_REGION: + case IR_PHI: + case IR_SYMBOL_TABLE: + case IR_INTEGER_ADD: + case IR_INTEGER_SUBSTRACT: + case IR_INTEGER_MULTIPLY: + case IR_INTEGER_DIVIDE: + case IR_INTEGER_REMAINDER: + case IR_INTEGER_SHIFT_LEFT: + case IR_INTEGER_SHIFT_RIGHT: + case IR_INTEGER_AND: + case IR_INTEGER_OR: + case IR_INTEGER_XOR: + case IR_INTEGER_COMPARE_EQUAL: + case IR_INTEGER_COMPARE_NOT_EQUAL: + case IR_INTEGER_NEGATION: + case IR_INTEGER_CONSTANT: + case MACHINE_COPY: + case MACHINE_MOVE: + case NODE_COUNT: + return 0; + case MACHINE_JUMP: + todo(); + } +} + +fn u8 node_has_memory_in(NodeId id) +{ + switch (id) + { + case IR_ROOT: + case IR_RETURN: + return 1; + case IR_PROJECTION: + case IR_REGION: + case IR_PHI: + case IR_SYMBOL_TABLE: + case IR_INTEGER_ADD: + case IR_INTEGER_SUBSTRACT: + case IR_INTEGER_MULTIPLY: + case IR_INTEGER_DIVIDE: + case IR_INTEGER_REMAINDER: + case IR_INTEGER_SHIFT_LEFT: + case IR_INTEGER_SHIFT_RIGHT: + case IR_INTEGER_AND: + case IR_INTEGER_OR: + case IR_INTEGER_XOR: + case IR_INTEGER_COMPARE_EQUAL: + case IR_INTEGER_COMPARE_NOT_EQUAL: + case IR_INTEGER_NEGATION: + case IR_INTEGER_CONSTANT: + case MACHINE_COPY: + case MACHINE_MOVE: + case NODE_COUNT: + return 0; + case MACHINE_JUMP: + todo(); + } +} + +fn NodeIndex node_memory_in(Thread* thread, Node* node) +{ + auto result = invalidi(Node); + if (node_has_memory_in(node->id)) + { + result = node_get_inputs(thread, node).pointer[1]; + } + + return result; +} + +fn s32 node_last_use_in_block(Thread* thread, VirtualBuffer(BasicBlockIndex) scheduled, Slice(s32) order, Node* node, BasicBlockIndex basic_block_index) +{ + auto outputs = node_get_outputs(thread, node); + s32 result = 0; + for (u16 i = 0; i < node->output_count; i += 1) + { + auto output_index = outputs.pointer[i]; + if (index_equal(basic_block_index, scheduled.pointer[geti(output_index)]) && result < order.pointer[geti(output_index)]) + { + result = order.pointer[geti(output_index)]; + } + } + + return result; +} + +fn BasicBlockIndex find_use_block(Thread* thread, VirtualBuffer(BasicBlockIndex) scheduled, NodeIndex node_index, NodeIndex actual_node_index, NodeIndex use_index) +{ + auto use_block_index = scheduled.pointer[geti(use_index)]; + if (!validi(use_block_index)) + { + return use_block_index; + } + + Node* use = thread_node_get(thread, use_index); + auto use_inputs = node_get_inputs(thread, use); + + if (use->id == IR_PHI) + { + auto use_first_input_index = use_inputs.pointer[0]; + auto use_first_input = thread_node_get(thread, use_first_input_index); + assert(use_first_input->id == IR_REGION); + assert(use->input_count == use_first_input->input_count + 1); + + auto use_first_input_inputs = node_get_inputs(thread, use_first_input); + + u16 i; + for (i = 0; i < use_inputs.length; i += 1) + { + auto use_input_index = use_inputs.pointer[i]; + if (index_equal(use_input_index, actual_node_index)) + { + // TODO: this assertion is mine for debugging when this function is only called from a single code path, + // it's not absolutely valid in other contexts + assert(index_equal(actual_node_index, node_index)); + auto input_index = use_first_input_inputs.pointer[i - 1]; + auto bb_index = scheduled.pointer[geti(input_index)]; + if (validi(bb_index)) + { + use_block_index = bb_index; + } + break; + } + } + + assert(i < use_inputs.length); + } + + return use_block_index; +} + +fn BasicBlockIndex find_lca(BasicBlockIndex a, BasicBlockIndex b) +{ + unused(a); + unused(b); + // TODO: dominators + return invalidi(BasicBlock); +} + +fn u8 node_is_ready(Thread* thread, VirtualBuffer(BasicBlockIndex) scheduled, WorkListHandle handle, Node* node, BasicBlockIndex basic_block_index) +{ + // TODO: this is my assert and might not be true after all + assert(node->input_capacity == node->input_count); + auto inputs = node_get_inputs(thread, node); + for (u16 i = 0; i < node->input_capacity; i += 1) + { + auto input = inputs.pointer[i]; + if (validi(input) && index_equal(scheduled.pointer[geti(input)], basic_block_index) && !thread_worklist_test(thread, handle, input)) + { + return 0; + } + } + + return 1; +} + +fn u64 node_get_latency(Thread* thread, Node* node, Node* end) +{ + unused(end); + unused(thread); + switch (node->id) + { + case IR_INTEGER_CONSTANT: + case IR_RETURN: + case MACHINE_COPY: + return 1; + case MACHINE_MOVE: + return 0; + default: + todo(); + } +} + +fn u64 node_get_unit_mask(Thread* thread, Node* node) +{ + unused(thread); + unused(node); + return 1; +} + +struct ReadyNode +{ + u64 unit_mask; + NodeIndex node_index; + s32 priority; +}; +typedef struct ReadyNode ReadyNode; +decl_vb(ReadyNode); + +struct InFlightNode +{ + NodeIndex node_index; + u32 end; + s32 unit_i; +}; +typedef struct InFlightNode InFlightNode; +decl_vb(InFlightNode); + +struct Scheduler +{ + Bitset ready_set; + VirtualBuffer(ReadyNode) ready; + NodeIndex cmp; +}; +typedef struct Scheduler Scheduler; + +fn s32 node_best_ready(Scheduler* restrict scheduler, u64 in_use_mask) +{ + auto length = scheduler->ready.length; + if (length == 1) + { + u64 available = scheduler->ready.pointer[0].unit_mask & ~in_use_mask; + return available ? 0 : -1; + } + + while (length--) + { + auto node_index = scheduler->ready.pointer[length].node_index; + if (index_equal(node_index, scheduler->cmp)) + { + continue; + } + + auto available = scheduler->ready.pointer[length].unit_mask & ~in_use_mask; + if (available == 0) + { + continue; + } + + return cast(s32, u32, length); + } + + return -1; +} + +declare_ip_functions(RegisterMask, register_mask) + +fn RegisterMaskIndex register_mask_intern(Thread* thread, RegisterMask register_mask) +{ + 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 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); + thread->buffer.register_masks.length -= result.existing; + + return final_index; +} + +fn RegisterMaskIndex node_constraint(Thread* thread, Node* node, Slice(RegisterMaskIndex) ins) +{ + switch (node->id) + { + case IR_PROJECTION: + { + auto backend_type = type_pair_get_backend(node->type); + if (backend_type == BACKEND_TYPE_MEMORY || backend_type == BACKEND_TYPE_CONTROL) + { + return empty_register_mask; + } + + auto index = node->projection.index; + auto inputs = node_get_inputs(thread, node); + + auto* first_input = thread_node_get(thread, inputs.pointer[0]); + if (first_input->id == IR_ROOT) + { + assert(index >= 2); + if (index == 2) + { + return empty_register_mask; + } + else + { + todo(); + } + todo(); + } + else + { + todo(); + } + } break; + case IR_INTEGER_CONSTANT: + return Index(RegisterMask, REGISTER_MASK_GPR); + case MACHINE_MOVE: + { + // TODO: float + auto mask = Index(RegisterMask, REGISTER_MASK_GPR); + if (ins.length) + { + ins.pointer[1] = mask; + } + return mask; + } break; + case MACHINE_COPY: + { + if (ins.length) + { + ins.pointer[1] = node->machine_copy.use_mask; + } + + return node->machine_copy.def_mask; + } break; + case IR_REGION: + { + if (ins.length) + { + for (u16 i = 1; i < node->input_count; i += 1) + { + ins.pointer[i] = empty_register_mask; + } + } + + return empty_register_mask; + } break; + case IR_PHI: + { + if (ins.length) + { + for (u16 i = 1; i < node->input_count; i += 1) + { + ins.pointer[i] = empty_register_mask; + } + } + + auto backend_type = type_pair_get_backend(node->type); + RegisterMaskIndex mask; + if (backend_type == BACKEND_TYPE_MEMORY) + { + mask = empty_register_mask; + } + // TODO: float + else + { + mask = Index(RegisterMask, REGISTER_MASK_GPR); + } + + return mask; + } break; + case IR_RETURN: + { + if (ins.length) + { + const global s32 ret_gprs[] = { RAX, RDX }; + + ins.pointer[1] = empty_register_mask; + ins.pointer[2] = empty_register_mask; + + // TODO: returns + auto index = 3; + ins.pointer[index] = register_mask_intern(thread, (RegisterMask) { + .class = REGISTER_CLASS_X86_64_GPR, + .may_spill = 0, + .mask = ((u32)1 << ret_gprs[index - 3]), + }); + + auto gpr_caller_saved = ((1u << RAX) | (1u << RDI) | (1u << RSI) | (1u << RCX) | (1u << RDX) | (1u << R8) | (1u << R9) | (1u << R10) | (1u << R11)); + auto gpr_callee_saved = ~gpr_caller_saved; + gpr_callee_saved &= ~(1u << RSP); + gpr_callee_saved &= ~(1u << RBP); + + auto j = 3 + 1; + + for (u32 i = 0; i < register_count_per_class[REGISTER_CLASS_X86_64_GPR]; i += 1) + { + if ((gpr_callee_saved >> i) & 1) + { + ins.pointer[j++] = register_mask_intern(thread, (RegisterMask) { + .class = REGISTER_CLASS_X86_64_GPR, + .mask = (u32)1 << i, + .may_spill = 0, + }); + } + } + + // TODO: float + } + + return empty_register_mask; + } break; + default: + todo(); + } +} + +fn u32 node_tmp_count(Node* node) +{ + switch (node->id) + { + case IR_ROOT: + case IR_PROJECTION: + case IR_RETURN: + case IR_REGION: + case IR_PHI: + case IR_SYMBOL_TABLE: + case IR_INTEGER_ADD: + case IR_INTEGER_SUBSTRACT: + case IR_INTEGER_MULTIPLY: + case IR_INTEGER_DIVIDE: + case IR_INTEGER_REMAINDER: + case IR_INTEGER_SHIFT_LEFT: + case IR_INTEGER_SHIFT_RIGHT: + case IR_INTEGER_AND: + case IR_INTEGER_OR: + case IR_INTEGER_XOR: + case IR_INTEGER_COMPARE_EQUAL: + case IR_INTEGER_COMPARE_NOT_EQUAL: + case IR_INTEGER_NEGATION: + case IR_INTEGER_CONSTANT: + case MACHINE_COPY: + case MACHINE_MOVE: + case NODE_COUNT: + return 0; + case MACHINE_JUMP: + todo(); + } +} + +struct VirtualRegister +{ + RegisterMaskIndex mask; + NodeIndex node_index; + f32 spill_cost; + f32 spill_bias; + s16 class; + s16 assigned; + s32 hint_vreg; +}; +typedef struct VirtualRegister VirtualRegister; +decl_vb(VirtualRegister); + + +fn s32 fixed_register_mask(RegisterMask mask) +{ + if (mask.class == REGISTER_CLASS_STACK) + { + todo(); + } + else + { + s32 set = -1; + // TODO: count? + for (s32 i = 0; i < 1; i += 1) + { + u32 m = mask.mask; + s32 found = 32 - __builtin_clz(m); + if (m == ((u32)1 << found)) + { + if (set >= 0) + { + return -1; + } + + set = i * 64 + found; + } + } + + return set; + } +} + +fn RegisterMaskIndex register_mask_meet(Thread* thread, RegisterMaskIndex a_index, RegisterMaskIndex b_index) +{ + if (index_equal(a_index, b_index)) + { + return a_index; + } + + if (!validi(a_index)) + { + return b_index; + } + if (!validi(b_index)) + { + return a_index; + } + + auto* a = thread_register_mask_get(thread, a_index); + auto* b = thread_register_mask_get(thread, b_index); + + u64 may_spill = a->may_spill && b->may_spill; + if (!may_spill && a->class != b->class) + { + return empty_register_mask; + } + + auto a_mask = a->mask; + auto b_mask = b->mask; + auto mask = a_mask & b_mask; + + auto result = register_mask_intern(thread, (RegisterMask) { + .class = mask == 0 ? 1 : a->class, + .may_spill = may_spill, + .mask = mask, + }); + + return result; +} + +fn s32 node_to_address(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + switch (node->id) + { + case IR_PHI: + case IR_INTEGER_CONSTANT: + return -1; + case MACHINE_COPY: + return 1; + default: + todo(); + } +} + +fn u8 interfere_in_block(Thread* thread, VirtualBuffer(BasicBlockIndex) scheduled, VirtualBuffer(BasicBlock) bb, Slice(s32) order, NodeIndex left, NodeIndex right, BasicBlockIndex block_index) +{ + assert(!index_equal(left, right)); + auto* block = &bb.pointer[geti(block_index)]; + auto left_live_out = bitset_get(&block->live_out, geti(left)); + auto right_live_out = bitset_get(&block->live_out, geti(right)); + auto* left_node = thread_node_get(thread, left); + auto* right_node = thread_node_get(thread, right); + + if (left_node->id == IR_PHI || right_node->id == IR_PHI) + { + auto phi = right; + auto other = left; + + if (left_node->id == IR_PHI && right_node->id != IR_PHI) + { + phi = left; + other = right; + } + + block_index = scheduled.pointer[geti(phi)]; + block = &bb.pointer[geti(block_index)]; + if (bitset_get(&block->live_out, geti(phi))) + { + todo(); + } + } + + if (left_live_out && right_live_out) + { + todo(); + } + else if (!left_live_out && !right_live_out) + { + auto first = left; + auto last = right; + + if (order.pointer[geti(left)] > order.pointer[geti(right)]) + { + first = right; + last = left; + } + + block_index = scheduled.pointer[geti(last)]; + block = &bb.pointer[geti(block_index)]; + + auto* first_node = thread_node_get(thread, first); + auto outputs = node_get_outputs(thread, first_node); + + for (u16 i = 0; i < first_node->output_count; i += 1) + { + auto output_index = outputs.pointer[i]; + assert(validi(output_index)); + auto* output_node = thread_node_get(thread, output_index); + auto output_inputs = node_get_inputs(thread, output_node); + + u16 i; + for (i = 0; i < output_node->input_count; i += 1) + { + auto input_index = output_inputs.pointer[i]; + if (index_equal(input_index, first)) + { + if (index_equal(block_index, scheduled.pointer[geti( output_index)])) + { + if (order.pointer[geti(output_index)] > order.pointer[geti(last)]) + { + return 1; + } + } + break; + } + } + assert(i < output_node->input_count); + } + } + else + { + todo(); + } + + return 0; +} + +fn u8 interfere(Thread* thread, VirtualBuffer(BasicBlockIndex) scheduled, VirtualBuffer(BasicBlock) bb, Slice(s32) order, NodeIndex left, NodeIndex right) +{ + auto left_block = scheduled.pointer[geti(left)]; + auto right_block = scheduled.pointer[geti(right)]; + // These asserts are mine, they might not be valid + assert(validi(left_block)); + assert(validi(right_block)); + + auto result = interfere_in_block(thread, scheduled, bb, order, left, right, left_block); + + if (!index_equal(left_block, right_block)) + { + result = result || interfere_in_block(thread, scheduled, bb, order, right, left, right_block); + } + + return result; +} + +fn Slice(s32) compute_ordinals(Thread* thread, VirtualBuffer(BasicBlock) bb, u32 node_count) +{ + auto order_cap = round_up_to_next_power_of_2(node_count); + auto order = arena_allocate(thread->arena, s32, order_cap); + + for (u32 i = 0; i < bb.length; i += 1) + { + auto* basic_block = & bb.pointer[i]; + s32 timeline = 1; + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + order[geti(node_index)] = timeline; + timeline += 1; + } + } + + return (Slice(s32)) { .pointer = order, .length = order_cap, }; +} + +fn u8 can_remat(Thread* thread, NodeIndex node_index) +{ + auto* node = thread_node_get(thread, node_index); + switch (node->id) + { + case MACHINE_COPY: + return 1; + default: + todo(); + } +} + +fn f32 get_spill_cost(Thread* thread, VirtualRegister* virtual_register) +{ + auto spill_cost = virtual_register->spill_cost; + if (__builtin_isnan(spill_cost)) + { + if (can_remat(thread, virtual_register->node_index)) + { + spill_cost = virtual_register->spill_bias - 1.0f; + } + else + { + todo(); + } + + virtual_register->spill_cost = spill_cost; + } + + return spill_cost; +} + +fn u8 register_mask_not_empty(RegisterMask mask) +{ + return mask.mask != 0; +} + +fn u8 register_mask_spill(RegisterMask mask) +{ + return mask.class != REGISTER_CLASS_STACK && (!register_mask_not_empty(mask) && mask.may_spill); +} + +fn void dataflow(Thread* thread, WorkListHandle worker, VirtualBuffer(BasicBlock) bb, VirtualBuffer(BasicBlockIndex) scheduled, u32 node_count) +{ + // Dataflow analysis + thread_worklist_clear(thread, worker); + + // TODO: separate per function + for (u32 i = 0; i < bb.length; i += 1) + { + BasicBlock* basic_block = &bb.pointer[i]; + + bitset_clear(&basic_block->gen); + bitset_clear(&basic_block->kill); + + bitset_ensure_length(&basic_block->gen, node_count); + bitset_ensure_length(&basic_block->kill, node_count); + } + + for (u32 i = 0; i < bb.length; i += 1) + { + BasicBlock* basic_block = &bb.pointer[i]; + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + NodeIndex node_index = basic_block->items.pointer[i]; + Node* node = thread_node_get(thread, node_index); + + if (node->id == IR_PHI) + { + auto phi_inputs = node_get_inputs(thread, node); + for (u16 i = 1; i < phi_inputs.length; i += 1) + { + auto input = phi_inputs.pointer[i]; + if (validi(input)) + { + auto input_bb_index = scheduled.pointer[geti(input)]; + bitset_set_value(&bb.pointer[geti(input_bb_index)].kill, geti(node_index), 1); + } + } + } + else + { + bitset_set_value(&basic_block->kill, geti(node_index), 1); + } + } + } + + for (u32 i = 0; i < bb.length; i += 1) + { + BasicBlock* basic_block = &bb.pointer[i]; + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + NodeIndex node_index = basic_block->items.pointer[i]; + Node* node = thread_node_get(thread, node_index); + + if (node->id != IR_PHI) + { + auto inputs = node_get_inputs(thread, node); + for (u16 i = 1; i < inputs.length; i += 1) + { + auto input_index = inputs.pointer[i]; + if (validi(input_index)) + { + auto* input = thread_node_get(thread, input_index); + if (input->id == IR_PHI || !bitset_get(&basic_block->kill, geti(input_index))) + { + bitset_set_value(&basic_block->gen, geti(input_index), 1); + } + } + } + } + } + } + + thread_worklist_clear(thread, worker); + + for (u32 i = 0; i < bb.length; i += 1) + { + BasicBlock* basic_block = &bb.pointer[i]; + assert(basic_block->gen.arr.length == basic_block->live_in.arr.length); + assert(basic_block->gen.arr.capacity == basic_block->live_in.arr.capacity); + memcpy(basic_block->live_in.arr.pointer, basic_block->gen.arr.pointer, sizeof(basic_block->gen.arr.pointer[0]) * basic_block->gen.arr.length); + basic_block->live_in.length = basic_block->gen.length; + thread_worklist_push(thread, worker, basic_block->start); + } + + while (thread_worklist_length(thread, worker) > 0) + { + auto bb_node_index = thread_worklist_pop(thread, worker); + auto basic_block_index = scheduled.pointer[geti(bb_node_index)]; + BasicBlock* basic_block = &bb.pointer[geti(basic_block_index)]; + + auto* live_out = &basic_block->live_out; + auto* live_in = &basic_block->live_in; + bitset_clear(live_out); + + auto end_index = basic_block->end; + auto* end = thread_node_get(thread, end_index); + auto cfg_is_fork = 0; + if (cfg_is_fork) + { + todo(); + } + else if (!cfg_is_endpoint(thread, end)) + { + auto succ_index = cfg_next_control(thread, end_index); + auto succ_bb_index = scheduled.pointer[geti(succ_index)]; + auto succ_bb = &bb.pointer[geti(succ_bb_index)]; + assert(live_out->arr.capacity == live_in->arr.capacity); + u64 changes = 0; + for (u32 i = 0; i < succ_bb->live_in.arr.capacity; i += 1) + { + auto old = live_out->arr.pointer[i]; + auto new = old | succ_bb->live_in.arr.pointer[i]; + + live_out->arr.pointer[i] = new; + changes |= (old ^ new); + } + unused(changes); + } + + auto* gen = &basic_block->gen; + auto* kill = &basic_block->kill; + + auto changes = 0; + + for (u32 i = 0; i < kill->arr.length; i += 1) + { + u64 new_in = (live_out->arr.pointer[i] & ~kill->arr.pointer[i]) | gen->arr.pointer[i]; + changes |= live_in->arr.pointer[i] != new_in; + live_in->arr.pointer[i] = new_in; + } + + if (changes) + { + todo(); + } + } +} + +fn void redo_dataflow(Thread* thread, WorkListHandle worker, VirtualBuffer(BasicBlock) bb, VirtualBuffer(BasicBlockIndex) scheduled, u32 node_count) +{ + for (u32 i = 0; i < bb.length; i += 1) + { + BasicBlock* basic_block = &bb.pointer[i]; + + bitset_clear(&basic_block->gen); + bitset_clear(&basic_block->kill); + + bitset_ensure_length(&basic_block->gen, node_count); + bitset_ensure_length(&basic_block->kill, node_count); + } + + dataflow(thread, worker, bb, scheduled, node_count); +} + +fn String gpr_to_string(GPR gpr) +{ + switch (gpr) + { + case_to_name(, RAX); + case_to_name(, RCX); + case_to_name(, RDX); + case_to_name(, RBX); + case_to_name(, RSP); + case_to_name(, RBP); + case_to_name(, RSI); + case_to_name(, RDI); + case_to_name(, R8); + case_to_name(, R9); + case_to_name(, R10); + case_to_name(, R11); + case_to_name(, R12); + case_to_name(, R13); + case_to_name(, R14); + case_to_name(, R15); + case_to_name(, GPR_NONE); + } +} + +fn u8 register_allocate(Thread* thread, VirtualBuffer(VirtualRegister) virtual_registers, VirtualBuffer(s32)* spills, Bitset* active, Bitset* future_active, VirtualBuffer(BasicBlockIndex) scheduled, VirtualBuffer(BasicBlock) bb, Slice(s32) order, u32 virtual_register_id, u32 in_use) +{ + if (bitset_get(future_active, virtual_register_id)) + { + todo(); + } + + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + auto mask = thread_register_mask_get(thread, virtual_register->mask); + + if (virtual_register->assigned >= 0) + { + bitset_set_value(active, virtual_register_id, 1); + return 1; + } + else if (register_mask_spill(*mask)) + { + todo(); + } + else if (mask->class == REGISTER_CLASS_STACK) + { + todo(); + } + + auto mask_value = mask->mask; + auto old_in_use = in_use; + in_use |= ~mask_value; + print("Vreg mask: {u32:x}. Complement: {u32:x}. In use before: {u32:x}. In use after: {u32:x}\n", mask_value, ~mask_value, old_in_use, in_use); + + spills->length = 0; + + *vb_add(spills, 1) = virtual_register_id; + + FOREACH_SET(i, active) + { + print("Active[{u64}] set\n", i); + VirtualRegister* other = &virtual_registers.pointer[i]; + if (other->class == mask->class) + { + print("Interfere with active: {u32}\n", (s32)other->assigned); + in_use |= ((u32)1 << other->assigned); + *vb_add(spills, 1) = i; + } + } + + FOREACH_SET(i, future_active) + { + print("Future active[{u64}] set\n", i); + VirtualRegister* other = &virtual_registers.pointer[i]; + if (other->class == mask->class && (in_use & ((u32)1 << other->assigned)) == 0) + { + if (interfere(thread, scheduled, bb, order, virtual_register->node_index, other->node_index)) + { + todo(); + } + } + } + + NodeIndex node_index = virtual_register->node_index; + auto hint_vreg = virtual_register->hint_vreg; + auto shared_edge = node_to_address(thread, node_index); + + if (shared_edge >= 0) + { + todo(); + } + + if (in_use == UINT32_MAX) + { + return 0; + } + + virtual_register->class = mask->class; + + auto hint_virtual_register = virtual_registers.pointer[hint_vreg]; + + s32 hint_reg = hint_vreg > 0 && hint_virtual_register.class == mask->class ? + hint_virtual_register.assigned : -1; + + print("IN USE: {u32:x}: ~ -> {u32:x}\n", in_use, ~in_use); + + if (hint_reg >= 0 && (in_use & ((u64)1 << hint_reg)) == 0) + { + todo(); + } + else + { + virtual_register->assigned = __builtin_ffsll(~in_use) - 1; + print("Register assigned: {s}\n", gpr_to_string(virtual_register->assigned)); + } + + bitset_set_value(active, virtual_register_id, 1); + + return 1; +} + +fn s32 machine_operand_at(u32* virtual_register_map, VirtualBuffer(VirtualRegister) virtual_registers, NodeIndex node_index, s32 class) +{ + assert(validi(node_index)); + auto virtual_register_id = virtual_register_map[geti(node_index)]; + assert(virtual_register_id > 0); + assert(virtual_register_id < virtual_registers.length); + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + assert(virtual_register->assigned >= 0); + assert(virtual_register->class == class); + + return virtual_register->assigned; +} + +typedef enum MachineOperandId : u8 +{ + MACHINE_OPERAND_MEMORY, + MACHINE_OPERAND_GPR, + MACHINE_OPERAND_XMM, +} MachineOperandId; + +struct MachineOperand +{ + MachineOperandId id; + s16 register_value; +}; +typedef struct MachineOperand MachineOperand; + +fn MachineOperand operand_from_node(Thread* thread, VirtualBuffer(VirtualRegister) virtual_registers, u32* virtual_register_map, NodeIndex node_index) +{ + assert(validi(node_index)); + auto virtual_register_id = virtual_register_map[geti(node_index)]; + assert(virtual_register_id > 0); + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + + if (virtual_register->class == REGISTER_CLASS_STACK) + { + todo(); + } + else + { + assert(virtual_register->assigned >= 0); + MachineOperandId id; + switch (virtual_register->class) + { + case REGISTER_CLASS_X86_64_GPR: + id = MACHINE_OPERAND_GPR; + break; + default: + todo(); + } + + return (MachineOperand) { + .id = id, + .register_value = virtual_register->assigned, + }; + } + + todo(); +} + +fn void node_ready_up(Thread* thread, Scheduler* scheduler, NodeIndex node_index, Node* end) +{ + auto* node = thread_node_get(thread, node_index); + auto priority = node_get_latency(thread, node, end); + auto unit_mask = node_get_unit_mask(thread, node); + + bitset_set_value(&scheduler->ready_set, geti(node_index), 1); + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + + u32 i; + auto count = scheduler->ready.length; + + for (i = 0; i < count; i += 1) + { + if (cast(s32, u64, priority) < scheduler->ready.pointer[i].priority) + { + break; + } + } + + *vb_add(&scheduler->ready, 1) = (ReadyNode){}; + + memmove(&scheduler->ready.pointer[i + 1], &scheduler->ready.pointer[i], (count - i) * sizeof(ReadyNode)); + + // print("Readying up node #{u32} ({s}) at index {u32}\n", geti(node_index), node_id_to_string(node->id), i); + + scheduler->ready.pointer[i] = (ReadyNode) { + .node_index = node_index, + .priority = cast(s32, u64, priority), + .unit_mask = unit_mask, + }; +} + +struct FixedBlockMap +{ + NodeIndex* keys; + u32 count; +}; +typedef struct FixedBlockMap FixedBlockMap; + +fn FixedBlockMap fixed_block_map_create(Thread* restrict thread, u32 count) +{ + auto* pointer = arena_allocate_bytes(thread->arena, sizeof(NodeIndex) * count + sizeof(BasicBlockIndex) * count, MAX(alignof(BasicBlockIndex), alignof(NodeIndex))); + return (FixedBlockMap) { + .keys = (NodeIndex*)pointer, + .count = count, + }; +} + +fn BasicBlockIndex* fixed_block_map_values(FixedBlockMap* restrict map) +{ + return (BasicBlockIndex*)(map->keys + map->count); +} + +fn void fixed_block_map_put(FixedBlockMap* restrict map, NodeIndex key, BasicBlockIndex value) +{ + auto count = map->count; + for (u32 i = 0; i < count; i += 1) + { + if (index_equal(key, map->keys[i])) + { + fixed_block_map_values(map)[i] = value; + break; + } + else if (!validi(map->keys[i])) + { + map->keys[i] = key; + fixed_block_map_values(map)[i] = value; + break; + } + } +} + +fn BasicBlockIndex fixed_block_map_get(FixedBlockMap* restrict map, NodeIndex key) +{ + auto count = map->count; + for (u32 i = 0; i < count; i += 1) + { + if (index_equal(key, map->keys[i])) + { + return fixed_block_map_values(map)[i]; + } + } + + return invalidi(BasicBlock); +} + +struct CFGBuilder +{ + VirtualBuffer(NodeIndex) pinned; + VirtualBuffer(BasicBlock) basic_blocks; + VirtualBuffer(BasicBlockIndex) scheduled; + FixedBlockMap block_map; + WorkListHandle walker; + WorkListHandle worker; +}; + +typedef struct CFGBuilder CFGBuilder; + +fn CFGBuilder cfg_builder_init(Thread* restrict thread) +{ + CFGBuilder cfg_builder = {}; + cfg_builder.walker = thread_worklist_acquire(thread); + cfg_builder.worker = thread_worklist_acquire(thread); + + return cfg_builder; +} + +fn void cfg_builder_clear(CFGBuilder* restrict builder, Thread* restrict thread) +{ + thread_worklist_clear(thread, builder->walker); + thread_worklist_clear(thread, builder->worker); + + builder->pinned.length = 0; + builder->basic_blocks.length = 0; + builder->scheduled.length = 0; +} + +struct CodegenOptions +{ + String test_name; + CompilerBackend backend; +}; +typedef struct CodegenOptions CodegenOptions; + +fn BasicBlockIndex cfg_get_predicate_basic_block(Thread* restrict thread, CFGBuilder* restrict builder, FixedBlockMap* map, NodeIndex arg_node_index, u16 i) +{ + auto* arg_node = thread_node_get(thread, arg_node_index); + auto arg_inputs = node_get_inputs(thread, arg_node); + auto node_index = arg_inputs.pointer[i]; + + while (1) + { + auto* node = thread_node_get(thread, node_index); + + auto search = fixed_block_map_get(map, node_index); + + if (validi(search)) + { + return search; + } + else + { + // TODO: or dead + if (node->id == IR_REGION) + { + return invalidi(BasicBlock); + } + } + + auto inputs = node_get_inputs(thread, node); + node_index = inputs.pointer[0]; + } + + unreachable(); +} + +fn void cfg_build(CFGBuilder* restrict builder, Thread* restrict thread, Function* restrict function) +{ + thread_worklist_push(thread, builder->worker, function->root); + + for (u64 i = 0; i < thread_worklist_length(thread, builder->worker); i += 1) + { + NodeIndex node_index = thread_worklist_get(thread, builder->worker, i); + Node* node = thread_node_get(thread, node_index); + auto pin = 0; + switch (node->id) + { + case IR_ROOT: + case IR_PHI: + case IR_RETURN: + case IR_REGION: + pin = 1; + break; + case IR_PROJECTION: + case IR_INTEGER_CONSTANT: + case IR_SYMBOL_TABLE: + break; + default: + todo(); + } + + if (pin) + { + *vb_add(&builder->pinned, 1) = node_index; + } + + auto outputs = node_get_outputs(thread, node); + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output = outputs.pointer[i]; + assert(validi(output)); + thread_worklist_push(thread, builder->worker, output); + } + } + + thread_worklist_clear(thread, builder->worker); + + for (u64 pin_index = 0; pin_index < builder->pinned.length; pin_index += 1) + { + auto pinned_node_index = builder->pinned.pointer[pin_index]; + thread_worklist_push(thread, builder->walker, pinned_node_index); + // auto* pinned_node = thread_node_get(thread, pinned_node_index); + + while (thread_worklist_length(thread, builder->walker) > 0) + { + auto node_index = thread_worklist_pop_array(thread, builder->walker); + auto* node = thread_node_get(thread, node_index); + assert(node->interned); + auto inputs = node_get_inputs(thread, node); + auto outputs = node_get_outputs(thread, node); + + if (node->id != IR_PROJECTION && node->output_count == 0) + { + todo(); + } + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_MEMORY) + { + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + auto* output = thread_node_get(thread, output_index); + if (output->output_count == 0) + { + thread_worklist_push(thread, builder->worker, output_index); + } + } + } + + node_gvn_remove(thread, node_index); + + auto new_node_index = node_select_instruction(thread, node_index); + + if (validi(new_node_index) && !index_equal(node_index, new_node_index)) + { + todo(); + } + + for (auto i = inputs.length; i > 0; i -= 1) + { + auto input = inputs.pointer[i - 1]; + if (validi(input)) + { + thread_worklist_push(thread, builder->walker, input); + } + } + + if (node->id == IR_REGION) + { + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + assert(validi(output_index)); + auto output = thread_node_get(thread, output_index); + if (output->id) + { + thread_worklist_push(thread, builder->walker, output_index); + } + } + } + } + } + + auto control_start = function_get_control_start(thread, function); + auto* top = create_block(thread, control_start); + thread_worklist_clear(thread, builder->worker); + thread_worklist_test_and_set(thread, builder->worker, control_start); + + while (top) + { + auto successor_count = top->successor_count; + if (successor_count > 0) + { + auto index = successor_count - 1; + auto node_index = top->successors[index]; + assert(validi(node_index)); + top->successor_count = index; + + // Returns valid when the node hasnt been pushed to the worklist yet + if (!thread_worklist_test_and_set(thread, builder->worker, node_index)) + { + auto* new_top = create_block(thread, node_index); + new_top->parent = top; + top = new_top; + } + } + else + { + Block* parent = top->parent; + *vb_add(&builder->basic_blocks, 1) = (BasicBlock) { + .start = top->start, + .end = top->end, + .dominator_depth = -1, + }; + + top = parent; + } + } + + for (u32 i = 0; i < builder->basic_blocks.length / 2; i += 1) + { + SWAP(builder->basic_blocks.pointer[i], builder->basic_blocks.pointer[(builder->basic_blocks.length - 1) - i]); + } + + auto* blocks = builder->basic_blocks.pointer; + blocks[0].dominator_depth = 0; + blocks[0].dominator = Index(BasicBlock, 0); + auto block_count = builder->basic_blocks.length; + + builder->block_map = fixed_block_map_create(thread, block_count); + for (u32 i = 0; i < block_count; i += 1) + { + auto* block = &blocks[i]; + auto block_index = Index(BasicBlock, i); + fixed_block_map_put(&builder->block_map, block->start, block_index); + } + + // Compute dominators + u8 changed = 1; + while (changed) + { + changed = 0; + + for (u32 i = 1; i < block_count; i += 1) + { + auto basic_block_index = Index(BasicBlock, i); + auto* basic_block = &blocks[i]; + + auto new_immediate_dominator_index = invalidi(BasicBlock); + + auto start_index = basic_block->start; + auto* start_node = thread_node_get(thread, start_index); + + auto start_inputs = node_get_inputs(thread, start_node); + + for (u16 j = 0; j < start_node->input_count; j += 1) + { + auto predecessor_basic_block_index = cfg_get_predicate_basic_block(thread, builder, &builder->block_map, start_index, j); + if (validi(predecessor_basic_block_index)) + { + auto* predecessor_basic_block = &blocks[geti(predecessor_basic_block_index)]; + auto immediate_dominator_predecessor_index = predecessor_basic_block->dominator; + if (validi(immediate_dominator_predecessor_index)) + { + if (validi(new_immediate_dominator_index)) + { + todo(); + } + else + { + new_immediate_dominator_index = predecessor_basic_block_index; + } + } + } + } + + assert(validi(new_immediate_dominator_index)); + if (!index_equal(basic_block->dominator, new_immediate_dominator_index)) + { + basic_block->dominator = new_immediate_dominator_index; + changed = 1; + } + } + } + + // Compute the depths + for (u32 i = 0; i < block_count; i += 1) + { + auto basic_block_index = Index(BasicBlock, i); + auto* basic_block = &blocks[geti(basic_block_index)]; + auto current_index = basic_block_index; + s32 depth = 0; + while (1) + { + auto* current = &blocks[geti(current_index)]; + if (current->dominator_depth >= 0) + { + break; + } + + current_index = current->dominator; + depth += 1; + } + + auto* current = &blocks[geti(current_index)]; + basic_block->dominator_depth = depth + current->dominator_depth; + } +} + +struct GlobalScheduleOptions +{ + u8 dataflow:1; +}; + +typedef struct GlobalScheduleOptions GlobalScheduleOptions; + +fn void basic_block_add_node(Thread* restrict thread, BasicBlock* restrict basic_block, NodeIndex node_index, u32 place) +{ + // if (geti(node_index) == 1) + // { + // breakpoint(); + // } + print("[PLACE #{u32}] Adding node #{u32} ({s}) to basic block 0x{u64:x} with index {u32}\n", place, geti(node_index), node_id_to_string(thread_node_get(thread, node_index)->id), basic_block, basic_block->items.length); + *vb_add(&basic_block->items, 1) = node_index; +} + +fn void cfg_global_schedule(CFGBuilder* restrict builder, Thread* restrict thread, Function* restrict function, GlobalScheduleOptions options) +{ + // Global code motion + + auto node_count = thread->buffer.nodes.length; + vb_add(&builder->scheduled, thread->buffer.nodes.length); + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + bitset_ensure_length(&basic_block->live_in, node_count); + 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)); + + 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)); + builder->scheduled.pointer[geti(basic_block->start)] = bb_index; + + if (i == 0) + { + auto* root_node = thread_node_get(thread, function->root); + auto outputs = node_get_outputs(thread, root_node); + + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output = outputs.pointer[i]; + builder->scheduled.pointer[geti(output)] = bb0; + basic_block_add_node(thread, &builder->basic_blocks.pointer[0], output, 0); + } + } + + auto* start = thread_node_get(thread, basic_block->start); + if (start->id == IR_REGION) + { + basic_block_add_node(thread, basic_block, basic_block->start, 1); + + auto outputs = node_get_outputs(thread, start); + + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output = outputs.pointer[i]; + auto* output_node = thread_node_get(thread, output); + if (output_node->id == IR_PHI) + { + builder->scheduled.pointer[geti(output)] = bb_index; + basic_block_add_node(thread, basic_block, output, 2); + } + } + } + } + + thread_worklist_clear(thread, builder->worker); + thread_worklist_push(thread, builder->worker, function->root); + + VirtualBuffer(NodeIndex) pins = {}; + + for (u32 i = 0; i < thread_worklist_length(thread, builder->worker); i += 1) + { + auto node_index = thread_worklist_get(thread, builder->worker, i); + auto* node = thread_node_get(thread, node_index); + if (node->id != IR_ROOT && node_is_pinned(node)) + { + auto bb_index = builder->scheduled.pointer[geti(node_index)]; + if (node->id == IR_PROJECTION && !node_is_pinned(thread_node_get(thread, node_get_inputs(thread, node).pointer[0]))) + { + } + else + { + auto current = node_index; + while (!validi(bb_index)) + { + bb_index = builder->scheduled.pointer[geti(current)]; + auto* current_node = thread_node_get(thread, current); + auto current_inputs = node_get_inputs(thread, current_node); + current = current_inputs.pointer[0]; + } + + auto* basic_block = &builder->basic_blocks.pointer[geti(bb_index)]; + builder->scheduled.pointer[geti(node_index)] = bb_index; + *vb_add(&pins, 1) = node_index; + basic_block_add_node(thread, basic_block, node_index, 3); + } + } + + auto outputs = node_get_outputs(thread, node); + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output = outputs.pointer[i]; + thread_worklist_push(thread, builder->worker, output); + } + } + + // Early schedule + thread_worklist_clear(thread, builder->worker); + + for (u32 i = 0; i < pins.length; i += 1) + { + auto pin_node_index = pins.pointer[i]; + auto* pin = thread_node_get(thread, pin_node_index); + + struct Elem + { + struct Elem* parent; + NodeIndex node; + u32 i; + }; + typedef struct Elem Elem; + + auto* top = arena_allocate(thread->arena, Elem, 1); + *top = (Elem) + { + .node = pin_node_index, + .parent = 0, + .i = pin->input_count, + }; + + while (top) + { + NodeIndex node_index = top->node; + Node* node = thread_node_get(thread, node_index); + auto node_inputs = node_get_inputs(thread, node); + + if (top->i > 0) + { + auto new_top_i = top->i - 1; + top->i = new_top_i; + NodeIndex input_index = node_inputs.pointer[new_top_i]; + + if (validi(input_index)) + { + Node* input = thread_node_get(thread, input_index); + if (input->id == IR_PROJECTION) + { + auto input_inputs = node_get_inputs(thread, input); + input_index = input_inputs.pointer[0]; + input = thread_node_get(thread, input_index); + } + + if (!node_is_pinned(input) && !thread_worklist_test_and_set(thread, builder->worker, input_index)) + { + auto* new_top = arena_allocate(thread->arena, Elem, 1); + *new_top = (Elem) + { + .parent = top, + .node = input_index, + .i = input->input_count, + }; + + top = new_top; + } + } + + continue; + } + + if (!index_equal(node_index, pin_node_index)) + { + auto best = Index(BasicBlock, 0); + s32 best_depth = 0; + + auto inputs = node_get_inputs(thread, node); + + for (u16 i = 0; i < node->input_count; i += 1) + { + auto input_index = inputs.pointer[i]; + if (validi(input_index)) + { + auto basic_block_index = builder->scheduled.pointer[geti(input_index)]; + if (validi(basic_block_index)) + { + auto* basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + + if (best_depth < basic_block->dominator_depth) + { + best_depth = basic_block->dominator_depth; + best = basic_block_index; + } + } + } + } + + builder->scheduled.pointer[geti(node_index)] = best; + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + + thread_worklist_push_array(thread, builder->worker, node_index); + } + + top = top->parent; + } + } + + // Late schedule + for (u64 i = thread_worklist_length(thread, builder->worker); i > 0; i -= 1) + { + auto node_index = thread_worklist_get(thread, builder->worker, i - 1); + auto* node = thread_node_get(thread, node_index); + assert(!node_is_pinned(node)); + auto current_basic_block_index = builder->scheduled.pointer[geti(node_index)]; + auto current_basic_block = &builder->basic_blocks.pointer[geti(current_basic_block_index)]; + + auto lca = invalidi(BasicBlock); + + if (!node_has_memory_out(node->id)) + { + auto memory_in = node_memory_in(thread, node); + if (validi(memory_in)) + { + todo(); + } + } + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + else + { + auto outputs = node_get_outputs(thread, node); + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output = outputs.pointer[i]; + auto use_block_index = find_use_block(thread, builder->scheduled, node_index, node_index, output); + if (validi(use_block_index)) + { + lca = find_lca(lca, use_block_index); + } + } + } + + if (validi(lca)) + { + todo(); + } + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + + basic_block_add_node(thread, current_basic_block, node_index, 4); + } + + if (options.dataflow) + { + dataflow(thread, builder->worker, builder->basic_blocks, builder->scheduled, node_count); + } +} + +fn void cfg_build_and_global_schedule(CFGBuilder* restrict builder, Thread* restrict thread, Function* restrict function, GlobalScheduleOptions options) +{ + cfg_build(builder, thread, function); + + cfg_global_schedule(builder, thread, function, options); +} + +fn void cfg_list_schedule(Thread* restrict thread, CFGBuilder* restrict builder, Function* restrict function, BasicBlockIndex basic_block_index) +{ + // print("=================================\nLIST SCHEDULER START\n=================================\n"); + thread_worklist_clear(thread, builder->worker); + + auto* restrict basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + thread_worklist_push(thread, builder->worker, basic_block->start); + + if (geti(basic_block_index) == 0) + { + auto* root_node = thread_node_get(thread, function->root); + auto root_outputs = node_get_outputs(thread, root_node); + + for (u32 i = 0; i < root_outputs.length; i += 1) + { + auto output = root_outputs.pointer[i]; + auto* output_node = thread_node_get(thread, output); + if (output_node->id == IR_PROJECTION) + { + thread_worklist_push(thread, builder->worker, output); + } + } + } + else + { + auto* bb_start = thread_node_get(thread, basic_block->start); + auto outputs = node_get_outputs(thread, bb_start); + for (u32 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + auto* output = thread_node_get(thread, output_index); + if (output->id == IR_PHI) + { + thread_worklist_push(thread, builder->worker, output_index); + } + } + } + + auto end_index = basic_block->end; + auto* end = thread_node_get(thread, end_index); + Scheduler scheduler = {}; + bitset_ensure_length(&scheduler.ready_set, thread->buffer.nodes.length); + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + + if (!thread_worklist_test(thread, builder->worker, node_index) && index_equal(builder->scheduled.pointer[geti(node_index)], basic_block_index) && node_is_ready(thread, builder->scheduled, builder->worker, node, basic_block_index)) + { + node_ready_up(thread, &scheduler, node_index, end); + } + } + + // TODO: IS BRANCH + VirtualBuffer(InFlightNode) active = {}; + u64 in_use_mask = 0; + u64 blocked_mask = UINT64_MAX >> (64 - 1); + u32 cycle = 0; + + while (active.length > 0 || scheduler.ready.length > 0) + { + while (in_use_mask != blocked_mask && scheduler.ready.length > 0) + { + auto signed_index = node_best_ready(&scheduler, in_use_mask); + if (signed_index < 0) + { + break; + } + auto index = cast(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 node_index = scheduler.ready.pointer[index].node_index; + auto* node = thread_node_get(thread, node_index); + + in_use_mask |= (u64)1 << unit_i; + + if (index + 1 < scheduler.ready.length) + { + todo(); + } + + scheduler.ready.length -= 1; + assert(node->id != IR_PROJECTION); + + auto end_cycle = cycle + node_get_latency(thread, node, end); + *vb_add(&active, 1) = (InFlightNode) + { + .node_index = node_index, + .end = cast(u32, u64, end_cycle), + .unit_i = unit_i, + }; + + if (node != end) + { + thread_worklist_push(thread, builder->worker, node_index); + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + } + } + + cycle += 1; + + for (u32 i = 0; i < active.length; i += 1) + { + auto active_i = active.pointer[i]; + auto node_index = active_i.node_index; + auto* node = thread_node_get(thread, node_index); + + if (active_i.end > cycle) + { + i += 1; + continue; + } + + in_use_mask &= ~((u64)1 << active_i.unit_i); + auto last = active.pointer[active.length - 1]; + active.pointer[i] = last; + active.length -= 1; + + auto outputs = node_get_outputs(thread, node); + for (u16 i = 0; i < outputs.length; i += 1) + { + auto output_index = outputs.pointer[i]; + auto* output = thread_node_get(thread, output_index); + if (output->id == IR_PROJECTION) + { + print("TODO: proj\n"); + todo(); + } + else if (!bitset_get(&scheduler.ready_set, geti(output_index)) && index_equal(builder->scheduled.pointer[geti(output_index)], basic_block_index) && !thread_worklist_test(thread, builder->worker, output_index) && node_is_ready(thread, builder->scheduled, builder->worker, output, basic_block_index)) + { + node_ready_up(thread, &scheduler, output_index, end); + } + } + } + } + + if (!index_equal(end_index, basic_block->start)) + { + thread_worklist_push(thread, builder->worker, end_index); + } + // print("=================================\nLIST SCHEDULER END\n=================================\n"); +} + +fn u8 operand_equal(MachineOperand a, MachineOperand b) +{ + if (a.id != b.id) + { + return 0; + } + + if (a.id == MACHINE_OPERAND_MEMORY) + { + todo(); + } + + return (a.id == MACHINE_OPERAND_GPR || a.id == MACHINE_OPERAND_XMM) ? a.register_value == b.register_value : 0; +} + +fn void code_generation(Thread* restrict thread, CodegenOptions options, char** envp) +{ + auto cfg_builder = cfg_builder_init(thread); + auto* restrict builder = &cfg_builder; + VirtualBuffer(u8) code = {}; + + for (u32 function_i = 0; function_i < thread->buffer.functions.length; function_i += 1) + { + Function* restrict function = &thread->buffer.functions.pointer[function_i]; + cfg_builder_clear(builder, thread); + + cfg_build_and_global_schedule(builder, thread, function, (GlobalScheduleOptions) { + .dataflow = 1, + }); + auto node_count = thread->buffer.nodes.length; + + u32 max_ins = 0; + u32 virtual_register_count = 1; + auto* virtual_register_map = arena_allocate(thread->arena, u32, round_up_to_next_power_of_2( node_count + 16)); + VirtualBuffer(s32) spills = {}; + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto basic_block_index = Index(BasicBlock, i); + BasicBlock* basic_block = &builder->basic_blocks.pointer[i]; + + cfg_list_schedule(thread, builder, function, basic_block_index); + + auto max_item_count = thread_worklist_length(thread, builder->worker); + print("Item count: {u32}\n", max_item_count); + + basic_block->items.length = 0; + + for (u32 i = 0; i < max_item_count; i += 1) + { + auto node_index = thread_worklist_get(thread, builder->worker, i); + basic_block_add_node(thread, basic_block, node_index, 5); + auto* node = thread_node_get(thread, node_index); + auto def_mask = node_constraint(thread, node, (Slice(RegisterMaskIndex)){}); + auto inputs = node->input_count + node_tmp_count(node); + + if (inputs > max_ins) + { + max_ins = inputs; + } + + u32 virtual_register_id = 0; + if (!index_equal(def_mask, empty_register_mask)) + { + if (node->id == MACHINE_MOVE) + { + assert(node->output_count == 1); + auto outputs = node_get_outputs(thread, node); + auto phi_index = outputs.pointer[0]; + auto* phi = thread_node_get(thread, phi_index); + assert(phi->id == IR_PHI); + + if (virtual_register_map[geti(phi_index)] == 0) + { + virtual_register_id = virtual_register_count; + virtual_register_count += 1; + virtual_register_map[geti(phi_index)] = virtual_register_id; + } + else + { + todo(); + } + } + else if (node->id == IR_PHI && virtual_register_map[geti(node_index)] > 0) + { + virtual_register_id = virtual_register_map[geti(node_index)]; + } + else + { + virtual_register_id = virtual_register_count; + virtual_register_count += 1; + } + } + + virtual_register_map[geti(node_index)] = virtual_register_id; + print("Assigning VR{u32} to node #{u32} ({s})\n", virtual_register_id, geti(node_index), node_id_to_string(node->id)); + } + } + + auto ins = (Slice(RegisterMaskIndex)) { + .pointer = arena_allocate(thread->arena, RegisterMaskIndex, max_ins), + .length = max_ins, + }; + // TODO: remove? + 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))); + virtual_registers.length = virtual_register_count; + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + + print("BB items: {u32}\n", basic_block->items.length); + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + auto virtual_register_id = virtual_register_map[geti(node_index)]; + assert(virtual_register_id >= 0 && virtual_register_id < virtual_register_count); + + if (virtual_register_id > 0 && node->id != MACHINE_MOVE) + { + auto mask_index = node_constraint(thread, node, (Slice(RegisterMaskIndex)){}); + print("Node #{u32} ({s}), VR{u32}, mask: ", geti(node_index), node_id_to_string(node->id), virtual_register_id); + if (validi(mask_index)) + { + print("0x{u32:x}", thread_register_mask_get(thread, mask_index)->mask); + } + else + { + print("invalid"); + } + print("\n"); + virtual_registers.pointer[virtual_register_id] = (VirtualRegister) { + .mask = mask_index, + .node_index = node_index, + .assigned = -1, + .spill_cost = NAN, + }; + } + } + } + + thread_worklist_clear(thread, builder->worker); + + u32 max_registers_in_class = 0; + auto* fixed = arena_allocate(thread->arena, s32, REGISTER_CLASS_X86_64_COUNT); + auto* in_use = arena_allocate(thread->arena, u32, REGISTER_CLASS_X86_64_COUNT); + + for (u32 class = 0; class < REGISTER_CLASS_X86_64_COUNT; class += 1) + { + auto count = register_count_per_class[class]; + max_registers_in_class = MAX(max_registers_in_class, count); + auto base = virtual_registers.length; + + for (u32 i = 0; i < count; i += 1) + { + auto mask = register_mask_intern(thread, (RegisterMask) { + .class = class, + .may_spill = 0, + .mask = class == 0 ? i : ((u64)1 << i), + }); + + *vb_add(&virtual_registers, 1) = (VirtualRegister) { + .mask = mask, + .class = cast(s16, u32, class), + .assigned = cast(s16, u32, i), + .spill_cost = INFINITY, + }; + } + + fixed[class] = cast(s32, u32, base); + } + + // Insert legalizing moves + 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, i); + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + auto tmp_count = node_tmp_count(node); + node_constraint(thread, node, ins); + + auto inputs = node_get_inputs(thread, node); + for (u16 i = 1; i < inputs.length; i += 1) + { + auto input_index = inputs.pointer[i]; + if (validi(input_index)) + { + // auto* input = thread_node_get(thread, input_index); + auto in_mask_index = ins.pointer[i]; + + if (!index_equal(in_mask_index, empty_register_mask)) + { + auto in_mask = thread_register_mask_get(thread, in_mask_index); + VirtualRegister* vreg = 0; + auto vreg_index = virtual_register_map[geti(input_index)]; + if (vreg_index > 0) + { + vreg = &virtual_registers.pointer[vreg_index]; + } + assert(vreg); + auto vreg_mask = thread_register_mask_get(thread, vreg->mask); + + auto hint = fixed_register_mask(*in_mask); + if (hint >= 0 && vreg_mask->class == in_mask->class) + { + vreg->hint_vreg = fixed[in_mask->class] + hint; + } + + auto new_mask_index = register_mask_meet(thread, in_mask_index, vreg->mask); + print("Input #{u32} ({s})\n", geti(input_index), node_id_to_string(thread_node_get(thread, input_index)->id)); + print("IN mask index: {u32}. TODO: not equal: {u32}, {u32}, {u32}\n", i, in_mask_index, empty_register_mask, new_mask_index); + if (!index_equal(in_mask_index, empty_register_mask) && index_equal(new_mask_index, empty_register_mask)) + { + // if (node->id == MACHINE_COPY) + { + print("{s} input count: {u32}\n", node_id_to_string(node->id), (u32)node->input_count); + } + todo(); + } + + auto* new_mask = thread_register_mask_get(thread, new_mask_index); + auto fixed = fixed_register_mask(*new_mask); + + if (fixed >= 0) + { + auto fixed_mask = ((u32)1 << fixed); + auto shared_edge = node_to_address(thread, input_index); + + if (shared_edge >= 0) + { + auto* input_node = thread_node_get(thread, input_index); + auto p_shared_edge = cast(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) + { + if (i != shared_edge) + { + auto input_index = inputs.pointer[i]; + if (validi(input_index)) + { + todo(); + } + } + } + } + } + + vreg->mask = new_mask_index; + } + } + } + + auto virtual_register_index = virtual_register_map[geti(node_index)]; + + if (tmp_count > 0) + { + todo(); + } + + if (virtual_register_index > 0) + { + auto* virtual_register = &virtual_registers.pointer[virtual_register_index]; + virtual_register->spill_cost = NAN; + + if (node->id == MACHINE_COPY) + { + auto* in = thread_node_get(thread, inputs.pointer[1]); + if (in->id == IR_PHI) + { + thread_worklist_push(thread, builder->worker, node_index); + } + } + } + } + } + + u8 changes = 0; + + if (thread_worklist_length(thread, builder->worker) > 0) + { + // Compute ordinals + auto order = compute_ordinals(thread, builder->basic_blocks, node_count); + while (thread_worklist_length(thread, builder->worker) > 0) + { + auto node_index = thread_worklist_pop(thread, builder->worker); + auto* node = thread_node_get(thread, node_index); + assert(node->id == MACHINE_COPY); + auto id = virtual_register_map[geti(node_index)]; + assert(id > 0); + auto mask_index = virtual_registers.pointer[id].mask; + auto inputs = node_get_inputs(thread, node); + + if (!interfere(thread, builder->scheduled, builder->basic_blocks, order, node_index, inputs.pointer[1])) + { + auto basic_block_index = builder->scheduled.pointer[geti(node_index)]; + auto* basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + u64 i = 0; + auto count = basic_block->items.length; + while (i < count && !index_equal(basic_block->items.pointer[i], node_index)) + { + i += 1; + } + + assert(index_equal(basic_block->items.pointer[i], node_index)); + memmove(&basic_block->items.pointer[i], &basic_block->items.pointer[i + 1], (count - (i + 1)) * sizeof(NodeIndex)); + basic_block->items.length -= 1; + builder->scheduled.pointer[geti(node_index)] = invalidi(BasicBlock); + subsume_node_without_killing(thread, node_index, inputs.pointer[1]); + changes = 1; + } + } + } + + // TODO: spills + if (spills.length) + { + todo(); + changes = 1; + } + + if (changes) + { + redo_dataflow(thread, builder->worker, builder->basic_blocks, builder->scheduled, node_count); + } + + auto al_index = 0; + // Allocate loop + while (1) + { + print("==============================\n#{u32} Allocate loop\n==============================\n", al_index++); + + auto order = compute_ordinals(thread, builder->basic_blocks, node_count); + + Bitset active = {}; + bitset_ensure_length(&active, virtual_registers.length); + Bitset future_active = {}; + bitset_ensure_length(&future_active, virtual_registers.length); + Bitset live_out = {}; + bitset_ensure_length(&live_out, node_count); + + for (u32 block_i = 0; block_i < builder->basic_blocks.length; block_i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[block_i]; + + for (u32 node_i = 0; node_i < basic_block->items.length; node_i += 1) + { + auto node_index = basic_block->items.pointer[node_i]; + auto virtual_register_id = virtual_register_map[geti(node_index)]; + + if (virtual_register_id > 0) + { + auto* node = thread_node_get(thread, node_index); + + auto mask_index = virtual_registers.pointer[virtual_register_id].mask; + auto mask_pointer = thread_register_mask_get(thread, mask_index); + auto mask_value = *mask_pointer; + auto reg = fixed_register_mask(mask_value); + + // print("Block #{u32}, Node index #{u32}, Node GVN #{u32}, Node id: {s}, VR{u32}. Mask: {u32:x}. Reg: {u32:x}\n", block_i, node_i, geti(node_index), node_id_to_string(node->id), virtual_register_id, mask_value.mask, reg); + + if (reg >= 0) + { + todo(); + } + } + } + } + + if (spills.length) + { + todo(); + } + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + print("============\nBlock #{u32}\n============\n", i); + + auto basic_block_index = Index(BasicBlock, i); + + auto bb_live_in = &basic_block->live_in; + auto bb_live_out = &basic_block->live_out; + + FOREACH_SET(j, &live_out) if (!bitset_get(bb_live_in, j)) + { + auto virtual_register_id = virtual_register_map[j]; + print("General live out not present in basic block live in: N{u64}, VR{u32}\n", j, virtual_register_id); + if (virtual_register_id != 0) + { + u8 pause = 0; + for (u32 k = i; k < builder->basic_blocks.length; k += 1) + { + auto* other_basic_block = &builder->basic_blocks.pointer[k]; + if (bitset_get(&other_basic_block->live_in, j)) + { + todo(); + } + } + + bitset_set_value(&active, virtual_register_id, 0); + bitset_set_value(&live_out, j, 0); + } + } + + FOREACH_SET(j, bb_live_in) if (!bitset_get(&live_out, j)) + { + auto virtual_register_id = virtual_register_map[j]; + print("Basic block live in not present in general live out: N{u64}, VR{u32}\n", j, virtual_register_id); + + if (virtual_register_id > 0) + { + { + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + auto node_index = virtual_register->node_index; + auto* node = thread_node_get(thread, node_index); + print("[BB LIVE IN ] Allocating register for node #{u32} ({s})\n", geti(node_index), node_id_to_string(node->id)); + } + + if (!register_allocate(thread, virtual_registers, &spills, &active, &future_active, builder->scheduled, builder->basic_blocks, order, virtual_register_id, 0)) + { + todo(); + } + } + } + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + NodeIndex node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + auto def = order.pointer[geti(node_index)]; + + auto inputs = node_get_inputs(thread, node); + + print("Node #{u32} ({s}). Def: {u32}\n", geti(node_index), node_id_to_string(node->id), def); + + if (node->id == IR_PROJECTION && !index_equal(inputs.pointer[0], function->root)) + { + print("Skipping...\n"); + continue; + } + + if (node->id != IR_PHI) + { + print("Node is not PHI. Examining inputs ({u32})...\n", (u32)node->input_count); + + for (u16 i = 1; i < node->input_count; i += 1) + { + auto input_index = inputs.pointer[i]; + if (validi(input_index)) + { + auto virtual_register_id = virtual_register_map[geti(input_index)]; + print("Input {u32}: node #{u32} ({s}). VR{u32}\n", i, geti(input_index), node_id_to_string(thread_node_get(thread, input_index)->id), virtual_register_id); + if (virtual_register_id == 0) + { + print("Invalid vreg id. Removing from general live out and skipping...\n"); + bitset_set_value(&live_out, geti(input_index), 0); + continue; + } + + if (!bitset_get(&live_out, geti(input_index))) + { + print("Duplicate input. Skipping...\n"); + continue; + } + + auto* input = thread_node_get(thread, input_index); + auto last_use = node_last_use_in_block(thread, builder->scheduled, order, input, basic_block_index); + print("Last use: {u32}\n", last_use); + + if (bitset_get(bb_live_out, geti(input_index))) + { + todo(); + } + + print("Removing node #{u32} from general liveout\n", geti(input_index)); + bitset_set_value(&live_out, geti(input_index), 0); + + auto pause = last_use > def; + if (!pause) + { + for (u32 i = geti(basic_block_index); i < builder->basic_blocks.length; i += 1) + { + auto* other = &builder->basic_blocks.pointer[i]; + if (bitset_get(&other->live_in, geti(input_index))) + { + pause = 1; + break; + } + } + } + + if (pause) + { + bitset_set_value(&future_active, virtual_register_id, 1); + } + + print("Removing VR{u32} from general active\n", virtual_register_id); + bitset_set_value(&active, virtual_register_id, 0); + } + } + } + + for (u32 i = 0; i < REGISTER_CLASS_X86_64_COUNT; i += 1) + { + in_use[i] = 0; + } + + // TODO: tmps + + auto virtual_register_id = virtual_register_map[geti(node_index)]; + + if (virtual_register_id > 0) + { + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + auto class = virtual_register->class; + auto in_use_local = in_use[class]; + print("[ALLOCATE LOOP] Allocating register for node #{u32} ({s}), VR{u32}\n", geti(node_index), node_id_to_string(node->id), virtual_register_id); + if (!register_allocate(thread, virtual_registers, &spills, &active, &future_active, builder->scheduled, builder->basic_blocks, order, virtual_register_id, in_use_local)) + { + todo(); + } + + print("[END ALLOCATE LOOP]\n"); + assert(virtual_register_map[geti(node_index)] == virtual_register_id); + + auto def = virtual_register->node_index; + bitset_set_value(&live_out, geti(def), 1); + print("Setting as general live out node #{u32} ({s})\n", geti(def), node_id_to_string(thread_node_get(thread, def)->id)); + } + else if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + } + } + + break; + } + + // Basic block scheduling + + 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, 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; + + if (first_node->id == IR_REGION) + { + for (u32 i = 1; i < item_count; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + if (node->id != IR_PHI) + { + empty = 0; + break; + } + } + } + else if (item_count > 1 || node_is_control_projection(first_node)) + { + empty = 0; + } + + if (empty) + { + todo(); + } + else + { + basic_block->forward = cast(s32, u32, i); + + auto* bb_end = thread_node_get(thread, basic_block->end); + if (!cfg_node_terminator(bb_end)) + { + auto jump_node_index = thread_node_add(thread, (NodeCreate) + { + .id = MACHINE_JUMP, + .inputs = array_to_slice(((NodeIndex[]) { + invalidi(Node), + })), + .type_pair = type_pair_make(invalidi(DebugType), BACKEND_TYPE_CONTROL), + }); + auto successor_node_index = cfg_next_user(thread, basic_block->end); + auto* successor_node = thread_node_get(thread, successor_node_index); + auto successor_inputs = node_get_inputs(thread, successor_node); + u16 i; + for (i = 0; i < successor_node->input_count; i += 1) + { + auto input_index = successor_inputs.pointer[i]; + if (index_equal(input_index, basic_block->end)) + { + break; + } + } + assert(i < successor_node->input_count); + node_set_input(thread, successor_node_index, i, jump_node_index); + node_set_input(thread, jump_node_index, 0, basic_block->end); + basic_block->end = jump_node_index; + + basic_block_add_node(thread, basic_block, jump_node_index, 6); + + assert(builder->scheduled.length == geti(jump_node_index)); + *vb_add(&builder->scheduled, 1) = basic_block_index; + } + } + } + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + + auto forward = basic_block->forward; + while (forward != builder->basic_blocks.pointer[forward].forward) + { + forward = builder->basic_blocks.pointer[forward].forward; + } + + basic_block->forward = forward; + } + + auto* order = arena_allocate(thread->arena, s32, builder->basic_blocks.length); + + u32 order_index = 0; + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + if (basic_block->forward == i) + { + auto* end_node = thread_node_get(thread, basic_block->end); + if (end_node->id != IR_RETURN) + { + order[order_index] = i; + order_index += 1; + } + } + } + + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* basic_block = &builder->basic_blocks.pointer[i]; + if (basic_block->forward == i) + { + auto* end_node = thread_node_get(thread, basic_block->end); + if (end_node->id == IR_RETURN) + { + order[order_index] = i; + order_index += 1; + } + } + } + + // Emit + auto final_order_count = order_index; + + for (u32 order_index = 0; order_index < final_order_count; order_index += 1) + { + auto i = order[order_index]; + auto* basic_block = &builder->basic_blocks.pointer[i]; + + for (u32 i = 0; i < basic_block->items.length; i += 1) + { + auto node_index = basic_block->items.pointer[i]; + auto* node = thread_node_get(thread, node_index); + auto virtual_register_id = virtual_register_map[geti(node_index)]; + auto* virtual_register = &virtual_registers.pointer[virtual_register_id]; + auto inputs = node_get_inputs(thread, node); + + auto fallthrough = INT32_MAX; + if (order_index + 1 < final_order_count) + { + fallthrough = order[order_index + 1]; + } + + switch (node->id) + { + case IR_PROJECTION: + case IR_REGION: + case IR_PHI: + break; + case IR_INTEGER_CONSTANT: + { + auto value = node->integer_constant.unsigned_value; + GPR gpr = machine_operand_at(virtual_register_map, virtual_registers, node_index, REGISTER_CLASS_X86_64_GPR); + auto backend_type = type_pair_get_backend(node->type); + + if (backend_type == BACKEND_TYPE_INTEGER_32) + { + if (value == 0) + { + if (gpr == RAX) + { + *vb_add(&code, 1) = 0x31; + *vb_add(&code, 1) = 0xc0; + } + else + { + todo(); + } + } + else + { + todo(); + } + } + } break; + case MACHINE_MOVE: + { + auto destination = operand_from_node(thread, virtual_registers, virtual_register_map, node_index); + auto source = operand_from_node(thread, virtual_registers, virtual_register_map, inputs.pointer[1]); + if (!operand_equal(destination, source)) + { + todo(); + } + } break; + case MACHINE_JUMP: + { + auto successor_node_index = cfg_next_control(thread, node_index); + assert(validi(successor_node_index)); + auto successor_basic_block_index = fixed_block_map_get(&builder->block_map, successor_node_index); + assert(validi(successor_basic_block_index)); + auto* successor_basic_block = &builder->basic_blocks.pointer[geti(successor_basic_block_index)]; + if (fallthrough != successor_basic_block->forward) + { + todo(); + } + } break; + case IR_RETURN: + { + *vb_add(&code, 1) = 0xc3; + } break; + default: + todo(); + } + } + } + } + + auto object_path = arena_join_string(thread->arena, (Slice(String)) array_to_slice(((String[]) { + strlit("nest/"), + options.test_name, + options.backend == COMPILER_BACKEND_C ? strlit(".c") : strlit(".o"), + }))); + + auto exe_path_view = s_get_slice(u8, object_path, 0, object_path.length - 2); + auto exe_path = (char*)arena_allocate_bytes(thread->arena, exe_path_view.length + 1, 1); + memcpy(exe_path, exe_path_view.pointer, exe_path_view.length); + exe_path[exe_path_view.length] = 0; + + switch (options.backend) + { + case COMPILER_BACKEND_C: + { + // auto lowered_source = c_lower(thread); + // // print("Transpiled to C:\n```\n{s}\n```\n", lowered_source); + // + // file_write(object_path, lowered_source); + // + // char* command[] = { + // clang_path, "-g", + // "-o", exe_path, + // string_to_c(object_path), + // 0, + // }; + // + // run_command((CStringSlice) array_to_slice(command), envp); + todo(); + } break; + case COMPILER_BACKEND_INTERPRETER: + { + // auto* main_function = &thread->buffer.functions.pointer[thread->main_function]; + // auto* interpreter = interpreter_create(thread); + // interpreter->function = main_function; + // interpreter->arguments = (Slice(String)) array_to_slice(((String[]) { + // test_name, + // })); + // auto exit_code = interpreter_run(interpreter, thread); + // print("Interpreter exited with exit code: {u32}\n", exit_code); + // syscall_exit(exit_code); + todo(); + } break; + case COMPILER_BACKEND_MACHINE: + { + auto code_slice = (Slice(u8)) { .pointer = code.pointer, .length = code.length, }; + write_elf(thread, envp, &(ELFOptions) { + .object_path = string_to_c(object_path), + .exe_path = exe_path, + .code = code_slice, + }); + } break; + } +} + +fn u8 node_is_empty_control_projection(Thread* restrict thread, CFGBuilder* restrict builder, NodeIndex node_index) +{ + auto* restrict node = thread_node_get(thread, node_index); + u8 result = 0; + + if (node_is_control_projection(node)) + { + auto basic_block_index = builder->scheduled.pointer[geti(node_index)]; + auto* basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + result = basic_block->items.length == 1; + } + + return result; +} + +struct SchedPhi +{ + NodeIndex phi; + NodeIndex node; +}; +typedef struct SchedPhi SchedPhi; +decl_vb(SchedPhi); + +fn void fill_phis(Thread* restrict thread, CFGBuilder* restrict builder, Function* restrict function, VirtualBuffer(SchedPhi)* sched_phis, Node* restrict successor_node, NodeIndex original_index) +{ + auto succesor_inputs = node_get_inputs(thread, successor_node); + u16 i; + for (i = 0; i < successor_node->input_count; i += 1) + { + auto input_index = succesor_inputs.pointer[i]; + if (index_equal(input_index, original_index)) + { + break; + } + } + assert(i < successor_node->input_count); + auto phi_index = i; + + auto successor_outputs = node_get_outputs(thread, successor_node); + for (u16 i = 0; i < successor_node->output_count; i += 1) + { + auto output_index = successor_outputs.pointer[i]; + auto* output_node = thread_node_get(thread, output_index); + if (output_node->id == IR_PHI) + { + auto output_inputs = node_get_inputs(thread, output_node); + assert(phi_index + 1 < output_node->input_count); + *vb_add(sched_phis, 1) = (SchedPhi) { + .phi = output_index, + .node = output_inputs.pointer[phi_index + 1], + }; + } + } +} + +typedef struct SchedNode SchedNode; +struct SchedNode +{ + SchedNode* parent; + NodeIndex node_index; + s32 index; +}; + +fn u8 sched_in_basic_block(Thread* restrict thread, CFGBuilder* restrict builder, BasicBlockIndex basic_block_index, NodeIndex node_index) +{ + return index_equal(builder->scheduled.pointer[geti(node_index)], basic_block_index) && !thread_worklist_test_and_set(thread, builder->worker, node_index); +} + +fn void greedy_scheduler(Thread* restrict thread, CFGBuilder* restrict builder, Function* restrict function, BasicBlockIndex basic_block_index) +{ + thread_worklist_clear(thread, builder->worker); + + auto* restrict basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + auto end_index = basic_block->end; + auto* end_node = thread_node_get(thread, end_index); + + VirtualBuffer(SchedPhi) phis = {}; + + if (node_is_cfg_fork(end_node)) + { + todo(); + } + else if (!cfg_is_endpoint(thread, end_node)) + { + auto successor_index = cfg_next_user(thread, end_index); + auto* successor_node = thread_node_get(thread, successor_index); + if (successor_node->id == IR_REGION) + { + fill_phis(thread, builder, function, &phis, successor_node, end_index); + } + } + + auto* top = arena_allocate(thread->arena, SchedNode, 1); + *top = (SchedNode) + { + .node_index = end_index, + }; + thread_worklist_test_and_set(thread, builder->worker, end_index); + + if (geti(basic_block_index) == 0) + { + auto* root_node = thread_node_get(thread, function->root); + auto outputs = node_get_outputs(thread, root_node); + for (u16 i = 0; i < root_node->output_count; i += 1) + { + auto output_index = outputs.pointer[i]; + auto* output_node = thread_node_get(thread, output_index); + + if (output_node->id == IR_PROJECTION && !thread_worklist_test_and_set(thread, builder->worker, output_index)) + { + thread_worklist_push_array(thread, builder->worker, output_index); + } + } + } + + u64 phi_current = 0; + u64 leftovers = 0; + auto leftover_count = basic_block->items.length; + + while (top) + { + auto node_index = top->node_index; + auto* node = thread_node_get(thread, node_index); + + if (node->id != IR_PHI && top->index < node->input_capacity) + { + auto inputs = node_get_inputs(thread, node); + auto input_index = inputs.pointer[top->index]; + top->index += 1; + + if (validi(input_index)) + { + auto* input_node = thread_node_get(thread, input_index); + if (input_node->id == IR_PROJECTION) + { + auto projection_inputs = node_get_inputs(thread, input_node); + input_index = projection_inputs.pointer[0]; + input_node = thread_node_get(thread, input_index); + } + + if (sched_in_basic_block(thread, builder, basic_block_index, input_index)) + { + auto* new_top = arena_allocate(thread->arena, SchedNode, 1); + *new_top = (SchedNode) + { + .node_index = input_index, + .parent = top, + }; + top = new_top; + } + } + + continue; + } + + if (index_equal(end_index, node_index)) + { + if (phi_current < phis.length) + { + auto* restrict phi = &phis.pointer[phi_current]; + phi_current += 1; + + auto value = phi->node; + if (sched_in_basic_block(thread, builder, basic_block_index, value)) + { + auto* new_top = arena_allocate(thread->arena, SchedNode, 1); + *new_top = (SchedNode) + { + .node_index = value, + .parent = top, + }; + top = new_top; + } + + continue; + } + + auto try_again = 0; + while (leftovers < leftover_count) + { + auto index = leftovers; + leftovers += 1; + + auto bb_node_index = basic_block->items.pointer[index]; + + if (!thread_worklist_test_and_set(thread, builder->worker, bb_node_index)) + { + auto* new_top = arena_allocate(thread->arena, SchedNode, 1); + *new_top = (SchedNode) + { + .node_index = bb_node_index, + .parent = top, + }; + top = new_top; + try_again = 1; + break; + } + } + + if (try_again) + { + continue; + } + } + + thread_worklist_push_array(thread, builder->worker, node_index); + auto* parent = top->parent; + top = parent; + + if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + } +} + +fn void print_reference_to_node(Thread* restrict thread, CFGBuilder* restrict builder, Function* restrict function, NodeIndex node_index, u8 def) +{ + auto* restrict node = thread_node_get(thread, node_index); + print("[#{u32} ({s})", geti(node_index), node_id_to_string(node->id)); + + switch (node->id) + { + case IR_PROJECTION: + { + if (node_is_control_projection(node)) + { + todo(); + } + else + { + } + } break; + case IR_INTEGER_CONSTANT: + { + print(": 0x{u64:x}", node->integer_constant.unsigned_value); + } break; + // TODO: + case IR_REGION: + break; + case IR_PHI: + break; + case MACHINE_COPY: + case MACHINE_MOVE: + break; + default: + todo(); + } + + print("]"); +} + +fn void print_basic_block(Thread* restrict thread, CFGBuilder* restrict builder, Function* restrict function, BasicBlockIndex basic_block_index) +{ + auto* restrict basic_block = &builder->basic_blocks.pointer[geti(basic_block_index)]; + print_reference_to_node(thread, builder, function, basic_block->start, 1); + print("\n"); + greedy_scheduler(thread, builder, function, basic_block_index); + + for (u32 i = 0; i < thread_worklist_length(thread, builder->worker); i += 1) + { + auto node_index = thread_worklist_get(thread, builder->worker, i); + auto* node = thread_node_get(thread, node_index); + + switch (node->id) + { + case IR_PROJECTION: + case IR_INTEGER_CONSTANT: + case IR_REGION: + case IR_PHI: + continue; + case MACHINE_MOVE: + case MACHINE_COPY: + case IR_RETURN: + { + auto is_branch = 0; + + if (is_branch) + { + todo(); + } + else if (type_pair_get_backend(node->type) == BACKEND_TYPE_TUPLE) + { + todo(); + } + else + { + print(" "); + print("#{u32}", geti(node_index)); + print(" = {s}.", node_id_to_string(node->id)); + // TODO: print type + } + + print(" I({u32})", (u32)node->input_count); + + u64 first = node->id != IR_PROJECTION; + auto inputs = node_get_inputs(thread, node); + if (node->input_count - first) + { + print(": "); + for (auto i = first; i < node->input_count; i += 1) + { + if (i != first) + { + print(", "); + } + print_reference_to_node(thread, builder, function, inputs.pointer[i], 0); + } + } + else + { + print(" "); + } + + switch (node->id) + { + case MACHINE_MOVE: + case MACHINE_COPY: + case IR_REGION: + case IR_PHI: + case IR_RETURN: + break; + default: + todo(); + } + } break; + default: + todo(); + } + + print("\n"); + } + + thread_worklist_clear(thread, builder->worker); + + auto* end_node = thread_node_get(thread, basic_block->end); + if (cfg_node_terminator(end_node)) + { + // todo(); + } +} + +fn void print_ir(Thread* restrict thread) +{ + auto cfg_builder = cfg_builder_init(thread); + auto* restrict builder = &cfg_builder; + + for (u32 i = 0; i < thread->buffer.functions.length; i += 1) + { + Function* restrict function = &thread->buffer.functions.pointer[i]; + cfg_builder_clear(builder, thread); + + cfg_build_and_global_schedule(builder, thread, function, (GlobalScheduleOptions) { + .dataflow = 0, + }); + + auto end_basic_block_index = invalidi(BasicBlock); + for (u32 i = 0; i < builder->basic_blocks.length; i += 1) + { + auto* restrict basic_block = &builder->basic_blocks.pointer[i]; + auto end_node_index = basic_block->end; + auto* end_node = thread_node_get(thread, end_node_index); + if (end_node->id == IR_RETURN) + { + end_basic_block_index = Index(BasicBlock, i); + continue; + } + else if (node_is_empty_control_projection(thread, builder, end_node_index)) + { + continue; + } + + print_basic_block(thread, builder, function, Index(BasicBlock, i)); + } + + if (validi(end_basic_block_index)) + { + print_basic_block(thread, builder, function, end_basic_block_index); + } + } +} + #if LINK_LIBC int main(int argc, const char* argv[], char* envp[]) { @@ -5184,20 +9721,23 @@ int main(int argc, const char* argv[], char* envp[]) void entry_point(int argc, const char* argv[]) { char** envp = (char**)&argv[argc + 1]; + unused(envp); #endif #if DO_UNIT_TESTS unit_tests(); #endif + // calibrate_cpu_timer(); + if (argc < 3) { fail(); } - Arena* global_arena = arena_init_default(KB(64)); + Arena* global_arena = arena_init(MB(2), KB(64), KB(64)); { - arguments.pointer = arena_allocate(global_arena, String, argc); - arguments.length = argc; + arguments.length = cast(u64, s32, argc); + arguments.pointer = arena_allocate(global_arena, String, arguments.length); for (int i = 0; i < argc; i += 1) { @@ -5211,97 +9751,40 @@ void entry_point(int argc, const char* argv[]) String source_file_path = arguments.pointer[1]; CompilerBackend compiler_backend = arguments.pointer[2].pointer[0]; + u8 emit_ir = arguments.length >= 4 && arguments.pointer[3].pointer[0] == 'y'; Thread* thread = arena_allocate(global_arena, Thread, 1); thread_init(thread); - syscall_mkdir("nest", 0755); + dir_make("nest"); File file = { .path = source_file_path, .source = file_read(thread->arena, source_file_path), }; analyze_file(thread, &file); - print("File path: {s}\n", source_file_path); - auto test_dir = string_no_extension(file.path); - print("Test dir path: {s}\n", test_dir); - auto test_name = string_base(test_dir); - print("Test name: {s}\n", test_name); - - for (u32 function_i = 0; function_i < thread->buffer.functions.length; function_i += 1) - { - Function* function = &thread->buffer.functions.pointer[function_i]; - NodeIndex start_node_index = function->start; - NodeIndex stop_node_index = function->stop; - iterate_peepholes(thread, function, stop_node_index); - // print_string(strlit("Before optimizations\n")); - // print_function(thread, function); - gcm_build_cfg(thread, start_node_index, stop_node_index); - // print_string(strlit("After optimizations\n")); - // print_function(thread, function); - } if (thread->main_function == -1) { fail(); } - auto object_path = arena_join_string(thread->arena, (Slice(String)) array_to_slice(((String[]) { - strlit("nest/"), - test_name, - compiler_backend == COMPILER_BACKEND_C ? strlit(".c") : strlit(".o"), - }))); + print("File path: {s}\n", source_file_path); + auto test_dir = string_no_extension(file.path); + print("Test dir path: {s}\n", test_dir); + auto test_name = string_base(test_dir); + print("Test name: {s}\n", test_name); - auto exe_path_view = s_get_slice(u8, object_path, 0, object_path.length - 2); - auto exe_path = (char*)arena_allocate_bytes(thread->arena, exe_path_view.length + 1, 1); - memcpy(exe_path, exe_path_view.pointer, exe_path_view.length); - exe_path[exe_path_view.length] = 0; - - switch (compiler_backend) + if (emit_ir) { - case COMPILER_BACKEND_C: - { - auto lowered_source = c_lower(thread); - // print("Transpiled to C:\n```\n{s}\n```\n", lowered_source); - - file_write(object_path, lowered_source); - - char* command[] = { - clang_path, "-g", - "-o", exe_path, - string_to_c(object_path), - 0, - }; - - run_command((CStringSlice) array_to_slice(command), envp); - } break; - case COMPILER_BACKEND_INTERPRETER: - { - auto* main_function = &thread->buffer.functions.pointer[thread->main_function]; - auto* interpreter = interpreter_create(thread); - interpreter->function = main_function; - interpreter->arguments = (Slice(String)) array_to_slice(((String[]) { - test_name, - })); - auto exit_code = interpreter_run(interpreter, thread); - print("Interpreter exited with exit code: {u32}\n", exit_code); - syscall_exit(exit_code); - } break; - case COMPILER_BACKEND_MACHINE: - { - // TODO: - // Code: - // main: - // xor eax, eax - // ret - u8 code[] = { 0x31, 0xc0, 0xc3 }; - auto code_slice = (Slice(u8)) { .pointer = code, .length = sizeof(code) }; - write_elf(thread, envp, &(ELFOptions) { - .object_path = string_to_c(object_path), - .exe_path = exe_path, - .code = code_slice, - }); - } break; + print_ir(thread); + } + else + { + code_generation(thread, (CodegenOptions) { + .test_name = test_name, + .backend = compiler_backend, + }, envp); } thread_clear(thread); diff --git a/licenses/tb/LICENSE.txt b/licenses/tb/LICENSE.txt new file mode 100644 index 0000000..b731914 --- /dev/null +++ b/licenses/tb/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Yasser Arguelles Snape + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/project.sh b/project.sh index 09513f9..004dad0 100755 --- a/project.sh +++ b/project.sh @@ -1,5 +1,5 @@ #!/bin/bash set -e mkdir -p build -clang -o build/build bootstrap/build.c -O3 -march=native -std=gnu2x -Wall -Wextra -Wpedantic -Wno-nested-anon-types -Wno-keyword-macro -Wno-gnu-auto-type -Wno-auto-decl-extensions -Wno-gnu-empty-initializer -Wno-fixed-enum-extension -pedantic -fno-exceptions -fno-stack-protector +time clang -o build/build bootstrap/build.c -g -march=native -std=gnu2x -Wall -Wextra -Wpedantic -Wno-nested-anon-types -Wno-keyword-macro -Wno-gnu-auto-type -Wno-auto-decl-extensions -Wno-gnu-empty-initializer -Wno-fixed-enum-extension -pedantic -fno-exceptions -fno-stack-protector build/build $@