Browse Source

Update Tilde for the new TB_Passes approach

gingerBill 2 years ago
parent
commit
c39a360372
10 changed files with 345 additions and 126 deletions
  1. 6 9
      core/runtime/os_specific_windows.odin
  2. 9 1
      core/runtime/print.odin
  3. 25 19
      src/tilde.cpp
  4. 12 0
      src/tilde.hpp
  5. 50 56
      src/tilde/tb.h
  6. BIN
      src/tilde/tb.lib
  7. 76 0
      src/tilde/tb_arena.h
  8. 38 4
      src/tilde_expr.cpp
  9. 121 37
      src/tilde_proc.cpp
  10. 8 0
      src/tilde_stmt.cpp

+ 6 - 9
core/runtime/os_specific_windows.odin

@@ -1,9 +1,6 @@
-//+private
 //+build windows
 //+build windows
 package runtime
 package runtime
 
 
-import "core:intrinsics"
-
 foreign import kernel32 "system:Kernel32.lib"
 foreign import kernel32 "system:Kernel32.lib"
 
 
 @(private="file")
 @(private="file")
@@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 
 
 	allocated_mem: rawptr
 	allocated_mem: rawptr
 	if old_ptr != nil {
 	if old_ptr != nil {
-		original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
+		original_old_ptr := ([^]rawptr)(old_ptr)[-1]
 		allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 		allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
 	} else {
 	} else {
 		allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 		allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
 	}
 	}
-	aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
+	aligned_mem := ([^]u8)(allocated_mem)[size_of(rawptr):]
 
 
 	ptr := uintptr(aligned_mem)
 	ptr := uintptr(aligned_mem)
 	aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
 	aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
@@ -116,10 +113,10 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
 		return nil, .Out_Of_Memory
 		return nil, .Out_Of_Memory
 	}
 	}
 
 
-	aligned_mem = rawptr(aligned_ptr)
-	intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
+	aligned_mem = ([^]byte)(aligned_ptr)
+	([^]rawptr)(aligned_mem)[-1] = allocated_mem
 
 
-	return byte_slice(aligned_mem, size), nil
+	return aligned_mem[:size], nil
 }
 }
 
 
 _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
 _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
@@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory
 
 
 _windows_default_free :: proc "contextless" (ptr: rawptr) {
 _windows_default_free :: proc "contextless" (ptr: rawptr) {
 	if ptr != nil {
 	if ptr != nil {
-		heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^)
+		heap_free(([^]rawptr)(ptr)[-1])
 	}
 	}
 }
 }
 
 

+ 9 - 1
core/runtime/print.odin

