diff --git a/bootstrap/main.cpp b/bootstrap/main.cpp index be4bafb..a847500 100644 --- a/bootstrap/main.cpp +++ b/bootstrap/main.cpp @@ -18,7 +18,7 @@ typedef int64_t s64; typedef float f32; typedef double f64; -typedef u32 Hash; +typedef u64 Hash; #define fn static #define method __attribute__((visibility("internal"))) @@ -35,6 +35,16 @@ typedef u32 Hash; #define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024) #define unused(x) (void)(x) +#ifdef DEMAND_LIBC +#if DEMAND_LIBC +#define LINK_LIBC 1 +#else +#define LINK_LIBC 0 +#endif +#else +#define LINK_LIBC 0 +#endif + #if __APPLE__ global auto constexpr page_size = KB(16); #else @@ -52,6 +62,28 @@ global constexpr auto parenthesis_close = ')'; global constexpr auto bracket_open = '['; global constexpr auto bracket_close = ']'; +// Lehmer's generator +// https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/ +__uint128_t rn_state; +fn u64 generate_random_number() +{ + rn_state *= 0xda942042e4dd58b5; + return rn_state >> 64; +} + +fn u64 round_up_to_next_power_of_2(u64 n) +{ + n -= 1; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n |= n >> 32; + n += 1; + return n; +} + extern "C" void* memcpy(void* __restrict dst, void* __restrict src, u64 size) { auto* destination = (u8*)dst; @@ -89,19 +121,6 @@ forceinline fn u8 mem_equal_range(T* a, T* b, u64 count) return memcmp(a, b, count * sizeof(T)) == 0; } -// fn u8 memeq(u8* a, u8* b, u64 size) -// { -// for (u64 i = 0; i < size; i += 1) -// { -// if (a[i] != b[i]) -// { -// return 0; -// } -// } -// -// return 1; -// } - template struct Slice { @@ -240,6 +259,8 @@ using String = Slice; #define ch_to_str(ch) String{ .pointer = &ch, .length = 1 } #define array_to_slice(arr) { .pointer = arr, .length = array_length(arr) } +#define case_to_name(prefix, e) case prefix::e: return strlit(#e) + fn u64 parse_decimal(String string) { u64 value = 0; @@ -350,25 +371,26 @@ fn Hash hash_bytes(String bytes) result *= fnv_prime; } - return (Hash)result; + return result; } +#if LINK_LIBC == 0 #ifdef __linux__ -// fn forceinline long syscall0(long n) -// { -// unsigned long ret; -// __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory"); -// return ret; -// } +may_be_unused fn forceinline long syscall0(long n) +{ + unsigned long ret; + __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory"); + return ret; +} -fn forceinline long syscall1(long n, long a1) +may_be_unused fn forceinline long syscall1(long n, long a1) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); return ret; } -fn forceinline long syscall2(long n, long a1, long a2) +may_be_unused fn forceinline long syscall2(long n, long a1, long a2) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2) @@ -376,7 +398,7 @@ fn forceinline long syscall2(long n, long a1, long a2) return ret; } -fn forceinline long syscall3(long n, long a1, long a2, long a3) +may_be_unused fn forceinline long syscall3(long n, long a1, long a2, long a3) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), @@ -384,26 +406,26 @@ fn forceinline long syscall3(long n, long a1, long a2, long a3) return ret; } -// fn forceinline long syscall4(long n, long a1, long a2, long a3, long a4) -// { -// unsigned 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"); -// return ret; -// } +may_be_unused fn forceinline long syscall4(long n, long a1, long a2, long a3, long a4) +{ + unsigned 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"); + return ret; +} -// fn forceinline long syscall5(long n, long a1, long a2, long a3, long a4, long a5) -// { -// unsigned 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), -// "d"(a3), "r"(r10), "r"(r8) : "rcx", "r11", "memory"); -// return ret; -// } +may_be_unused fn forceinline long syscall5(long n, long a1, long a2, long a3, long a4, long a5) +{ + unsigned 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), + "d"(a3), "r"(r10), "r"(r8) : "rcx", "r11", "memory"); + return ret; +} -fn forceinline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) +may_be_unused fn forceinline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) { unsigned long ret; register long r10 __asm__("r10") = a4; @@ -414,7 +436,6 @@ fn forceinline long syscall6(long n, long a1, long a2, long a3, long a4, long a5 return ret; } - enum class SyscallX86_64 : u64 { read = 0, write = 1, @@ -785,71 +806,99 @@ enum class SyscallX86_64 : u64 { futex_wait = 455, futex_requeue = 456, }; - +#endif #endif fn void* syscall_mmap(void* address, size_t length, int protection_flags, int map_flags, int fd, signed long offset) { +#if LINK_LIBC + return mmap(address, length, protection_flags, map_flags, fd, offset); +#else #ifdef __linux__ return (void*) syscall6(static_cast(SyscallX86_64::mmap), (unsigned long)address, length, protection_flags, map_flags, fd, offset); #else - return mmap(address, length, protection_flags, map_flags, fd, offset); +#error "Unsupported operating system for static linking" +#endif #endif } fn int syscall_mprotect(void *address, size_t length, int protection_flags) { +#if LINK_LIBC + return mprotect(address, length, protection_flags); +#else #ifdef __linux__ return syscall3(static_cast(SyscallX86_64::mprotect), (unsigned long)address, length, protection_flags); #else return mprotect(address, length, protection_flags); #endif +#endif } fn int syscall_open(const char *file_path, int flags, int mode) { +#if LINK_LIBC + return open(file_path, flags, mode); +#else #ifdef __linux__ return syscall3(static_cast(SyscallX86_64::open), (unsigned long)file_path, flags, mode); #else return open(file_path, flags, mode); #endif +#endif } fn int syscall_fstat(int fd, struct stat *buffer) { +#if LINK_LIBC + return fstat(fd, buffer); +#else #ifdef __linux__ return syscall2(static_cast(SyscallX86_64::fstat), fd, (unsigned long)buffer); #else return fstat(fd, buffer); #endif +#endif } fn ssize_t syscall_read(int fd, void* buffer, size_t bytes) { +#if LINK_LIBC + return read(fd, buffer, bytes); +#else #ifdef __linux__ return syscall3(static_cast(SyscallX86_64::read), fd, (unsigned long)buffer, bytes); #else return read(fd, buffer, bytes); #endif +#endif } may_be_unused fn ssize_t syscall_write(int fd, const void *buffer, size_t bytes) { +#if LINK_LIBC + return write(fd, buffer, bytes); +#else #ifdef __linux__ return syscall3(static_cast(SyscallX86_64::write), fd, (unsigned long)buffer, bytes); #else return write(fd, buffer, bytes); #endif +#endif } [[noreturn]] [[gnu::cold]] fn void syscall_exit(int status) { +#if LINK_LIBC + _exit(status); +#else #ifdef __linux__ (void)syscall1(231, status); trap(); #else _exit(status); #endif +#endif } [[noreturn]] [[gnu::cold]] fn void fail() @@ -944,6 +993,40 @@ may_be_unused fn void print(const char* format, ...) auto* bit_count_end = it; auto bit_count = parse_decimal(String::from_pointer_range((u8*)bit_count_start, (u8*)bit_count_end)); + enum class IntegerFormat + { + hexadecimal, + decimal, + octal, + binary, + }; + + IntegerFormat format = IntegerFormat::decimal; + + if (*it == ':') + { + it += 1; + switch (*it) + { + case 'x': + format = IntegerFormat::hexadecimal; + break; + case 'd': + format = IntegerFormat::decimal; + break; + case 'o': + format = IntegerFormat::octal; + break; + case 'b': + format = IntegerFormat::binary; + break; + default: + trap(); + } + + it += 1; + } + u64 original_value; switch (bit_count) { @@ -959,32 +1042,63 @@ may_be_unused fn void print(const char* format, ...) trap(); } - // TODO: maybe print in one go? - - u8 reverse_buffer[64]; - u8 reverse_index = 0; u64 value = original_value; if (value) { - while (value) + switch (format) { - u8 decimal_value = (value % 10); - u8 ascii_ch = decimal_value + '0'; - value /= 10; - reverse_buffer[reverse_index] = ascii_ch; - reverse_index += 1; + case IntegerFormat::hexadecimal: + { + u8 reverse_buffer[16]; + u8 reverse_index = 0; + + while (value) + { + u8 digit_value = value % 16; + u8 ascii_ch = digit_value >= 10 ? (digit_value + 'a' - 10) : (digit_value + '0'); + value /= 16; + reverse_buffer[reverse_index] = ascii_ch; + reverse_index += 1; + } + + while (reverse_index > 0) + { + reverse_index -= 1; + buffer[buffer_i] = reverse_buffer[reverse_index]; + buffer_i += 1; + } + } break; + case IntegerFormat::decimal: + { + // TODO: maybe print in one go? + + u8 reverse_buffer[64]; + u8 reverse_index = 0; + + while (value) + { + u8 digit_value = (value % 10); + u8 ascii_ch = digit_value + '0'; + value /= 10; + reverse_buffer[reverse_index] = ascii_ch; + reverse_index += 1; + } + + while (reverse_index > 0) + { + reverse_index -= 1; + buffer[buffer_i] = reverse_buffer[reverse_index]; + buffer_i += 1; + } + } break; + case IntegerFormat::octal: + case IntegerFormat::binary: + trap(); } } else { - reverse_buffer[0] = '0'; - reverse_index += 1; - } - - while (reverse_index > 0) - { - reverse_index -= 1; - buffer[buffer_i] = reverse_buffer[reverse_index]; + buffer[buffer_i] = '0'; buffer_i += 1; } } break; @@ -1053,6 +1167,7 @@ struct Arena void* result = (u8*)this + aligned_offset; commit_position = aligned_size_after; + assert(commit_position <= committed); return result; } @@ -1076,6 +1191,12 @@ struct Arena .length = count, }; } + + method void reset() + { + this->commit_position = sizeof(Arena); + memset(this + 1, 0, this->committed - sizeof(Arena)); + } }; static_assert(sizeof(Arena) == 64, "Arena must be cache aligned"); @@ -1129,6 +1250,8 @@ template struct PinnedArray; fn void generic_pinned_array_ensure_capacity(PinnedArray* array, u32 additional_T, u32 size_of_T); fn u8* generic_pinned_array_add_with_capacity(PinnedArray* array, u32 additional_T, u32 size_of_T); +global constexpr auto granularity = page_size; +global constexpr auto reserved_size = ((u64)GB(4) - granularity); template struct PinnedArray @@ -1137,9 +1260,6 @@ struct PinnedArray u32 length; u32 capacity; - global constexpr auto granularity = page_size; - global constexpr auto reserved_size = ((u64)GB(4) - granularity); - // static_assert(sizeof(T) % granularity == 0); method forceinline T& operator[](u32 index) @@ -1243,11 +1363,11 @@ fn void generic_pinned_array_ensure_capacity(PinnedArray* array, u32 additio { assert(array->length == 0); assert(array->pointer == 0); - array->pointer = static_cast(reserve(PinnedArray::reserved_size)); + array->pointer = static_cast(reserve(reserved_size)); } - u64 currently_committed_size = align_forward(array->capacity * size_of_T, array->granularity); - u64 wanted_committed_size = align_forward(wanted_capacity * size_of_T, array->granularity); + u64 currently_committed_size = align_forward(array->capacity * size_of_T, granularity); + u64 wanted_committed_size = align_forward(wanted_capacity * size_of_T, granularity); void* commit_pointer = array->pointer + currently_committed_size; u64 commit_size = wanted_committed_size - currently_committed_size; assert(commit_size > 0); @@ -1259,22 +1379,25 @@ fn void generic_pinned_array_ensure_capacity(PinnedArray* array, u32 additio fn u8* generic_pinned_array_add_with_capacity(PinnedArray* array, u32 additional_T, u32 size_of_T) { u32 current_length_bytes = generic_pinned_array_length(array, size_of_T); - assert(current_length_bytes < PinnedArray::reserved_size); + assert(current_length_bytes < reserved_size); u8* pointer = array->pointer + current_length_bytes; array->length += additional_T; return pointer; } -template struct PinnedHashmap; +template +struct Put +{ + K* key; + V* value; +}; template struct GetOrPut { - K* key; - V* value; + Put result; u8 existing; }; -// fn GetOrPut generic_pinned_hashmap_get_or_put(PinnedHashmap* hashmap, u8* new_key_pointer, u32 key_size, u8* new_value_pointer, u32 value_size); template struct PutResult { @@ -1282,174 +1405,6 @@ struct PutResult V* value; }; -// fn PutResult generic_pinned_hashmap_put_assume_not_existing(PinnedHashmap* hashmap, u8* new_key_pointer, u32 key_size, u8* new_value_pointer, u32 value_size); - -template -struct PinnedHashmap -{ - K* keys; - V* values; - u32 length; - u16 key_page_capacity; - u16 value_page_capacity; - - global constexpr auto invalid_index = ~0u; - global constexpr auto granularity = PinnedArray::granularity; - global constexpr auto reserved_size = PinnedArray::reserved_size; - - static_assert(granularity % sizeof(K) == 0, ""); - static_assert(granularity % sizeof(V) == 0, ""); - - method forceinline Slice key_slice() - { - return { - .pointer = keys, - .length = length, - }; - } - - method forceinline Slice value_slice() - { - return { - .pointer = values, - .length = length, - }; - } - - method V* get(K key) - { - V* result = 0; - - for (u32 i = 0; i < length; i += 1) - { - K k = keys[i]; - if (k == key) - { - result = &values[i]; - break; - } - } - - return result; - } - - method forceinline PinnedHashmap* generic() - { - auto* generic_hashmap = (PinnedHashmap*)(this); - return generic_hashmap; - } - - method forceinline void ensure_capacity(u32 additional) - { - generic_pinned_hashmap_ensure_capacity(generic(), sizeof(K), sizeof(K), additional); - } - - method forceinline GetOrPut get_or_put(K key, V value) - { - auto generic_get_or_put = generic_pinned_hashmap_get_or_put(generic(), (u8*)&key, sizeof(K), (u8*)&value, sizeof(V)); - return *(GetOrPut*)&generic_get_or_put; - } - - method forceinline V* put_assume_not_existing(K key, V value) - { - auto result = generic_pinned_hashmap_put_assume_not_existing(generic(), (u8*)&key, sizeof(K), (u8*)&value, sizeof(V)); - return (V*)(result.value); - } -}; - -// Returns the generic value pointer if the key is present -// fn u32 generic_pinned_hashmap_get_index(PinnedHashmap* hashmap, u8* key_pointer, u32 key_size) -// { -// u32 index = hashmap->invalid_index; -// -// for (u32 i = 0; i < hashmap->length; i += 1) -// { -// u8* it_key_pointer = &hashmap->keys[i * key_size]; -// if (memeq(it_key_pointer, key_pointer, key_size)) -// { -// index = (it_key_pointer - hashmap->keys) / key_size; -// break; -// } -// } -// -// return index; -// } - -// fn void generic_pinned_hashmap_ensure_capacity(PinnedHashmap* hashmap, u32 key_size, u32 value_size, u32 additional_elements) -// { -// if (additional_elements != 0) -// { -// if (hashmap->key_page_capacity == 0) -// { -// assert(hashmap->value_page_capacity == 0); -// hashmap->keys = (u8*)reserve(hashmap->reserved_size); -// hashmap->values = (u8*)reserve(hashmap->reserved_size); -// } -// -// u32 target_element_capacity = hashmap->length + additional_elements; -// -// { -// u32 key_byte_capacity = hashmap->key_page_capacity * hashmap->granularity; -// u32 target_byte_capacity = target_element_capacity * key_size; -// if (key_byte_capacity < target_byte_capacity) -// { -// u32 aligned_target_byte_capacity = align_forward(target_byte_capacity, hashmap->granularity); -// void* commit_pointer = hashmap->keys + key_byte_capacity; -// u32 commit_size = aligned_target_byte_capacity - key_byte_capacity; -// commit(commit_pointer, commit_size); -// hashmap->key_page_capacity = aligned_target_byte_capacity / hashmap->granularity; -// } -// } -// -// { -// u32 value_byte_capacity = hashmap->value_page_capacity * hashmap->granularity; -// u32 target_byte_capacity = target_element_capacity * value_size; -// if (value_byte_capacity < target_byte_capacity) -// { -// u32 aligned_target_byte_capacity = align_forward(target_byte_capacity, hashmap->granularity); -// void* commit_pointer = hashmap->values + value_byte_capacity; -// u32 commit_size = aligned_target_byte_capacity - value_byte_capacity; -// commit(commit_pointer, commit_size); -// hashmap->value_page_capacity = aligned_target_byte_capacity / hashmap->granularity; -// } -// } -// } -// } - -// fn PutResult generic_pinned_hashmap_put_assume_not_existing(PinnedHashmap* hashmap, u8* new_key_pointer, u32 key_size, u8* new_value_pointer, u32 value_size) -// { -// generic_pinned_hashmap_ensure_capacity(hashmap, key_size, value_size, 1); -// u32 new_index = hashmap->length; -// hashmap->length += 1; -// u8* key_pointer = &hashmap->keys[new_index * key_size]; -// u8* value_pointer = &hashmap->values[new_index * value_size]; -// memcpy(key_pointer, new_key_pointer, key_size); -// memcpy(value_pointer, new_value_pointer, value_size); -// -// return { -// .key = key_pointer, -// .value = value_pointer, -// }; -// } - -// fn GetOrPut generic_pinned_hashmap_get_or_put(PinnedHashmap* hashmap, u8* new_key_pointer, u32 key_size, u8* new_value_pointer, u32 value_size) -// { -// u32 index = generic_pinned_hashmap_get_index(hashmap, new_key_pointer, key_size); -// if (index != hashmap->invalid_index) -// { -// trap(); -// } -// else -// { -// auto put_result = generic_pinned_hashmap_put_assume_not_existing(hashmap, new_key_pointer, key_size, new_value_pointer, value_size); -// return { -// .key = put_result.key, -// .value = put_result.value, -// .existing = 0, -// }; -// } -// } - global constexpr auto map_initial_capacity = 16; template @@ -1507,9 +1462,9 @@ struct StringMap return result; } - method u32 find_index(String key) + method Hash find_index(String key) { - u32 hash = hash_bytes(key); + auto hash = hash_bytes(key); auto index = hash & (capacity - 1); return index; } @@ -1558,17 +1513,24 @@ struct StringMap { auto wraparound_index = (index + i) & (capacity - 1); candidate_pair = &pairs[wraparound_index]; + if (candidate_pair->key.length == 0) { + *candidate_pair = { + .key = key, + .value = value, + }; return { .pair = candidate_pair, .existing = 0, }; } - - if (candidate_pair->key.equal(key)) + else if (candidate_pair->key.equal(key)) { - trap(); + return { + .pair = candidate_pair, + .existing = 1, + }; } } @@ -1596,6 +1558,288 @@ struct StringMap { return pairs + capacity; } + + method void clear() + { + *this = {}; + } +}; + +template +struct InternPool +{ + T** pointer; + u32 length; + u32 capacity; + + struct InternGetOrPut + { + T* result; + u8 existing; + }; + + Hash get_index(Hash hash) + { + assert(capacity % 2 == 0); + auto index = hash & (capacity - 1); + assert(index <= 0xffffffff); + static_assert(sizeof(index) == 8); + return index; + } + + method void ensure_capacity(Arena* arena, u32 additional) + { + u64 wanted_capacity = max(round_up_to_next_power_of_2(length + additional), 32); + + if (capacity < wanted_capacity) + { + T** new_array = arena->allocate_many(wanted_capacity); + memset(new_array, 0, sizeof(T*) * wanted_capacity); + + auto* old_pointer = pointer; + auto old_capacity = capacity; + + length = 0; + pointer = new_array; + capacity = wanted_capacity; + + // print("==========\nPUTTING\n==================\n"); + for (u32 i = 0; i < old_capacity; i += 1) + { + auto* key = old_pointer[i]; + if (key) + { + put_assume_capacity_assume_not_existent(key); + } + } + // print("==========\nEND PUTTING\n==================\n"); + + for (u32 i = 0; i < old_capacity; i += 1) + { + auto* key = old_pointer[i]; + if (key) + { + // print("Getting {u64:x} (hash: {u64:x})\n", key, key->hash); + auto* result = get(key); + assert(result); + assert(result == key); + } + } + } + } + + method T* get(T* key) + { + auto hash = key->hash; + assert(hash); + assert(hash == key->get_hash()); + + auto original_index = get_index(hash); + auto it_index = original_index; + + for (u32 i = 0; i < capacity; i += 1) + { + auto index = it_index & (capacity - 1); + auto* element = &pointer[index]; + auto* map_key = *element; + + if (!map_key) + { + // TODO: + // Linear probing makes holes into the hash table, so we gotta skip null spots, + // otherwise the removal operation will fail + // fail(); + } + else if (map_key->equal(key)) + { + return map_key; + } + else + { + // TODO: + // assert(map_key->hash); + // assert(hash != map_key->hash); + } + + it_index += 1; + } + + return 0; + } + + method Hash put_assume_not_existent(Arena* arena, T* key) + { + ensure_capacity(arena, 1); + return put_assume_capacity_assume_not_existent(key); + } + + method Hash get_suitable_index(T* key, Hash original_index) + { + auto it_index = original_index; + + for (u32 i = 0; i < capacity; i += 1) + { + auto index = it_index & (capacity - 1); + auto* element = &pointer[index]; + auto* map_key = *element; + + if (!map_key) + { + return index; + } + else if (map_key->equal(key)) + { + trap(); + } + else + { + // TODO: + // assert(map_key->hash); + // assert(hash != map_key->hash); + } + + it_index += 1; + } + + trap(); + } + + method Hash put_assume_capacity_assume_not_existent(T* key) + { + assert(key->hash); + auto original_index = get_index(key->hash); + auto suitable_index = get_suitable_index(key, original_index); + // print("Putting {u64:x} (hash: {u64:x}) at index {u64}\n", key, key->hash, suitable_index); + put_at_assume_capacity_not_existent(key, suitable_index); + return suitable_index; + } + + method void put_at_assume_capacity_not_existent(T* key, u32 index) + { + assert(length < capacity); + pointer[index] = key; + length += 1; + } + + method T* remove(T* value_to_remove) + { + auto hash = value_to_remove->hash; + assert(hash); + assert(hash == value_to_remove->get_hash()); + + auto original_index = get_index(hash); + auto it_index = original_index; + + // print("Trying to remove {u64:x} (hash: {u64:x}, original index: {u64:x}, capacity: {u32}\n", value_to_remove, hash, original_index, capacity); + + for (u32 i = 0; i < capacity; i += 1) + { + auto index = it_index & (capacity - 1); + auto* element = &pointer[index]; + auto* map_key = *element; + + if (!map_key) + { + // TODO: + // Linear probing makes holes into the hash table, so we gotta skip null spots, + // otherwise the removal operation will fail + // fail(); + } + else if (value_to_remove->equal(map_key)) + { + *element = {}; + length -= 1; + return map_key; + } + else + { + // TODO: + // assert(map_key->hash); + // assert(hash != map_key->hash); + } + + it_index += 1; + } + + trap(); + } + + method InternGetOrPut get_or_put(Arena* arena, T* new_value) + { + auto hash = new_value->get_hash(); + auto original_index = get_index(hash); + auto it_index = original_index; + + s32 first_free_spot = -1; + + for (u32 i = 0; i < capacity; i += 1) + { + auto index = it_index & (capacity - 1); + assert(index <= 0xffffffff); + static_assert(sizeof(index) == 8); + + auto* element = &pointer[index]; + auto* map_key = *element; + // if (hash == 0x800040e1) + // { + // print("Comparing {u64:x} with {u64:x} ({u64:x})\n", new_value, map_key, map_key ? map_key->hash : (u64)0); + // } + + // TODO: This is a mess, undo + if (!map_key) + { + if (first_free_spot == -1) + { + first_free_spot = index; + } + } + else if (new_value->equal(map_key)) + { + assert(hash == map_key->hash); + // print("Getting {u64:x} with hash {u64:x} at index {u64:x} (from {u64:x} with hash {u64:x})\n", map_key, map_key->hash, index, new_value, hash); + return { + .result = map_key, + .existing = 1, + }; + } + else + { + // TODO: + // assert(map_key->hash); + // assert(hash != map_key->hash); + } + + it_index += 1; + } + + Hash index; + if (length < capacity) + { + index = first_free_spot; + index &= capacity - 1; + put_at_assume_capacity_not_existent(new_value, index); + } + else if (length == capacity) + { + index = put_assume_not_existent(arena, new_value); + } + else + { + trap(); + } + + // print("Adding {u64:x} with hash {u64:x} at index {u64}\n", new_value, hash, index); + + return { + .result = new_value, + .existing = 0, + }; + } + + method void clear() + { + *this = {}; + } }; template @@ -1609,7 +1853,7 @@ typedef enum FileStatus FILE_STATUS_ANALYZING = 3, } FileStatus; -enum class SemaTypeId: u8 +enum class DebugTypeId: u8 { VOID, NORETURN, @@ -1621,29 +1865,26 @@ enum class SemaTypeId: u8 COUNT, }; global auto constexpr type_id_bit_count = 3; -static_assert(static_cast(SemaTypeId::COUNT) < (1 << type_id_bit_count), "Type bit count for id must be respected"); +static_assert(static_cast(DebugTypeId::COUNT) < (1 << type_id_bit_count), "Type bit count for id must be respected"); global auto constexpr type_flags_bit_count = 32 - (type_id_bit_count + 1); + +struct Thread; struct NodeType { enum class Id: u8 { - INVALID, - BOTTOM, + BOTTOM = 1, TOP, LIVE_CONTROL, DEAD_CONTROL, INTEGER, MULTIVALUE, - MEMORY, - POINTER, // TODO: this is mine. Check if it is correct: FUNCTION, CALL, }; - Id id; - union { struct @@ -1653,16 +1894,17 @@ struct NodeType } constant; struct { - Slice types; + Slice types; } multi; } payload = {}; + Hash hash = 0; + Id id; + method u8 is_simple() { switch (id) { - case Id::INVALID: - trap(); case Id::BOTTOM: case Id::TOP: case Id::LIVE_CONTROL: @@ -1673,23 +1915,47 @@ struct NodeType } } - method u8 equal(NodeType other) + method u8 equal(NodeType* other) { - if (id != other.id) + if (this == other) + { + return 1; + } + + if (id != other->id) { return 0; } switch (id) { - case NodeType::Id::INTEGER: - return (payload.constant.is_constant == other.payload.constant.is_constant) & (payload.constant.constant == other.payload.constant.constant); - case NodeType::Id::LIVE_CONTROL: - case NodeType::Id::DEAD_CONTROL: - case NodeType::Id::BOTTOM: + case Id::BOTTOM: + case Id::TOP: + case Id::LIVE_CONTROL: + case Id::DEAD_CONTROL: + case Id::FUNCTION: + case Id::CALL: + return 1; + case Id::INTEGER: + return (payload.constant.is_constant == other->payload.constant.is_constant) & + (payload.constant.constant == other->payload.constant.constant); + case Id::MULTIVALUE: + if (payload.multi.types.length == other->payload.multi.types.length) + { + for (u32 i = 0; i < payload.multi.types.length; i += 1) + { + if (payload.multi.types[i] != other->payload.multi.types[i]) + { + return 0; + } + } + return 1; - default: - trap(); + } + else + { + return 0; + } } } @@ -1697,125 +1963,39 @@ struct NodeType { switch (id) { - case Id::DEAD_CONTROL: - case Id::TOP: - return 1; case Id::INTEGER: return payload.constant.is_constant; - case Id::LIVE_CONTROL: - case Id::MULTIVALUE: - case Id::BOTTOM: - return 0; - case Id::FUNCTION: - case Id::CALL: - return 0; default: - trap(); + return 0; } } - method NodeType meet(NodeType other) + method NodeType* meet(Thread* thread, NodeType* other) { - if (equal(other)) + if (this == other) { - return *this; + return this; } - if (id == other.id) + if (id == other->id) { - return x_meet(other); + return x_meet(thread, other); } if (is_simple()) { - return x_meet(other); + return x_meet(thread, other); } - if (other.is_simple()) + if (other->is_simple()) { - return other.x_meet(*this); + return other->x_meet(thread, this); } - return { .id = NodeType::Id::BOTTOM }; + return x_meet(thread, other); } - method NodeType x_meet(NodeType other) - { - switch (id) - { - case Id::BOTTOM: - case Id::TOP: - case Id::LIVE_CONTROL: - case Id::DEAD_CONTROL: - { - assert(is_simple()); - if ((id == Id::BOTTOM) | (other.id == Id::TOP)) - { - return *this; - } - - if ((id == Id::TOP) | (other.id == Id::BOTTOM)) - { - return other; - } - - if (!other.is_simple()) - { - return NodeType{ .id = NodeType::Id::BOTTOM }; - } - - auto new_id = ((id == Id::LIVE_CONTROL) | (other.id == Id::LIVE_CONTROL)) ? Id::LIVE_CONTROL : Id::DEAD_CONTROL; - return { .id = new_id }; - } - case Id::INTEGER: - { - if (equal(other)) - { - return *this; - } - - if (other.id != Id::INTEGER) - { - return meet(other); - } - - if (is_bot()) - { - return *this; - } - - if (other.is_bot()) - { - return other; - } - - if (other.is_top()) - { - return *this; - } - - if (is_top()) - { - return other; - } - - assert(is_constant() & other.is_constant()); - - if (payload.constant.constant == other.payload.constant.constant) - { - return *this; - } - else - { - return { .id = Id::BOTTOM }; - } - } - case Id::MULTIVALUE: - fail(); - default: - trap(); - } - } + method NodeType* x_meet(Thread* thread, NodeType* other); method u8 is_bot() { @@ -1828,99 +2008,99 @@ struct NodeType assert(id == Id::INTEGER); return !payload.constant.is_constant & (payload.constant.constant == 0); } + + method u8 is_a(Thread* thread, NodeType* other) + { + return this->meet(thread, other) == other; + } + + method NodeType* dual() + { + switch (id) + { + case Id::TOP: + case Id::BOTTOM: + case Id::DEAD_CONTROL: + case Id::LIVE_CONTROL: + trap(); + // return { .id = id }; + default: + trap(); + } + } + + method NodeType* join(Thread* thread, NodeType* other) + { + if (this == other) + { + return this; + } + + return dual()->meet(thread, other->dual())->dual(); + } + method NodeType* intern_type(Thread* thread); + method u8 is_high_or_const() + { + switch (id) + { + case Id::TOP: + case Id::DEAD_CONTROL: + return 1; + case Id::LIVE_CONTROL: + case Id::BOTTOM: + case Id::MULTIVALUE: + return 0; + case Id::FUNCTION: + case Id::CALL: + return 0; + case Id::INTEGER: + return payload.constant.is_constant | (payload.constant.constant == 0); + } + } + + method Hash get_hash() + { + if (!hash) + { + switch (id) + { + case NodeType::Id::MULTIVALUE: + { + Hash sum = 0; + // print("{u64:x} Field count: {u64}\n", this, payload.multi.types.length); + for (auto* type : payload.multi.types) + { + auto field_hash = type->get_hash(); + // print("Field of {u64:x} hash {u64:x}: {u32:x}\n", this, type, field_hash); + sum ^= field_hash; + } + + hash = sum; + } + break; + case NodeType::Id::INTEGER: + hash = payload.constant.constant ^ (payload.constant.is_constant ? 0 : 0x4000); + break; + default: + hash = (Hash)id; + break; + } + + if (!hash) + { + hash = 0xDEADBEEF; + } + } + + return hash; + } }; -may_be_unused global auto constexpr integer_top = NodeType{ - .id = NodeType::Id::INTEGER, - .payload = { - .constant = { - .constant = 0, - .is_constant = 0, - }, - }, -}; - -may_be_unused global auto constexpr integer_bot = NodeType{ - .id = NodeType::Id::INTEGER, - .payload = { - .constant = { - .constant = 1, - .is_constant = 0, - }, - }, -}; - -may_be_unused global auto constexpr integer_zero = NodeType{ - .id = NodeType::Id::INTEGER, - .payload = { - .constant = { - .constant = 0, - .is_constant = 1, - }, - }, -}; - -global NodeType if_both_types[2] = { - { .id = NodeType::Id::LIVE_CONTROL }, - { .id = NodeType::Id::LIVE_CONTROL }, -}; - -global NodeType if_neither_types[2] = { - { .id = NodeType::Id::DEAD_CONTROL }, - { .id = NodeType::Id::DEAD_CONTROL }, -}; - -global NodeType if_true_types[2] = { - { .id = NodeType::Id::LIVE_CONTROL }, - { .id = NodeType::Id::DEAD_CONTROL }, -}; - -global NodeType if_false_types[2] = { - { .id = NodeType::Id::DEAD_CONTROL }, - { .id = NodeType::Id::LIVE_CONTROL }, -}; - -global auto constexpr type_if_both = NodeType{ - .id = NodeType::Id::MULTIVALUE, - .payload = { - .multi = { - .types = array_to_slice(if_both_types), - }, - }, -}; - -global auto constexpr type_if_neither = NodeType{ - .id = NodeType::Id::MULTIVALUE, - .payload = { - .multi = { - .types = array_to_slice(if_neither_types), - }, - }, -}; - -global auto constexpr type_if_true = NodeType{ - .id = NodeType::Id::MULTIVALUE, - .payload = { - .multi = { - .types = array_to_slice(if_true_types), - }, - }, -}; - -global auto constexpr type_if_false = NodeType{ - .id = NodeType::Id::MULTIVALUE, - .payload = { - .multi = { - .types = array_to_slice(if_false_types), - }, - }, -}; - -struct SemaType +struct DebugType { u64 size; u64 alignment; - SemaTypeId id : type_id_bit_count; + DebugTypeId id : type_id_bit_count; u32 resolved: 1; u32 flags: type_flags_bit_count; u32 reserved = 0; @@ -1928,7 +2108,7 @@ struct SemaType method u8 get_bit_count() { - assert(id == SemaTypeId::INTEGER); + assert(id == DebugTypeId::INTEGER); u32 bit_count_mask = (1 << (type_flags_bit_count - 1)) - 1; u8 bit_count = flags & bit_count_mask; assert(bit_count <= size * 8); @@ -1936,30 +2116,9 @@ struct SemaType return bit_count; } - method NodeType lower() - { - switch (id) - { - case SemaTypeId::VOID: - trap(); - case SemaTypeId::NORETURN: - trap(); - case SemaTypeId::POINTER: - trap(); - case SemaTypeId::INTEGER: - return integer_bot; - case SemaTypeId::ARRAY: - trap(); - case SemaTypeId::STRUCT: - trap(); - case SemaTypeId::UNION: - trap(); - case SemaTypeId::COUNT: - trap(); - } - } + method NodeType* lower(Thread* thread); }; -static_assert(sizeof(SemaType) == sizeof(u64) * 5, "Type must be 24 bytes"); +static_assert(sizeof(DebugType) == sizeof(u64) * 5, "Type must be 24 bytes"); struct Symbol { @@ -1993,13 +2152,6 @@ typedef enum AbiInfoKind : u8 ABI_INFO_EXPAND, } AbiInfoKind; -enum class Side : u8 -{ - left, - right, -}; - - global auto constexpr void_type_index = 0; global auto constexpr noreturn_type_index = 1; global auto constexpr opaque_pointer_type_index = 2; @@ -2010,13 +2162,241 @@ global auto constexpr integer_type_count = 64 * 2; global auto constexpr builtin_type_count = integer_type_count + integer_type_offset + 1; struct Function; +struct Node; + +struct Bitset +{ + PinnedArray arr; + + method u8 get(u64 index) + { + auto element_index = index / (sizeof(u64) * 8); + if (element_index < arr.length) + { + auto bit_index = index % (sizeof(u64) * 8); + auto result = (arr[element_index] & (1 << bit_index)) != 0; + return result; + } + + return 0; + } + + method void ensure_length(u64 max) + { + auto length = (max / (sizeof(u64) * 8)) + (max % (sizeof(u64) * 8) != 0); + auto old_length = arr.length; + if (old_length < length) + { + auto new_element_count = length - old_length; + unused(arr.add(new_element_count)); + for (u64 byte : arr.slice().slice(old_length, length)) + { + assert(!byte); + } + } + } + + method void set_assert_unset(u64 index) + { + ensure_length(index + 1); + auto element_index = index / (sizeof(u64) * 8); + auto bit_index = index % (sizeof(u64) * 8); + assert((arr[element_index] & (1 << bit_index)) == 0); + arr[element_index] |= (1 << bit_index); + } + + method u8 is_empty() + { + return arr.length == 0; + } + + method void clear(u64 index) + { + auto bit_index = index % (sizeof(u64) * 8); + auto element_index = index / (sizeof(u64) * 8); + auto mask = ~(1 << bit_index); + static_assert(sizeof(bit_index) == sizeof(arr.pointer[0])); + arr[element_index] &= mask; + } + + method void clear() + { + memset(arr.pointer, 0, arr.capacity * sizeof(u64)); + arr.clear(); + } +}; + +struct WorkList +{ + PinnedArray nodes = {}; + Bitset bitset = {}; + + method void add_many(Slice nodes) + { + for (auto* node : nodes) + { + push(node); + } + } + + method Node* push(Node* node); + + method Node* pop() + { + Node* result = 0; + + if (nodes.length) + { + u64 rng = generate_random_number(); + u32 index = rng & (nodes.capacity - 1); + result = nodes[index]; + nodes[index] = nodes[nodes.length - 1]; + nodes.length -= 1; + bitset.clear(index); + } + + return result; + } + + method u8 on(Node* node); + + method void clear() + { + nodes.clear(); + bitset.clear(); + } +}; + struct Thread { Arena* arena; PinnedArray functions = {}; + PinnedArray types = {}; + PinnedArray nodes = {}; + InternPool interned_types = {}; + InternPool interned_nodes = {}; + WorkList worklist = {}; + Bitset visited = {}; + + struct + { + NodeType* bottom; + NodeType* top; + NodeType* live_control; + NodeType* dead_control; + NodeType* integer_bot; + NodeType* integer_top; + NodeType* integer_zero; + NodeType* if_both; + NodeType* if_neither; + NodeType* if_true; + NodeType* if_false; + } common_types; + u64 peephole_iteration_count = 0; + u64 peephole_nop_iteration_count = 0; u32 node_count = 0; + u8 mid_assert = 0; + + + method void init(); + method void clear(); + + method u8 progress_on_list(Function* function, Node* node); }; +method NodeType* NodeType::x_meet(Thread* thread, NodeType* other) +{ + switch (id) + { + case Id::BOTTOM: + case Id::TOP: + case Id::LIVE_CONTROL: + case Id::DEAD_CONTROL: + { + assert(is_simple()); + if (this == thread->common_types.bottom || other == thread->common_types.top) + { + return this; + } + + if ((this == thread->common_types.top) | (other == thread->common_types.bottom)) + { + return other; + } + + if (!other->is_simple()) + { + return thread->common_types.bottom; + } + + if (this == thread->common_types.live_control || other == thread->common_types.live_control) + { + return thread->common_types.live_control; + } + else + { + return thread->common_types.dead_control; + } + } + case Id::INTEGER: + if (this == thread->common_types.integer_bot) + { + return this; + } + + if (other == thread->common_types.integer_bot) + { + return other; + } + + if (other == thread->common_types.integer_top) + { + return this; + } + + if (this == thread->common_types.integer_top) + { + return other; + } + + return thread->common_types.integer_bot; + case Id::MULTIVALUE: + fail(); + default: + trap(); + } +} + +method NodeType* DebugType::lower(Thread* thread) +{ + switch (id) + { + case DebugTypeId::VOID: + trap(); + case DebugTypeId::NORETURN: + trap(); + case DebugTypeId::POINTER: + trap(); + case DebugTypeId::INTEGER: + return thread->common_types.integer_bot; + case DebugTypeId::ARRAY: + trap(); + case DebugTypeId::STRUCT: + trap(); + case DebugTypeId::UNION: + trap(); + case DebugTypeId::COUNT: + trap(); + } +} + + +method NodeType* NodeType::intern_type(Thread* thread) +{ + auto result = thread->interned_types.get_or_put(thread->arena, this); + return result.result; +} + struct Unit { // PinnedArray files; @@ -2024,27 +2404,24 @@ struct Unit // Arena* arena; // Arena* node_arena; // Arena* type_arena; - // PinnedHashmap identifiers; - SemaType* builtin_types; + DebugType* builtin_types; u64 generate_debug_information : 1; - method SemaType* get_integer_type(u8 bit_count, u8 signedness) + method DebugType* get_integer_type(u8 bit_count, u8 signedness) { auto index = integer_type_offset + signedness * 64 + bit_count - 1; return &builtin_types[index]; } }; - - union AbiInfoPayload { - NodeType direct; - NodeType direct_pair[2]; - NodeType direct_coerce; + NodeType* direct; + NodeType* direct_pair[2]; + NodeType* direct_coerce; struct { - NodeType type; + NodeType* type; u32 alignment; } indirect; }; @@ -2068,17 +2445,16 @@ struct AbiInfo AbiInfoKind kind; }; -struct Node; struct Function { struct Prototype { AbiInfo* argument_type_abis; // The count for this array is "original_argument_count", not "abi_argument_count" - SemaType** original_argument_types; + DebugType** original_argument_types; // TODO: are these needed? // Node::DataType* abi_argument_types; // u32 abi_argument_count; - SemaType* original_return_type; + DebugType* original_return_type; AbiInfo return_type_abi; u32 original_argument_count; // TODO: is this needed? @@ -2092,18 +2468,18 @@ struct Function Node** parameters; Function::Prototype prototype; // u32 node_count; + u32 uid; u16 parameter_count; + + method Node* iterate(Thread* thread); }; struct ConstantIntData { u64 value; Node* input; - u8 bit_count; }; -[[nodiscard]] fn Node* add_constant_integer(Thread* thread, ConstantIntData data); - struct File; // This is a node in the "sea of nodes" sense: // https://en.wikipedia.org/wiki/Sea_of_nodes @@ -2116,8 +2492,7 @@ struct Node PROJECTION, RETURN, IF, - CONSTANT_INT, - CONSTANT_CONTROL, + CONSTANT, SCOPE, SYMBOL_FUNCTION, CALL, @@ -2138,10 +2513,12 @@ struct Node using Type = NodeType; - Type type; - Array inputs; - Array outputs; - u32 gvn; + Array inputs = {}; + Array outputs = {}; + Array dependencies = {}; + Type* type = 0; + u32 uid; + Hash hash = 0; Id id; s32 immediate_depth = 0; @@ -2158,7 +2535,8 @@ struct Node } scope; struct { - Type args; + Type* args; + Function* function; } root; Symbol* symbol; struct @@ -2169,9 +2547,13 @@ struct Node { Node* immediate_dominator = 0; } region; + struct + { + Type* type; + } constant; } payload; - u8 padding[32] = {}; + u8 padding[20 ] = {}; method forceinline Slice get_inputs() { @@ -2191,22 +2573,18 @@ struct Node struct NodeData { - Type type; Slice inputs; Id id; }; [[nodiscard]] fn Node* add(Thread* thread, NodeData data) { - auto gvn = thread->node_count; + auto node_id = thread->node_count; thread->node_count += 1; - auto* node = thread->arena->allocate_one(); + auto* node = thread->nodes.add_one(); *node = { - .type = data.type, - .inputs = {}, - .outputs = {}, - .gvn = gvn, + .uid = node_id, .id = data.id, .payload = {}, }; @@ -2231,8 +2609,9 @@ struct Node return this; } - method Node* add_input(Node* input) + method Node* add_input(Thread* thread, Node* input) { + unlock(thread); inputs.append_one(input); if (input) { @@ -2241,8 +2620,36 @@ struct Node return input; } - method Node* set_input(Arena* arena, s32 index, Node* new_input) + method Node* add_dependency(Thread* thread, Node* dependency) { + if (thread->mid_assert) + { + return this; + } + + if (dependencies.slice().find(dependency)) + { + return this; + } + + if (inputs.slice().find(dependency)) + { + return this; + } + + if (outputs.slice().find(dependency)) + { + return this; + } + + dependencies.append_one(dependency); + + return this; + } + + method Node* set_input(Thread* thread, s32 index, Node* new_input) + { + unlock(thread); Node* old_input = inputs[index]; if (old_input == new_input) { @@ -2256,7 +2663,7 @@ struct Node if (old_input && old_input->remove_output(this)) { - old_input->kill(arena); + old_input->kill(thread); } inputs[index] = new_input; @@ -2272,13 +2679,14 @@ struct Node return outputs.length == 0; } - method void remove_input(Arena* arena, u32 index) + method void remove_input(Thread* thread, u32 index) { + unlock(thread); if (Node* old_input = inputs[index]) { if (old_input->remove_output(this)) { - old_input->kill(arena); + old_input->kill(thread); } } @@ -2298,17 +2706,16 @@ struct Node { return 0; } - case Id::CONSTANT_INT: - case Id::CONSTANT_CONTROL: + case Id::CONSTANT: return 0; case Id::STOP: { auto input_count = inputs.length; for (u32 i = 0; i < inputs.length; i += 1) { - if (inputs[i]->type.id == NodeType::Id::DEAD_CONTROL) + if (inputs[i]->type == thread->common_types.dead_control) { - remove_input(thread->arena, i); + remove_input(thread, i); i -= 1; } } @@ -2325,11 +2732,11 @@ struct Node case Id::PROJECTION: { auto* control = get_control(); - if (control->type.id == NodeType::Id::MULTIVALUE) + if (control->type->id == NodeType::Id::MULTIVALUE) { - auto control_types = control->type.payload.multi.types; + auto control_types = control->type->payload.multi.types; auto projection_index = payload.projection.index; - if (control_types[projection_index].id == NodeType::Id::DEAD_CONTROL) + if (control_types[projection_index] == thread->common_types.dead_control) { trap(); } @@ -2339,7 +2746,7 @@ struct Node // auto index = 1 - projection_index; // assert(index >= 0); // assert((u32)index < control_types.length); - // if (control_types[index].id == NodeType::Id::DEAD_CONTROL) + // if (control_types[index] == thread->common_types.dead_control) // { // trap(); // } @@ -2348,15 +2755,28 @@ struct Node return 0; } case Id::ROOT: + return 0; case Id::IF: + if (!predicate()->type->is_high_or_const()) + { + for (Node* dominator = get_immediate_dominator(), *prior = this; dominator; prior = dominator, dominator = dominator->get_immediate_dominator()) + { + auto* dep = dominator->add_dependency(thread, this); + if (dep->id == Id::IF && dep->predicate()->add_dependency(thread, this) == predicate() && prior->id == Id::PROJECTION) + { + trap(); + } + } + } + return 0; case Id::INTEGER_ADD: { auto* left = inputs[1]; auto* right = inputs[2]; - assert(!(left->type.is_constant() && right->type.is_constant())); + assert(!(left->type->is_constant() && right->type->is_constant())); - if (right->type.id == NodeType::Id::INTEGER && right->type.payload.constant.constant == 0) + if (right->type->id == NodeType::Id::INTEGER && right->type->payload.constant.constant == 0) { return left; } @@ -2375,18 +2795,18 @@ struct Node // Find dead input for (u32 i = 1; i < inputs.length; i += 1) { - if (inputs[i]->type.id == NodeType::Id::DEAD_CONTROL) + if (inputs[i]->type == thread->common_types.dead_control) { for (u32 output_index = 0; output_index < outputs.length; output_index += 1) { Node* output = outputs[output_index]; if (output->id == Id::PHI) { - output->remove_input(thread->arena, i); + output->remove_input(thread, i); } } - remove_input(thread->arena, i); + remove_input(thread, i); if (inputs.length == 2) { @@ -2412,7 +2832,7 @@ struct Node return 0; case Id::SCOPE: - trap(); + return 0; // TODO: case Id::SYMBOL_FUNCTION: case Id::CALL: @@ -2434,20 +2854,24 @@ struct Node case Id::PHI: { auto* region = phi_get_region(); - auto is_r = region->is_region(); - if (!is_r || region->region_in_progress()) + if (!region->is_region()) + { + return inputs[1]; + } + + if (region->region_in_progress() || region->inputs.length <= 1) { return 0; } - else + + Node* single_unique_input = 0; + if (!(region->id == Id::REGION_LOOP && region->loop_entry()->type == thread->common_types.dead_control)) { - // Single unique input search Node* live = 0; - Node* region = phi_get_region(); + for (u32 i = 1; i < inputs.length; i += 1) { - - if (region->inputs[i]->type.id != NodeType::Id::DEAD_CONTROL && inputs[i] != this) + if (region->inputs[i]->add_dependency(thread, this)->type != thread->common_types.dead_control && inputs[i] != this) { if (!live || live == inputs[i]) { @@ -2461,54 +2885,54 @@ struct Node } } - if (live) + single_unique_input = live; + } + + if (single_unique_input) + { + return single_unique_input; + } + + Node* operand = inputs[1]; + + if (operand->inputs.length == 3 && !operand->inputs[0] && !operand->is_cfg() && phi_same_operand()) + { + u32 input_count = inputs.length; + auto lefts = thread->arena->allocate_slice(input_count); + auto rights = thread->arena->allocate_slice(input_count); + + lefts[0] = inputs[0]; + rights[0] = inputs[0]; + + for (u32 i = 1; i < input_count; i += 1) { - return live; + lefts[i] = inputs[i]->inputs[1]; + rights[i] = inputs[i]->inputs[2]; } - Node* operand = inputs[1]; - - if (operand->inputs.length == 3 && !operand->inputs[0] && !operand->is_cfg() && phi_same_operand()) - { - u32 input_count = inputs.length; - auto lefts = thread->arena->allocate_slice(input_count); - auto rights = thread->arena->allocate_slice(input_count); - - lefts[0] = inputs[0]; - rights[0] = inputs[0]; - - for (u32 i = 1; i < input_count; i += 1) - { - lefts[i] = inputs[i]->inputs[1]; - rights[i] = inputs[i]->inputs[2]; - } - - auto* left_phi = Node::add(thread, { - .type = {}, + auto* left_phi = Node::add(thread, { .inputs = lefts, .id = Node::Id::PHI, - }); - left_phi->payload.phi.label = payload.phi.label; - left_phi = left_phi->peephole(thread, function); + }); + left_phi->payload.phi.label = payload.phi.label; + left_phi = left_phi->peephole(thread, function); - auto* right_phi = Node::add(thread, { - .type = {}, + auto* right_phi = Node::add(thread, { .inputs = rights, .id = Node::Id::PHI, - }); - right_phi->payload.phi.label = payload.phi.label; - right_phi = right_phi->peephole(thread, function); + }); + right_phi->payload.phi.label = payload.phi.label; + right_phi = right_phi->peephole(thread, function); - auto* result = operand->copy(thread, left_phi, right_phi); - return result; - } - - return 0; + auto* result = operand->copy(thread, left_phi, right_phi); + return result; } + + return 0; } case Id::RETURN: { - if (get_control()->type.id == Node::Type::Id::DEAD_CONTROL) + if (get_control()->type->id == Node::Type::Id::DEAD_CONTROL) { trap(); } @@ -2534,7 +2958,6 @@ struct Node { Node* inputs[] = { 0, left, right }; auto* result = Node::add(thread, { - .type = {}, .inputs = array_to_slice(inputs), .id = id, }); @@ -2601,11 +3024,12 @@ struct Node method u8 is_dead() { - return is_unused() & (inputs.length == 0) & (type.id == Node::Type::Id::INVALID); + return is_unused() & (inputs.length == 0) && !type; } - method void pop_inputs(Arena* arena, u32 count) + method void pop_inputs(Thread* thread, u32 count) { + unlock(thread); for (u32 i = 0; i < count; i += 1) { Node* old_input = inputs.pop(); @@ -2613,52 +3037,145 @@ struct Node { if (old_input->remove_output(this)) { - old_input->kill(arena); + old_input->kill(thread); } } } } - method void kill(Arena* arena) + method String get_id_name() { - assert(is_unused()); + switch (id) + { + case_to_name(Id, ROOT); + case_to_name(Id, STOP); + case_to_name(Id, PROJECTION); + case_to_name(Id, RETURN); + case_to_name(Id, IF); + case_to_name(Id, CONSTANT); + case_to_name(Id, SCOPE); + case_to_name(Id, SYMBOL_FUNCTION); + case_to_name(Id, CALL); + case_to_name(Id, REGION); + case_to_name(Id, REGION_LOOP); + case_to_name(Id, PHI); + case_to_name(Id, INTEGER_ADD); + case_to_name(Id, INTEGER_SUB); + case_to_name(Id, INTEGER_COMPARE_EQUAL); + case_to_name(Id, INTEGER_COMPARE_NOT_EQUAL); + case_to_name(Id, INTEGER_COMPARE_LESS); + case_to_name(Id, INTEGER_COMPARE_LESS_EQUAL); + case_to_name(Id, INTEGER_COMPARE_GREATER); + case_to_name(Id, INTEGER_COMPARE_GREATER_EQUAL); + } + } - pop_inputs(arena, get_inputs().length); - type = {}; + method void kill(Thread* thread) + { + unlock(thread); + assert(is_unused()); + type = 0; + + while (inputs.length > 0) + { + if (Node* old_input = inputs.pop()) + { + thread->worklist.push(old_input); + if (old_input->remove_output(this)) + { + old_input->kill(thread); + } + } + } assert(is_dead()); } global auto constexpr enable_peephole = 1; - method Node* peephole(Thread* thread, Function* function) + [[gnu::hot]] method Node* peephole(Thread* thread, Function* function) { - Node::Type type = this->type = compute(); - if (!enable_peephole) { + type = compute(thread); return this; } - - if ((!is_constant()) & type.is_constant()) - { - auto* constant_int = Node::add(thread, { - .type = type, - .inputs = { .pointer = &function->root_node, .length = 1 }, - .id = Id::CONSTANT_INT, - }); - auto* result = constant_int->peephole(thread, function); - return dead_code_elimination(thread->arena, result); - } - - Node* idealized = idealize(thread, function); - if (idealized) - { - return dead_code_elimination(thread->arena, idealized->peephole(thread, function)); - } else { - return this; + if (Node* node = peephole_optimize(thread, function)) + { + auto* new_node = node->peephole(thread, function); + auto* result = dead_code_elimination(thread, new_node); + return result; + } + else + { + return this; + } + } + } + + method Node::Type* set_type(Thread* thread, Node::Type* new_type) + { + auto* old_type = type; + + assert(!old_type || new_type->is_a(thread, old_type)); + + if (old_type != new_type) + { + type = new_type; + thread->worklist.add_many(outputs.slice()); + move_dependencies_to_worklist(thread); + } + + return old_type; + } + + method void move_dependencies_to_worklist(Thread* thread) + { + thread->worklist.add_many(dependencies.slice()); + dependencies.clear(); + } + + [[gnu::hot]] method Node* peephole_optimize(Thread* thread, Function* function) + { + thread->peephole_iteration_count += 1; + auto old_type = set_type(thread, compute(thread)); + if (!is_constant() && type->is_high_or_const()) + { + auto* constant_node = Node::add(thread, { + .inputs = { .pointer = &function->root_node, .length = 1 }, + .id = Id::CONSTANT, + }); + constant_node->payload.constant.type = type; + return constant_node->peephole_optimize(thread, function); + } + + if (!hash) + { + auto gop = thread->interned_nodes.get_or_put(thread->arena, this); + // print("{s} node {u64:x} ({u64:x}) -> {u64:x} ({u64:x})\n", gop.existing ? strlit("Getting") : strlit("Adding"), this, this->get_hash(), gop.result, gop.result->get_hash()); + if (gop.existing) + { + Node* found = gop.result; + found->set_type(thread, found->type->join(thread, type)); + hash = 0; + auto* result = dead_code_elimination(thread, found); + return result; + } + } + + Node* node = idealize(thread, function); + if (node) + { + return node; + } + else + { + u8 condition = old_type == type; + thread->peephole_nop_iteration_count += condition; + auto* result = condition ? 0 : this; + return result; } } @@ -2679,8 +3196,7 @@ struct Node { default: return 0; - case Id::CONSTANT_INT: - case Id::CONSTANT_CONTROL: + case Id::CONSTANT: return 1; } } @@ -2691,48 +3207,45 @@ struct Node return inputs[1]; } - method Node::Type compute() + method Node::Type* compute(Thread* thread) { switch (id) { case Node::Id::ROOT: return payload.root.args; case Node::Id::STOP: - return { .id = Type::Id::BOTTOM }; + return thread->common_types.bottom; case Node::Id::IF: { auto* control_node = get_control(); - if (control_node->type.id != NodeType::Id::LIVE_CONTROL && control_node->type.id != Node::Type::Id::BOTTOM) + if (control_node->type != thread->common_types.live_control && control_node->type != thread->common_types.bottom) { - return type_if_neither; + return thread->common_types.if_neither; } auto* this_predicate = predicate(); - if ((this_predicate->type.id == Node::Type::Id::INTEGER) & this_predicate->type.is_constant()) + auto* predicate_type = this_predicate->type; + + if (predicate_type == thread->common_types.top || predicate_type == thread->common_types.integer_top) { - auto value = this_predicate->type.payload.constant.constant; + return thread->common_types.if_neither; + } + + if ((predicate_type->id == Node::Type::Id::INTEGER) & predicate_type->is_constant()) + { + auto value = predicate_type->payload.constant.constant; if (value) { - return type_if_true; + return thread->common_types.if_true; } else { - return type_if_false; + return thread->common_types.if_false; } } - for (Node* dom = get_immediate_dominator(), *prior = this; dom; prior = dom, dom = dom->get_immediate_dominator()) - { - if ((dom->id == Id::IF) && dom->predicate() == this_predicate) - { - unused(prior); - trap(); - } - } - - return type_if_both; + return thread->common_types.if_both; } - // return type_if; case Node::Id::INTEGER_ADD: case Node::Id::INTEGER_SUB: case Node::Id::INTEGER_COMPARE_EQUAL: @@ -2744,71 +3257,65 @@ struct Node { auto left_type = inputs[1]->type; auto right_type = inputs[2]->type; - if ((left_type.id == Node::Type::Id::INTEGER) & (right_type.id == Node::Type::Id::INTEGER)) + + if ((left_type->id == Node::Type::Id::INTEGER) & (right_type->id == Node::Type::Id::INTEGER)) { - if (left_type.is_constant() & right_type.is_constant()) + if (left_type->is_constant() & right_type->is_constant()) { u64 result; switch (id) { default: trap(); - case Id::INTEGER_ADD: - result = left_type.payload.constant.constant + right_type.payload.constant.constant; - break; - case Id::INTEGER_SUB: - result = left_type.payload.constant.constant - right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_EQUAL: - result = left_type.payload.constant.constant == right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_NOT_EQUAL: - result = left_type.payload.constant.constant != right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_LESS: - result = left_type.payload.constant.constant < right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_LESS_EQUAL: - result = left_type.payload.constant.constant <= right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_GREATER: - result = left_type.payload.constant.constant > right_type.payload.constant.constant; - break; - case Id::INTEGER_COMPARE_GREATER_EQUAL: - result = left_type.payload.constant.constant >= right_type.payload.constant.constant; - break; + case Id::INTEGER_ADD: + result = left_type->payload.constant.constant + right_type->payload.constant.constant; + break; + case Id::INTEGER_SUB: + result = left_type->payload.constant.constant - right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_EQUAL: + result = left_type->payload.constant.constant == right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_NOT_EQUAL: + result = left_type->payload.constant.constant != right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_LESS: + result = left_type->payload.constant.constant < right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_LESS_EQUAL: + result = left_type->payload.constant.constant <= right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_GREATER: + result = left_type->payload.constant.constant > right_type->payload.constant.constant; + break; + case Id::INTEGER_COMPARE_GREATER_EQUAL: + result = left_type->payload.constant.constant >= right_type->payload.constant.constant; + break; } - return Node::Type{ - .id = Node::Type::Id::INTEGER, + return thread->types.append_one({ .payload = { .constant = { .constant = result, - // .bit_count = left_type.payload.integer.bit_count, .is_constant = 1, }, }, - }; - } - else - { - return left_type.meet(right_type); + .id = Node::Type::Id::INTEGER, + })->intern_type(thread); } } - else - { - return Node::Type{ .id = NodeType::Id::BOTTOM }; - } + + auto* result = left_type->meet(thread, right_type); + return result; } - case Node::Id::CONSTANT_INT: - case Node::Id::CONSTANT_CONTROL: - return type; + case Node::Id::CONSTANT: + return payload.constant.type; case Node::Id::PROJECTION: { auto* control_node = inputs[0]; - if (control_node->type.id == NodeType::Id::MULTIVALUE) + if (control_node->type->id == NodeType::Id::MULTIVALUE) { - auto type = control_node->type.payload.multi.types[this->payload.projection.index]; + auto type = control_node->type->payload.multi.types[this->payload.projection.index]; return type; } else @@ -2818,29 +3325,38 @@ struct Node } break; // TODO: change case Node::Id::SYMBOL_FUNCTION: - return { .id = Type::Id::FUNCTION }; + trap(); + // return { .id = Type::Id::FUNCTION }; case Node::Id::CALL: - return { .id = Type::Id::CALL }; + { + // TODO: undo this mess + return thread->common_types.integer_bot; + } + // return { .id = Type::Id::CALL }; case Node::Id::RETURN: { - Array types = {}; + Array types = {}; // First INPUT: control // Second INPUT: expression types.append_one(inputs[0]->type); types.append_one(inputs[1]->type); - return Type{ - .id = Node::Type::Id::MULTIVALUE, + + auto* ty = thread->types.append_one({ .payload = { .multi = { .types = types.slice(), }, }, - }; + .id = Node::Type::Id::MULTIVALUE, + }); + auto* interned_type = ty->intern_type(thread); + // print("Ty: {u64:x} -> interned {u64:x}\n", ty, interned_type); + return interned_type; } case Node::Id::REGION_LOOP: if (region_in_progress()) { - return { .id = Type::Id::LIVE_CONTROL }; + return thread->common_types.live_control; } else { @@ -2850,40 +3366,44 @@ struct Node case Node::Id::REGION: if (region_in_progress()) { - return { .id = Type::Id::LIVE_CONTROL }; + trap(); + // return { .id = Type::Id::LIVE_CONTROL }; } else { - Type ty = { .id = Type::Id::DEAD_CONTROL }; + auto* ty = thread->common_types.dead_control; for (u32 i = 1; i < inputs.length; i += 1) { - ty = ty.meet(inputs[i]->type); + ty = ty->meet(thread, inputs[i]->type); } - return ty; } case Node::Id::PHI: { auto* region = phi_get_region(); - auto is_r = region->is_region(); - if (!is_r || region->region_in_progress()) + auto is_reg = region->is_region(); + if (!is_reg) { - return { .id = Type::Id::BOTTOM }; + trap(); } - else + if (region->region_in_progress()) { - Node::Type ty = { .id = Type::Id::TOP }; + return thread->common_types.bottom; + } - for (u32 i = 1; i < inputs.length; i += 1) + Type* ty = thread->common_types.top; + for (u32 i = 1; i < inputs.length; i += 1) + { + if (region->inputs[i]->add_dependency(thread, this)->type != thread->common_types.dead_control && inputs[i] != this) { - ty = ty.meet(inputs[i]->type); + ty = ty->meet(thread, inputs[i]->type); } - - return ty; } + + return ty; } - default: - trap(); + case Id::SCOPE: + return thread->common_types.bottom; } } @@ -2917,9 +3437,8 @@ struct Node method Node* project(Thread* thread, Node* control, s32 index, String label) { - assert(type.id == Node::Type::Id::MULTIVALUE); + assert(type->id == Node::Type::Id::MULTIVALUE); auto* projection = Node::add(thread, { - .type = {}, .inputs = { .pointer = &control, .length = 1 }, .id = Node::Id::PROJECTION, }); @@ -2928,24 +3447,22 @@ struct Node return projection; } - method Node* dead_code_elimination(Arena* arena, Node* new_node) + [[gnu::hot]] method Node* dead_code_elimination(Thread* thread, Node* new_node) { if (new_node != this && is_unused()) { new_node->keep(); - kill(arena); + kill(thread); new_node->unkeep(); } return new_node; } - method SemaType* get_debug_type(Unit* unit) + method DebugType* get_debug_type(Unit* unit) { - switch (type.id) + switch (type->id) { - case NodeType::Id::INVALID: - trap(); case NodeType::Id::BOTTOM: // TODO: return unit->get_integer_type(32, 1); @@ -2959,10 +3476,6 @@ struct Node return unit->get_integer_type(32, 1); case NodeType::Id::MULTIVALUE: trap(); - case NodeType::Id::MEMORY: - trap(); - case NodeType::Id::POINTER: - trap(); case NodeType::Id::FUNCTION: trap(); case NodeType::Id::CALL: @@ -2984,8 +3497,9 @@ struct Node } } - method Node* swap_inputs_1_2() + method Node* swap_inputs_1_2(Thread* thread) { + unlock(thread); Node* temporal = inputs[1]; inputs[1] = inputs[2]; inputs[2] = temporal; @@ -3006,7 +3520,7 @@ struct Node for (u32 i = 1; i < inputs.length; i += 1) { - if (!inputs[i]->type.is_constant()) + if (!inputs[i]->type->is_constant()) { return 0; } @@ -3093,6 +3607,12 @@ struct Node method u8 region_in_progress() { assert(is_region()); + return inputs.length > 1 && !(inputs[inputs.length - 1]); + } + + method u8 phi_in_progress() + { + assert(id == Id::PHI); return !(inputs[inputs.length - 1]); } @@ -3108,11 +3628,11 @@ struct Node return inputs[2]; } - method Node* set_control(Arena* arena, Node* node) + method Node* set_control(Thread* thread, Node* node) { assert(id == Id::SCOPE); - return set_input(arena, 0, node); + return set_input(thread, 0, node); } method Node* phi_get_region() @@ -3121,20 +3641,21 @@ struct Node return inputs[0]; } - method void subsume(Arena* arena, Node* node) + method void subsume(Thread* thread, Node* node) { assert(node != this); while (outputs.length > 0) { Node* n = outputs.pop(); + n->unlock(thread); s32 index = n->inputs.slice().find_index(this); assert(index != -1); n->inputs[index] = node; node->add_output(n); } - kill(arena); + kill(thread); } method Slice scope_reverse_names(Arena* arena) @@ -3189,21 +3710,20 @@ struct Node 0, }; auto* phi_node = Node::add(thread, { - .type = {}, .inputs = array_to_slice(phi_inputs), .id = Node::Id::PHI, }); phi_node->payload.phi.label = name; phi_node = phi_node->peephole(thread, function); - old = loop->set_input(thread->arena, index, phi_node); + old = loop->set_input(thread, index, phi_node); } - set_input(thread->arena, index, old); + set_input(thread, index, old); } if (node) { - return set_input(thread->arena, index, node); + return set_input(thread, index, node); } else { @@ -3231,7 +3751,7 @@ struct Node Node* control_node = get_control(); assert(control_node->id == Id::REGION_LOOP); assert(control_node->region_in_progress()); - control_node->set_input(thread->arena, 2, back->get_control()); + control_node->set_input(thread, 2, back->get_control()); for (u32 i = 1; i < inputs.length; i += 1) { if (back->inputs[i] != this) @@ -3240,16 +3760,16 @@ struct Node assert(phi->id == Id::PHI); assert(phi->phi_get_region() == get_control()); assert(!phi->inputs[2]); - phi->set_input(thread->arena, 2, back->inputs[i]); + phi->set_input(thread, 2, back->inputs[i]); } if (exit->inputs[i] == this) { - exit->set_input(thread->arena, i, inputs[i]); + exit->set_input(thread, i, inputs[i]); } } - back->kill(thread->arena); + back->kill(thread); for (u32 i = 1; i < inputs.length; i += 1) { @@ -3259,17 +3779,217 @@ struct Node Node* input = node->peephole(thread, function); if (input != node) { - node->subsume(thread->arena, input); - set_input(thread->arena, i, input); + node->subsume(thread, input); + set_input(thread, i, input); } } } } + method Hash get_hash() + { + if (!hash) + { + Hash new_hash; + switch (id) + { + case Id::PROJECTION: + new_hash = payload.projection.index; + break; + case Id::CONSTANT: + new_hash = payload.constant.type->get_hash(); + break; + default: + new_hash = 0; + break; + } + + for (Node* input : inputs.slice()) + { + if (input) + { + new_hash = new_hash ^ (new_hash << 17) ^ (new_hash >> 13) ^ input->uid; + } + } + + if (!new_hash) + { + new_hash = 0xDEADBEEF; + } + + hash = new_hash; + } + + // print("Hashing node {u64:x} -> {u32:x}\n", this, hash); + + return hash; + } + + method void unlock(Thread* thread) + { + // print("Removing node {u64:x} (in: {u32}, out: {u32}, id: {s}, hash: {u64:x})\n", this, inputs.length, outputs.length, get_id_name(), hash); + if (hash) + { + Node* old = thread->interned_nodes.remove(this); + if (old != this) + { + // print("Tried to remove node {u64:x} ({u64:x}), but got {u64:x} instead ({u64:x})\n", this, this->hash, old, old->hash); + trap(); + } + hash = 0; + } + } + + method u8 equal(Node* other) + { + if (this == other) + { + return 1; + } + else if (id != other->id) + { + return 0; + } + else if (inputs.length != other->inputs.length) + { + return 0; + } + else + { + u32 input_count = inputs.length; + for (u32 i = 0; i < input_count; i += 1) + { + if (inputs[i] != other->inputs[i]) + { + return 0; + } + } + + switch (id) + { + case Id::PROJECTION: + return payload.projection.index == other->payload.projection.index; + case Id::PHI: + return !phi_in_progress(); + case Id::CONSTANT: + return type == other->type; + case Id::REGION: + case Id::REGION_LOOP: + trap(); + case Id::ROOT: + return payload.root.function == other->payload.root.function; + case Id::SCOPE: + return 1; + case Id::STOP: + case Id::RETURN: + case Id::IF: + case Id::SYMBOL_FUNCTION: + case Id::CALL: + case Id::INTEGER_ADD: + case Id::INTEGER_SUB: + case Id::INTEGER_COMPARE_EQUAL: + case Id::INTEGER_COMPARE_NOT_EQUAL: + case Id::INTEGER_COMPARE_LESS: + case Id::INTEGER_COMPARE_LESS_EQUAL: + case Id::INTEGER_COMPARE_GREATER: + case Id::INTEGER_COMPARE_GREATER_EQUAL: + trap(); + } + } + } + + method Node* walk(Thread* thread, Function* function, Node* (*callback)(Thread*, Function*, Node*)) + { + assert(thread->visited.is_empty()); + Node* node = walk_internal(thread, function, callback); + thread->visited.clear(); + return node; + } + + + method Node* walk_internal(Thread* thread, Function* function, Node* (*callback)(Thread*, Function*, Node*)) + { + if (!thread->visited.get(this->uid)) + { + thread->visited.set_assert_unset(this->uid); + if (Node* node = callback(thread, function, this)) + { + return node; + } + + Slice lists[] = {inputs.slice(), outputs.slice()}; + for (auto list : lists) + { + for (Node* node : list) + { + if (node) + { + if (Node* result = node->walk_internal(thread, function, callback)) + { + return result; + } + } + } + } + } + + return 0; + } + method Node* scope_lookup(Thread* thread, Function* function, File* file, String name); method Node* merge_scopes(Thread* thread, File* file, Function* function, Node* other); }; +Node* Function::iterate(Thread* thread) +{ + assert(thread->progress_on_list(this, stop_node)); + u64 count = 0; + unused(count); + + while (auto* node = thread->worklist.pop()) + { + if (!node->is_dead()) + { + count += 1; + if (Node* x = node->peephole_optimize(thread, this)) + { + if (!x->is_dead()) + { + if (!x->type) + { + x->set_type(thread, x->compute(thread)); + } + + if (x != node || x->id != Node::Id::CONSTANT) + { + for (Node* output : node->outputs.slice()) + { + thread->worklist.push(output); + } + + thread->worklist.push(x); + + if (x != node) + { + for (Node* input : node->inputs.slice()) + { + thread->worklist.push(input); + } + + node->subsume(thread, x); + } + } + + node->move_dependencies_to_worklist(thread); + assert(thread->progress_on_list(this, stop_node)); + } + } + } + } + + return stop_node; +} + struct File { String path; @@ -3300,8 +4020,7 @@ method Node* Node::merge_scopes(Thread* thread, File* file, Function* function, other_scope->get_control(), }; - auto* region_node = set_control(thread->arena, Node::add(thread, { - .type = {}, + auto* region_node = set_control(thread, Node::add(thread, { .inputs = array_to_slice(region_inputs), .id = Node::Id::REGION, })->keep()); @@ -3323,57 +4042,23 @@ method Node* Node::merge_scopes(Thread* thread, File* file, Function* function, }; auto* phi_node = Node::add(thread, { - .type = {}, .inputs = array_to_slice(inputs), .id = Node::Id::PHI, }); phi_node->payload.phi.label = label; phi_node = phi_node->peephole(thread, function); - set_input(thread->arena, i, phi_node); + set_input(thread, i, phi_node); } } - other_scope->kill(thread->arena); + other_scope->kill(thread); return region_node->unkeep()->peephole(thread, function); } static_assert(sizeof(Node) == 128); static_assert(page_size % sizeof(Node) == 0); -[[nodiscard]] fn Node* add_constant_integer(Thread* thread, ConstantIntData data) -{ - auto* constant_int = Node::add(thread, { - .type = - { - .id = Node::Type::Id::INTEGER, - .payload = { - .constant = { - .constant = data.value, - // .bit_count = data.bit_count, - .is_constant = 1, - }, - }, - }, - .inputs = { .pointer = &data.input, .length = 1 }, - .id = Node::Id::CONSTANT_INT, - }); - return constant_int; -} - -fn u64 round_up_to_next_power_of_2(u64 n) -{ - n -= 1; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n |= n >> 32; - n += 1; - return n; -} - global String integer_names[] = { strlit("u1"), @@ -3509,7 +4194,7 @@ global String integer_names[] = fn void unit_initialize(Unit* unit) { Arena* type_arena = Arena::init(Arena::default_size, Arena::minimum_granularity, KB(64)); - SemaType* builtin_types = type_arena->allocate_many(builtin_type_count); + DebugType* builtin_types = type_arena->allocate_many(builtin_type_count); *unit = { // .arena = Arena::init(Arena::default_size, Arena::minimum_granularity, KB(4)), @@ -3522,7 +4207,7 @@ fn void unit_initialize(Unit* unit) builtin_types[void_type_index] = { .size = 0, .alignment = 1, - .id = SemaTypeId::VOID, + .id = DebugTypeId::VOID, .resolved = 1, .flags = 0, .name = strlit("void"), @@ -3530,7 +4215,7 @@ fn void unit_initialize(Unit* unit) builtin_types[noreturn_type_index] = { .size = 0, .alignment = 1, - .id = SemaTypeId::NORETURN, + .id = DebugTypeId::NORETURN, .resolved = 1, .flags = 0, .name = strlit("noreturn"), @@ -3538,7 +4223,7 @@ fn void unit_initialize(Unit* unit) builtin_types[opaque_pointer_type_index] = { .size = 8, .alignment = 8, - .id = SemaTypeId::POINTER, + .id = DebugTypeId::POINTER, .resolved = 1, .flags = 0, .name = strlit("*any"), @@ -3558,7 +4243,7 @@ fn void unit_initialize(Unit* unit) { .size = byte_count, .alignment = byte_count, - .id = SemaTypeId::INTEGER, + .id = DebugTypeId::INTEGER, .resolved = 1, .flags = static_cast(bit_count), .name = integer_names[bit_count - 1], @@ -3577,7 +4262,7 @@ fn void unit_initialize(Unit* unit) { .size = byte_count, .alignment = byte_count, - .id = SemaTypeId::INTEGER, + .id = DebugTypeId::INTEGER, .resolved = 1, .flags = static_cast(bit_count | (1 << (type_flags_bit_count - 1))), // Signedness bit .name = integer_names[bit_count + 63], @@ -3607,6 +4292,7 @@ fn Thread* instance_add_thread(Instance* instance) auto* thread = instance->arena->allocate_one(); *thread = { .arena = Arena::init_default(KB(64)), + .common_types = {}, }; return thread; } @@ -3617,7 +4303,7 @@ struct Parser u32 line; u32 column; - method void skip_space(String src) + [[gnu::hot]] method void skip_space(String src) { u64 original_i = i; @@ -3665,7 +4351,7 @@ struct Parser } } - method void expect_character(String src, u8 expected_ch) + [[gnu::hot]] method void expect_character(String src, u8 expected_ch) { u64 index = i; if (expect(index < src.length, 1)) @@ -3693,7 +4379,7 @@ struct Parser } } - method String parse_raw_identifier(String src) + [[gnu::hot]] method String parse_raw_identifier(String src) { u64 identifier_start_index = i; u64 is_string_literal = src.pointer[identifier_start_index] == '"'; @@ -3792,7 +4478,7 @@ global constexpr auto local_symbol_declaration_start = '>'; global constexpr auto array_expression_start = bracket_open; // global constexpr auto array_expression_end = bracket_close; -global constexpr auto composite_initialization_start = brace_open; +// global constexpr auto composite_initialization_start = brace_open; // global constexpr auto composite_initialization_end = brace_close; global String function_attributes[] = @@ -3847,10 +4533,10 @@ static_assert(array_length(global_symbol_attributes) == GLOBAL_SYMBOL_ATTRIBUTE_ Node* create_scope(Thread* thread) { auto* scope = Node::add(thread, { - .type = { .id = Node::Type::Id::BOTTOM }, - .inputs = {}, - .id = Node::Id::SCOPE, - }); + .inputs = {}, + .id = Node::Id::SCOPE, + }); + scope->type = thread->common_types.bottom; scope->payload.scope.stack = {}; return scope; @@ -3865,14 +4551,14 @@ struct Analyzer File* file; - method Node* set_control(Arena* arena, Node* node) + method Node* set_control(Thread* thread, Node* node) { - return scope->set_control(arena, node); + return scope->set_control(thread, node); } - method void kill_control(Arena* arena) + method void kill_control(Thread* thread) { - set_control(arena, 0); + set_control(thread, 0); } method Node* add_return(Thread* thread, Node* return_value) @@ -3880,21 +4566,21 @@ struct Analyzer Node* inputs[] = { get_control(), return_value }; auto* return_node = Node::add(thread, { - .type = {}, .inputs = array_to_slice(inputs), .id = Node::Id::RETURN, })->peephole(thread, function); - auto* node = function->stop_node->add_input(return_node); + auto* node = function->stop_node->add_input(thread, return_node); // Kill control auto* dead_control = Node::add(thread, { - .type = { .id = Node::Type::Id::DEAD_CONTROL }, .inputs = { .pointer = &function->root_node, .length = 1 }, - .id = Node::Id::CONSTANT_CONTROL, - })->peephole(thread, function); - set_control(thread->arena, dead_control); - + .id = Node::Id::CONSTANT, + }); + dead_control->payload.constant.type = thread->common_types.dead_control; + dead_control = dead_control->peephole(thread, function); + set_control(thread, dead_control); + return node; } @@ -3917,11 +4603,11 @@ struct Analyzer duplicate_scope->payload.scope.stack.append_one(duplicate_hashmap); } - duplicate_scope->add_input(get_control()); + duplicate_scope->add_input(thread, get_control()); for (u32 i = 1; i < original_scope->inputs.length; i += 1) { - duplicate_scope->add_input(loop ? original_scope : original_scope->inputs[i]); + duplicate_scope->add_input(thread, loop ? original_scope : original_scope->inputs[i]); } assert(duplicate_scope->inputs.length == original_input_count); return duplicate_scope; @@ -3929,7 +4615,7 @@ struct Analyzer }; -fn SemaType* analyze_type(Parser* parser, Unit* unit, String src) +fn DebugType* analyze_type(Parser* parser, Unit* unit, String src) { u64 start_index = parser->i; u8 start_ch = src.pointer[start_index]; @@ -4066,508 +4752,350 @@ fn u64 parse_hex(String string) return value; } +[[nodiscard]] [[gnu::hot]] fn Node* analyze_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src); -[[nodiscard]] fn Node* parse_constant_integer(Parser* parser, Thread* thread, String src, SemaType* type, Node* input) +[[nodiscard]] [[gnu::hot]] fn Node* analyze_primary_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) { - u64 value = 0; - auto starting_index = parser->i; - auto starting_ch = src[starting_index]; - - if (starting_ch == '0') - { - auto follow_up_character = src[parser->i + 1]; - auto is_hex_start = follow_up_character == 'x'; - auto is_octal_start = follow_up_character == 'o'; - auto is_bin_start = follow_up_character == 'b'; - auto is_prefixed_start = is_hex_start | is_octal_start | is_bin_start; - auto follow_up_alpha = is_alphabetic(follow_up_character); - auto follow_up_digit = is_decimal_digit(follow_up_character); - auto is_valid_after_zero = is_space(follow_up_character, get_next_ch_safe(src, follow_up_character)) | (!follow_up_digit and !follow_up_alpha); - - if (is_prefixed_start) { - enum class IntegerPrefix { - hexadecimal, - octal, - binary, - }; - IntegerPrefix prefix; - switch (follow_up_character) { - case 'x': prefix = IntegerPrefix::hexadecimal; break; - case 'o': prefix = IntegerPrefix::octal; break; - case 'b': prefix = IntegerPrefix::binary; break; - default: fail(); - }; - - parser->i += 2; - - auto start = parser->i; - - switch (prefix) { - case IntegerPrefix::hexadecimal: - { - while (is_hex_digit(src[parser->i])) { - parser->i += 1; - } - - auto slice = src.slice(start, parser->i); - value = parse_hex(slice); - } - case IntegerPrefix::octal: - trap(); - case IntegerPrefix::binary: - trap(); - } - } else if (is_valid_after_zero) { - value = 0; - parser->i += 1; - } else { - fail(); - } - } - else - { - while (is_decimal_digit(src[parser->i])) - { - parser->i += 1; - } - - auto slice = src.slice(starting_index, parser->i); - value = parse_decimal(slice); - } - - Node* result = add_constant_integer(thread, { - .value = value, - .input = input, - .bit_count = type->get_bit_count(), - }); - - return result; -} - - -[[nodiscard]] fn Node* analyze_single_expression(Analyzer* analyzer, Parser* parser, Unit* unit, Thread* thread, String src, SemaType* type, Side side) -{ - unused(side); - enum class Unary - { - NONE, - ONE_COMPLEMENT, - NEGATION, - }; - auto unary_operation = Unary::NONE; - auto* function = analyzer->function; - - auto original_starting_ch_index = parser->i; - u8 original_starting_ch = src[original_starting_ch_index]; - - switch (src[parser->i]) - { - case '\'': - trap(); - case '"': - trap(); - case '-': - trap(); - case '~': - trap(); - case '#': - trap(); - case composite_initialization_start: - trap(); - case array_expression_start: - trap(); - default: - assert(is_decimal_digit(original_starting_ch) | is_identifier_start(original_starting_ch)); - break; - } - + parser->skip_space(src); auto starting_ch_index = parser->i; u8 starting_ch = src[starting_ch_index]; auto is_digit = is_decimal_digit(starting_ch); auto is_identifier = is_identifier_start(starting_ch); + auto is_open_parenthesis = starting_ch == parenthesis_open; - // auto line = get_line(parser); - // auto column = get_column(parser); - - if (is_digit) - { - SemaType* integer_type; - if (type) - { - integer_type = type; - } - else - { - switch (unary_operation) - { - case Unary::NONE: - integer_type = unit->get_integer_type(64, 0); - break; - case Unary::ONE_COMPLEMENT: - fail(); - case Unary::NEGATION: - fail(); - } - } - - if (integer_type->id != SemaTypeId::INTEGER) - { - fail(); - } - - Node* constant_int = parse_constant_integer(parser, thread, src, integer_type, function->root_node); - - return constant_int; - } - else if (is_identifier) + if (is_identifier) { String identifier = parser->parse_and_check_identifier(src); - auto* node = analyzer->scope->scope_lookup(thread, function, analyzer->file, identifier); + auto* node = analyzer->scope->scope_lookup(thread, analyzer->function, analyzer->file, identifier); if (!node) { fail(); } - switch (src[parser->i]) + return node; + } + else if (is_digit) + { + u64 value = 0; + + if (starting_ch == '0') { - case ' ': - case ',': - case ';': - case function_argument_end: - // TODO: take into account 'side'? - return node; - case function_argument_start: - { - parser->i += 1; - Array argument_nodes = {}; - while (1) - { - parser->skip_space(src); + auto follow_up_character = src[parser->i + 1]; + auto is_hex_start = follow_up_character == 'x'; + auto is_octal_start = follow_up_character == 'o'; + auto is_bin_start = follow_up_character == 'b'; + auto is_prefixed_start = is_hex_start | is_octal_start | is_bin_start; + auto follow_up_alpha = is_alphabetic(follow_up_character); + auto follow_up_digit = is_decimal_digit(follow_up_character); + auto is_valid_after_zero = is_space(follow_up_character, get_next_ch_safe(src, follow_up_character)) | (!follow_up_digit and !follow_up_alpha); - if (src[parser->i] == function_argument_end) + if (is_prefixed_start) { + enum class IntegerPrefix { + hexadecimal, + octal, + binary, + }; + IntegerPrefix prefix; + switch (follow_up_character) { + case 'x': prefix = IntegerPrefix::hexadecimal; break; + case 'o': prefix = IntegerPrefix::octal; break; + case 'b': prefix = IntegerPrefix::binary; break; + default: fail(); + }; + + parser->i += 2; + + auto start = parser->i; + + switch (prefix) { + case IntegerPrefix::hexadecimal: { - break; - } - - Node* argument_value = analyze_single_expression(analyzer, parser, unit, thread, src, type, side)->peephole(thread, function); - argument_nodes.append_one(argument_value); - - parser->skip_space(src); - - switch (src[parser->i]) - { - case function_argument_end: - break; - case ',': + while (is_hex_digit(src[parser->i])) { parser->i += 1; - break; - default: - fail(); + } + + auto slice = src.slice(start, parser->i); + value = parse_hex(slice); } - } - - parser->expect_character(src, function_argument_end); - - // Add function definition - argument_nodes.append_one(node); - - Node* call_node = Node::add(thread, { - .type = {}, - .inputs = argument_nodes.slice(), - .id = Node::Id::CALL, - })->peephole(thread, function); - return call_node; + case IntegerPrefix::octal: + trap(); + case IntegerPrefix::binary: + trap(); } - default: - trap(); + } else if (is_valid_after_zero) { + value = 0; + parser->i += 1; + } else { + fail(); + } } + else + { + while (is_decimal_digit(src[parser->i])) + { + parser->i += 1; + } + + auto slice = src.slice(starting_ch_index, parser->i); + value = parse_decimal(slice); + } + + NodeType* type; + if (value == 0) + { + type = thread->common_types.integer_zero; + } + else + { + type = thread->types.append_one({ + .payload = { + .constant = { + .constant = value, + .is_constant = 1, + }, + }, + .id = NodeType::Id::INTEGER, + })->intern_type(thread); + } + + auto* constant_int = Node::add(thread, { + .inputs = { .pointer = &analyzer->function->root_node, .length = 1 }, + .id = Node::Id::CONSTANT, + }); + constant_int->payload.constant.type = type; + return constant_int->peephole(thread, analyzer->function); + } + else if (is_open_parenthesis) + { + trap(); } else { - fail(); + trap(); } + + trap(); } -[[nodiscard]] fn Node* analyze_expression(Analyzer* analyzer, Parser* parser, Unit* unit, Thread* thread, String src, SemaType* type, Side side) +[[nodiscard]] fn Node* analyze_unary_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) { - enum class CurrentOperation + parser->skip_space(src); + Node* result; + + switch (src[parser->i]) { - NONE, - ASSIGN, - INTEGER_ADD, - INTEGER_ADD_ASSIGN, - INTEGER_SUB, - INTEGER_SUB_ASSIGN, - INTEGER_COMPARE_EQUAL, - INTEGER_COMPARE_NOT_EQUAL, - INTEGER_COMPARE_LESS, - INTEGER_COMPARE_LESS_EQUAL, - INTEGER_COMPARE_GREATER, - INTEGER_COMPARE_GREATER_EQUAL, - INTEGER_SHIFT_LEFT, - INTEGER_SHIFT_LEFT_ASSIGN, - INTEGER_SHIFT_RIGHT, - INTEGER_SHIFT_RIGHT_ASSIGN, - }; + case '-': + trap(); + default: + result = analyze_primary_expression(analyzer, parser, thread, src); + break; + } - u64 iterations = 0; - SemaType* iteration_type = type; - auto current_operation = CurrentOperation::NONE; - Node* previous_node = 0; + switch (src[parser->i]) + { + // Function call + case function_argument_start: + { + parser->i += 1; + Array argument_nodes = {}; + while (1) + { + parser->skip_space(src); + if (src[parser->i] == function_argument_end) + { + break; + } + + Node* argument_value = analyze_expression(analyzer, parser, thread, src)->peephole(thread, analyzer->function); + argument_nodes.append_one(argument_value); + + parser->skip_space(src); + + switch (src[parser->i]) + { + case function_argument_end: + break; + case ',': + parser->i += 1; + break; + default: + fail(); + } + } + + parser->expect_character(src, function_argument_end); + + // Add function definition + argument_nodes.append_one(result); + + Node* call_node = Node::add(thread, { + .inputs = argument_nodes.slice(), + .id = Node::Id::CALL, + })->peephole(thread, analyzer->function); + result = call_node; + } + default: + break; + } + + return result; +} + +[[nodiscard]] fn Node* analyze_multiplication_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) +{ + auto* result = analyze_unary_expression(analyzer, parser, thread, src); while (1) { - if ((iterations == 1) & !iteration_type) - { - iteration_type = previous_node->get_debug_type(unit); - } + parser->skip_space(src); - // u32 line = get_line(parser); - // u32 column = get_column(parser); - Node* current_node; - if (src[parser->i] == '(') + u8 op_ch = src[parser->i]; + if (op_ch == '*') + { + trap(); + } + else if (op_ch == '/') { trap(); } else { - current_node = analyze_single_expression(analyzer, parser, unit, thread, src, iteration_type, side); - } - - parser->skip_space(src); - - switch (current_operation) - { - case CurrentOperation::NONE: - previous_node = current_node; break; - case CurrentOperation::INTEGER_ADD: - case CurrentOperation::INTEGER_SUB: - { - Node::Id id; - switch (current_operation) - { - case CurrentOperation::NONE: - trap(); - case CurrentOperation::INTEGER_ADD: - id = Node::Id::INTEGER_ADD; - break; - case CurrentOperation::INTEGER_SUB: - id = Node::Id::INTEGER_SUB; - break; - case CurrentOperation::INTEGER_ADD_ASSIGN: - case CurrentOperation::INTEGER_SUB_ASSIGN: - trap(); - case CurrentOperation::INTEGER_COMPARE_EQUAL: - trap(); - case CurrentOperation::ASSIGN: - case CurrentOperation::INTEGER_COMPARE_NOT_EQUAL: - case CurrentOperation::INTEGER_COMPARE_LESS: - case CurrentOperation::INTEGER_COMPARE_LESS_EQUAL: - case CurrentOperation::INTEGER_COMPARE_GREATER: - case CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL: - case CurrentOperation::INTEGER_SHIFT_LEFT: - case CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN: - case CurrentOperation::INTEGER_SHIFT_RIGHT: - case CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN: - trap(); - } - - Node* inputs[] = { - 0, - previous_node, - current_node, - }; - - auto* binary = Node::add(thread, { - .type = current_node->type, - .inputs = array_to_slice(inputs), - .id = id, - }); - - previous_node = binary; - } break; - case CurrentOperation::INTEGER_COMPARE_EQUAL: - case CurrentOperation::INTEGER_COMPARE_NOT_EQUAL: - case CurrentOperation::INTEGER_COMPARE_LESS: - case CurrentOperation::INTEGER_COMPARE_LESS_EQUAL: - case CurrentOperation::INTEGER_COMPARE_GREATER: - case CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL: - { - Node::Id id; - switch (current_operation) - { - case CurrentOperation::INTEGER_COMPARE_EQUAL: - id = Node::Id::INTEGER_COMPARE_EQUAL; - break; - case CurrentOperation::INTEGER_COMPARE_NOT_EQUAL: - id = Node::Id::INTEGER_COMPARE_NOT_EQUAL; - break; - case CurrentOperation::INTEGER_COMPARE_LESS: - id = Node::Id::INTEGER_COMPARE_LESS; - break; - case CurrentOperation::INTEGER_COMPARE_LESS_EQUAL: - id = Node::Id::INTEGER_COMPARE_LESS_EQUAL; - break; - case CurrentOperation::INTEGER_COMPARE_GREATER: - id = Node::Id::INTEGER_COMPARE_GREATER; - break; - case CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL: - id = Node::Id::INTEGER_COMPARE_GREATER_EQUAL; - break; - default: - trap(); - } - - Node* inputs[] = { - 0, - previous_node, - current_node, - }; - - auto* binary = Node::add(thread, { - .type = current_node->type, - .inputs = { .pointer = inputs, .length = array_length(inputs), }, - .id = id, - }); - - previous_node = binary; - } break; - case CurrentOperation::ASSIGN: - case CurrentOperation::INTEGER_SHIFT_LEFT: - case CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN: - case CurrentOperation::INTEGER_SHIFT_RIGHT: - case CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN: - case CurrentOperation::INTEGER_ADD_ASSIGN: - case CurrentOperation::INTEGER_SUB_ASSIGN: - trap(); - } - - previous_node = previous_node->peephole(thread, analyzer->function); - - auto original_index = parser->i; - u8 original = src[original_index]; - - switch (original) - { - case end_of_statement: - case end_of_argument: - case parenthesis_close: - case bracket_close: - return previous_node; - case '+': - current_operation = CurrentOperation::INTEGER_ADD; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_ADD_ASSIGN; - parser->i += 1; - break; - default: - break; - } - break; - case '-': - current_operation = CurrentOperation::INTEGER_SUB; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_SUB_ASSIGN; - parser->i += 1; - break; - default: - break; - } - break; - case '=': - current_operation = CurrentOperation::ASSIGN; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_COMPARE_EQUAL; - parser->i += 1; - break; - default: - break; - } - break; - case '<': - current_operation = CurrentOperation::INTEGER_COMPARE_LESS; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_COMPARE_LESS_EQUAL; - parser->i += 1; - break; - case '<': // Shift left - current_operation = CurrentOperation::INTEGER_SHIFT_LEFT; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_SHIFT_LEFT_ASSIGN; - parser->i += 1; - break; - default: - break; - } - - break; - default: - break; - } - break; - case '>': - current_operation = CurrentOperation::INTEGER_COMPARE_GREATER; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_COMPARE_GREATER_EQUAL; - parser->i += 1; - break; - case '>': // Shift right - current_operation = CurrentOperation::INTEGER_SHIFT_RIGHT; - parser->i += 1; - - switch (src[parser->i]) - { - case '=': - current_operation = CurrentOperation::INTEGER_SHIFT_RIGHT_ASSIGN; - parser->i += 1; - break; - default: - break; - } - - break; - default: - break; - } - break; - case function_argument_start: - { - assert(previous_node->id == Node::Id::SYMBOL_FUNCTION); - trap(); - } break; - default: - trap(); } + trap(); + } + + return result; +} + +[[nodiscard]] fn Node* analyze_addition_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) +{ + auto* result = analyze_multiplication_expression(analyzer, parser, thread, src); + while (1) + { parser->skip_space(src); - iterations += 1; + auto* left = result; + u8 op_ch = src[parser->i]; + Node* inputs[] = { + 0, + left, + 0, + }; + + Node::Id new_node_id; + + if (op_ch == '+') + { + new_node_id = Node::Id::INTEGER_ADD; + } + else if (op_ch == '-') + { + new_node_id = Node::Id::INTEGER_SUB; + } + else + { + break; + } + + parser->i += 1; + + result = Node::add(thread, { + .inputs = array_to_slice(inputs), + .id = new_node_id, + }); + + auto* right = analyze_addition_expression(analyzer, parser, thread, src); + result->set_input(thread, 2, right); + result = result->peephole(thread, analyzer->function); } + + return result; +} + +[[nodiscard]] fn Node* analyze_comparison_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) +{ + auto* result = analyze_addition_expression(analyzer, parser, thread, src); + while (1) + { + parser->skip_space(src); + + auto* left = result; + Node* inputs[] = { + 0, + left, + 0, + }; + u8 op_ch = src[parser->i]; + u8 next_ch = src[parser->i + 1]; + + Node::Id id; + u8 negate = 0; + + if (op_ch == '=' && next_ch == '=') + { + parser->i += 1; + id = Node::Id::INTEGER_COMPARE_EQUAL; + } + else if (op_ch == '!' && next_ch == '=') + { + parser->i += 1; + id = Node::Id::INTEGER_COMPARE_NOT_EQUAL; + negate = 1; + } + else if (op_ch == '<') + { + if (next_ch == '=') + { + parser->i += 1; + id = Node::Id::INTEGER_COMPARE_LESS_EQUAL; + } + else + { + id = Node::Id::INTEGER_COMPARE_LESS; + } + } + else if (op_ch == '>') + { + if (next_ch == '=') + { + parser->i += 1; + id = Node::Id::INTEGER_COMPARE_GREATER_EQUAL; + } + else + { + id = Node::Id::INTEGER_COMPARE_GREATER; + } + } + else + { + break; + } + + parser->i += 1; + + result = Node::add(thread, { + .inputs = array_to_slice(inputs), + .id = id, + }); + + auto* right = analyze_addition_expression(analyzer, parser, thread, src); + result->set_input(thread, 2, right); + + result = result->peephole(thread, analyzer->function); + if (negate) + { + trap(); + } + } + + return result; +} + +[[nodiscard]] [[gnu::hot]] fn Node* analyze_expression(Analyzer* analyzer, Parser* parser, Thread* thread, String src) +{ + return analyze_comparison_expression(analyzer, parser, thread, src); } fn void push_scope(Analyzer* analyzer) @@ -4575,12 +5103,12 @@ fn void push_scope(Analyzer* analyzer) analyzer->scope->payload.scope.stack.append_one({}); } -fn void pop_scope(Analyzer* analyzer) +fn void pop_scope(Analyzer* analyzer, Thread* thread) { - analyzer->scope->payload.scope.stack.pop(); + analyzer->scope->pop_inputs(thread, analyzer->scope->payload.scope.stack.pop().length); } -fn Node* define_variable(Analyzer* analyzer, Arena* arena, String name, Node* node) +fn Node* define_variable(Analyzer* analyzer, Thread* thread, String name, Node* node) { auto* stack = &analyzer->scope->payload.scope.stack; assert(stack->length); @@ -4588,13 +5116,13 @@ fn Node* define_variable(Analyzer* analyzer, Arena* arena, String name, Node* no auto input_index = analyzer->scope->inputs.length; - if (last->get_or_put(arena, name, input_index).existing) + if (last->get_or_put(thread->arena, name, input_index).existing) { trap(); return 0; } - return analyzer->scope->add_input(node); + return analyzer->scope->add_input(thread, node); } fn Node* analyze_local_block(Analyzer* analyzer, Parser* parser, Unit* unit, Thread* thread, String src); @@ -4604,24 +5132,23 @@ fn Node* jump_to(Analyzer* analyzer, Thread* thread, Node* target_scope) auto* current_scope = analyzer->duplicate_scope(thread, 0); // Kill current scope auto* dead_control = Node::add(thread, { - .type = { .id = Node::Type::Id::DEAD_CONTROL, }, .inputs = { .pointer = &analyzer->function->root_node, .length = 1 }, - .id = Node::Id::CONSTANT_CONTROL, - })->peephole(thread, analyzer->function); - analyzer->set_control(thread->arena, dead_control); - + .id = Node::Id::CONSTANT, + }); + dead_control->payload.constant.type = thread->common_types.dead_control; + dead_control = dead_control->peephole(thread, analyzer->function); + analyzer->set_control(thread, dead_control); + while (current_scope->payload.scope.stack.length > analyzer->break_scope->payload.scope.stack.length) { current_scope->payload.scope.stack.pop(); } - + if (target_scope) { assert(target_scope->payload.scope.stack.length <= analyzer->break_scope->payload.scope.stack.length); auto* result = target_scope->merge_scopes(thread, analyzer->file, analyzer->function, current_scope); - unused(result); - // TODO: is this right? - // assert(result == target_scope); + target_scope->set_control(thread, result); return target_scope; } else @@ -4645,7 +5172,7 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa { parser->skip_space(src); - auto* return_value = analyze_expression(analyzer, parser, unit, thread, src, analyzer->function->prototype.original_return_type, Side::right)->peephole(thread, function); + auto* return_value = analyze_expression(analyzer, parser, thread, src)->peephole(thread, function); parser->expect_character(src, ';'); auto* return_node = analyzer->add_return(thread, return_value); @@ -4654,17 +5181,13 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa else if (identifier.equal(strlit("if"))) { parser->skip_space(src); - parser->expect_character(src, parenthesis_open); - parser->skip_space(src); - auto* predicate_node = analyze_expression(analyzer, parser, unit, thread, src, 0, Side::right); + auto* predicate_node = analyze_expression(analyzer, parser, thread, src); parser->skip_space(src); - parser->expect_character(src, parenthesis_close); - parser->skip_space(src); Node* if_inputs[] = { @@ -4673,30 +5196,23 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa }; auto* if_node = Node::add(thread, { - .type = {}, - .inputs = array_to_slice(if_inputs), - .id = Node::Id::IF, - })->keep()->peephole(thread, function); + .inputs = array_to_slice(if_inputs), + .id = Node::Id::IF, + })->peephole(thread, function); - Node* if_true = if_node->project(thread, if_node, 0, strlit("True"))->peephole(thread, function); - if_node->unkeep(); - Node* if_false = if_node->project(thread, if_node, 1, strlit("False"))->peephole(thread, function); + Node* if_true = if_node->project(thread, if_node->keep(), 0, strlit("True"))->peephole(thread, function)->keep(); + Node* if_false = if_node->project(thread, if_node->unkeep(), 1, strlit("False"))->peephole(thread, function)->keep(); u32 original_input_count = analyzer->scope->inputs.length; auto* false_scope = analyzer->duplicate_scope(thread, 0); - analyzer->set_control(thread->arena, if_true); - assert(analyzer->scope->get_control()); - + analyzer->set_control(thread, if_true->unkeep()); analyze_statement(analyzer, parser, unit, thread, src); - + parser->skip_space(src); auto* true_scope = analyzer->scope; analyzer->scope = false_scope; - analyzer->set_control(thread->arena, if_false); - assert(analyzer->scope->get_control()); - - parser->skip_space(src); + analyzer->set_control(thread, if_false->unkeep()); if (is_identifier_start(src[parser->i])) { @@ -4724,14 +5240,14 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa analyzer->scope = true_scope; auto* merged_scope = true_scope->merge_scopes(thread, analyzer->file, analyzer->function, false_scope); - statement_node = analyzer->set_control(thread->arena, merged_scope); + statement_node = analyzer->set_control(thread, merged_scope); assert(statement_node); } else if (identifier.equal(strlit("while"))) { parser->skip_space(src); - parser->expect_character(src, parenthesis_open); + parser->skip_space(src); auto* old_break_scope = analyzer->break_scope; auto* old_continue_scope = analyzer->continue_scope; @@ -4742,57 +5258,51 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa 0, }; auto* loop_node = Node::add(thread, { - .type = {}, .inputs = array_to_slice(loop_inputs), .id = Node::Id::REGION_LOOP, })->peephole(thread, function); - analyzer->set_control(thread->arena, loop_node); + analyzer->set_control(thread, loop_node); - Node* head = analyzer->scope->keep(); + Node* head_scope = analyzer->scope->keep(); analyzer->scope = analyzer->duplicate_scope(thread, 1); - parser->skip_space(src); - - auto* predicate_node = analyze_expression(analyzer, parser, unit, thread, src, 0, Side::right); + auto* predicate_node = analyze_expression(analyzer, parser, thread, src); parser->skip_space(src); - parser->expect_character(src, parenthesis_close); - parser->skip_space(src); + Node* if_inputs[] = { analyzer->get_control(), predicate_node, }; auto* if_node = Node::add(thread, { - .type = {}, .inputs = array_to_slice(if_inputs), .id = Node::Id::IF, - })->keep()->peephole(thread, function); + })->peephole(thread, function); - Node* if_true = if_node->project(thread, if_node, 0, strlit("True"))->peephole(thread, function); - if_node->unkeep(); - Node* if_false = if_node->project(thread, if_node, 1, strlit("False"))->peephole(thread, function); + Node* if_true = if_node->project(thread, if_node->keep(), 0, strlit("True"))->peephole(thread, function)->keep(); + Node* if_false = if_node->project(thread, if_node->unkeep(), 1, strlit("False"))->peephole(thread, function); - analyzer->set_control(thread->arena, if_false); + analyzer->set_control(thread, if_false); analyzer->break_scope = analyzer->duplicate_scope(thread, 0); analyzer->continue_scope = 0; - analyzer->set_control(thread->arena, if_true); + analyzer->set_control(thread, if_true->unkeep()); analyze_statement(analyzer, parser, unit, thread, src); if (analyzer->continue_scope) { analyzer->continue_scope = jump_to(analyzer, thread, analyzer->continue_scope); - analyzer->scope->kill(thread->arena); + analyzer->scope->kill(thread); analyzer->scope = analyzer->continue_scope; } auto* exit_scope = analyzer->break_scope; - head->scope_end_loop(thread, function, analyzer->scope, exit_scope); - head->unkeep()->kill(thread->arena); + head_scope->scope_end_loop(thread, function, analyzer->scope, exit_scope); + head_scope->unkeep()->kill(thread); analyzer->break_scope = old_break_scope; analyzer->continue_scope = old_continue_scope; @@ -4838,15 +5348,15 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa { parser->skip_space(src); - enum class StatementOperation : u8 + enum class LoadStoreOperation : u8 { - ASSIGN, + NONE }; - StatementOperation operation; + LoadStoreOperation operation; switch (src[parser->i]) { case '=': - operation = StatementOperation::ASSIGN; + operation = LoadStoreOperation::NONE; parser->i += 1; break; default: @@ -4855,14 +5365,14 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa parser->skip_space(src); - Node* right_expression = analyze_expression(analyzer, parser, unit, thread, src, 0, Side::right); + Node* right_expression = analyze_expression(analyzer, parser, thread, src); parser->skip_space(src); parser->expect_character(src, ';'); switch (operation) { - case StatementOperation::ASSIGN: + case LoadStoreOperation::NONE: if (!analyzer->scope->scope_update(thread, function, identifier, right_expression)) { fail(); @@ -4906,7 +5416,7 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa struct LocalResult { Node* node; - SemaType* type; + DebugType* type; }; LocalResult local_result = {}; @@ -4917,14 +5427,14 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa parser->i += 1; parser->skip_space(src); - SemaType* type = analyze_type(parser, unit, src); + DebugType* type = analyze_type(parser, unit, src); parser->skip_space(src); parser->expect_character(src, '='); parser->skip_space(src); - auto* initial_node = analyze_expression(analyzer, parser, unit, thread, src, type, Side::right); - if (!define_variable(analyzer, thread->arena, name, initial_node)) + auto* initial_node = analyze_expression(analyzer, parser, thread, src); + if (!define_variable(analyzer, thread, name, initial_node)) { fail(); } @@ -4938,8 +5448,8 @@ fn Node* analyze_statement(Analyzer* analyzer, Parser* parser, Unit* unit, Threa parser->i += 1; parser->skip_space(src); - auto* initial_node = analyze_expression(analyzer, parser, unit, thread, src, 0, Side::right); - if (!define_variable(analyzer, thread->arena, name, initial_node)) + auto* initial_node = analyze_expression(analyzer, parser, thread, src); + if (!define_variable(analyzer, thread, name, initial_node)) { fail(); } @@ -4985,7 +5495,7 @@ fn Node* analyze_local_block(Analyzer* analyzer, Parser* parser, Unit* unit, Thr parser->expect_character(src, block_end); - pop_scope(analyzer); + pop_scope(analyzer, thread); return 0; } @@ -5010,7 +5520,7 @@ struct SystemVRegisterCount u32 sse_registers; }; -fn SystemVClassification systemv_classify(SemaType* type, u64 base_offset) +fn SystemVClassification systemv_classify(DebugType* type, u64 base_offset) { SystemVClassification result; u32 is_memory = base_offset >= 8; @@ -5020,13 +5530,13 @@ fn SystemVClassification systemv_classify(SemaType* type, u64 base_offset) switch (type->id) { - case SemaTypeId::VOID: + case DebugTypeId::VOID: trap(); - case SemaTypeId::NORETURN: + case DebugTypeId::NORETURN: trap(); - case SemaTypeId::POINTER: + case DebugTypeId::POINTER: trap(); - case SemaTypeId::INTEGER: + case DebugTypeId::INTEGER: { u8 bit_count = type->get_bit_count(); switch (bit_count) @@ -5038,7 +5548,7 @@ fn SystemVClassification systemv_classify(SemaType* type, u64 base_offset) trap(); } } break; - case SemaTypeId::COUNT: + case DebugTypeId::COUNT: trap(); default: trap(); @@ -5047,7 +5557,7 @@ fn SystemVClassification systemv_classify(SemaType* type, u64 base_offset) return result; } -fn u8 contains_no_user_data(SemaType* type, u64 start, u64 end) +fn u8 contains_no_user_data(DebugType* type, u64 start, u64 end) { unused(end); if (type->size <= start) @@ -5057,32 +5567,32 @@ fn u8 contains_no_user_data(SemaType* type, u64 start, u64 end) switch (type->id) { - case SemaTypeId::ARRAY: + case DebugTypeId::ARRAY: trap(); - case SemaTypeId::STRUCT: + case DebugTypeId::STRUCT: trap(); - case SemaTypeId::UNION: + case DebugTypeId::UNION: trap(); default: return 0; - case SemaTypeId::COUNT: + case DebugTypeId::COUNT: trap(); } } -fn SemaType* systemv_get_int_type_at_offset(SemaType* type, u64 offset, SemaType* source_type, u64 source_offset) +fn DebugType* systemv_get_int_type_at_offset(DebugType* type, u64 offset, DebugType* source_type, u64 source_offset) { unused(source_type); switch (type->id) { - case SemaTypeId::VOID: + case DebugTypeId::VOID: trap(); - case SemaTypeId::NORETURN: + case DebugTypeId::NORETURN: trap(); - case SemaTypeId::POINTER: + case DebugTypeId::POINTER: trap(); - case SemaTypeId::INTEGER: + case DebugTypeId::INTEGER: { u8 bit_count = type->get_bit_count(); switch (bit_count) @@ -5107,18 +5617,18 @@ fn SemaType* systemv_get_int_type_at_offset(SemaType* type, u64 offset, SemaType } trap(); } break; - case SemaTypeId::COUNT: + case DebugTypeId::COUNT: trap(); - case SemaTypeId::ARRAY: + case DebugTypeId::ARRAY: trap(); - case SemaTypeId::STRUCT: + case DebugTypeId::STRUCT: trap(); - case SemaTypeId::UNION: + case DebugTypeId::UNION: trap(); } } -fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file) +fn Function* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file) { String src = file->source_code; parser->expect_character(src, 'f'); @@ -5223,20 +5733,20 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file fail(); } + auto function_index = thread->functions.length; auto* function = thread->functions.add_one(); - auto function_gvn = thread->node_count; + auto node_id = thread->node_count; thread->node_count += 1; auto symbol_result = file->symbols.get_or_put(thread->arena, name, Node{ .type = {}, - .inputs = {}, - .outputs = {}, - .gvn = function_gvn, + .uid = node_id, .id = Node::Id::SYMBOL_FUNCTION, .payload = { .symbol = &function->symbol, }, }); + if (symbol_result.existing) { fail(); @@ -5322,7 +5832,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file parser->expect_character(src, function_argument_start); - Array original_argument_types = {}; + Array original_argument_types = {}; Array argument_names = {}; while (1) @@ -5341,7 +5851,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file parser->expect_character(src, ':'); parser->skip_space(src); - SemaType* argument_type = analyze_type(parser, unit, src); + DebugType* argument_type = analyze_type(parser, unit, src); original_argument_types.append_one(argument_type); parser->skip_space(src); @@ -5361,7 +5871,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file parser->skip_space(src); - SemaType* original_return_type = analyze_type(parser, unit, src); + DebugType* original_return_type = analyze_type(parser, unit, src); parser->skip_space(src); @@ -5377,12 +5887,12 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file SystemVClassification return_type_classes = systemv_classify(original_return_type, 0); assert(return_type_classes.v[1] != SYSTEMV_CLASS_MEMORY | return_type_classes.v[0] == SYSTEMV_CLASS_MEMORY); assert(return_type_classes.v[1] != SYSTEMV_CLASS_SSEUP | return_type_classes.v[0] == SYSTEMV_CLASS_SSE); - SemaType* low_part = 0; + DebugType* low_part = 0; switch (return_type_classes.v[0]) { case SYSTEMV_CLASS_INTEGER: { - SemaType* result_type = systemv_get_int_type_at_offset(original_return_type, 0, original_return_type, 0); + DebugType* result_type = systemv_get_int_type_at_offset(original_return_type, 0, original_return_type, 0); if (return_type_classes.v[1] == SYSTEMV_CLASS_NONE & original_return_type->get_bit_count() < 32) { trap(); @@ -5395,7 +5905,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file } assert(low_part); - SemaType* high_part = 0; + DebugType* high_part = 0; switch (return_type_classes.v[1]) { case SYSTEMV_CLASS_NONE: @@ -5425,7 +5935,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file return_type_abi = { .payload = { - .direct = low_part->lower(), + .direct = low_part->lower(thread), }, .kind = ABI_INFO_DIRECT, }; @@ -5470,16 +5980,16 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file { return_type_abi = { .payload = { - .direct = original_return_type->lower(), + .direct = original_return_type->lower(thread), }, .kind = ABI_INFO_DIRECT, }; - for (SemaType* original_argument_type : original_argument_types.slice()) + for (DebugType* original_argument_type : original_argument_types.slice()) { argument_type_abis.append_one({ .payload = { - .direct = original_argument_type->lower(), + .direct = original_argument_type->lower(thread), }, .kind = AbiInfoKind::ABI_INFO_DIRECT, }); @@ -5514,7 +6024,6 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file trap(); } - *function = { .symbol = { .name = name, @@ -5532,12 +6041,13 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file .original_argument_count = original_argument_types.length, .varags = 0, }, + .uid = function_index, .parameter_count = (u16)argument_type_abis.length, }; - Array abi_argument_types = {}; - Array root_arg_types = {}; - root_arg_types.append_one({ .id = Node::Type::Id::LIVE_CONTROL }); + Array abi_argument_types = {}; + Array root_arg_types = {}; + root_arg_types.append_one(thread->common_types.live_control); for (u32 i = 0; i < argument_type_abis.length; i += 1) { @@ -5578,24 +6088,25 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file root_arg_types.append(abi_argument_types.slice()); - Node::Type root_type = { - .id = Node::Type::Id::MULTIVALUE, + auto* root_type = thread->types.append_one({ .payload = { .multi = { .types = root_arg_types.slice(), }, }, - }; + .id = NodeType::Id::MULTIVALUE, + })->intern_type(thread); + function->root_node = Node::add(thread, { - .type = root_type, .inputs = {}, .id = Node::Id::ROOT, }); + function->root_node->type = root_type, function->root_node->payload.root.args = root_type; + function->root_node->payload.root.function = function; function->root_node->peephole(thread, function); function->stop_node = Node::add(thread, { - .type = {}, .inputs = {}, .id = Node::Id::STOP, }); @@ -5610,7 +6121,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file s32 next_index = 0; Node* control_node = function->root_node->project(thread, function->root_node, next_index, control_name)->peephole(thread, function); next_index += 1; - define_variable(&analyzer, thread->arena, control_name, control_node); + define_variable(&analyzer, thread, control_name, control_node); // assert(abi_argument_type_count == 0); // TODO: reserve memory for them @@ -5630,7 +6141,7 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file case ABI_INFO_DIRECT: { auto* argument_node = function->root_node->project(thread, function->root_node, next_index, argument_name)->peephole(thread, function); - define_variable(&analyzer, thread->arena, argument_name, argument_node); + define_variable(&analyzer, thread, argument_name, argument_node); next_index += 1; } break; case ABI_INFO_DIRECT_PAIR: @@ -5652,11 +6163,11 @@ fn Node* analyze_function(Parser* parser, Thread* thread, Unit* unit, File* file analyze_local_block(&analyzer, parser, unit, thread, src); - pop_scope(&analyzer); + pop_scope(&analyzer, thread); function->stop_node->peephole(thread, function); - return function->stop_node; + return function; } break; case 1: trap(); @@ -5694,7 +6205,8 @@ fn void unit_file_analyze(Thread* thread, Unit* unit, File* file) case 'f': if (get_next_ch_safe(src, declaration_start_index) == 'n') { - analyze_function(&parser, thread, unit, file); + auto* function = analyze_function(&parser, thread, unit, file); + function->iterate(thread); } else { @@ -5707,6 +6219,177 @@ fn void unit_file_analyze(Thread* thread, Unit* unit, File* file) } } +method void Thread::init() +{ + auto* live_control = types.append_one({ + .id = NodeType::Id::LIVE_CONTROL, + })->intern_type(this); + auto* dead_control = types.append_one({ + .id = NodeType::Id::DEAD_CONTROL, + })->intern_type(this); + + auto if_both_types = arena->allocate_slice(2); + if_both_types[0] = live_control; + if_both_types[1] = live_control; + + auto if_neither_types = arena->allocate_slice(2); + if_neither_types[0] = dead_control; + if_neither_types[1] = dead_control; + + auto if_true_types = arena->allocate_slice(2); + if_true_types[0] = live_control; + if_true_types[1] = dead_control; + + auto if_false_types = arena->allocate_slice(2); + if_false_types[0] = dead_control; + if_false_types[1] = live_control; + + common_types = { + .bottom = types.append_one({ + .id = NodeType::Id::BOTTOM, + })->intern_type(this), + .top = types.append_one({ + .id = NodeType::Id::TOP, + })->intern_type(this), + .live_control = types.append_one({ + .id = NodeType::Id::LIVE_CONTROL, + })->intern_type(this), + .dead_control = types.append_one({ + .id = NodeType::Id::DEAD_CONTROL, + })->intern_type(this), + .integer_bot = types.append_one({ + .payload = { + .constant = { + .constant = 0, + .is_constant = 0, + }, + }, + .id = NodeType::Id::INTEGER, + })->intern_type(this), + .integer_top = types.append_one({ + .payload = { + .constant = { + .constant = 1, + .is_constant = 0, + }, + }, + .id = NodeType::Id::INTEGER, + })->intern_type(this), + .integer_zero = types.append_one({ + .payload = { + .constant = { + .constant = 0, + .is_constant = 1, + }, + }, + .id = NodeType::Id::INTEGER, + })->intern_type(this), + .if_both = types.append_one({ + .payload = { + .multi = { + .types = if_both_types, + }, + }, + .id = NodeType::Id::MULTIVALUE, + })->intern_type(this), + .if_neither = types.append_one({ + .payload = { + .multi = { + .types = if_neither_types, + }, + }, + .id = NodeType::Id::MULTIVALUE, + })->intern_type(this), + .if_true = types.append_one({ + .payload = { + .multi = { + .types = if_true_types, + }, + }, + .id = NodeType::Id::MULTIVALUE, + })->intern_type(this), + .if_false = types.append_one({ + .payload = { + .multi = { + .types = if_false_types, + }, + }, + .id = NodeType::Id::MULTIVALUE, + })->intern_type(this), + }; + +} + +method void Thread::clear() +{ + functions.clear(); + nodes.clear(); + types.clear(); + interned_nodes.clear(); + interned_types.clear(); + worklist.clear(); + visited.clear(); + common_types = {}; + node_count = 0; + peephole_iteration_count = 0; + peephole_nop_iteration_count = 0; + arena->reset(); +} + +fn Node* progress_on_list_callback(Thread* thread, Function* function, Node* node) +{ + Node* result = 0; + + if (!thread->worklist.on(node)) + { + Node* new_node = node->peephole_optimize(thread, function); + if (new_node) + { + result = new_node; + } + } + + return result; +} + +method u8 Thread::progress_on_list(Function* function, Node* stop) +{ + mid_assert = 1; + auto old_iteration_count = peephole_iteration_count; + auto old_nop_iteration_count = peephole_nop_iteration_count; + + Node* changed = stop->walk(this, function, &progress_on_list_callback); + peephole_iteration_count = old_iteration_count; + peephole_nop_iteration_count = old_nop_iteration_count; + mid_assert = 0; + assert(changed == 0); + return changed == 0; +} + +method u8 WorkList::on(Node* node) +{ + return bitset.get(node->uid); +} + +method Node* WorkList::push(Node* node) +{ + Node* result = 0; + + if (node) + { + result = node; + u32 index = node->uid; + + if (!bitset.get(index)) + { + bitset.set_assert_unset(index); + nodes.append_one(node); + } + } + + return result; +} + global Instance instance; global String test_file_paths[] = { @@ -5720,24 +6403,26 @@ global String test_file_paths[] = { strlit("tests/break_continue/main.nat"), }; -#ifdef __linux__ -extern "C" void entry_point() -#else +#if LINK_LIBC int main() +#else +extern "C" void entry_point() #endif { instance.arena = Arena::init(Arena::default_size, Arena::minimum_granularity, KB(4)); - + Thread* thread = instance_add_thread(&instance); for (String test_file_path : test_file_paths) { + thread->init(); print(test_file_path); print(strlit("... ")); Unit* unit = instance_add_unit(&instance); unit_initialize(unit); - Thread* thread = instance_add_thread(&instance); File* file = add_file(thread->arena, test_file_path); unit_file_analyze(thread, unit, file); print(strlit("[\x1b[32mOK\x1b[0m]\n")); + file->symbols.clear(); + thread->clear(); } print(strlit("\x1b[32mTESTS SUCCEEDED!\x1b[0m\n")); diff --git a/compile.sh b/compile.sh index 7b96c83..612dc6b 100755 --- a/compile.sh +++ b/compile.sh @@ -12,9 +12,9 @@ function compile() compile_command="clang++ -o $build_dir/$exe_name $debug_info $optimizations -std=gnu++20 -Wall -Wextra -Wpedantic -Wno-nested-anon-types -pedantic -fno-exceptions -fno-stack-protector -ferror-limit=1 -MJ $build_dir/compile_commands.json" case "$OSTYPE" in - darwin*) ;; - linux*) compile_command="$compile_command -ffreestanding -nostdlib -static bootstrap/entry.S" ;; - *) echo "unknown: $OSTYPE" ;; + darwin*) compile_command="$compile_command -DDEMAND_LIBC=1";; + linux*) compile_command="$compile_command -ffreestanding -nostdlib -static bootstrap/entry.S -DDEMAND_LIBC=0" ;; + *) echo "Unknown operating system $OSTYPE: no specific flags were added" ;; esac compile_command="$compile_command bootstrap/main.cpp" diff --git a/run_tests.sh b/run_tests.sh index c0edcf4..1907b2d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -2,5 +2,9 @@ set -e source ./compile.sh -compile "build" "nest" "-g" ""; +build_dir="build" +exe_name="nest" +exe_path=$build_dir/$exe_name + +compile $build_dir $exe_name "-g" ""; build/nest diff --git a/tests/if/main.nat b/tests/if/main.nat index 981df95..a1aad77 100644 --- a/tests/if/main.nat +++ b/tests/if/main.nat @@ -74,6 +74,7 @@ fn if4(arg: s32) s32 fn if5(arg: s32) s32 { >a: s32 = 1; + if (arg == 1) { if (arg == 2)