@@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
 when !ODIN_NO_RTTI {
 when !ODIN_NO_RTTI {
 	print_any_single :: proc "contextless" (arg: any) {
 	print_any_single :: proc "contextless" (arg: any) {
 		x := arg
 		x := arg
+		if x.data == nil {
+			print_string("nil")
+			return
+		}
+
 		if loc, ok := x.(Source_Code_Location); ok {
 		if loc, ok := x.(Source_Code_Location); ok {
 			print_caller_location(loc)
 			print_caller_location(loc)
 			return
 			return
@@ -48,6 +53,7 @@ when !ODIN_NO_RTTI {
 		case int:     print_int(v)
 		case int:     print_int(v)
 		case uint:    print_uint(v)
 		case uint:    print_uint(v)
 		case uintptr: print_uintptr(v)
 		case uintptr: print_uintptr(v)
+		case rawptr:  print_uintptr(uintptr(v))
 
 
 		case bool: print_string("true" if v else "false")
 		case bool: print_string("true" if v else "false")
 		case b8:   print_string("true" if v else "false")
 		case b8:   print_string("true" if v else "false")
@@ -58,7 +64,7 @@ when !ODIN_NO_RTTI {
 		case:
 		case:
 			ti := type_info_of(x.id)
 			ti := type_info_of(x.id)
 			#partial switch v in ti.variant {
 			#partial switch v in ti.variant {
-			case Type_Info_Pointer:
+			case Type_Info_Pointer, Type_Info_Multi_Pointer:
 				print_uintptr((^uintptr)(x.data)^)
 				print_uintptr((^uintptr)(x.data)^)
 				return
 				return
 			}
 			}
@@ -67,7 +73,9 @@ when !ODIN_NO_RTTI {
 		}
 		}
 	}
 	}
 	println_any :: proc "contextless" (args: ..any) {
 	println_any :: proc "contextless" (args: ..any) {
+		context = default_context()
 		loop: for arg, i in args {
 		loop: for arg, i in args {
+			assert(arg.id != nil)
 			if i != 0 {
 			if i != 0 {
 				print_string(" ")
 				print_string(" ")
 			}
 			}

+ 25 - 19
src/tilde.cpp

@@ -1,16 +1,16 @@
 #include "tilde.hpp"
 #include "tilde.hpp"
 
 
 
 
-gb_global Slice<TB_Arena *> global_tb_arenas;
+gb_global Slice<TB_Arena> global_tb_arenas;
 
 
 gb_internal TB_Arena *cg_arena(void) {
 gb_internal TB_Arena *cg_arena(void) {
-	return global_tb_arenas[current_thread_index()];
+	return &global_tb_arenas[current_thread_index()];
 }
 }
 
 
 gb_internal void cg_global_arena_init(void) {
 gb_internal void cg_global_arena_init(void) {
-	global_tb_arenas = slice_make<TB_Arena *>(permanent_allocator(), global_thread_pool.threads.count);
+	global_tb_arenas = slice_make<TB_Arena>(permanent_allocator(), global_thread_pool.threads.count);
 	for_array(i, global_tb_arenas) {
 	for_array(i, global_tb_arenas) {
-		global_tb_arenas[i] = tb_default_arena();
+		tb_arena_create(&global_tb_arenas[i], 2ull<<20);
 	}
 	}
 }
 }
 
 
@@ -426,14 +426,15 @@ gb_internal cgModule *cg_module_create(Checker *c) {
 
 
 	map_init(&m->values);
 	map_init(&m->values);
 	map_init(&m->symbols);
 	map_init(&m->symbols);
-
 	map_init(&m->file_id_map);
 	map_init(&m->file_id_map);
-
 	map_init(&m->debug_type_map);
 	map_init(&m->debug_type_map);
 	map_init(&m->proc_debug_type_map);
 	map_init(&m->proc_debug_type_map);
 	map_init(&m->proc_proto_map);
 	map_init(&m->proc_proto_map);
-
 	map_init(&m->anonymous_proc_lits_map);
 	map_init(&m->anonymous_proc_lits_map);
+	map_init(&m->equal_procs);
+	map_init(&m->hasher_procs);
+	map_init(&m->map_get_procs);
+	map_init(&m->map_set_procs);
 
 
 	array_init(&m->single_threaded_procedure_queue, heap_allocator());
 	array_init(&m->single_threaded_procedure_queue, heap_allocator());
 
 
@@ -456,6 +457,10 @@ gb_internal void cg_module_destroy(cgModule *m) {
 	map_destroy(&m->proc_debug_type_map);
 	map_destroy(&m->proc_debug_type_map);
 	map_destroy(&m->proc_proto_map);
 	map_destroy(&m->proc_proto_map);
 	map_destroy(&m->anonymous_proc_lits_map);
 	map_destroy(&m->anonymous_proc_lits_map);
+	map_destroy(&m->equal_procs);
+	map_destroy(&m->hasher_procs);
+	map_destroy(&m->map_get_procs);
+	map_destroy(&m->map_set_procs);
 
 
 	array_free(&m->single_threaded_procedure_queue);
 	array_free(&m->single_threaded_procedure_queue);
 
 
@@ -751,6 +756,19 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
 			array_add(&procedures_to_generate, p);
 			array_add(&procedures_to_generate, p);
 		}
 		}
 	}
 	}
+	for (cgProcedure *p : procedures_to_generate) {
+		cg_add_procedure_to_queue(p);
+	}
+
+	if (!m->do_threading) {
+		for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
+			cgProcedure *p = m->single_threaded_procedure_queue[i];
+			cg_procedure_generate(p);
+		}
+	}
+
+	thread_pool_wait();
+
 	{
 	{
 		cgProcedure *p = cg_startup_runtime_proc;
 		cgProcedure *p = cg_startup_runtime_proc;
 		cg_procedure_begin(p);
 		cg_procedure_begin(p);
@@ -765,18 +783,6 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
 		cg_procedure_end(p);
 		cg_procedure_end(p);
 	}
 	}
 
 
-	for (cgProcedure *p : procedures_to_generate) {
-		cg_add_procedure_to_queue(p);
-	}
-
-	if (!m->do_threading) {
-		for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
-			cgProcedure *p = m->single_threaded_procedure_queue[i];
-			cg_procedure_generate(p);
-		}
-	}
-
-	thread_pool_wait();
 
 
 
 
 	TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;
 	TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;

+ 12 - 0
src/tilde.hpp

@@ -6,6 +6,7 @@
 #endif
 #endif
 
 
 #include "tilde/tb.h"
 #include "tilde/tb.h"
+#include "tilde/tb_arena.h"
 
 
 #define TB_TYPE_F16    TB_DataType{ { TB_INT, 0, 16 } }
 #define TB_TYPE_F16    TB_DataType{ { TB_INT, 0, 16 } }
 #define TB_TYPE_I128   TB_DataType{ { TB_INT, 0, 128 } }
 #define TB_TYPE_I128   TB_DataType{ { TB_INT, 0, 128 } }
@@ -230,6 +231,12 @@ struct cgModule {
 	BlockingMutex anonymous_proc_lits_mutex;
 	BlockingMutex anonymous_proc_lits_mutex;
 	PtrMap<Ast *, cgProcedure *> anonymous_proc_lits_map;
 	PtrMap<Ast *, cgProcedure *> anonymous_proc_lits_map;
 
 
+	RecursiveMutex generated_procs_mutex;
+	PtrMap<Type *, cgProcedure *> equal_procs;
+	PtrMap<Type *, cgProcedure *> hasher_procs;
+	PtrMap<Type *, cgProcedure *> map_get_procs;
+	PtrMap<Type *, cgProcedure *> map_set_procs;
+
 
 
 	// NOTE(bill): no need to protect this with a mutex
 	// NOTE(bill): no need to protect this with a mutex
 	PtrMap<uintptr, TB_FileID> file_id_map; // Key: AstFile.id (i32 cast to uintptr)
 	PtrMap<uintptr, TB_FileID> file_id_map; // Key: AstFile.id (i32 cast to uintptr)
@@ -319,6 +326,7 @@ gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr);
 
 
 gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results);
 gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results);
 gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results);
 gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results);
+gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result);
 gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node);
 gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node);
 
 
 gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e);
 gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e);
@@ -341,6 +349,10 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t);
 gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x);
 gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x);
 gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right);
 gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right);
 gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type);
 gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type);
+gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type);
+
+gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type);
+
 
 
 gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args);
 gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args);
 gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);
 gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);

+ 50 - 56
src/tilde/tb.h

@@ -15,20 +15,21 @@
 #define TB_VERSION_MINOR 2
 #define TB_VERSION_MINOR 2
 #define TB_VERSION_PATCH 0
 #define TB_VERSION_PATCH 0
 
 
-#ifdef __cplusplus
-#define TB_EXTERN extern "C"
-#else
-#define TB_EXTERN
-#endif
-
-#ifdef TB_DLL
-#  ifdef TB_IMPORT_DLL
-#    define TB_API TB_EXTERN __declspec(dllimport)
+#ifndef TB_API
+#  ifdef __cplusplus
+#    define TB_EXTERN extern "C"
 #  else
 #  else
-#    define TB_API TB_EXTERN __declspec(dllexport)
+#    define TB_EXTERN
+#  endif
+#  ifdef TB_DLL
+#    ifdef TB_IMPORT_DLL
+#      define TB_API TB_EXTERN __declspec(dllimport)
+#    else
+#      define TB_API TB_EXTERN __declspec(dllexport)
+#    endif
+#  else
+#    define TB_API TB_EXTERN
 #  endif
 #  endif
-#else
-#  define TB_API TB_EXTERN
 #endif
 #endif
 
 
 // These are flags
 // These are flags
@@ -171,7 +172,6 @@ typedef enum TB_FloatFormat {
 typedef union TB_DataType {
 typedef union TB_DataType {
     struct {
     struct {
         uint8_t type;
         uint8_t type;
-        // 2^N where N is the width value.
         // Only integers and floats can be wide.
         // Only integers and floats can be wide.
         uint8_t width;
         uint8_t width;
         // for integers it's the bitwidth
         // for integers it's the bitwidth
@@ -406,8 +406,6 @@ typedef struct TB_Symbol {
 
 
 typedef int TB_Reg;
 typedef int TB_Reg;
 
 
-#define TB_NULL_REG NULL
-
 typedef struct TB_Node TB_Node;
 typedef struct TB_Node TB_Node;
 struct TB_Node {
 struct TB_Node {
     TB_NodeType type;
     TB_NodeType type;
@@ -586,29 +584,13 @@ typedef struct {
 
 
 typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...);
 typedef void (*TB_PrintCallback)(void* user_data, const char* fmt, ...);
 
 
-////////////////////////////////
-// Arena
-////////////////////////////////
-// the goal is to move more things to transparent arenas, for now it's just function
-// IR which is a big one if you're interested in freeing them in whatever organization
-// you please.
-
-// allocations can make no guarentees about being sequential
-// tho it would be greatly appreciated at least to some degree.
+// defined in common/arena.h
 typedef struct TB_Arena TB_Arena;
 typedef struct TB_Arena TB_Arena;
-struct TB_Arena {
-    // alignment never goes past max_align_t
-    void* (*alloc)(TB_Arena* arena, size_t size, size_t align);
 
 
-    // clearing but we're not done with it yet, cheap
-    void (*clear)(TB_Arena* arena);
-
-    // frees everything within the arena, potentially expensive
-    void (*free)(TB_Arena* arena);
-};
-
-// allocates in 16MiB chunks and does linear allocation in 'em
-TB_API TB_Arena* tb_default_arena(void);
+// 0 for default
+TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
+TB_API void tb_arena_destroy(TB_Arena* restrict arena);
+TB_API bool tb_arena_is_empty(TB_Arena* arena);
 
 
 ////////////////////////////////
 ////////////////////////////////
 // Module management
 // Module management
@@ -651,8 +633,7 @@ struct TB_Assembly {
 // this is where the machine code and other relevant pieces go.
 // this is where the machine code and other relevant pieces go.
 typedef struct TB_FunctionOutput TB_FunctionOutput;
 typedef struct TB_FunctionOutput TB_FunctionOutput;
 
 
-// returns NULL if it fails
-TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode, bool emit_asm);
+TB_API void tb_output_print_asm(TB_FunctionOutput* out, FILE* fp);
 
 
 TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
 TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
 
 
@@ -865,6 +846,9 @@ TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_
 
 
 TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type);
 TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type);
 
 
+TB_API size_t tb_debug_func_return_count(TB_DebugType* type);
+TB_API size_t tb_debug_func_param_count(TB_DebugType* type);
+
 // you'll need to fill these if you make a function
 // you'll need to fill these if you make a function
 TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
@@ -895,9 +879,6 @@ TB_API void tb_default_print_callback(void* user_data, const char* fmt, ...);
 
 
 TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line);
 TB_API void tb_inst_set_location(TB_Function* f, TB_FileID file, int line);
 
 
-// this only allows for power of two vector types
-TB_API TB_DataType tb_vector_type(TB_DataTypeEnum type, int width);
-
 // if section is NULL, default to .text
 // if section is NULL, default to .text
 TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
 TB_API TB_Function* tb_function_create(TB_Module* m, ptrdiff_t len, const char* name, TB_Linkage linkage, TB_ComdatType comdat);
 
 
@@ -1073,25 +1054,40 @@ TB_API void tb_inst_branch(TB_Function* f, TB_DataType dt, TB_Node* key, TB_Node
 TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values);
 TB_API void tb_inst_ret(TB_Function* f, size_t count, TB_Node** values);
 
 
 ////////////////////////////////
 ////////////////////////////////
-// Optimizer
+// Passes
 ////////////////////////////////
 ////////////////////////////////
 // Function analysis, optimizations, and codegen are all part of this
 // Function analysis, optimizations, and codegen are all part of this
-typedef struct TB_FuncOpt TB_FuncOpt;
+typedef struct TB_Passes TB_Passes;
 
 
-// the arena is used to allocate the nodes
-TB_API TB_FuncOpt* tb_funcopt_enter(TB_Function* f, TB_Arena* arena);
-TB_API void tb_funcopt_exit(TB_FuncOpt* opt);
+// the arena is used to allocate the nodes while passes are being done.
+TB_API TB_Passes* tb_pass_enter(TB_Function* f, TB_Arena* arena);
+TB_API void tb_pass_exit(TB_Passes* opt);
 
 
-TB_API bool tb_funcopt_peephole(TB_FuncOpt* opt);
-TB_API bool tb_funcopt_mem2reg(TB_FuncOpt* opt);
-TB_API bool tb_funcopt_loop(TB_FuncOpt* opt);
+// transformation passes:
+//   peephole: runs most simple reductions on the code,
+//     should be run after any bigger passes (it's incremental
+//     so it's not that bad)
+//
+//   mem2reg: lowers TB_LOCALs into SSA values, this makes more
+//     data flow analysis possible on the code and allows to codegen
+//     to place variables into registers.
+//
+//   loop: NOT READY
+//
+TB_API bool tb_pass_peephole(TB_Passes* opt);
+TB_API bool tb_pass_mem2reg(TB_Passes* opt);
+TB_API bool tb_pass_loop(TB_Passes* opt);
 
 
-// isn't an optimization, just does the name flat form of IR printing
-TB_API bool tb_funcopt_print(TB_FuncOpt* opt);
+// analysis
+//   print: prints IR in a flattened text form.
+TB_API bool tb_pass_print(TB_Passes* opt);
 
 
-TB_API void tb_funcopt_kill(TB_FuncOpt* opt, TB_Node* n);
-TB_API bool tb_funcopt_mark(TB_FuncOpt* opt, TB_Node* n);
-TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n);
+// codegen
+TB_API TB_FunctionOutput* tb_pass_codegen(TB_Passes* opt, bool emit_asm);
+
+TB_API void tb_pass_kill_node(TB_Passes* opt, TB_Node* n);
+TB_API bool tb_pass_mark(TB_Passes* opt, TB_Node* n);
+TB_API void tb_pass_mark_users(TB_Passes* opt, TB_Node* n);
 
 
 ////////////////////////////////
 ////////////////////////////////
 // IR access
 // IR access
@@ -1099,8 +1095,6 @@ TB_API void tb_funcopt_mark_users(TB_FuncOpt* opt, TB_Node* n);
 TB_API const char* tb_node_get_name(TB_Node* n);
 TB_API const char* tb_node_get_name(TB_Node* n);
 
 
 TB_API TB_Node* tb_get_parent_region(TB_Node* n);
 TB_API TB_Node* tb_get_parent_region(TB_Node* n);
-TB_API bool tb_has_effects(TB_Node* n);
-
 TB_API bool tb_node_is_constant_non_zero(TB_Node* n);
 TB_API bool tb_node_is_constant_non_zero(TB_Node* n);
 TB_API bool tb_node_is_constant_zero(TB_Node* n);
 TB_API bool tb_node_is_constant_zero(TB_Node* n);
 
 

BIN
src/tilde/tb.lib


+ 76 - 0
src/tilde/tb_arena.h

@@ -0,0 +1,76 @@
+#pragma once
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifndef TB_API
+#  ifdef __cplusplus
+#    define TB_EXTERN extern "C"
+#  else
+#    define TB_EXTERN
+#  endif
+#  ifdef TB_DLL
+#    ifdef TB_IMPORT_DLL
+#      define TB_API TB_EXTERN __declspec(dllimport)
+#    else
+#      define TB_API TB_EXTERN __declspec(dllexport)
+#    endif
+#  else
+#    define TB_API TB_EXTERN
+#  endif
+#endif
+
+enum {
+    TB_ARENA_SMALL_CHUNK_SIZE  =        4 * 1024,
+    TB_ARENA_MEDIUM_CHUNK_SIZE =      512 * 1024,
+    TB_ARENA_LARGE_CHUNK_SIZE  = 2 * 1024 * 1024,
+
+    TB_ARENA_ALIGNMENT = 16,
+};
+
+typedef struct TB_ArenaChunk TB_ArenaChunk;
+struct TB_ArenaChunk {
+    TB_ArenaChunk* next;
+    size_t pad;
+    char data[];
+};
+
+typedef struct TB_Arena {
+    size_t chunk_size;
+    TB_ArenaChunk* base;
+    TB_ArenaChunk* top;
+
+    // top of the allocation space
+    char* watermark;
+    char* high_point; // &top->data[chunk_size]
+} TB_Arena;
+
+typedef struct TB_ArenaSavepoint {
+    TB_ArenaChunk* top;
+    char* watermark;
+} TB_ArenaSavepoint;
+
+#define TB_ARENA_FOR(it, arena) for (TB_ArenaChunk* it = (arena)->base; it != NULL; it = it->next)
+
+#define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T))
+#define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T))
+
+TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
+TB_API void tb_arena_destroy(TB_Arena* restrict arena);
+
+TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size);
+TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size);
+
+// asserts if ptr+size != watermark
+TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size);
+
+// in case you wanna mix unaligned and aligned arenas
+TB_API void tb_arena_realign(TB_Arena* restrict arena);
+
+TB_API bool tb_arena_is_empty(TB_Arena* arena);
+
+// savepoints
+TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena);
+TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp);
+
+// resets to only having one chunk
+TB_API void tb_arena_clear(TB_Arena* arena);

+ 38 - 4
src/tilde_expr.cpp

@@ -304,6 +304,42 @@ gb_internal cgValue cg_emit_byte_swap(cgProcedure *p, cgValue value, Type *end_t
 	return cg_emit_transmute(p, value, end_type);
 	return cg_emit_transmute(p, value, end_type);
 }
 }
 
 
+gb_internal cgValue cg_emit_comp_records(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right, Type *type) {
+	GB_ASSERT((is_type_struct(type) || is_type_union(type)) && is_type_comparable(type));
+	cgValue left_ptr  = cg_address_from_load_or_generate_local(p, left);
+	cgValue right_ptr = cg_address_from_load_or_generate_local(p, right);
+	cgValue res = {};
+	if (type_size_of(type) == 0) {
+		switch (op_kind) {
+		case Token_CmpEq:
+			return cg_const_bool(p, t_bool, true);
+		case Token_NotEq:
+			return cg_const_bool(p, t_bool, false);
+		}
+		GB_PANIC("invalid operator");
+	}
+	TEMPORARY_ALLOCATOR_GUARD();
+	if (is_type_simple_compare(type)) {
+		// TODO(bill): Test to see if this is actually faster!!!!
+		auto args = slice_make<cgValue>(temporary_allocator(), 3);
+		args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
+		args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
+		args[2] = cg_const_int(p, t_int, type_size_of(type));
+		res = cg_emit_runtime_call(p, "memory_equal", args);
+	} else {
+		cgProcedure *equal_proc = cg_equal_proc_for_type(p->module, type);
+		cgValue value = cg_value(tb_inst_get_symbol_address(p->func, equal_proc->symbol), equal_proc->type);
+		auto args = slice_make<cgValue>(temporary_allocator(), 2);
+		args[0] = cg_emit_conv(p, left_ptr, t_rawptr);
+		args[1] = cg_emit_conv(p, right_ptr, t_rawptr);
+		res = cg_emit_call(p, value, args);
+	}
+	if (op_kind == Token_NotEq) {
+		res = cg_emit_unary_arith(p, Token_Not, res, res.type);
+	}
+	return res;
+}
+
 gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right) {
 gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right) {
 	GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
 	GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
 
 
@@ -440,13 +476,11 @@ gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left
 	}
 	}
 
 
 	if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) {
 	if ((is_type_struct(a) || is_type_union(a)) && is_type_comparable(a)) {
-		GB_PANIC("TODO(bill): cg_compare_records");
-		// return cg_compare_records(p, op_kind, left, right, a);
+		return cg_emit_comp_records(p, op_kind, left, right, a);
 	}
 	}
 
 
 	if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) {
 	if ((is_type_struct(b) || is_type_union(b)) && is_type_comparable(b)) {
-		GB_PANIC("TODO(bill): cg_compare_records");
-		// return cg_compare_records(p, op_kind, left, right, b);
+		return cg_emit_comp_records(p, op_kind, left, right, b);
 	}
 	}
 
 
 	if (is_type_string(a)) {
 	if (is_type_string(a)) {

+ 121 - 37
src/tilde_proc.cpp

@@ -368,22 +368,17 @@ gb_internal void cg_procedure_begin(cgProcedure *p) {
 gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
 gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
 	cgProcedure *p = cast(cgProcedure *)data;
 	cgProcedure *p = cast(cgProcedure *)data;
 
 
-	bool emit_asm = false;
-
-	if (false &&
-	    string_starts_with(p->name, str_lit("bug@main"))) {
-		TB_Arena *arena = cg_arena();
-		TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
-		defer (tb_funcopt_exit(opt));
-
-		tb_funcopt_peephole(opt);
-		tb_funcopt_mem2reg(opt);
-		tb_funcopt_peephole(opt);
-
-		emit_asm = true;
+	TB_Passes *opt = tb_pass_enter(p->func, cg_arena());
+	defer (tb_pass_exit(opt));
+
+	// optimization passes
+	if (false) {
+		tb_pass_peephole(opt);
+		tb_pass_mem2reg(opt);
+		tb_pass_peephole(opt);
 	}
 	}
 
 
-
+	bool emit_asm = false;
 	if (
 	if (
 	    // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc_or_resize")) ||
 	    // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc_or_resize")) ||
 	    false
 	    false
@@ -391,12 +386,27 @@ gb_internal WORKER_TASK_PROC(cg_procedure_compile_worker_proc) {
 		emit_asm = true;
 		emit_asm = true;
 	}
 	}
 
 
-	TB_FunctionOutput *output = tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST, emit_asm);
+	// emit ir
+	if (
+	    // string_starts_with(p->name, str_lit("bug@main")) ||
+	    // p->name == str_lit("runtime@_windows_default_alloc_or_resize") ||
+	    false
+	) { // IR Printing
+		TB_Arena *arena = cg_arena();
+		TB_Passes *passes = tb_pass_enter(p->func, arena);
+		defer (tb_pass_exit(passes));
+
+		tb_pass_print(passes);
+		fprintf(stdout, "\n");
+	}
+	if (false) { // GraphViz printing
+		tb_function_print(p->func, tb_default_print_callback, stdout);
+	}
+
+	// compile
+	TB_FunctionOutput *output = tb_pass_codegen(opt, emit_asm);
 	if (emit_asm) {
 	if (emit_asm) {
-		TB_Assembly *assembly = tb_output_get_asm(output);
-		for (TB_Assembly *node = assembly; node != nullptr; node = node->next) {
-			fprintf(stdout, "%.*s", cast(int)node->length, node->data);
-		}
+		tb_output_print_asm(output, stdout);
 		fprintf(stdout, "\n");
 		fprintf(stdout, "\n");
 	}
 	}
 
 
@@ -427,27 +437,9 @@ gb_internal void cg_procedure_generate(cgProcedure *p) {
 		return;
 		return;
 	}
 	}
 
 
-
 	cg_procedure_begin(p);
 	cg_procedure_begin(p);
 	cg_build_stmt(p, p->body);
 	cg_build_stmt(p, p->body);
 	cg_procedure_end(p);
 	cg_procedure_end(p);
-
-
-	if (
-	    // string_starts_with(p->name, str_lit("runtime@_windows_default_alloc")) ||
-	    // p->name == str_lit("runtime@_windows_default_alloc_or_resize") ||
-	    false
-	) { // IR Printing
-		TB_Arena *arena = tb_default_arena();
-		defer (arena->free(arena));
-		TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
-		defer (tb_funcopt_exit(opt));
-		tb_funcopt_print(opt);
-		fprintf(stdout, "\n");
-	}
-	if (false) { // GraphViz printing
-		tb_function_print(p->func, tb_default_print_callback, stdout);
-	}
 }
 }
 
 
 gb_internal void cg_build_nested_proc(cgProcedure *p, AstProcLit *pd, Entity *e) {
 gb_internal void cg_build_nested_proc(cgProcedure *p, AstProcLit *pd, Entity *e) {
@@ -989,3 +981,95 @@ gb_internal cgValue cg_build_call_expr_internal(cgProcedure *p, Ast *expr) {
 
 
 	return cg_emit_call(p, value, call_args);
 	return cg_emit_call(p, value, call_args);
 }
 }
+
+
+
+
+gb_internal cgProcedure *cg_equal_proc_for_type(cgModule *m, Type *type) {
+	type = base_type(type);
+	GB_ASSERT(is_type_comparable(type));
+
+	mutex_lock(&m->generated_procs_mutex);
+	defer (mutex_unlock(&m->generated_procs_mutex));
+
+	cgProcedure **found = map_get(&m->equal_procs, type);
+	if (found) {
+		return *found;
+	}
+
+	static std::atomic<u32> proc_index;
+
+	char buf[32] = {};
+	isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
+	char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
+	String proc_name = make_string_c(str);
+
+
+	cgProcedure *p = cg_procedure_create_dummy(m, proc_name, t_equal_proc);
+	map_set(&m->equal_procs, type, p);
+
+	cg_procedure_begin(p);
+
+	TB_Node *x = tb_inst_param(p->func, 0);
+	TB_Node *y = tb_inst_param(p->func, 1);
+	GB_ASSERT(x->dt.type == TB_PTR);
+	GB_ASSERT(y->dt.type == TB_PTR);
+
+	TB_DataType ret_dt = TB_PROTOTYPE_RETURNS(p->proto)->dt;
+
+	TB_Node *node_true  = tb_inst_uint(p->func, ret_dt, true);
+	TB_Node *node_false = tb_inst_uint(p->func, ret_dt, false);
+
+	TB_Node *same_ptr_region = cg_control_region(p, "same_ptr");
+	TB_Node *diff_ptr_region = cg_control_region(p, "diff_ptr");
+
+	TB_Node *is_same_ptr = tb_inst_cmp_eq(p->func, x, y);
+	tb_inst_if(p->func, is_same_ptr, same_ptr_region, diff_ptr_region);
+
+	tb_inst_set_control(p->func, same_ptr_region);
+	tb_inst_ret(p->func, 1, &node_true);
+
+	tb_inst_set_control(p->func, diff_ptr_region);
+
+	Type *pt = alloc_type_pointer(type);
+	cgValue lhs = cg_value(x, pt);
+	cgValue rhs = cg_value(y, pt);
+
+	if (type->kind == Type_Struct) {
+		type_set_offsets(type);
+
+		TB_Node *false_region  = cg_control_region(p, "bfalse");
+		cgValue res = cg_const_bool(p, t_bool, true);
+
+		for_array(i, type->Struct.fields) {
+			TB_Node *next_region = cg_control_region(p, "btrue");
+
+			cgValue plhs  = cg_emit_struct_ep(p, lhs, i);
+			cgValue prhs  = cg_emit_struct_ep(p, rhs, i);
+			cgValue left  = cg_emit_load(p, plhs);
+			cgValue right = cg_emit_load(p, prhs);
+			cgValue ok    = cg_emit_comp(p, Token_CmpEq, left, right);
+
+			cg_emit_if(p, ok, next_region, false_region);
+
+			cg_emit_goto(p, next_region);
+			tb_inst_set_control(p->func, next_region);
+		}
+
+		tb_inst_ret(p->func, 1, &node_true);
+		tb_inst_set_control(p->func, false_region);
+		tb_inst_ret(p->func, 1, &node_false);
+
+	} else if (type->kind == Type_Union) {
+		GB_PANIC("TODO(bill): union comparison");
+	} else {
+		cgValue left  = cg_lvalue_addr(x, type);
+		cgValue right = cg_lvalue_addr(y, type);
+		cgValue ok = cg_emit_comp(p, Token_CmpEq, left, right);
+		cg_build_return_stmt_internal_single(p, ok);
+	}
+
+	cg_procedure_end(p);
+
+	return p;
+}

+ 8 - 0
src/tilde_stmt.cpp

@@ -1047,6 +1047,14 @@ gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) {
 	}
 	}
 }
 }
 
 
+gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result) {
+	Slice<cgValue> results = {};
+	results.data = &result;
+	results.count = 1;
+	cg_build_return_stmt_internal(p, results);
+}
+
+
 gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results) {
 gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results) {
 	TypeTuple *tuple  = &p->type->Proc.results->Tuple;
 	TypeTuple *tuple  = &p->type->Proc.results->Tuple;
 	isize return_count = p->type->Proc.result_count;
 	isize return_count = p->type->Proc.result_count;