Ginger Bill 9 лет назад
Родитель
Сommit
8534e064b9
60 измененных файлов с 7969 добавлено и 5377 удалено
  1. 17 5
      code/demo.odin
  2. 5 5
      core/_preload.odin
  3. 10 0
      src/array.cpp
  4. 23 6
      src/checker/types.cpp
  5. 143 0
      src/dyncall/include/dyncall.h
  6. 47 0
      src/dyncall/include/dyncall_alloc_wx.h
  7. 66 0
      src/dyncall/include/dyncall_args.h
  8. 46 0
      src/dyncall/include/dyncall_args_arm32_arm.h
  9. 33 0
      src/dyncall/include/dyncall_args_arm32_thumb.h
  10. 42 0
      src/dyncall/include/dyncall_args_mips.h
  11. 43 0
      src/dyncall/include/dyncall_args_ppc32.h
  12. 40 0
      src/dyncall/include/dyncall_args_ppc64.h
  13. 38 0
      src/dyncall/include/dyncall_args_sparc32.h
  14. 38 0
      src/dyncall/include/dyncall_args_sparc64.h
  15. 45 0
      src/dyncall/include/dyncall_args_x64.h
  16. 59 0
      src/dyncall/include/dyncall_args_x86.h
  17. 53 0
      src/dyncall/include/dyncall_callback.h
  18. 46 0
      src/dyncall/include/dyncall_callback_arm32_arm.h
  19. 34 0
      src/dyncall/include/dyncall_callback_arm32_thumb.h
  20. 43 0
      src/dyncall/include/dyncall_callback_mips.h
  21. 43 0
      src/dyncall/include/dyncall_callback_ppc32.h
  22. 56 0
      src/dyncall/include/dyncall_callback_ppc64.h
  23. 44 0
      src/dyncall/include/dyncall_callback_sparc32.h
  24. 45 0
      src/dyncall/include/dyncall_callback_x64.h
  25. 50 0
      src/dyncall/include/dyncall_callback_x86.h
  26. 56 0
      src/dyncall/include/dyncall_callf.h
  27. 47 0
      src/dyncall/include/dyncall_config.h
  28. 289 0
      src/dyncall/include/dyncall_macros.h
  29. 72 0
      src/dyncall/include/dyncall_signature.h
  30. 90 0
      src/dyncall/include/dyncall_thunk.h
  31. 41 0
      src/dyncall/include/dyncall_thunk_arm32_arm.h
  32. 36 0
      src/dyncall/include/dyncall_thunk_arm32_thumb.h
  33. 42 0
      src/dyncall/include/dyncall_thunk_arm64.h
  34. 38 0
      src/dyncall/include/dyncall_thunk_mips.h
  35. 40 0
      src/dyncall/include/dyncall_thunk_ppc32.h
  36. 55 0
      src/dyncall/include/dyncall_thunk_ppc64.h
  37. 37 0
      src/dyncall/include/dyncall_thunk_sparc32.h
  38. 37 0
      src/dyncall/include/dyncall_thunk_sparc64.h
  39. 40 0
      src/dyncall/include/dyncall_thunk_x64.h
  40. 40 0
      src/dyncall/include/dyncall_thunk_x86.h
  41. 75 0
      src/dyncall/include/dyncall_types.h
  42. 91 0
      src/dyncall/include/dyncall_value.h
  43. 66 0
      src/dyncall/include/dynload.h
  44. BIN
      src/dyncall/lib/libdyncall_s.lib
  45. BIN
      src/dyncall/lib/libdyncallback_s.lib
  46. BIN
      src/dyncall/lib/libdynload_s.lib
  47. 4 5
      src/main.cpp
  48. 5340 0
      src/ssa.cpp
  49. 0 1965
      src/ssa/build.cpp
  50. 0 671
      src/ssa/codegen.cpp
  51. 0 48
      src/ssa/debug.cpp
  52. 0 1150
      src/ssa/emit.cpp
  53. 0 495
      src/ssa/make.cpp
  54. 0 0
      src/ssa/module.cpp
  55. 0 114
      src/ssa/proc.cpp
  56. 0 0
      src/ssa/procedure.cpp
  57. 0 846
      src/ssa/ssa.cpp
  58. 11 0
      src/ssa_opt.cpp
  59. 0 0
      src/ssa_to_llvm.cpp
  60. 313 67
      src/vm.cpp

+ 17 - 5
code/demo.odin

@@ -4,12 +4,24 @@ Vec2 :: struct {
 	x, y: i64
 }
 
+
 main :: proc() {
-	bar :: proc() -> i64 {
-		a := [3]i64{7, 4, 2}
-		v := Vec2{a[0], 2}
-		return v.x
+	foo :: proc() -> i64 {
+		bar :: proc() -> (i64, i64) {
+			a := [3]i64{7, 4, 2}
+			v := Vec2{a[0], 2}
+			return v.x, v.y
+		}
+
+		x, y := bar()
+
+		return x + y
+	}
+
+	test :: proc(s: string) -> string {
+		return s
 	}
 
-	bar()
+	foo()
+	x := test("Hello")
 }

+ 5 - 5
core/_preload.odin

@@ -292,11 +292,11 @@ __string_cmp :: proc(a, b : string) -> int {
 	return mem.compare(a.data, b.data, min(a.count, b.count))
 }
 
-__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
-__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
-__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
-__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
-__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
+__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b) }
+__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0 }
+__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0 }
+__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0 }
+__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0 }
 
 
 __assert :: proc(file: string, line, column: int, msg: string) #inline {

+ 10 - 0
src/array.cpp

@@ -19,6 +19,7 @@ struct Array {
 };
 
 template <typename T> void     array_init        (Array<T> *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0));
+template <typename T> void     array_init_count  (Array<T> *array, gbAllocator a, isize count);
 template <typename T> Array<T> array_make        (T *data, isize count, isize capacity);
 template <typename T> void     array_free        (Array<T> *array);
 template <typename T> void     array_add         (Array<T> *array, T const &t);
@@ -37,6 +38,15 @@ void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
 	array->capacity = init_capacity;
 }
 
+template <typename T>
+void array_init_count(Array<T> *array, gbAllocator a, isize count) {
+	array->allocator = a;
+	array->data = gb_alloc_array(a, T, count);
+	array->count = count;
+	array->capacity = count;
+}
+
+
 template <typename T>
 Array<T> array_make(T *data, isize count, isize capacity) {
 	Array<T> a = {};

+ 23 - 6
src/checker/types.cpp

@@ -150,6 +150,8 @@ struct Type {
 		struct {
 			Entity **variables; // Entity_Variable
 			i32      variable_count;
+			b32      are_offsets_set;
+			i64 *    offsets;
 		} Tuple;
 		struct {
 			Scope *scope;
@@ -1109,11 +1111,21 @@ i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields
 }
 
 b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
-	GB_ASSERT(is_type_struct(t));
-	if (!t->Record.struct_are_offsets_set) {
-		t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed);
-		t->Record.struct_are_offsets_set = true;
-		return true;
+	t = base_type(t);
+	if (is_type_struct(t)) {
+		if (!t->Record.struct_are_offsets_set) {
+			t->Record.struct_offsets = type_set_offsets_of(s, allocator, t->Record.fields, t->Record.field_count, t->Record.struct_is_packed);
+			t->Record.struct_are_offsets_set = true;
+			return true;
+		}
+	} else if (is_type_tuple(t)) {
+		if (!t->Tuple.are_offsets_set) {
+			t->Tuple.offsets = type_set_offsets_of(s, allocator, t->Tuple.variables, t->Tuple.variable_count, false);
+			t->Tuple.are_offsets_set = true;
+			return true;
+		}
+	} else {
+		GB_PANIC("Invalid type for setting offsets");
 	}
 	return false;
 }
@@ -1239,7 +1251,12 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index)
 		if (gb_is_between(index, 0, t->Record.field_count-1)) {
 			return t->Record.struct_offsets[index];
 		}
-	} else if (t->kind == Type_Basic) {
+	} else if (t->kind == Type_Tuple) {
+		type_set_offsets(s, allocator, t);
+		if (gb_is_between(index, 0, t->Tuple.variable_count-1)) {
+			return t->Tuple.offsets[index];
+		}
+	}  else if (t->kind == Type_Basic) {
 		if (t->Basic.kind == Basic_string) {
 			switch (index) {
 			case 0: return 0;

+ 143 - 0
src/dyncall/include/dyncall.h

@@ -0,0 +1,143 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall.h
+ Description: public header for library dyncall
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+/*
+
+  dyncall C API
+
+  REVISION
+  2015/07/08 added SYS_PPC64 system call
+  2015/01/16 added SYS_PPC32 system call
+  2007/12/11 initial
+  
+*/
+
+#ifndef DYNCALL_H
+#define DYNCALL_H
+
+#include "dyncall_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+typedef struct DCCallVM_    DCCallVM;
+typedef struct DCstruct_    DCstruct;
+
+/* Supported Calling Convention Modes */
+
+#define DC_CALL_C_DEFAULT               0
+#define DC_CALL_C_ELLIPSIS            100
+#define DC_CALL_C_ELLIPSIS_VARARGS    101
+#define DC_CALL_C_X86_CDECL             1
+#define DC_CALL_C_X86_WIN32_STD         2
+#define DC_CALL_C_X86_WIN32_FAST_MS     3
+#define DC_CALL_C_X86_WIN32_FAST_GNU    4
+#define DC_CALL_C_X86_WIN32_THIS_MS     5
+#define DC_CALL_C_X86_WIN32_THIS_GNU    6
+#define DC_CALL_C_X64_WIN64             7
+#define DC_CALL_C_X64_SYSV              8
+#define DC_CALL_C_PPC32_DARWIN          9
+#define DC_CALL_C_PPC32_OSX            DC_CALL_C_PPC32_DARWIN /* alias */
+#define DC_CALL_C_ARM_ARM_EABI         10
+#define DC_CALL_C_ARM_THUMB_EABI       11
+#define DC_CALL_C_ARM_ARMHF            30
+#define DC_CALL_C_MIPS32_EABI          12
+#define DC_CALL_C_MIPS32_PSPSDK        DC_CALL_C_MIPS32_EABI /* alias - deprecated. */
+#define DC_CALL_C_PPC32_SYSV           13
+#define DC_CALL_C_PPC32_LINUX          DC_CALL_C_PPC32_SYSV /* alias */
+#define DC_CALL_C_ARM_ARM              14
+#define DC_CALL_C_ARM_THUMB            15
+#define DC_CALL_C_MIPS32_O32           16
+#define DC_CALL_C_MIPS64_N32           17
+#define DC_CALL_C_MIPS64_N64           18
+#define DC_CALL_C_X86_PLAN9            19
+#define DC_CALL_C_SPARC32              20
+#define DC_CALL_C_SPARC64              21
+#define DC_CALL_C_ARM64                22
+#define DC_CALL_C_PPC64                23
+#define DC_CALL_C_PPC64_LINUX          DC_CALL_C_PPC64 /* alias */
+#define DC_CALL_SYS_DEFAULT           200
+#define DC_CALL_SYS_X86_INT80H_LINUX  201
+#define DC_CALL_SYS_X86_INT80H_BSD    202
+#define DC_CALL_SYS_PPC32             210
+#define DC_CALL_SYS_PPC64             211
+
+/* Error codes. */
+
+#define DC_ERROR_NONE                0
+#define DC_ERROR_UNSUPPORTED_MODE   -1
+
+DC_API DCCallVM*  dcNewCallVM     (DCsize size);
+DC_API void       dcFree          (DCCallVM* vm);
+DC_API void       dcReset         (DCCallVM* vm);
+
+DC_API void       dcMode          (DCCallVM* vm, DCint mode);
+
+DC_API void       dcArgBool       (DCCallVM* vm, DCbool     value);
+DC_API void       dcArgChar       (DCCallVM* vm, DCchar     value);
+DC_API void       dcArgShort      (DCCallVM* vm, DCshort    value);
+DC_API void       dcArgInt        (DCCallVM* vm, DCint      value);
+DC_API void       dcArgLong       (DCCallVM* vm, DClong     value);
+DC_API void       dcArgLongLong   (DCCallVM* vm, DClonglong value);
+DC_API void       dcArgFloat      (DCCallVM* vm, DCfloat    value);
+DC_API void       dcArgDouble     (DCCallVM* vm, DCdouble   value);
+DC_API void       dcArgPointer    (DCCallVM* vm, DCpointer  value);
+DC_API void       dcArgStruct     (DCCallVM* vm, DCstruct* s, DCpointer value);
+
+DC_API void       dcCallVoid      (DCCallVM* vm, DCpointer funcptr);
+DC_API DCbool     dcCallBool      (DCCallVM* vm, DCpointer funcptr);
+DC_API DCchar     dcCallChar      (DCCallVM* vm, DCpointer funcptr);
+DC_API DCshort    dcCallShort     (DCCallVM* vm, DCpointer funcptr);
+DC_API DCint      dcCallInt       (DCCallVM* vm, DCpointer funcptr);
+DC_API DClong     dcCallLong      (DCCallVM* vm, DCpointer funcptr);
+DC_API DClonglong dcCallLongLong  (DCCallVM* vm, DCpointer funcptr);
+DC_API DCfloat    dcCallFloat     (DCCallVM* vm, DCpointer funcptr);
+DC_API DCdouble   dcCallDouble    (DCCallVM* vm, DCpointer funcptr);
+DC_API DCpointer  dcCallPointer   (DCCallVM* vm, DCpointer funcptr);
+DC_API void       dcCallStruct    (DCCallVM* vm, DCpointer funcptr, DCstruct* s, DCpointer returnValue);
+
+DC_API DCint      dcGetError      (DCCallVM* vm);
+
+#define DEFAULT_ALIGNMENT 0
+DC_API DCstruct*  dcNewStruct      (DCsize fieldCount, DCint alignment);
+DC_API void       dcStructField    (DCstruct* s, DCint type, DCint alignment, DCsize arrayLength);
+DC_API void       dcSubStruct      (DCstruct* s, DCsize fieldCount, DCint alignment, DCsize arrayLength);  	
+/* Each dcNewStruct or dcSubStruct call must be paired with a dcCloseStruct. */
+DC_API void       dcCloseStruct    (DCstruct* s);  	
+DC_API DCsize     dcStructSize     (DCstruct* s);  	
+DC_API DCsize     dcStructAlignment(DCstruct* s);  	
+DC_API void       dcFreeStruct     (DCstruct* s);
+
+DC_API DCstruct*  dcDefineStruct  (const char* signature);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNCALL_H */
+

+ 47 - 0
src/dyncall/include/dyncall_alloc_wx.h

@@ -0,0 +1,47 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_alloc_wx.h
+ Description: Allocate write/executable memory - Interface
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_ALLOC_WX_HPP
+#define DYNCALL_ALLOC_WX_HPP
+
+#include "dyncall_types.h"
+
+typedef int DCerror;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DCerror dcAllocWX(DCsize size, void** p);
+void    dcFreeWX (void* p, DCsize size);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* DYNCALL_ALLOC_WX_HPP */
+

+ 66 - 0
src/dyncall/include/dyncall_args.h

@@ -0,0 +1,66 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args.h
+ Description: Callback's Arguments VM - Interface
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_ARGS_H
+#define DYNCALL_ARGS_H
+
+/*
+ * dyncall args C API
+ *
+ * dyncall args provides serialized access to arguments of a function call.
+ * related concepts: callback
+ *
+ */
+
+#include "dyncall.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DCArgs DCArgs;
+
+DC_API DCbool      dcbArgBool     (DCArgs*);
+DC_API DCchar      dcbArgChar     (DCArgs*);
+DC_API DCshort     dcbArgShort    (DCArgs*);
+DC_API DCint       dcbArgInt      (DCArgs*);
+DC_API DClong      dcbArgLong     (DCArgs*);
+DC_API DClonglong  dcbArgLongLong (DCArgs*);
+DC_API DCuchar     dcbArgUChar    (DCArgs*);
+DC_API DCushort    dcbArgUShort   (DCArgs*);
+DC_API DCuint      dcbArgUInt     (DCArgs*);
+DC_API DCulong     dcbArgULong    (DCArgs*);
+DC_API DCulonglong dcbArgULongLong(DCArgs*);
+DC_API DCfloat     dcbArgFloat    (DCArgs*);
+DC_API DCdouble    dcbArgDouble   (DCArgs*);
+DC_API DCpointer   dcbArgPointer  (DCArgs*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNCALL_ARGS_H */

+ 46 - 0
src/dyncall/include/dyncall_args_arm32_arm.h

@@ -0,0 +1,46 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_arm32_arm.h
+ Description: Callback's Arguments VM - Header for ARM32 (ARM mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_ARM32_ARM_H
+#define DYNCALLBACK_ARGS_ARM32_ARM_H
+
+#include "dyncall_args.h"
+
+struct DCArgs
+{
+	/* Don't change order! */
+	long  reg_data[4];
+	int   reg_count;
+	long* stack_ptr;
+#if defined(DC__ABI_ARM_HF)
+	DCfloat f[16];
+	int     freg_count;
+	int     dreg_count;
+#endif
+};
+
+#endif /* DYNCALLBACK_ARGS_ARM32_ARM_H */
+

+ 33 - 0
src/dyncall/include/dyncall_args_arm32_thumb.h

@@ -0,0 +1,33 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_arm32_thumb.h
+ Description: Callback's Arguments VM - Header for ARM32 (THUMB mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_ARM32_THUMB_H
+#define DYNCALLBACK_ARGS_ARM32_THUMB_H
+
+#include "dyncall_args_arm32_arm.h"	/* Uses same code as ARM mode. */
+
+#endif /* DYNCALLBACK_ARGS_ARM32_THUMB_H */
+

+ 42 - 0
src/dyncall/include/dyncall_args_mips.h

@@ -0,0 +1,42 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_mips.h
+ Description: Callback's Arguments VM - Header for MIPS
+ License:
+
+   Copyright (c) 2013-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_MIPS_H
+#define DYNCALLBACK_ARGS_MIPS_H
+
+#include "dyncall_args.h"
+
+struct DCArgs
+{
+  int            ireg_data[8];
+  float          freg_data[8];
+  int            ireg_count;
+  int            freg_count;
+  unsigned char* stackptr;
+};
+
+#endif /* DYNCALLBACK_ARGS_MIPS_H */
+

+ 43 - 0
src/dyncall/include/dyncall_args_ppc32.h

@@ -0,0 +1,43 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_ppc32.h
+ Description: Callback's Arguments VM - Header for ppc32
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALLBACK_ARGS_PPC32_H
+#define DYNCALLBACK_ARGS_PPC32_H
+
+#include "dyncall_args.h"
+
+/* Common Args iterator for Apple and System V ABI. */
+
+struct DCArgs
+{
+  int            ireg_data[8];		/* offset: 0   size: 4*8 = 32  */
+  double         freg_data[13];		/* offset: 32  size: 8*13= 104 */	
+  unsigned char* stackptr;		/* offset: 136 size:       4   */
+  int            ireg_count;            /* offset: 140 size:       4   */
+  int            freg_count;            /* offset: 144 size:       4   */
+};                                      /*       total size:       148 */
+
+#endif /* DYNCALLBACK_ARGS_PPC32_H */
+

+ 40 - 0
src/dyncall/include/dyncall_args_ppc64.h

@@ -0,0 +1,40 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_ppc64.h
+ Description: Callback's Arguments VM - Header for ppc64
+ License:
+
+   Copyright (c) 2014-2015 Masanori Mitsugi <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALLBACK_ARGS_PPC64_H
+#define DYNCALLBACK_ARGS_PPC64_H
+
+#include "dyncall_args.h"
+
+struct DCArgs
+{
+  long long      ireg_data[8];
+  double         freg_data[13];
+  unsigned char* stackptr;
+  int            ireg_count;
+  int            freg_count;
+};
+
+#endif /* DYNCALLBACK_ARGS_PPC64_H */
+

+ 38 - 0
src/dyncall/include/dyncall_args_sparc32.h

@@ -0,0 +1,38 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_sparc32.h
+ Description: Callback's Arguments VM - Header for sparc32
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_SPARC32_H
+#define DYNCALLBACK_ARGS_SPARC32_H
+
+#include "dyncall_args.h"
+
+struct DCArgs
+{
+  int dummy;
+};
+
+#endif /* DYNCALLBACK_ARGS_SPARC32_H */
+

+ 38 - 0
src/dyncall/include/dyncall_args_sparc64.h

@@ -0,0 +1,38 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_sparc64.h
+ Description: Callback's Arguments VM - Header for sparc32 - not yet
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_SPARC64_H
+#define DYNCALLBACK_ARGS_SPARC64_H
+
+#include "dyncall_args.h"
+
+struct DCArgs
+{
+  int dummy;
+};
+
+#endif /* DYNCALLBACK_ARGS_SPARC64_H */
+

+ 45 - 0
src/dyncall/include/dyncall_args_x64.h

@@ -0,0 +1,45 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_x64.h
+ Description: Callback's Arguments VM - Header for x64
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALLBACK_ARGS_X64_H
+#define DYNCALLBACK_ARGS_X64_H
+
+#include "dyncall_args.h"
+#include "dyncall_callvm_x64.h"  /* reuse structures */
+
+
+struct DCArgs
+{
+	/* state */
+	int64*          stack_ptr;
+	DCRegCount_x64  reg_count;	/* @@@ win64 version should maybe force alignment to 8 in order to be secure */
+
+	/* reg data */
+	DCRegData_x64_s reg_data;
+};
+
+#endif /* DYNCALLBACK_ARGS_X64_H */
+

+ 59 - 0
src/dyncall/include/dyncall_args_x86.h

@@ -0,0 +1,59 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_args_x86.h
+ Description: Callback's Arguments VM - Header for x86
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNCALL_ARGS_X86_H_
+#define DYNCALL_ARGS_X86_H_
+
+#include "dyncall_args.h"
+
+typedef struct
+{
+	DCint      (*i32)(DCArgs*);
+	DClonglong (*i64)(DCArgs*);
+	DCfloat    (*f32)(DCArgs*);
+	DCdouble   (*f64)(DCArgs*);
+} DCArgsVT;
+
+extern DCArgsVT dcArgsVT_default;
+extern DCArgsVT dcArgsVT_this_ms;
+extern DCArgsVT dcArgsVT_fast_ms;
+extern DCArgsVT dcArgsVT_fast_gnu;
+
+struct DCArgs
+{
+	/* callmode */
+	DCArgsVT* vt;
+
+	/* state */
+	int* stack_ptr;
+
+	/* fast data / 'this-ptr' info */
+	int  fast_data[2];
+	int  fast_count;
+};
+
+#endif /* DYNCALL_ARGS_X86_H_ */

+ 53 - 0
src/dyncall/include/dyncall_callback.h

@@ -0,0 +1,53 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback.h
+ Description: Callback - Interface
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_CALLBACK_H
+#define DYNCALL_CALLBACK_H
+
+#include "dyncall_args.h"
+#include "dyncall_signature.h"
+#include "dyncall_value.h"
+
+typedef struct DCCallback DCCallback;
+
+// TODO: return value is the type encoded as a signature char (character of the set [vBcCsSiIjJlLfd]).
+
+typedef char (DCCallbackHandler)(DCCallback* pcb, DCArgs* args, DCValue* result, void* userdata);
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+DCCallback* dcbNewCallback(const char* signature, DCCallbackHandler* funcptr, void* userdata);
+void        dcbInitCallback(DCCallback* pcb, const char* signature, DCCallbackHandler* handler, void* userdata);
+void        dcbFreeCallback(DCCallback* pcb);
+void*       dcbGetUserData (DCCallback* pcb);
+
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif /* DYNCALL_CALLBACK_H */

+ 46 - 0
src/dyncall/include/dyncall_callback_arm32_arm.h

@@ -0,0 +1,46 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_arm32_arm.h
+ Description: Callback - Header for ARM32 (ARM mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNCALL_CALLBACK_ARM32_ARM_H_
+#define DYNCALL_CALLBACK_ARM32_ARM_H_
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_arm32_arm.h"
+
+
+struct DCCallback
+{
+  DCThunk  	         thunk;    // offset 0
+  DCCallbackHandler* handler;  // offset 12
+  void*              userdata; // offset 16
+};
+
+
+#endif /* DYNCALL_CALLBACK_ARM32_ARM_H_ */
+

+ 34 - 0
src/dyncall/include/dyncall_callback_arm32_thumb.h

@@ -0,0 +1,34 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_arm32_thumb.h
+ Description: Callback - Header for ARM32 (THUMB mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNCALL_CALLBACK_ARM32_THUMB_H_
+#define DYNCALL_CALLBACK_ARM32_THUMB_H_
+
+#include "dyncall_callback_arm32_arm.h" /* Uses same code as ARM mode. */
+
+#endif /* DYNCALL_CALLBACK_ARM32_THUMB_H_ */
+

+ 43 - 0
src/dyncall/include/dyncall_callback_mips.h

@@ -0,0 +1,43 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_mips.h
+ Description: Callback - Header for MIPS
+ License:
+
+   Copyright (c) 2013-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_CALLBACK_MIPS_H
+#define DYNCALL_CALLBACK_MIPS_H
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_mips.h"
+
+struct DCCallback
+{
+  DCThunk            thunk;         /* offset  0, size 20 */
+  DCCallbackHandler* handler;       /* offset 20, size  4 */
+  void*              userdata;      /* offset 24, size  4 */
+};
+
+#endif /* DYNCALL_CALLBACK_MIPS_H */
+

+ 43 - 0
src/dyncall/include/dyncall_callback_ppc32.h

@@ -0,0 +1,43 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_ppc32.h
+ Description: Callback - Header for ppc32
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_CALLBACK_PPC32_H
+#define DYNCALL_CALLBACK_PPC32_H
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_ppc32.h"
+
+struct DCCallback
+{
+  DCThunk            thunk;         /* offset  0, size 24 */
+  DCCallbackHandler* handler;       /* offset 24, size  4 */
+  size_t             stack_cleanup; /* offset 28, size  4 */
+  void*              userdata;      /* offset 32, size  4 */
+};                                  /*      total size 36 */                                  
+
+#endif /* DYNCALL_CALLBACK_PPC32_H */
+

+ 56 - 0
src/dyncall/include/dyncall_callback_ppc64.h

@@ -0,0 +1,56 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_ppc64.h
+ Description: Callback - Header for ppc64
+ License:
+
+   Copyright (c) 2014-2015 Masanori Mitsugi <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_CALLBACK_PPC64_H
+#define DYNCALL_CALLBACK_PPC64_H
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_ppc64.h"
+
+/*
+  ELF v2
+  thunk           : offset 0,  size 48
+  handler         : offset 48, size  8
+  stack_cleanup   : offset 56, size  8
+  userdata        : offset 64, size  8
+
+  ELF v1
+  thunk           : offset 0,  size 64
+  handler         : offset 64, size  8
+  stack_cleanup   : offset 72, size  8
+  userdata        : offset 80, size  8
+*/
+
+struct DCCallback
+{
+  DCThunk            thunk;
+  DCCallbackHandler* handler;
+  size_t             stack_cleanup;
+  void*              userdata;
+};
+
+#endif /* DYNCALL_CALLBACK_PPC64_H */
+

+ 44 - 0
src/dyncall/include/dyncall_callback_sparc32.h

@@ -0,0 +1,44 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_sparc32.h
+ Description: Callback - Header for sparc32
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_CALLBACK_SPARC32_H
+#define DYNCALL_CALLBACK_SPARC32_H
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_sparc32.h"
+
+struct DCCallback
+{
+  DCThunk            thunk;         /* offset  0, size ?? */
+  DCCallbackHandler* handler;       /* offset ??, size  4 */
+  size_t             stack_cleanup; /* offset ??, size  4 */
+  void*              userdata;      /* offset ??, size  4 */
+};
+
+#endif /* DYNCALL_CALLBACK_SPARC32_H */
+

+ 45 - 0
src/dyncall/include/dyncall_callback_x64.h

@@ -0,0 +1,45 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_x64.h
+ Description: Callback - Header for x64
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNCALL_CALLBACK_X64_H_
+#define DYNCALL_CALLBACK_X64_H_
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_x64.h"
+
+
+struct DCCallback
+{
+  DCThunk  	         thunk;    // offset 0,  size 24
+  DCCallbackHandler* handler;  // offset 24
+  void*              userdata; // offset 32
+};
+
+#endif /* DYNCALL_CALLBACK_X64_H_ */
+

+ 50 - 0
src/dyncall/include/dyncall_callback_x86.h

@@ -0,0 +1,50 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_callback_x86.h
+ Description: Callback - Header for x86
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNCALL_CALLBACK_X86_H_
+#define DYNCALL_CALLBACK_X86_H_
+
+#include "dyncall_callback.h"
+
+#include "dyncall_thunk.h"
+#include "dyncall_args_x86.h"
+
+struct DCCallback
+{
+  DCThunk            thunk;         /* offset 0,  size 16 */
+  DCCallbackHandler* handler;       /* offset 16 */
+  DCArgsVT*          args_vt;       /* offset 20 */
+  size_t             stack_cleanup; /* offset 24 */
+  void*              userdata;      /* offset 28 */
+};
+
+int dcCleanupSize_x86_cdecl   (const char* args_signature);
+int dcCleanupSize_x86_std     (const char* args_signature);
+int dcCleanupSize_x86_fast_ms (const char* args_signature);
+int dcCleanupSize_x86_fast_gnu(const char* args_signature);
+
+#endif /* DYNCALL_CALLBACK_X86_H_ */

+ 56 - 0
src/dyncall/include/dyncall_callf.h

@@ -0,0 +1,56 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_callf.h
+ Description: formatted call interface to dyncall
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+/*
+
+  dyncall formatted calls C API
+
+  REVISION
+  2007/12/11 initial
+  
+*/
+
+
+#ifndef DYNCALL_CALLF_H
+#define DYNCALL_CALLF_H
+
+/* dyncall formatted calls */
+
+#include "dyncall.h"
+#include "dyncall_signature.h"
+#include "dyncall_value.h"
+
+#include <stdarg.h>
+
+void dcArgF (DCCallVM* vm, const DCsigchar* signature, ...);
+void dcVArgF(DCCallVM* vm, const DCsigchar* signature, va_list args);
+
+void dcCallF (DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, ...);
+void dcVCallF(DCCallVM* vm, DCValue* result, DCpointer funcptr, const DCsigchar* signature, va_list args);
+
+#endif /* DYNCALL_CALLF_H */
+

+ 47 - 0
src/dyncall/include/dyncall_config.h

@@ -0,0 +1,47 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_config.h
+ Description: Macro configuration file for non-standard C types
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+/*
+
+  dyncall type configuration
+
+  REVISION
+  2007/12/11 initial
+
+*/
+
+#ifndef DYNCALL_CONFIG_H
+#define DYNCALL_CONFIG_H
+
+#include "dyncall_macros.h"
+
+#define DC_BOOL         int
+#define DC_LONG_LONG    long long
+#define DC_POINTER      void*
+
+#endif /* DYNCALL_CONFIG_H */
+

+ 289 - 0
src/dyncall/include/dyncall_macros.h

@@ -0,0 +1,289 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_macros.h
+ Description: Platform detection macros
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+/*
+
+  dyncall macros
+
+  Platform detection, specific defines and configuration.
+  The purpose of this file is to provide coherent platform and compiler
+  specific defines. So instead of defines like WIN32, _OpenBSD_ or
+  __GNUC__, one should use DC__OS_Win32, DC__OS_OpenBSD or DC__C_GNU,
+  respectively.
+
+  REVISION
+  2007/12/11 initial
+
+*/
+
+
+#ifndef DYNCALL_MACROS_H
+#define DYNCALL_MACROS_H
+
+
+/* Platform specific defines. */
+
+/* MS Windows XP x64/Vista64 or later. */
+#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
+#define DC__OS_Win64
+
+/* MS Windows NT/95/98/ME/2000/XP/Vista32. */
+#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__WINDOWS__) || defined(_WINDOWS)
+#define DC__OS_Win32
+
+/* All the OS' based on Darwin OS (MacOS X, OpenDarwin). Note that '__APPLE__' may be defined for classic MacOS, too. */
+/* __MACOSX__ is not defined in gcc assembler mode (switch: -S) */
+/* @@@ TODO: Check for Classic OS */
+
+#elif defined(__APPLE__) || defined(__Darwin__)
+#  define DC__OS_Darwin
+#  if defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
+#    define DC__OS_IPhone
+#  else /* defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) */
+#    define DC__OS_MacOSX
+#  endif
+
+/* The most popular open source Unix-like OS - Linux. */
+#elif defined(__linux__) || defined(__linux) || defined(__gnu_linux__)
+#define DC__OS_Linux
+
+/* The most powerful open source Unix-like OS - FreeBSD. */
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define DC__OS_FreeBSD
+
+/* The most secure open source Unix-like OS - OpenBSD. */
+#elif defined(__OpenBSD__)
+#define DC__OS_OpenBSD
+
+/* The most portable open source Unix-like OS - NetBSD. */
+#elif defined(__NetBSD__)
+#define DC__OS_NetBSD
+
+/* The FreeBSD fork having heavy clusterization in mind - DragonFlyBSD. */
+#elif defined(__DragonFly__)
+#define DC__OS_DragonFlyBSD
+
+/* Sun's Unix-like OS - SunOS / Solaris. */
+#elif defined(__sun__) || defined(__sun) || defined(sun)
+#define DC__OS_SunOS
+
+/* The "Linux-like environment for Windows" - Cygwin. */
+#elif defined(__CYGWIN__)
+#define DC__OS_Cygwin
+
+/* The "Minimalist GNU for Windows" - MinGW. */
+#elif defined(__MINGW__)/*@@@*/
+#define DC__OS_MinGW
+
+/* The Nintendo DS (homebrew) using devkitpro. */
+#elif defined(__nds__)
+#define DC__OS_NDS
+
+/* The PlayStation Portable (homebrew) SDK. */
+#elif defined(__psp__) || defined(PSP)
+#define DC__OS_PSP
+
+/* Haiku (BeOS alike). */
+#elif defined(__HAIKU__)
+#define DC__OS_BeOS
+
+/* The Unix successor - Plan9 from Bell Labs */
+#elif defined(Plan9) || defined(__Plan9__)
+#define DC__OS_Plan9
+
+/* Digital's Unix-like OS - VMS */
+#elif defined(__vms)
+#define DC__OS_VMS
+
+#elif defined(__minix)
+#define DC__OS_Minix
+
+#else
+	#error Unsupported OS.
+#endif
+
+
+
+/* Compiler specific defines. Do not change the order, because  */
+/* some of the compilers define flags for compatible ones, too. */
+
+/* Intel's C/C++ compiler. */
+#if defined(__INTEL_COMPILER)
+#define DC__C_Intel
+
+/* MS C/C++ compiler. */
+#elif defined(_MSC_VER)
+#define DC__C_MSVC
+
+/* LLVM clang. */
+#elif defined(__clang__)
+#define DC__C_CLANG
+
+/* The GNU Compiler Collection - GCC. */
+#elif defined(__GNUC__)
+#define DC__C_GNU
+
+/* Watcom compiler. */
+#elif defined(__WATCOMC__)
+#define DC__C_WATCOM
+
+/* Portable C Compiler. */
+#elif defined(__PCC__)
+#define DC__C_PCC
+
+/* Sun Pro C. */
+#elif defined(__SUNPRO_C)
+#define DC__C_SUNPRO
+
+/* Undetected C Compiler. */
+#else
+#define DC__C_UNKNOWN
+#endif
+
+
+
+/* Architecture. */
+
+/* Check architecture. */
+#if defined(_M_X64_) || defined(_M_AMD64) || defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) 
+# define DC__Arch_AMD64
+#elif defined(_M_IX86) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__386__) || defined(__i386)
+# define DC__Arch_Intel_x86
+#elif defined(_M_IA64) || defined(__ia64__)
+# define DC__Arch_Itanium
+#elif defined(_M_PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__ppc__) || defined(__power__)
+# if defined(__ppc64__) || defined(_ARCH_PPC64) || defined(__power64__) || defined(__powerpc64__)
+#   define DC__Arch_PPC64
+# else
+#   define DC__Arch_PPC32
+# endif
+#elif defined(__mips64__) || defined(__mips64)
+# define DC__Arch_MIPS64
+#elif defined(_M_MRX000) || defined(__mips__) || defined(__mips) || defined(_mips)
+# define DC__Arch_MIPS
+#elif defined(__arm__)
+# define DC__Arch_ARM
+#elif defined(__aarch64__)
+# define DC__Arch_ARM64
+#elif defined(__sh__)
+# define DC__Arch_SuperH
+#elif defined(__sparcv9) || defined(__sparc64__) || ( defined(__sparc) && defined(__arch64__) ) 
+/* this could be needed on Linux/GNU sparc64 in the future: || ( defined(__sparc) && defined(__arch64__) ) */
+# define DC__Arch_Sparcv9
+#elif defined(__sparc)
+# define DC__Arch_Sparc
+#endif
+
+
+
+/* Rough OS classification. */
+
+#if defined(DC__OS_Win32) || defined(DC__OS_Win64)
+# define DC_WINDOWS
+#elif defined(DC__OS_Plan9)
+# define DC_PLAN9
+#elif defined(DC__OS_NDS) || defined(DC__OS_PSP)
+# define DC_OTHER
+#else
+# define DC_UNIX
+#endif
+
+
+
+/* Misc machine-dependent modes, ABIs, etc.. */
+
+#if defined(__arm__) && !defined(__thumb__)
+# define DC__Arch_ARM_ARM
+#elif defined(__arm__) && defined(__thumb__)
+# define DC__Arch_ARM_THUMB
+#endif
+
+#if defined(DC__Arch_ARM_ARM) || defined(DC__Arch_ARM_THUMB)
+# if defined(__ARM_EABI__) || defined(DC__OS_NDS)
+#  if defined (__ARM_PCS_VFP) && (__ARM_PCS_VFP == 1)
+#    define DC__ABI_ARM_HF
+#  else
+#    define DC__ABI_ARM_EABI
+#  endif
+# elif defined(__APCS_32__)
+#  define DC__ABI_ARM_OABI
+# endif
+#endif /* ARM */
+
+#if defined(DC__Arch_MIPS) || defined(DC__Arch_MIPS64)
+# if defined(_ABIO32) || defined(_MIPS_ARCH_MIPS1) || defined(_MIPS_ARCH_MIPS2)
+#  define DC__ABI_MIPS_O32
+# elif defined(_ABIN32)
+#  define DC__ABI_MIPS_N32
+# elif defined(_ABI64)
+#  define DC__ABI_MIPS_N64
+# else
+#  define DC__ABI_MIPS_EABI
+# endif
+#endif /* MIPS */
+
+#if defined(DC__Arch_PPC64)
+# if defined(_CALL_ELF)
+#  define DC__ABI_PPC64_ELF_V _CALL_ELF
+# else
+#  define DC__ABI_PPC64_ELF_V 0 /* 0 means not explicitly set, otherwise this is 1 (big endian) and 2 (little endian) */
+# endif
+#endif /* MIPS */
+
+
+/* Endian detection. */
+#if defined(DC__Arch_Intel_x86) || defined(DC__Arch_AMD64) /* always little */
+# define DC__Endian_LITTLE
+#elif defined(DC__Arch_Sparc)                              /*always big until v9*/
+# define DC__Endian_BIG
+#else                                                      /* all others are bi-endian */
+/* @@@check flags used on following bi-endianness archs:
+DC__Arch_ARM
+DC__Arch_ARM64
+DC__Arch_Itanium
+DC__Arch_MIPS
+DC__Arch_MIPS64
+DC__Arch_PPC32
+DC__Arch_PPC64
+DC__Arch_Sparcv9
+DC__Arch_SuperH
+*/
+# if (defined(DC__Arch_PPC64) && (DC__ABI_PPC64_ELF_V == 1)) || defined(_BIG_ENDIAN) || defined(MIPSEB)
+#  define DC__Endian_BIG
+# elif (defined(DC__Arch_PPC64) && (DC__ABI_PPC64_ELF_V == 2)) || defined(_LITTLE_ENDIAN) || defined(MIPSEL)
+#  define DC__Endian_LITTLE
+# endif /* no else, leave unset if not sure */
+#endif
+
+
+/* Internal macro/tag. */
+#if !defined(DC_API)
+#define DC_API
+#endif
+
+#endif /* DYNCALL_MACROS_H */
+

+ 72 - 0
src/dyncall/include/dyncall_signature.h

@@ -0,0 +1,72 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_signature.h
+ Description: Type and calling-convention signature character defines
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+/*
+
+  dyncall signature characters
+
+  REVISION
+  2007/12/11 initial
+  
+*/
+
+
+#ifndef DYNCALL_SIGNATURE_H
+#define DYNCALL_SIGNATURE_H
+
+typedef char DCsigchar;
+
+#define DC_SIGCHAR_VOID         'v'
+#define DC_SIGCHAR_BOOL         'B'
+#define DC_SIGCHAR_CHAR         'c'
+#define DC_SIGCHAR_UCHAR        'C'
+#define DC_SIGCHAR_SHORT        's'
+#define DC_SIGCHAR_USHORT       'S'
+#define DC_SIGCHAR_INT          'i'
+#define DC_SIGCHAR_UINT         'I'
+#define DC_SIGCHAR_LONG         'j'
+#define DC_SIGCHAR_ULONG        'J'
+#define DC_SIGCHAR_LONGLONG     'l'
+#define DC_SIGCHAR_ULONGLONG    'L'
+#define DC_SIGCHAR_FLOAT        'f'
+#define DC_SIGCHAR_DOUBLE       'd'
+#define DC_SIGCHAR_POINTER      'p'
+#define DC_SIGCHAR_STRING       'Z'
+#define DC_SIGCHAR_STRUCT       'T'
+#define DC_SIGCHAR_ENDARG       ')' /* also works for end struct */
+
+/* callback signatures */
+
+#define DC_SIGCHAR_CC_PREFIX        '_'
+#define DC_SIGCHAR_CC_ELLIPSIS      'e'
+#define DC_SIGCHAR_CC_STDCALL       's'
+#define DC_SIGCHAR_CC_FASTCALL_GNU  'f'
+#define DC_SIGCHAR_CC_FASTCALL_MS   'F'
+#define DC_SIGCHAR_CC_THISCALL_MS   '+'
+
+#endif /* DYNCALL_SIGNATURE_H */
+

+ 90 - 0
src/dyncall/include/dyncall_thunk.h

@@ -0,0 +1,90 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk.h
+ Description: Thunk - Interface
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_H
+#define DYNCALL_THUNK_H
+
+/**
+ ** dyncall thunks
+ **
+ ** thunks are small-size hybrid code/data objects, created at run-time to
+ ** be used as function pointers with associated data and entry functions.
+ **
+ ** The header contains code, that does load its address into a designated scratch
+ ** register and will jump to a thunk function.
+ **
+ ** Thunk entry procedures are compiled functions, that are called as a result of
+ ** a thunk function.
+ ** There is one thunk entry currently for supporting callbacks.
+ **
+ ** Thunk context register ( ::= an available scratch register in the calling convention):
+ **
+ ** x86:  eax
+ ** x64:  rax
+ ** ppc:   r2
+ ** arm:  r12
+ ** arm64: x9
+ **
+ **/
+
+#include "dyncall_macros.h"
+
+typedef struct DCThunk_ DCThunk;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void   dcbInitThunk(DCThunk* p, void (*entry)());
+
+#if defined(DC__Arch_Intel_x86)
+#include "dyncall_thunk_x86.h"
+#elif defined (DC__Arch_AMD64)
+#include "dyncall_thunk_x64.h"
+#elif defined (DC__Arch_PPC32)
+#include "dyncall_thunk_ppc32.h"
+#elif defined (DC__Arch_PPC64)
+#include "dyncall_thunk_ppc64.h"
+#elif defined (DC__Arch_ARM_ARM)
+#include "dyncall_thunk_arm32_arm.h"
+#elif defined (DC__Arch_ARM_THUMB)
+#include "dyncall_thunk_arm32_thumb.h"
+#elif defined (DC__Arch_MIPS)
+#include "dyncall_thunk_mips.h"
+#elif defined (DC__Arch_Sparc)
+#include "dyncall_thunk_sparc32.h"
+#elif defined (DC__Arch_Sparcv9)
+#include "dyncall_thunk_sparc64.h"
+#elif defined (DC__Arch_ARM64)
+#include "dyncall_thunk_arm64.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* DYNCALL_THUNK_H */

+ 41 - 0
src/dyncall/include/dyncall_thunk_arm32_arm.h

@@ -0,0 +1,41 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_arm32_arm.h
+ Description: Thunk - Header for ARM32 (ARM mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_ARM32_ARM_H
+#define DYNCALL_THUNK_ARM32_ARM_H
+
+
+struct DCThunk_
+{
+  unsigned int code[2];
+  void       (*entry)();
+};
+
+#define DCTHUNK_ARM32_ARM_SIZE 12
+
+
+#endif /* DYNCALL_THUNK_ARM32_ARM_H */
+

+ 36 - 0
src/dyncall/include/dyncall_thunk_arm32_thumb.h

@@ -0,0 +1,36 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_arm32_thumb.h
+ Description: Thunk - Header for ARM32 (THUMB mode)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_ARM32_THUMB_H
+#define DYNCALL_THUNK_ARM32_THUMB_H
+
+#include "dyncall_thunk_arm32_arm.h" /* Uses same code as ARM mode. */
+
+#define DCTHUNK_ARM32_THUMB_SIZE 12
+
+
+#endif /* DYNCALL_THUNK_ARM32_THUMB_H */
+

+ 42 - 0
src/dyncall/include/dyncall_thunk_arm64.h

@@ -0,0 +1,42 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_arm64.h
+ Description: Thunk - Header for ARM64 / ARMv8 / AAPCS64
+ License:
+
+   Copyright (c) 2015 Daniel Adler <[email protected]>,
+                      Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_THUNK_ARM64_H
+#define DYNCALL_THUNK_ARM64_H
+
+struct DCThunk_
+{
+                                // off  size
+                                //-----|----------
+  unsigned int code[4];		//   0    16
+  void (*entry)();		//  16     8
+  void* reserved;         	//  24     8
+
+                                //  32 total size
+  
+};
+
+#endif /* DYNCALL_THUNK_ARM64_H */
+

+ 38 - 0
src/dyncall/include/dyncall_thunk_mips.h

@@ -0,0 +1,38 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_mips.h
+ Description: Thunk - Header for MIPS
+ License:
+
+   Copyright (c) 2013-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_MIPS_H
+#define DYNCALL_THUNK_MIPS_H
+
+struct DCThunk_
+{
+  unsigned short data[6];
+  unsigned int   jump;
+  unsigned short bddt[2];
+};
+
+#endif /* DYNCALL_THUNK_MIPS_H */
+

+ 40 - 0
src/dyncall/include/dyncall_thunk_ppc32.h

@@ -0,0 +1,40 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_ppc32.h
+ Description: Thunk - Header for ppc32 (darwin/sysv)
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_THUNK_PPC32_H
+#define DYNCALL_THUNK_PPC32_H
+
+struct DCThunk_
+{
+  unsigned short code_load_hi, addr_self_hi;  /* offset:  0  size: 4  */
+  unsigned short code_load_lo, addr_self_lo;  /* offset:  4  size: 4  */
+  unsigned int   code_jump[3];                /* offset:  8  size: 12 */
+  void          (*addr_entry)();              /* offset: 20  size:  4 */
+};                                            /*       total size: 24 */
+
+#define DCTHUNK_SIZE_PPC32 24
+
+#endif /* DYNCALL_THUNK_PPC32_H */
+

+ 55 - 0
src/dyncall/include/dyncall_thunk_ppc64.h

@@ -0,0 +1,55 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_ppc64.h
+ Description: Thunk - Header for ppc64
+ License:
+
+   Copyright (c) 2014-2015 Masanori Mitsugi <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+#ifndef DYNCALL_THUNK_PPC64_H
+#define DYNCALL_THUNK_PPC64_H
+
+#if DC__ABI_PPC64_ELF_V != 2
+struct DCThunk_              /* v1 */
+{
+  void          (*thunk_entry)();                 /* offset:  0 */
+  long           toc_thunk;                       /* offset:  8 */
+  unsigned short code_load_hi, addr_self_hi;      /* offset: 16 */
+  unsigned short code_load_lo, addr_self_lo;      /* offset: 20 */
+  unsigned int   code_jump[6];                    /* offset: 24 */
+  void          (*addr_entry)();                  /* offset: 48 */
+  long           toc_entry;                       /* offset: 56 */
+};
+#define DCTHUNK_SIZE_PPC64 64
+#else
+struct DCThunk_              /* v2 */
+{
+  unsigned short addr_self_hist, code_load_hist;  /* offset:  0 */
+  unsigned short addr_self_hier, code_load_hier;  /* offset:  4 */
+  unsigned int   code_rot;                        /* offset:  8 */
+  unsigned short addr_self_hi, code_load_hi;      /* offset: 12 */
+  unsigned short addr_self_lo, code_load_lo;      /* offset: 16 */
+  unsigned int   code_jump[5];                    /* offset: 20 */
+  void          (*addr_entry)();                  /* offset: 40 */
+};
+#define DCTHUNK_SIZE_PPC64 48
+#endif
+
+#endif /* DYNCALL_THUNK_PPC64_H */
+

+ 37 - 0
src/dyncall/include/dyncall_thunk_sparc32.h

@@ -0,0 +1,37 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_sparc32.h
+ Description: Thunk - Header for sparc32 - not yet implemented
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_SPARC32_H
+#define DYNCALL_THUNK_SPARC32_H
+
+struct DCThunk_
+{
+  int x[4]; /* dummy */
+};
+
+#define DCTHUNK_SIZE_SPARC32 32
+
+#endif /* DYNCALL_THUNK_SPARC32_H */

+ 37 - 0
src/dyncall/include/dyncall_thunk_sparc64.h

@@ -0,0 +1,37 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_sparc64.h
+ Description: Thunk - Header for sparc64 - not yet implemented
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_SPARC64_H
+#define DYNCALL_THUNK_SPARC64_H
+
+struct DCThunk_
+{
+  int x[4]; /* dummy */
+};
+
+#define DCTHUNK_SIZE_SPARC64 32
+
+#endif /* DYNCALL_THUNK_SPARC32_H */

+ 40 - 0
src/dyncall/include/dyncall_thunk_x64.h

@@ -0,0 +1,40 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_x64.h
+ Description: Thunk - Header for x64
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_X64_H
+#define DYNCALL_THUNK_X64_H
+
+struct DCThunk_
+{
+  unsigned long long code[2];
+  void (*entry)();
+};
+
+#define DCTHUNK_X64_SIZE	24
+
+
+#endif /* DYNCALL_THUNK_X64_H */
+

+ 40 - 0
src/dyncall/include/dyncall_thunk_x86.h

@@ -0,0 +1,40 @@
+/*
+
+ Package: dyncall
+ Library: dyncallback
+ File: dyncallback/dyncall_thunk_x86.h
+ Description: Thunk - Header for x86
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>,
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+#ifndef DYNCALL_THUNK_X86_H
+#define DYNCALL_THUNK_X86_H
+
+struct DCThunk_
+{
+  unsigned int code_load;
+  void*        addr_self;
+  unsigned int code_jump;
+  void        (*addr_entry)();
+};
+
+#define DCTHUNK_X86_SIZE	16
+
+#endif /* DYNCALL_THUNK_X86_H */

+ 75 - 0
src/dyncall/include/dyncall_types.h

@@ -0,0 +1,75 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_types.h
+ Description: Typedefs
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+/*
+
+  dyncall argument- and return-types
+
+  REVISION
+  2007/12/11 initial
+  
+*/
+
+#ifndef DYNCALL_TYPES_H
+#define DYNCALL_TYPES_H
+
+#include <stddef.h>
+
+#include "dyncall_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+typedef void            DCvoid;
+typedef DC_BOOL         DCbool;
+typedef char            DCchar;
+typedef unsigned char   DCuchar;
+typedef short           DCshort;
+typedef unsigned short  DCushort;
+typedef int             DCint;
+typedef unsigned int    DCuint;
+typedef long            DClong;
+typedef unsigned long   DCulong;
+typedef DC_LONG_LONG    DClonglong;
+typedef unsigned DC_LONG_LONG DCulonglong;
+typedef float           DCfloat;
+typedef double          DCdouble;
+typedef DC_POINTER      DCpointer;
+typedef const char*     DCstring;
+
+typedef size_t          DCsize;
+
+#define DC_TRUE   1
+#define DC_FALSE  0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNCALL_TYPES_H */
+

+ 91 - 0
src/dyncall/include/dyncall_value.h

@@ -0,0 +1,91 @@
+/*
+
+ Package: dyncall
+ Library: dyncall
+ File: dyncall/dyncall_value.h
+ Description: Value variant type
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+/*
+
+  dyncall value variant
+
+  a value variant union-type that carries all supported dyncall types.
+
+  REVISION
+  2007/12/11 initial
+
+*/
+
+#ifndef DYNCALL_VALUE_H
+#define DYNCALL_VALUE_H
+
+#include "dyncall_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+typedef union DCValue_ DCValue;
+
+union DCValue_
+{
+#if defined (DC__Arch_PPC32) && defined(DC__Endian_BIG)
+  DCbool        B;
+  struct { DCchar  c_pad[3]; DCchar  c; };
+  struct { DCuchar C_pad[3]; DCuchar C; };
+  struct { DCshort s_pad;    DCshort s; };
+  struct { DCshort S_pad;    DCshort S; };
+  DCint         i;
+  DCuint        I;
+#elif defined (DC__Arch_PPC64) && defined(DC__Endian_BIG)
+  struct { DCbool  B_pad;    DCbool  B; };
+  struct { DCchar  c_pad[7]; DCchar  c; };
+  struct { DCuchar C_pad[7]; DCuchar C; };
+  struct { DCshort s_pad[3]; DCshort s; };
+  struct { DCshort S_pad[3]; DCshort S; };
+  struct { DCint   i_pad;    DCint   i; };
+  struct { DCint   I_pad;    DCuint  I; };
+#else
+  DCbool        B;
+  DCchar        c;
+  DCuchar       C;
+  DCshort       s;
+  DCushort      S;
+  DCint         i;
+  DCuint        I;
+#endif
+  DClong        j;
+  DCulong       J;
+  DClonglong    l;
+  DCulonglong   L;
+  DCfloat       f;
+  DCdouble      d;
+  DCpointer     p;
+  DCstring      Z;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNCALL_VALUE_H */
+

+ 66 - 0
src/dyncall/include/dynload.h

@@ -0,0 +1,66 @@
+/*
+
+ Package: dyncall
+ Library: dynload
+ File: dynload/dynload.h
+ Description: public header for library dynload
+ License:
+
+   Copyright (c) 2007-2015 Daniel Adler <[email protected]>, 
+                           Tassilo Philipp <[email protected]>
+
+   Permission to use, copy, modify, and distribute this software for any
+   purpose with or without fee is hereby granted, provided that the above
+   copyright notice and this permission notice appear in all copies.
+
+   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+*/
+
+
+
+#ifndef DYNLOAD_H
+#define DYNLOAD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef DL_API
+#define DL_API
+#endif
+
+/* --- public api ---------------------------------------------------------- */
+
+/* shared library loading and explicit symbol resolving */
+
+typedef struct DLLib_ DLLib;
+
+DL_API DLLib* dlLoadLibrary(const char* libpath);
+DL_API void   dlFreeLibrary(DLLib* pLib);
+DL_API void*  dlFindSymbol(DLLib* pLib, const char* pSymbolName);
+
+/* symbol table enumeration - only for symbol lookup, not resolve */
+
+typedef struct DLSyms_ DLSyms;
+
+DL_API DLSyms*     dlSymsInit   (const char* libPath);
+DL_API void        dlSymsCleanup(DLSyms* pSyms);
+
+DL_API int         dlSymsCount        (DLSyms* pSyms);
+DL_API const char* dlSymsName         (DLSyms* pSyms, int index);
+DL_API const char* dlSymsNameFromValue(DLSyms* pSyms, void* value); /* symbol must be loaded */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DYNLOAD_H */
+

BIN
src/dyncall/lib/libdyncall_s.lib


BIN
src/dyncall/lib/libdyncallback_s.lib


BIN
src/dyncall/lib/libdynload_s.lib


+ 4 - 5
src/main.cpp

@@ -7,9 +7,9 @@
 #include "parser.cpp"
 // #include "printer.cpp"
 #include "checker/checker.cpp"
-#include "ssa/ssa.cpp"
-#include "llvm/ssa_to_text.cpp"
-#include "vm/vm.cpp"
+#include "ssa.cpp"
+#include "ssa_to_llvm.cpp"
+#include "vm.cpp"
 
 // NOTE(bill): `name` is used in debugging and profiling modes
 i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
@@ -172,9 +172,8 @@ int main(int argc, char **argv) {
 		vm_init(&vm, &ssa.module);
 		defer (vm_destroy(&vm));
 
-		ssaProcedure *start_proc = vm_lookup_procedure(&vm, make_string("main"));
 		Array<vmValue> args = {}; // Empty
-		vm_call_procedure(&vm, start_proc, args);
+		vm_call_proc_by_name(&vm, make_string("main"), args);
 	}
 #endif
 

+ 5340 - 0
src/ssa.cpp

@@ -0,0 +1,5340 @@
+struct ssaProcedure;
+struct ssaBlock;
+struct ssaValue;
+struct ssaDebugInfo;
+
+struct ssaModule {
+	CheckerInfo * info;
+	BaseTypeSizes sizes;
+	gbArena       arena;
+	gbArena       tmp_arena;
+	gbAllocator   allocator;
+	gbAllocator   tmp_allocator;
+	b32 generate_debug_info;
+
+	u32 stmt_state_flags;
+
+	// String source_filename;
+	String layout;
+	// String triple;
+
+
+	Map<Entity *>       min_dep_map; // Key: Entity *
+	Map<ssaValue *>     values;      // Key: Entity *
+	Map<ssaValue *>     members;     // Key: String
+	Map<String>         type_names;  // Key: Type *
+	Map<ssaDebugInfo *> debug_info;  // Key: Unique pointer
+	i32                 global_string_index;
+	i32                 global_array_index; // For ConstantSlice
+
+	Array<ssaValue *> procs; // NOTE(bill): Procedures to generate
+};
+
+// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory)
+struct ssaDomNode {
+	ssaBlock *        idom; // Parent (Immediate Dominator)
+	Array<ssaBlock *> children;
+	i32               pre, post; // Ordering in tree
+};
+
+
+struct ssaBlock {
+	i32           index;
+	String        label;
+	ssaProcedure *parent;
+	AstNode *     node; // Can be NULL
+	Scope *       scope;
+	isize         scope_index;
+	ssaDomNode    dom;
+	i32           gaps;
+
+	Array<ssaValue *> instrs;
+	Array<ssaValue *> locals;
+
+	Array<ssaBlock *> preds;
+	Array<ssaBlock *> succs;
+};
+
+struct ssaTargetList {
+	ssaTargetList *prev;
+	ssaBlock *     break_;
+	ssaBlock *     continue_;
+	ssaBlock *     fallthrough_;
+};
+
+enum ssaDeferExitKind {
+	ssaDeferExit_Default,
+	ssaDeferExit_Return,
+	ssaDeferExit_Branch,
+};
+enum ssaDeferKind {
+	ssaDefer_Node,
+	ssaDefer_Instr,
+};
+
+struct ssaDefer {
+	ssaDeferKind kind;
+	isize        scope_index;
+	ssaBlock *   block;
+	union {
+		AstNode *stmt;
+		// NOTE(bill): `instr` will be copied every time to create a new one
+		ssaValue *instr;
+	};
+};
+
+struct ssaProcedure {
+	ssaProcedure *        parent;
+	Array<ssaProcedure *> children;
+
+	Entity *              entity;
+	ssaModule *           module;
+	String                name;
+	Type *                type;
+	AstNode *             type_expr;
+	AstNode *             body;
+	u64                   tags;
+
+	Array<ssaValue *>     params;
+	Array<ssaDefer>       defer_stmts;
+	Array<ssaBlock *>     blocks;
+	i32                   scope_index;
+	ssaBlock *            decl_block;
+	ssaBlock *            entry_block;
+	ssaBlock *            curr_block;
+	ssaTargetList *       target_list;
+	Array<ssaValue *>     referrers;
+
+	i32                   local_count;
+	i32                   instr_count;
+	i32                   block_count;
+};
+
+#define SSA_STARTUP_RUNTIME_PROC_NAME  "__$startup_runtime"
+#define SSA_TYPE_INFO_DATA_NAME        "__$type_info_data"
+#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member"
+
+
+#define SSA_INSTR_KINDS \
+	SSA_INSTR_KIND(Invalid), \
+	SSA_INSTR_KIND(Comment), \
+	SSA_INSTR_KIND(Local), \
+	SSA_INSTR_KIND(ZeroInit), \
+	SSA_INSTR_KIND(Store), \
+	SSA_INSTR_KIND(Load), \
+	SSA_INSTR_KIND(PtrOffset), \
+	SSA_INSTR_KIND(ArrayElementPtr), \
+	SSA_INSTR_KIND(StructElementPtr), \
+	SSA_INSTR_KIND(ArrayExtractValue), \
+	SSA_INSTR_KIND(StructExtractValue), \
+	SSA_INSTR_KIND(Conv), \
+	SSA_INSTR_KIND(Jump), \
+	SSA_INSTR_KIND(If), \
+	SSA_INSTR_KIND(Return), \
+	SSA_INSTR_KIND(Select), \
+	SSA_INSTR_KIND(Phi), \
+	SSA_INSTR_KIND(Unreachable), \
+	SSA_INSTR_KIND(BinaryOp), \
+	SSA_INSTR_KIND(Call), \
+	SSA_INSTR_KIND(VectorExtractElement), \
+	SSA_INSTR_KIND(VectorInsertElement), \
+	SSA_INSTR_KIND(VectorShuffle), \
+	SSA_INSTR_KIND(StartupRuntime), \
+	SSA_INSTR_KIND(BoundsCheck), \
+	SSA_INSTR_KIND(SliceBoundsCheck), \
+
+#define SSA_CONV_KINDS \
+	SSA_CONV_KIND(Invalid), \
+	SSA_CONV_KIND(trunc), \
+	SSA_CONV_KIND(zext), \
+	SSA_CONV_KIND(fptrunc), \
+	SSA_CONV_KIND(fpext), \
+	SSA_CONV_KIND(fptoui), \
+	SSA_CONV_KIND(fptosi), \
+	SSA_CONV_KIND(uitofp), \
+	SSA_CONV_KIND(sitofp), \
+	SSA_CONV_KIND(ptrtoint), \
+	SSA_CONV_KIND(inttoptr), \
+	SSA_CONV_KIND(bitcast),
+
+enum ssaInstrKind {
+#define SSA_INSTR_KIND(x) GB_JOIN2(ssaInstr_, x)
+	SSA_INSTR_KINDS
+#undef SSA_INSTR_KIND
+};
+
+String const ssa_instr_strings[] = {
+#define SSA_INSTR_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}
+	SSA_INSTR_KINDS
+#undef SSA_INSTR_KIND
+};
+
+enum ssaConvKind {
+#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x)
+	SSA_CONV_KINDS
+#undef SSA_CONV_KIND
+};
+
+String const ssa_conv_strings[] = {
+#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}
+	SSA_CONV_KINDS
+#undef SSA_CONV_KIND
+};
+
+struct ssaInstr {
+	ssaInstrKind kind;
+
+	ssaBlock *parent;
+	Type *type;
+
+	union {
+		struct {
+			String text;
+		} Comment;
+		struct {
+			Entity *          entity;
+			Type *            type;
+			b32               zero_initialized;
+			Array<ssaValue *> referrers;
+		} Local;
+		struct {
+			ssaValue *address;
+		} ZeroInit;
+		struct {
+			ssaValue *address;
+			ssaValue *value;
+		} Store;
+		struct {
+			Type *type;
+			ssaValue *address;
+		} Load;
+		struct {
+			ssaValue *address;
+			Type *    result_type;
+			ssaValue *elem_index;
+		} ArrayElementPtr;
+		struct {
+			ssaValue *address;
+			Type *    result_type;
+			i32       elem_index;
+		} StructElementPtr;
+		struct {
+			ssaValue *address;
+			ssaValue *offset;
+		} PtrOffset;
+		struct {
+			ssaValue *address;
+			Type *    result_type;
+			i32       index;
+		} ArrayExtractValue;
+		struct {
+			ssaValue *address;
+			Type *    result_type;
+			i32       index;
+		} StructExtractValue;
+		struct {
+			ssaValue *value;
+			ssaValue *elem;
+			i32       index;
+		} InsertValue;
+		struct {
+			ssaConvKind kind;
+			ssaValue *value;
+			Type *from, *to;
+		} Conv;
+		struct {
+			ssaBlock *block;
+		} Jump;
+		struct {
+			ssaValue *cond;
+			ssaBlock *true_block;
+			ssaBlock *false_block;
+		} If;
+		struct {
+			ssaValue *value;
+		} Return;
+		struct {} Unreachable;
+		struct {
+			ssaValue *cond;
+			ssaValue *true_value;
+			ssaValue *false_value;
+		} Select;
+		struct {
+			Array<ssaValue *> edges;
+			Type *type;
+		} Phi;
+		struct {
+			Type *type;
+			TokenKind op;
+			ssaValue *left, *right;
+		} BinaryOp;
+		struct {
+			Type *type; // return type
+			ssaValue *value;
+			ssaValue **args;
+			isize arg_count;
+		} Call;
+		struct {
+			ssaValue *vector;
+			ssaValue *index;
+		} VectorExtractElement;
+		struct {
+			ssaValue *vector;
+			ssaValue *elem;
+			ssaValue *index;
+		} VectorInsertElement;
+		struct {
+			ssaValue *vector;
+			i32 *indices;
+			i32 index_count;
+			Type *type;
+		} VectorShuffle;
+
+		struct {} StartupRuntime;
+		struct {
+			TokenPos  pos;
+			ssaValue *index;
+			ssaValue *len;
+		} BoundsCheck;
+		struct {
+			TokenPos  pos;
+			ssaValue *low;
+			ssaValue *high;
+			ssaValue *max;
+			b32       is_substring;
+		} SliceBoundsCheck;
+	};
+};
+
+
+enum ssaValueKind {
+	ssaValue_Invalid,
+
+	ssaValue_Constant,
+	ssaValue_ConstantSlice,
+	ssaValue_Nil,
+	ssaValue_TypeName,
+	ssaValue_Global,
+	ssaValue_Param,
+
+	ssaValue_Proc,
+	ssaValue_Block,
+	ssaValue_Instr,
+
+	ssaValue_Count,
+};
+
+struct ssaValue {
+	ssaValueKind kind;
+	i32 index;
+	union {
+		struct {
+			Type *     type;
+			ExactValue value;
+		} Constant;
+		struct {
+			Type *    type;
+			ssaValue *backing_array;
+			i64       count;
+		} ConstantSlice;
+		struct {
+			Type *type;
+		} Nil;
+		struct {
+			Type * type;
+			String name;
+		} TypeName;
+		struct {
+			Entity *          entity;
+			Type *            type;
+			ssaValue *        value;
+			Array<ssaValue *> referrers;
+			b8                is_constant;
+			b8                is_private;
+			b8                is_thread_local;
+			b8                is_unnamed_addr;
+		} Global;
+		struct {
+			ssaProcedure *    parent;
+			Entity *          entity;
+			Type *            type;
+			Array<ssaValue *> referrers;
+		} Param;
+		ssaProcedure Proc;
+		ssaBlock     Block;
+		ssaInstr     Instr;
+	};
+};
+
+gb_global ssaValue *v_zero    = NULL;
+gb_global ssaValue *v_one     = NULL;
+gb_global ssaValue *v_zero32  = NULL;
+gb_global ssaValue *v_one32   = NULL;
+gb_global ssaValue *v_two32   = NULL;
+gb_global ssaValue *v_false   = NULL;
+gb_global ssaValue *v_true    = NULL;
+
+enum ssaAddrKind {
+	ssaAddr_Default,
+	ssaAddr_Vector,
+};
+
+struct ssaAddr {
+	ssaValue *  addr;
+	AstNode *   expr; // NOTE(bill): Just for testing - probably remove later
+	ssaAddrKind kind;
+	union {
+		struct { ssaValue *index; } Vector;
+	};
+};
+
+ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) {
+	ssaAddr v = {addr, expr};
+	return v;
+}
+ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) {
+	ssaAddr v = ssa_make_addr(addr, expr);
+	v.kind = ssaAddr_Vector;
+	v.Vector.index = index;
+	return v;
+}
+
+
+
+enum ssaDebugEncoding {
+	ssaDebugBasicEncoding_Invalid       = 0,
+
+	ssaDebugBasicEncoding_address       = 1,
+	ssaDebugBasicEncoding_boolean       = 2,
+	ssaDebugBasicEncoding_float         = 3,
+	ssaDebugBasicEncoding_signed        = 4,
+	ssaDebugBasicEncoding_signed_char   = 5,
+	ssaDebugBasicEncoding_unsigned      = 6,
+	ssaDebugBasicEncoding_unsigned_char = 7,
+
+	ssaDebugBasicEncoding_member       = 13,
+	ssaDebugBasicEncoding_pointer_type = 15,
+	ssaDebugBasicEncoding_typedef      = 22,
+
+	ssaDebugBasicEncoding_array_type       = 1,
+	ssaDebugBasicEncoding_enumeration_type = 4,
+	ssaDebugBasicEncoding_structure_type   = 19,
+	ssaDebugBasicEncoding_union_type       = 23,
+
+};
+
+enum ssaDebugInfoKind {
+	ssaDebugInfo_Invalid,
+
+	ssaDebugInfo_CompileUnit,
+	ssaDebugInfo_File,
+	ssaDebugInfo_Scope,
+	ssaDebugInfo_Proc,
+	ssaDebugInfo_AllProcs,
+
+	ssaDebugInfo_BasicType,      // basic types
+	ssaDebugInfo_ProcType,
+	ssaDebugInfo_DerivedType,    // pointer, typedef
+	ssaDebugInfo_CompositeType,  // array, struct, enum, (raw_)union
+	ssaDebugInfo_Enumerator,     // For ssaDebugInfo_CompositeType if enum
+	ssaDebugInfo_GlobalVariable,
+	ssaDebugInfo_LocalVariable,
+
+
+	ssaDebugInfo_Count,
+};
+
+struct ssaDebugInfo {
+	ssaDebugInfoKind kind;
+	i32 id;
+
+	union {
+		struct {
+			AstFile *     file;
+			String        producer;
+			ssaDebugInfo *all_procs;
+		} CompileUnit;
+		struct {
+			AstFile *file;
+			String   filename;
+			String   directory;
+		} File;
+		struct {
+			ssaDebugInfo *parent;
+			ssaDebugInfo *file;
+			TokenPos      pos;
+			Scope *       scope; // Actual scope
+		} Scope;
+		struct {
+			Entity *      entity;
+			String        name;
+			ssaDebugInfo *file;
+			TokenPos      pos;
+		} Proc;
+		struct {
+			Array<ssaDebugInfo *> procs;
+		} AllProcs;
+
+
+		struct {
+			String           name;
+			i32              size;
+			i32              align;
+			ssaDebugEncoding encoding;
+		} BasicType;
+		struct {
+			ssaDebugInfo *        return_type;
+			Array<ssaDebugInfo *> param_types;
+		} ProcType;
+		struct {
+			ssaDebugInfo *   base_type;
+			ssaDebugEncoding encoding;
+		} DerivedType;
+		struct {
+			ssaDebugEncoding      encoding;
+			String                name;
+			String                identifier;
+			ssaDebugInfo *        file;
+			TokenPos              pos;
+			i32                   size;
+			i32                   align;
+			Array<ssaDebugInfo *> elements;
+		} CompositeType;
+		struct {
+			String name;
+			i64    value;
+		} Enumerator;
+		struct {
+			String        name;
+			String        linkage_name;
+			ssaDebugInfo *scope;
+			ssaDebugInfo *file;
+			TokenPos      pos;
+			ssaValue     *variable;
+			ssaDebugInfo *declaration;
+		} GlobalVariable;
+		struct {
+			String        name;
+			ssaDebugInfo *scope;
+			ssaDebugInfo *file;
+			TokenPos      pos;
+			i32           arg; // Non-zero if proc parameter
+			ssaDebugInfo *type;
+		} LocalVariable;
+	};
+};
+
+
+
+struct ssaFileBuffer {
+	gbVirtualMemory vm;
+	isize offset;
+	gbFile *output;
+};
+
+void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) {
+	isize size = 8*gb_virtual_memory_page_size(NULL);
+	f->vm = gb_vm_alloc(NULL, size);
+	f->offset = 0;
+	f->output = output;
+}
+
+void ssa_file_buffer_destroy(ssaFileBuffer *f) {
+	if (f->offset > 0) {
+		// NOTE(bill): finish writing buffered data
+		gb_file_write(f->output, f->vm.data, f->offset);
+	}
+
+	gb_vm_free(f->vm);
+}
+
+void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) {
+	if (len > f->vm.size) {
+		gb_file_write(f->output, data, len);
+		return;
+	}
+
+	if ((f->vm.size - f->offset) < len) {
+		gb_file_write(f->output, f->vm.data, f->offset);
+		f->offset = 0;
+	}
+	u8 *cursor = cast(u8 *)f->vm.data + f->offset;
+	gb_memmove(cursor, data, len);
+	f->offset += len;
+}
+
+
+void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) {
+	va_list va;
+	va_start(va, fmt);
+	char buf[4096] = {};
+	isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+	ssa_file_buffer_write(f, buf, len-1);
+	va_end(va);
+}
+
+
+void ssa_file_write(ssaFileBuffer *f, void *data, isize len) {
+	ssa_file_buffer_write(f, data, len);
+}
+
+ssaValue *ssa_lookup_member(ssaModule *m, String name) {
+	ssaValue **v = map_get(&m->members, hash_string(name));
+	if (v != NULL) {
+		return *v;
+	}
+	return NULL;
+}
+
+
+Type *ssa_type(ssaValue *value);
+Type *ssa_instr_type(ssaInstr *instr) {
+	switch (instr->kind) {
+	case ssaInstr_Local:
+		return instr->Local.type;
+	case ssaInstr_Load:
+		return instr->Load.type;
+	case ssaInstr_StructElementPtr:
+		return instr->StructElementPtr.result_type;
+	case ssaInstr_ArrayElementPtr:
+		return instr->ArrayElementPtr.result_type;
+	case ssaInstr_PtrOffset:
+		return ssa_type(instr->PtrOffset.address);
+	case ssaInstr_Phi:
+		return instr->Phi.type;
+	case ssaInstr_ArrayExtractValue:
+		return instr->ArrayExtractValue.result_type;
+	case ssaInstr_StructExtractValue:
+		return instr->StructExtractValue.result_type;
+	case ssaInstr_BinaryOp:
+		return instr->BinaryOp.type;
+	case ssaInstr_Conv:
+		return instr->Conv.to;
+	case ssaInstr_Select:
+		return ssa_type(instr->Select.true_value);
+	case ssaInstr_Call: {
+		Type *pt = base_type(instr->Call.type);
+		if (pt != NULL) {
+			if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) {
+				return pt->Tuple.variables[0]->type;
+			}
+			return pt;
+		}
+		return NULL;
+	} break;
+	case ssaInstr_VectorExtractElement: {
+		Type *vt = ssa_type(instr->VectorExtractElement.vector);
+		Type *bt = base_vector_type(vt);
+		GB_ASSERT(!is_type_vector(bt));
+		return bt;
+	} break;
+	case ssaInstr_VectorInsertElement:
+		return ssa_type(instr->VectorInsertElement.vector);
+	case ssaInstr_VectorShuffle:
+		return instr->VectorShuffle.type;
+	}
+	return NULL;
+}
+
+Type *ssa_type(ssaValue *value) {
+	switch (value->kind) {
+	case ssaValue_Constant:
+		return value->Constant.type;
+	case ssaValue_ConstantSlice:
+		return value->ConstantSlice.type;
+	case ssaValue_Nil:
+		return value->Nil.type;
+	case ssaValue_TypeName:
+		return value->TypeName.type;
+	case ssaValue_Global:
+		return value->Global.type;
+	case ssaValue_Param:
+		return value->Param.type;
+	case ssaValue_Proc:
+		return value->Proc.type;
+	case ssaValue_Instr:
+		return ssa_instr_type(&value->Instr);
+	}
+	return NULL;
+}
+
+Type *ssa_addr_type(ssaAddr lval) {
+	if (lval.addr != NULL) {
+		Type *t = ssa_type(lval.addr);
+		GB_ASSERT(is_type_pointer(t));
+		return type_deref(t);
+	}
+	return NULL;
+}
+
+
+
+b32 ssa_is_blank_ident(AstNode *node) {
+	if (node->kind == AstNode_Ident) {
+		ast_node(i, Ident, node);
+		return is_blank_ident(i->string);
+	}
+	return false;
+}
+
+
+ssaInstr *ssa_get_last_instr(ssaBlock *block) {
+	if (block != NULL) {
+		isize len = block->instrs.count;
+		if (len > 0) {
+			ssaValue *v = block->instrs[len-1];
+			GB_ASSERT(v->kind == ssaValue_Instr);
+			return &v->Instr;
+		}
+	}
+	return NULL;
+
+}
+
+b32 ssa_is_instr_terminating(ssaInstr *i) {
+	if (i != NULL) {
+		switch (i->kind) {
+		case ssaInstr_Return:
+		case ssaInstr_Unreachable:
+			return true;
+		}
+	}
+
+	return false;
+}
+
+
+void ssa_add_edge(ssaBlock *from, ssaBlock *to) {
+	array_add(&from->succs, to);
+	array_add(&to->preds, from);
+}
+
+void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) {
+	if (instr->kind == ssaValue_Instr) {
+		instr->Instr.parent = parent;
+	}
+}
+
+Array<ssaValue *> *ssa_value_referrers(ssaValue *v) {
+	switch (v->kind) {
+	case ssaValue_Global:
+		return &v->Global.referrers;
+	case ssaValue_Param:
+		return &v->Param.referrers;
+	case ssaValue_Proc: {
+		if (v->Proc.parent != NULL) {
+			return &v->Proc.referrers;
+		}
+		return NULL;
+	}
+	case ssaValue_Instr: {
+		ssaInstr *i = &v->Instr;
+		switch (i->kind) {
+		case ssaInstr_Local:
+			return &i->Local.referrers;
+		}
+	} break;
+	}
+
+	return NULL;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Make
+//
+////////////////////////////////////////////////////////////////
+
+void      ssa_module_add_value    (ssaModule *m, Entity *e, ssaValue *v);
+ssaValue *ssa_emit_zero_init      (ssaProcedure *p, ssaValue *address);
+ssaValue *ssa_emit_comment        (ssaProcedure *p, String text);
+ssaValue *ssa_emit_store          (ssaProcedure *p, ssaValue *address, ssaValue *value);
+ssaValue *ssa_emit_load           (ssaProcedure *p, ssaValue *address);
+void      ssa_emit_jump           (ssaProcedure *proc, ssaBlock *block);
+ssaValue *ssa_emit_conv           (ssaProcedure *proc, ssaValue *value, Type *t);
+ssaValue *ssa_type_info           (ssaProcedure *proc, Type *type);
+ssaValue *ssa_build_expr          (ssaProcedure *proc, AstNode *expr);
+void      ssa_build_stmt          (ssaProcedure *proc, AstNode *node);
+void      ssa_build_cond          (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block);
+void      ssa_build_defer_stmt    (ssaProcedure *proc, ssaDefer d);
+ssaAddr   ssa_build_addr          (ssaProcedure *proc, AstNode *expr);
+void      ssa_build_proc          (ssaValue *value, ssaProcedure *parent);
+void      ssa_gen_global_type_name(ssaModule *m, Entity *e, String name);
+
+
+
+
+ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) {
+	ssaValue *v = gb_alloc_item(a, ssaValue);
+	v->kind = kind;
+	return v;
+}
+ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) {
+	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr);
+	v->Instr.kind = kind;
+	proc->instr_count++;
+	return v;
+}
+ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) {
+	ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo);
+	di->kind = kind;
+	return di;
+}
+
+
+
+
+ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName);
+	v->TypeName.name = name;
+	v->TypeName.type = type;
+	return v;
+}
+
+ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_Global);
+	v->Global.entity = e;
+	v->Global.type = make_type_pointer(a, e->type);
+	v->Global.value = value;
+	array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
+	return v;
+}
+ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_Param);
+	v->Param.parent = parent;
+	v->Param.entity = e;
+	v->Param.type   = e->type;
+	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
+	return v;
+}
+ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_Nil);
+	v->Nil.type = type;
+	return v;
+}
+
+
+
+ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local);
+	ssaInstr *i = &v->Instr;
+	i->Local.entity = e;
+	i->Local.type = make_type_pointer(p->module->allocator, e->type);
+	i->Local.zero_initialized = zero_initialized;
+	array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
+	ssa_module_add_value(p->module, e, v);
+	return v;
+}
+
+
+ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store);
+	ssaInstr *i = &v->Instr;
+	i->Store.address = address;
+	i->Store.value = value;
+	return v;
+}
+
+ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit);
+	ssaInstr *i = &v->Instr;
+	i->ZeroInit.address = address;
+	return v;
+}
+
+ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load);
+	ssaInstr *i = &v->Instr;
+	i->Load.address = address;
+	i->Load.type = type_deref(ssa_type(address));
+	return v;
+}
+
+ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr);
+	ssaInstr *i = &v->Instr;
+	Type *t = ssa_type(address);
+	GB_ASSERT(is_type_pointer(t));
+	t = base_type(type_deref(t));
+	GB_ASSERT(is_type_array(t));
+
+	Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem);
+
+	i->ArrayElementPtr.address = address;
+	i->ArrayElementPtr.elem_index = elem_index;
+	i->ArrayElementPtr.result_type = result_type;
+
+	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
+	              "%s", type_to_string(ssa_type(address)));
+	return v;
+}
+ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr);
+	ssaInstr *i = &v->Instr;
+	i->StructElementPtr.address     = address;
+	i->StructElementPtr.elem_index  = elem_index;
+	i->StructElementPtr.result_type = result_type;
+
+	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
+	              "%s", type_to_string(ssa_type(address)));
+	return v;
+}
+ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset);
+	ssaInstr *i = &v->Instr;
+	i->PtrOffset.address = address;
+	i->PtrOffset.offset  = offset;
+
+	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
+	              "%s", type_to_string(ssa_type(address)));
+	GB_ASSERT_MSG(is_type_integer(ssa_type(offset)),
+	              "%s", type_to_string(ssa_type(address)));
+
+	return v;
+}
+
+
+
+ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue);
+	ssaInstr *i = &v->Instr;
+	i->ArrayExtractValue.address = address;
+	i->ArrayExtractValue.index = index;
+	Type *t = base_type(ssa_type(address));
+	GB_ASSERT(is_type_array(t));
+	i->ArrayExtractValue.result_type = t->Array.elem;
+	return v;
+}
+
+ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue);
+	ssaInstr *i = &v->Instr;
+	i->StructExtractValue.address = address;
+	i->StructExtractValue.index = index;
+	i->StructExtractValue.result_type = result_type;
+	return v;
+}
+
+ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp);
+	ssaInstr *i = &v->Instr;
+	i->BinaryOp.op = op;
+	i->BinaryOp.left = left;
+	i->BinaryOp.right = right;
+	i->BinaryOp.type = type;
+	return v;
+}
+
+ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump);
+	ssaInstr *i = &v->Instr;
+	i->Jump.block = block;
+	return v;
+}
+ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_If);
+	ssaInstr *i = &v->Instr;
+	i->If.cond = cond;
+	i->If.true_block = true_block;
+	i->If.false_block = false_block;
+	return v;
+}
+
+
+ssaValue *ssa_make_instr_phi(ssaProcedure *p, Array<ssaValue *> edges, Type *type) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi);
+	ssaInstr *i = &v->Instr;
+	i->Phi.edges = edges;
+	i->Phi.type = type;
+	return v;
+}
+
+ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable);
+	return v;
+}
+
+ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return);
+	v->Instr.Return.value = value;
+	return v;
+}
+
+ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select);
+	v->Instr.Select.cond = cond;
+	v->Instr.Select.true_value = t;
+	v->Instr.Select.false_value = f;
+	return v;
+}
+
+ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call);
+	v->Instr.Call.value = value;
+	v->Instr.Call.args = args;
+	v->Instr.Call.arg_count = arg_count;
+	v->Instr.Call.type = result_type;
+	return v;
+}
+
+ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv);
+	v->Instr.Conv.kind = kind;
+	v->Instr.Conv.value = value;
+	v->Instr.Conv.from = from;
+	v->Instr.Conv.to = to;
+	return v;
+}
+
+ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement);
+	v->Instr.VectorExtractElement.vector = vector;
+	v->Instr.VectorExtractElement.index = index;
+	return v;
+}
+
+ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement);
+	v->Instr.VectorInsertElement.vector = vector;
+	v->Instr.VectorInsertElement.elem   = elem;
+	v->Instr.VectorInsertElement.index  = index;
+	return v;
+}
+
+ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle);
+	v->Instr.VectorShuffle.vector      = vector;
+	v->Instr.VectorShuffle.indices     = indices;
+	v->Instr.VectorShuffle.index_count = index_count;
+
+	Type *vt = base_type(ssa_type(vector));
+	v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count);
+
+	return v;
+}
+
+ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment);
+	v->Instr.Comment.text = text;
+	return v;
+}
+
+ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck);
+	v->Instr.BoundsCheck.pos   = pos;
+	v->Instr.BoundsCheck.index = index;
+	v->Instr.BoundsCheck.len   = len;
+	return v;
+}
+ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) {
+	ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck);
+	v->Instr.SliceBoundsCheck.pos  = pos;
+	v->Instr.SliceBoundsCheck.low  = low;
+	v->Instr.SliceBoundsCheck.high = high;
+	v->Instr.SliceBoundsCheck.max  = max;
+	v->Instr.SliceBoundsCheck.is_substring = is_substring;
+	return v;
+}
+
+
+
+ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_Constant);
+	v->Constant.type  = type;
+	v->Constant.value = value;
+	return v;
+}
+
+
+ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice);
+	v->ConstantSlice.type = type;
+	v->ConstantSlice.backing_array = backing_array;
+	v->ConstantSlice.count = count;
+	return v;
+}
+
+ssaValue *ssa_make_const_int(gbAllocator a, i64 i) {
+	return ssa_make_value_constant(a, t_int, make_exact_value_integer(i));
+}
+ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) {
+	return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i));
+}
+ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) {
+	return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i));
+}
+ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) {
+	return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0));
+}
+ssaValue *ssa_make_const_string(gbAllocator a, String s) {
+	return ssa_make_value_constant(a, t_string, make_exact_value_string(s));
+}
+
+ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) {
+	ssaValue *v = ssa_alloc_value(a, ssaValue_Proc);
+	v->Proc.module = m;
+	v->Proc.entity = entity;
+	v->Proc.type   = type;
+	v->Proc.type_expr = type_expr;
+	v->Proc.body   = body;
+	v->Proc.name   = name;
+	array_init(&v->Proc.referrers, heap_allocator(), 0); // TODO(bill): replace heap allocator
+
+	Type *t = base_type(type);
+	GB_ASSERT(is_type_proc(t));
+	array_init(&v->Proc.params, heap_allocator(), t->Proc.param_count);
+
+	return v;
+}
+
+ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) {
+	Scope *scope = NULL;
+	if (node != NULL) {
+		Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node));
+		if (found) {
+			scope = *found;
+		} else {
+			GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind]));
+		}
+	}
+
+	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block);
+	v->Block.label  = make_string(label);
+	v->Block.node   = node;
+	v->Block.scope  = scope;
+	v->Block.parent = proc;
+
+	array_init(&v->Block.instrs, heap_allocator());
+	array_init(&v->Block.locals, heap_allocator());
+
+	array_init(&v->Block.preds,  heap_allocator());
+	array_init(&v->Block.succs,  heap_allocator());
+
+	ssaBlock *block = &v->Block;
+
+	array_add(&proc->blocks, block);
+	proc->block_count++;
+
+	return block;
+}
+
+
+
+
+
+ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) {
+	ssaDefer d = {ssaDefer_Node};
+	d.scope_index = scope_index;
+	d.block = proc->curr_block;
+	d.stmt = stmt;
+	array_add(&proc->defer_stmts, d);
+	return d;
+}
+
+
+ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) {
+	ssaDefer d = {ssaDefer_Instr};
+	d.scope_index = proc->scope_index;
+	d.block = proc->curr_block;
+	d.instr = instr; // NOTE(bill): It will make a copy everytime it is called
+	array_add(&proc->defer_stmts, d);
+	return d;
+}
+
+
+
+ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) {
+	if (is_type_slice(type)) {
+		ast_node(cl, CompoundLit, value.value_compound);
+		gbAllocator a = m->allocator;
+
+		isize count = cl->elems.count;
+		if (count == 0) {
+			return ssa_make_value_nil(a, type);
+		}
+		Type *elem = base_type(type)->Slice.elem;
+		Type *t = make_type_array(a, elem, count);
+		ssaValue *backing_array = ssa_add_module_constant(m, t, value);
+
+
+		isize max_len = 7+8+1;
+		u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
+		isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index);
+		m->global_array_index++;
+
+		String name = make_string(str, len-1);
+
+		Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value);
+		ssaValue *g = ssa_make_value_global(a, e, backing_array);
+		ssa_module_add_value(m, e, g);
+		map_set(&m->members, hash_string(name), g);
+
+		return ssa_make_value_constant_slice(a, type, g, count);
+	}
+
+	return ssa_make_value_constant(m->allocator, type, value);
+}
+
+ssaValue *ssa_add_global_string_array(ssaModule *m, String string) {
+	gbAllocator a = m->allocator;
+
+	isize max_len = 6+8+1;
+	u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
+	isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index);
+	m->global_string_index++;
+
+	String name = make_string(str, len-1);
+	Token token = {Token_String};
+	token.string = name;
+	Type *type = make_type_array(a, t_u8, string.len);
+	ExactValue ev = make_exact_value_string(string);
+	Entity *entity = make_entity_constant(a, NULL, token, type, ev);
+	ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev));
+	g->Global.is_private      = true;
+	// g->Global.is_unnamed_addr = true;
+	// g->Global.is_constant = true;
+
+	ssa_module_add_value(m, entity, g);
+	map_set(&m->members, hash_string(name), g);
+
+	return g;
+}
+
+
+
+
+ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) {
+	ssaBlock *b = proc->decl_block; // all variables must be in the first block
+	ssaValue *instr = ssa_make_instr_local(proc, e, zero_initialized);
+	instr->Instr.parent = b;
+	array_add(&b->instrs, instr);
+	array_add(&b->locals, instr);
+	proc->local_count++;
+
+	// if (zero_initialized) {
+		ssa_emit_zero_init(proc, instr);
+	// }
+
+	return instr;
+}
+
+ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) {
+	Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name));
+	if (found) {
+		Entity *e = *found;
+		ssa_emit_comment(proc, e->token.string);
+		return ssa_add_local(proc, e, zero_initialized);
+	}
+	return NULL;
+}
+
+ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
+	GB_ASSERT(type != NULL);
+
+	Scope *scope = NULL;
+	if (proc->curr_block) {
+		scope = proc->curr_block->scope;
+	}
+	Entity *e = make_entity_variable(proc->module->allocator,
+	                                 scope,
+	                                 empty_token,
+	                                 type);
+	return ssa_add_local(proc, e, true);
+}
+
+ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
+	ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e);
+#if 1
+	ssaValue *l = ssa_add_local(proc, e);
+	ssa_emit_store(proc, l, v);
+#else
+	ssa_module_add_value(proc->module, e, v);
+#endif
+	return v;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Debug
+//
+////////////////////////////////////////////////////////////////
+
+ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) {
+	if (!proc->module->generate_debug_info) {
+		return NULL;
+	}
+
+	GB_ASSERT(file != NULL);
+	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File);
+	di->File.file = file;
+
+	String filename = file->tokenizer.fullpath;
+	String directory = filename;
+	isize slash_index = 0;
+	for (isize i = filename.len-1; i >= 0; i--) {
+		if (filename.text[i] == '\\' ||
+		    filename.text[i] == '/') {
+			break;
+		}
+		slash_index = i;
+	}
+	directory.len = slash_index-1;
+	filename.text = filename.text + slash_index;
+	filename.len -= slash_index;
+
+
+	di->File.filename = filename;
+	di->File.directory = directory;
+
+	map_set(&proc->module->debug_info, hash_pointer(file), di);
+	return di;
+}
+
+
+ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) {
+	if (!proc->module->generate_debug_info) {
+		return NULL;
+	}
+
+	GB_ASSERT(entity != NULL);
+	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc);
+	di->Proc.entity = entity;
+	di->Proc.name = name;
+	di->Proc.file = file;
+	di->Proc.pos = entity->token.pos;
+
+	map_set(&proc->module->debug_info, hash_pointer(entity), di);
+	return di;
+}
+
+////////////////////////////////////////////////////////////////
+//
+// @Emit
+//
+////////////////////////////////////////////////////////////////
+
+
+ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) {
+	GB_ASSERT(instr->kind == ssaValue_Instr);
+	ssaBlock *b = proc->curr_block;
+	instr->Instr.parent = b;
+	if (b != NULL) {
+		ssaInstr *i = ssa_get_last_instr(b);
+		if (!ssa_is_instr_terminating(i)) {
+			array_add(&b->instrs, instr);
+		}
+	}
+	return instr;
+}
+ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
+	return ssa_emit(p, ssa_make_instr_store(p, address, value));
+}
+ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
+	return ssa_emit(p, ssa_make_instr_load(p, address));
+}
+ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
+	return ssa_emit(p, ssa_make_instr_select(p, cond, t, f));
+}
+
+ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address)  {
+	return ssa_emit(p, ssa_make_instr_zero_init(p, address));
+}
+
+ssaValue *ssa_emit_comment(ssaProcedure *p, String text) {
+	return ssa_emit(p, ssa_make_instr_comment(p, text));
+}
+
+
+ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
+	Type *pt = base_type(ssa_type(value));
+	GB_ASSERT(pt->kind == Type_Proc);
+	Type *results = pt->Proc.results;
+	return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
+}
+
+ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) {
+	String name = make_string(name_);
+	ssaValue **found = map_get(&proc->module->members, hash_string(name));
+	GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name));
+	ssaValue *gp = *found;
+	return ssa_emit_call(proc, gp, args, arg_count);
+}
+
+
+
+void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
+	isize count = proc->defer_stmts.count;
+	isize i = count;
+	while (i --> 0) {
+		ssaDefer d = proc->defer_stmts[i];
+		if (kind == ssaDeferExit_Default) {
+			if (proc->scope_index == d.scope_index &&
+			    d.scope_index > 1) {
+				ssa_build_defer_stmt(proc, d);
+				array_pop(&proc->defer_stmts);
+				continue;
+			} else {
+				break;
+			}
+		} else if (kind == ssaDeferExit_Return) {
+			ssa_build_defer_stmt(proc, d);
+		} else if (kind == ssaDeferExit_Branch) {
+			GB_ASSERT(block != NULL);
+			isize lower_limit = block->scope_index+1;
+			if (lower_limit < d.scope_index) {
+				ssa_build_defer_stmt(proc, d);
+			}
+		}
+	}
+}
+
+
+void ssa_open_scope(ssaProcedure *proc) {
+	proc->scope_index++;
+}
+
+void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
+	ssa_emit_defer_stmts(proc, kind, block);
+	GB_ASSERT(proc->scope_index > 0);
+	proc->scope_index--;
+}
+
+
+
+void ssa_emit_unreachable(ssaProcedure *proc) {
+	ssa_emit(proc, ssa_make_instr_unreachable(proc));
+}
+
+void ssa_emit_return(ssaProcedure *proc, ssaValue *v) {
+	ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL);
+	ssa_emit(proc, ssa_make_instr_return(proc, v));
+}
+
+void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) {
+	ssaBlock *b = proc->curr_block;
+	if (b == NULL) {
+		return;
+	}
+	ssa_emit(proc, ssa_make_instr_jump(proc, target_block));
+	ssa_add_edge(b, target_block);
+	proc->curr_block = NULL;
+}
+
+void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
+	ssaBlock *b = proc->curr_block;
+	if (b == NULL) {
+		return;
+	}
+	ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block));
+	ssa_add_edge(b, true_block);
+	ssa_add_edge(b, false_block);
+	proc->curr_block = NULL;
+}
+
+void ssa_emit_startup_runtime(ssaProcedure *proc) {
+	GB_ASSERT(proc->parent == NULL && proc->name == "main");
+	ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime));
+}
+
+
+
+
+ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) {
+	if (addr.addr == NULL) {
+		return NULL;
+	}
+
+	if (addr.kind == ssaAddr_Vector) {
+		ssaValue *v = ssa_emit_load(proc, addr.addr);
+		Type *elem_type = base_type(ssa_type(v))->Vector.elem;
+		ssaValue *elem = ssa_emit_conv(proc, value, elem_type);
+		ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index));
+		return ssa_emit_store(proc, addr.addr, out);
+	} else {
+		ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr));
+		return ssa_emit_store(proc, addr.addr, v);
+	}
+}
+ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) {
+	if (addr.addr == NULL) {
+		GB_PANIC("Illegal addr load");
+		return NULL;
+	}
+
+	if (addr.kind == ssaAddr_Vector) {
+		ssaValue *v = ssa_emit_load(proc, addr.addr);
+		return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index));
+	}
+	Type *t = base_type(ssa_type(addr.addr));
+	if (t->kind == Type_Proc) {
+		// NOTE(bill): Imported procedures don't require a load as they are pointers
+		return addr.addr;
+	}
+	return ssa_emit_load(proc, addr.addr);
+}
+
+
+
+
+ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) {
+	offset = ssa_emit_conv(proc, offset, t_int);
+	return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset));
+}
+
+ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
+	Type *t_left = ssa_type(left);
+	Type *t_right = ssa_type(right);
+
+	if (op == Token_Add) {
+		if (is_type_pointer(t_left)) {
+			ssaValue *ptr = ssa_emit_conv(proc, left, type);
+			ssaValue *offset = right;
+			return ssa_emit_ptr_offset(proc, ptr, offset);
+		} else if (is_type_pointer(ssa_type(right))) {
+			ssaValue *ptr = ssa_emit_conv(proc, right, type);
+			ssaValue *offset = left;
+			return ssa_emit_ptr_offset(proc, ptr, offset);
+		}
+	} else if (op == Token_Sub) {
+		if (is_type_pointer(t_left) && is_type_integer(t_right)) {
+			// ptr - int
+			ssaValue *ptr = ssa_emit_conv(proc, left, type);
+			ssaValue *offset = right;
+			return ssa_emit_ptr_offset(proc, ptr, offset);
+		} else if (is_type_pointer(t_left) && is_type_pointer(t_right)) {
+			GB_ASSERT(is_type_integer(type));
+			Type *ptr_type = t_left;
+			ssaModule *m = proc->module;
+			ssaValue *x = ssa_emit_conv(proc, left, type);
+			ssaValue *y = ssa_emit_conv(proc, right, type);
+			ssaValue *diff = ssa_emit_arith(proc, op, x, y, type);
+			ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type));
+			return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type);
+		}
+	}
+
+
+	switch (op) {
+	case Token_AndNot: {
+		// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
+		// NOTE(bill): "not" `x` == `x` "xor" `-1`
+		ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1));
+		op = Token_Xor;
+		right = ssa_emit_arith(proc, op, right, neg, type);
+		GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp);
+		right->Instr.BinaryOp.type = type;
+		op = Token_And;
+	} /* fallthrough */
+	case Token_Add:
+	case Token_Sub:
+	case Token_Mul:
+	case Token_Quo:
+	case Token_Mod:
+	case Token_And:
+	case Token_Or:
+	case Token_Xor:
+		left  = ssa_emit_conv(proc, left, type);
+		right = ssa_emit_conv(proc, right, type);
+		break;
+	}
+
+	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type));
+}
+
+ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) {
+	Type *a = base_type(ssa_type(left));
+	Type *b = base_type(ssa_type(right));
+
+	if (are_types_identical(a, b)) {
+		// NOTE(bill): No need for a conversion
+	} else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) {
+		left = ssa_emit_conv(proc, left, ssa_type(right));
+	} else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) {
+		right = ssa_emit_conv(proc, right, ssa_type(left));
+	}
+
+	Type *result = t_bool;
+	if (is_type_vector(a)) {
+		result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count);
+	}
+	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result));
+}
+
+ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) {
+	Type *st = base_type(type_deref(ssa_type(s)));
+	GB_ASSERT(is_type_array(st));
+
+	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
+	index = ssa_emit_conv(proc, index, t_i32);
+	return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index));
+}
+
+ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
+	return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index));
+}
+
+
+ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
+	gbAllocator a = proc->module->allocator;
+	Type *t = base_type(type_deref(ssa_type(s)));
+	Type *result_type = NULL;
+	ssaValue *gep = NULL;
+
+	if (is_type_struct(t)) {
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		result_type = make_type_pointer(a, t->Record.fields[index]->type);
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->Tuple.variable_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+		result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
+	} else if (is_type_slice(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
+		case 1: result_type = make_type_pointer(a, t_int); break;
+		case 2: result_type = make_type_pointer(a, t_int); break;
+		}
+	} else if (is_type_string(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
+		case 1: result_type = make_type_pointer(a, t_int);    break;
+		}
+	} else if (is_type_any(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
+		case 1: result_type = make_type_pointer(a, t_rawptr);        break;
+		}
+	} else if (is_type_maybe(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t->Maybe.elem); break;
+		case 1: result_type = make_type_pointer(a, t_bool);        break;
+		}
+	} else if (is_type_union(t)) {
+		switch (index) {
+		case 1: result_type = make_type_pointer(a, t_int); break;
+
+		case 0:
+		default:
+			GB_PANIC("TODO(bill): struct_gep 0 for unions");
+			break;
+		}
+	} else {
+		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index);
+	}
+
+	GB_ASSERT(result_type != NULL);
+
+	gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type);
+	return ssa_emit(proc, gep);
+}
+
+
+
+ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
+	Type *st = base_type(ssa_type(s));
+	GB_ASSERT(is_type_array(st));
+	return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index));
+}
+
+ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
+	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
+
+	gbAllocator a = proc->module->allocator;
+	Type *t = base_type(ssa_type(s));
+	Type *result_type = NULL;
+
+	if (is_type_struct(t)) {
+		GB_ASSERT(t->Record.field_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
+		result_type = t->Record.fields[index]->type;
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->Tuple.variable_count > 0);
+		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
+		result_type = t->Tuple.variables[index]->type;
+	} else if (is_type_slice(t)) {
+		switch (index) {
+		case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
+		case 1: result_type = t_int; break;
+		case 2: result_type = t_int; break;
+		}
+	} else if (is_type_string(t)) {
+		switch (index) {
+		case 0: result_type = t_u8_ptr; break;
+		case 1: result_type = t_int;    break;
+		}
+	} else if (is_type_any(t)) {
+		switch (index) {
+		case 0: result_type = t_type_info_ptr; break;
+		case 1: result_type = t_rawptr;        break;
+		}
+	} else if (is_type_maybe(t)) {
+		switch (index) {
+		case 0: result_type = t->Maybe.elem; break;
+		case 1: result_type = t_bool;        break;
+		}
+	} else if (is_type_union(t)) {
+		switch (index) {
+		case 1: result_type = t_int; break;
+
+		case 0:
+		default:
+			GB_PANIC("TODO(bill): struct_gep 0 for unions");
+			break;
+		}
+	} else {
+		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index);
+	}
+
+	GB_ASSERT(result_type != NULL);
+
+	return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type));
+}
+
+
+ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
+	GB_ASSERT(sel.index.count > 0);
+
+	for_array(i, sel.index) {
+		i32 index = cast(i32)sel.index[i];
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			e = ssa_emit_load(proc, e);
+			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
+		}
+		type = base_type(type);
+
+
+		if (is_type_raw_union(type)) {
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
+		} else if (type->kind == Type_Record) {
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_struct_ep(proc, e, index);
+		} else if (type->kind == Type_Basic) {
+			switch (type->Basic.kind) {
+			case Basic_any: {
+				if (index == 0) {
+					type = t_type_info_ptr;
+				} else if (index == 1) {
+					type = t_rawptr;
+				}
+				e = ssa_emit_struct_ep(proc, e, index);
+			} break;
+
+			case Basic_string:
+				e = ssa_emit_struct_ep(proc, e, index);
+				break;
+
+			default:
+				GB_PANIC("un-gep-able type");
+				break;
+			}
+		} else if (type->kind == Type_Slice) {
+			e = ssa_emit_struct_ep(proc, e, index);
+		} else {
+			GB_PANIC("un-gep-able type");
+		}
+	}
+
+	return e;
+}
+
+
+ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
+	GB_ASSERT(sel.index.count > 0);
+
+	for_array(i, sel.index) {
+		isize index = sel.index[i];
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			e = ssa_emit_load(proc, e);
+			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
+		}
+		type = base_type(type);
+
+
+		if (is_type_raw_union(type)) {
+			type = type->Record.fields[index]->type;
+			e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
+		} else {
+			e = ssa_emit_struct_ev(proc, e, index);
+		}
+	}
+
+	return e;
+}
+
+
+
+
+ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) {
+	return ssa_emit_array_ep(proc, array, v_zero32);
+}
+ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) {
+	Type *t = ssa_type(array);
+	GB_ASSERT(t->kind == Type_Array);
+	return ssa_make_const_int(proc->module->allocator, t->Array.count);
+}
+ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) {
+	return ssa_array_len(proc, array);
+}
+
+ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) {
+	Type *t = ssa_type(slice);
+	GB_ASSERT(t->kind == Type_Slice);
+	return ssa_emit_struct_ev(proc, slice, 0);
+}
+ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) {
+	Type *t = ssa_type(slice);
+	GB_ASSERT(t->kind == Type_Slice);
+	return ssa_emit_struct_ev(proc, slice, 1);
+}
+ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) {
+	Type *t = ssa_type(slice);
+	GB_ASSERT(t->kind == Type_Slice);
+	return ssa_emit_struct_ev(proc, slice, 2);
+}
+
+ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
+	Type *t = ssa_type(string);
+	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
+	return ssa_emit_struct_ev(proc, string, 0);
+}
+ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
+	Type *t = ssa_type(string);
+	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
+	return ssa_emit_struct_ev(proc, string, 1);
+}
+
+
+
+ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) {
+	// TODO(bill): array bounds checking for slice creation
+	// TODO(bill): check that low < high <= max
+	gbAllocator a = proc->module->allocator;
+	Type *bt = base_type(ssa_type(base));
+
+	if (low == NULL) {
+		low = v_zero;
+	}
+	if (high == NULL) {
+		switch (bt->kind) {
+		case Type_Array:   high = ssa_array_len(proc, base); break;
+		case Type_Slice:   high = ssa_slice_len(proc, base); break;
+		case Type_Pointer: high = v_one;                     break;
+		}
+	}
+	if (max == NULL) {
+		switch (bt->kind) {
+		case Type_Array:   max = ssa_array_cap(proc, base); break;
+		case Type_Slice:   max = ssa_slice_cap(proc, base); break;
+		case Type_Pointer: max = high;                      break;
+		}
+	}
+	GB_ASSERT(max != NULL);
+
+	ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
+	ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
+
+	ssaValue *elem = NULL;
+	switch (bt->kind) {
+	case Type_Array:   elem = ssa_array_elem(proc, base); break;
+	case Type_Slice:   elem = ssa_slice_elem(proc, base); break;
+	case Type_Pointer: elem = ssa_emit_load(proc, base);  break;
+	}
+
+	elem = ssa_emit_ptr_offset(proc, elem, low);
+
+	ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+
+	ssaValue *gep = NULL;
+	gep = ssa_emit_struct_ep(proc, slice, 0);
+	ssa_emit_store(proc, gep, elem);
+
+	gep = ssa_emit_struct_ep(proc, slice, 1);
+	ssa_emit_store(proc, gep, len);
+
+	gep = ssa_emit_struct_ep(proc, slice, 2);
+	ssa_emit_store(proc, gep, cap);
+
+	return slice;
+}
+
+ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) {
+	ssaValue *str = ssa_add_local_generated(proc, t_string);
+	ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0);
+	ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1);
+	ssa_emit_store(proc, str_elem, elem);
+	ssa_emit_store(proc, str_len, len);
+	return ssa_emit_load(proc, str);
+}
+
+
+
+
+String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
+	Type *prev_src = src;
+	// Type *prev_dst = dst;
+	src = base_type(type_deref(src));
+	// dst = base_type(type_deref(dst));
+	b32 src_is_ptr = src != prev_src;
+	// b32 dst_is_ptr = dst != prev_dst;
+
+	GB_ASSERT(is_type_struct(src));
+	for (isize i = 0; i < src->Record.field_count; i++) {
+		Entity *f = src->Record.fields[i];
+		if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) {
+			if (are_types_identical(dst, f->type)) {
+				return f->token.string;
+			}
+			if (src_is_ptr && is_type_pointer(dst)) {
+				if (are_types_identical(type_deref(dst), f->type)) {
+					return f->token.string;
+				}
+			}
+			String name = lookup_polymorphic_field(info, dst, f->type);
+			if (name.len > 0) {
+				return name;
+			}
+		}
+	}
+	return make_string("");
+}
+
+ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) {
+	return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type));
+}
+
+
+ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) {
+	Type *src_type = ssa_type(value);
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+
+	Type *src = get_enum_base_type(base_type(src_type));
+	Type *dst = get_enum_base_type(base_type(t));
+
+	if (value->kind == ssaValue_Constant) {
+		if (is_type_any(dst)) {
+			ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type));
+			ssa_emit_store(proc, default_value, value);
+			return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any);
+		} else if (dst->kind == Type_Basic) {
+			ExactValue ev = value->Constant.value;
+			if (is_type_float(dst)) {
+				ev = exact_value_to_float(ev);
+			} else if (is_type_string(dst)) {
+				// Handled elsewhere
+				GB_ASSERT(ev.kind == ExactValue_String);
+			} else if (is_type_integer(dst)) {
+				ev = exact_value_to_integer(ev);
+			} else if (is_type_pointer(dst)) {
+				// IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null`
+				ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev);
+				return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst));
+			}
+			return ssa_add_module_constant(proc->module, t, ev);
+		}
+	}
+
+	if (are_types_identical(src, dst)) {
+		return value;
+	}
+
+	if (is_type_maybe(dst)) {
+		ssaValue *maybe = ssa_add_local_generated(proc, dst);
+		ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0);
+		ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1);
+		ssa_emit_store(proc, val, value);
+		ssa_emit_store(proc, set, v_true);
+		return ssa_emit_load(proc, maybe);
+	}
+
+	// integer -> integer
+	if (is_type_integer(src) && is_type_integer(dst)) {
+		GB_ASSERT(src->kind == Type_Basic &&
+		          dst->kind == Type_Basic);
+		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
+		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
+		if (sz == dz) {
+			// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
+			return value;
+		}
+
+		ssaConvKind kind = ssaConv_trunc;
+		if (dz >= sz) {
+			kind = ssaConv_zext;
+		}
+		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
+	}
+
+	// boolean -> integer
+	if (is_type_boolean(src) && is_type_integer(dst)) {
+		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst));
+	}
+
+	// integer -> boolean
+	if (is_type_integer(src) && is_type_boolean(dst)) {
+		return ssa_emit_comp(proc, Token_NotEq, value, v_zero);
+	}
+
+
+	// float -> float
+	if (is_type_float(src) && is_type_float(dst)) {
+		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
+		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
+		ssaConvKind kind = ssaConv_fptrunc;
+		if (dz >= sz) {
+			kind = ssaConv_fpext;
+		}
+		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
+	}
+
+	// float <-> integer
+	if (is_type_float(src) && is_type_integer(dst)) {
+		ssaConvKind kind = ssaConv_fptosi;
+		if (is_type_unsigned(dst)) {
+			kind = ssaConv_fptoui;
+		}
+		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
+	}
+	if (is_type_integer(src) && is_type_float(dst)) {
+		ssaConvKind kind = ssaConv_sitofp;
+		if (is_type_unsigned(src)) {
+			kind = ssaConv_uitofp;
+		}
+		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
+	}
+
+	// Pointer <-> int
+	if (is_type_pointer(src) && is_type_int_or_uint(dst)) {
+		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst));
+	}
+	if (is_type_int_or_uint(src) && is_type_pointer(dst)) {
+		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst));
+	}
+
+	if (is_type_union(dst)) {
+		for (isize i = 0; i < dst->Record.field_count; i++) {
+			Entity *f = dst->Record.fields[i];
+			if (are_types_identical(f->type, src_type)) {
+				ssa_emit_comment(proc, make_string("union - child to parent"));
+				gbAllocator allocator = proc->module->allocator;
+				ssaValue *parent = ssa_add_local_generated(proc, t);
+				ssaValue *tag = ssa_make_const_int(allocator, i);
+				ssa_emit_store(proc, ssa_emit_struct_ep(proc, parent, 1), tag);
+
+				ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
+
+				Type *tag_type = src_type;
+				Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
+				ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr);
+				ssa_emit_store(proc, underlying, value);
+
+				return ssa_emit_load(proc, parent);
+			}
+		}
+	}
+
+	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
+	// subtype polymorphism casting
+	{
+		Type *sb = base_type(type_deref(src));
+		b32 src_is_ptr = src != sb;
+		if (is_type_struct(sb)) {
+			String field_name = lookup_polymorphic_field(proc->module->info, t, src);
+			// gb_printf("field_name: %.*s\n", LIT(field_name));
+			if (field_name.len > 0) {
+				// NOTE(bill): It can be casted
+				Selection sel = lookup_field(proc->module->allocator, sb, field_name, false);
+				if (sel.entity != NULL) {
+					ssa_emit_comment(proc, make_string("cast - polymorphism"));
+					if (src_is_ptr) {
+						value = ssa_emit_load(proc, value);
+					}
+					return ssa_emit_deep_field_ev(proc, sb, value, sel);
+				}
+			}
+		}
+	}
+
+
+
+	// Pointer <-> Pointer
+	if (is_type_pointer(src) && is_type_pointer(dst)) {
+		return ssa_emit_bitcast(proc, value, dst);
+	}
+
+
+
+	// proc <-> proc
+	if (is_type_proc(src) && is_type_proc(dst)) {
+		return ssa_emit_bitcast(proc, value, dst);
+	}
+
+	// pointer -> proc
+	if (is_type_pointer(src) && is_type_proc(dst)) {
+		return ssa_emit_bitcast(proc, value, dst);
+	}
+	// proc -> pointer
+	if (is_type_proc(src) && is_type_pointer(dst)) {
+		return ssa_emit_bitcast(proc, value, dst);
+	}
+
+
+
+	// []byte/[]u8 <-> string
+	if (is_type_u8_slice(src) && is_type_string(dst)) {
+		ssaValue *elem = ssa_slice_elem(proc, value);
+		ssaValue *len  = ssa_slice_len(proc, value);
+		return ssa_emit_string(proc, elem, len);
+	}
+	if (is_type_string(src) && is_type_u8_slice(dst)) {
+		ssaValue *elem = ssa_string_elem(proc, value);
+		ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem));
+		ssa_emit_store(proc, elem_ptr, elem);
+
+		ssaValue *len  = ssa_string_len(proc, value);
+		ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len);
+		return ssa_emit_load(proc, slice);
+	}
+
+	if (is_type_vector(dst)) {
+		Type *dst_elem = dst->Vector.elem;
+		value = ssa_emit_conv(proc, value, dst_elem);
+		ssaValue *v = ssa_add_local_generated(proc, t);
+		v = ssa_emit_load(proc, v);
+		v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32));
+		// NOTE(bill): Broadcast lowest value to all values
+		isize index_count = dst->Vector.count;
+		i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
+		for (isize i = 0; i < index_count; i++) {
+			indices[i] = 0;
+		}
+
+		v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count));
+		return v;
+	}
+
+	if (is_type_any(dst)) {
+		ssaValue *result = ssa_add_local_generated(proc, t_any);
+
+		if (is_type_untyped_nil(src)) {
+			return ssa_emit_load(proc, result);
+		}
+
+		ssaValue *data = NULL;
+		if (value->kind == ssaValue_Instr &&
+		    value->Instr.kind == ssaInstr_Load) {
+			// NOTE(bill): Addressable value
+			data = value->Instr.Load.address;
+		} else {
+			// NOTE(bill): Non-addressable value
+			data = ssa_add_local_generated(proc, src_type);
+			ssa_emit_store(proc, data, value);
+		}
+		GB_ASSERT(is_type_pointer(ssa_type(data)));
+		GB_ASSERT(is_type_typed(src_type));
+		data = ssa_emit_conv(proc, data, t_rawptr);
+
+
+		ssaValue *ti = ssa_type_info(proc, src_type);
+
+		ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0);
+		ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1);
+		ssa_emit_store(proc, gep0, ti);
+		ssa_emit_store(proc, gep1, data);
+
+		return ssa_emit_load(proc, result);
+	}
+
+	if (is_type_untyped_nil(src) && type_has_nil(dst)) {
+		return ssa_make_value_nil(proc->module->allocator, t);
+	}
+
+
+	gb_printf_err("ssa_emit_conv: src -> dst\n");
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
+	gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
+
+
+	GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
+
+	return NULL;
+}
+
+
+ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
+	Type *src_type = ssa_type(value);
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+	Type *src = base_type(src_type);
+	Type *dst = base_type(t);
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+	i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
+	i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
+
+	if (sz == dz) {
+		return ssa_emit_bitcast(proc, value, dst);
+	}
+
+
+	GB_PANIC("Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
+
+	return NULL;
+}
+
+ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
+	GB_ASSERT(is_type_pointer(ssa_type(value)));
+	gbAllocator allocator = proc->module->allocator;
+
+	String field_name = check_down_cast_name(t, type_deref(ssa_type(value)));
+	GB_ASSERT(field_name.len > 0);
+	Selection sel = lookup_field(proc->module->allocator, t, field_name, false);
+	Type *t_u8_ptr = make_type_pointer(allocator, t_u8);
+	ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr);
+
+	i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel);
+	ssaValue *offset = ssa_make_const_int(allocator, -offset_);
+	ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset);
+	return ssa_emit_conv(proc, head, t);
+}
+
+ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) {
+	GB_ASSERT(tuple->kind == Type_Tuple);
+	gbAllocator a = proc->module->allocator;
+
+	Type *src_type = ssa_type(value);
+	b32 is_ptr = is_type_pointer(src_type);
+
+	ssaValue *v = ssa_add_local_generated(proc, tuple);
+
+	if (is_ptr) {
+		Type *src = base_type(type_deref(src_type));
+		Type *src_ptr = src_type;
+		GB_ASSERT(is_type_union(src));
+		Type *dst_ptr = tuple->Tuple.variables[0]->type;
+		Type *dst = type_deref(dst_ptr);
+
+		ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_ep(proc, value, 1));
+		ssaValue *dst_tag = NULL;
+		for (isize i = 1; i < src->Record.field_count; i++) {
+			Entity *f = src->Record.fields[i];
+			if (are_types_identical(f->type, dst)) {
+				dst_tag = ssa_make_const_int(a, i);
+				break;
+			}
+		}
+		GB_ASSERT(dst_tag != NULL);
+
+		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
+		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
+		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+		ssa_emit_if(proc, cond, ok_block, end_block);
+		proc->curr_block = ok_block;
+
+		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
+		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
+
+		ssaValue *data = ssa_emit_conv(proc, value, dst_ptr);
+		ssa_emit_store(proc, gep0, data);
+		ssa_emit_store(proc, gep1, v_true);
+
+		ssa_emit_jump(proc, end_block);
+		proc->curr_block = end_block;
+
+	} else {
+		Type *src = base_type(src_type);
+		GB_ASSERT(is_type_union(src));
+		Type *dst = tuple->Tuple.variables[0]->type;
+		Type *dst_ptr = make_type_pointer(a, dst);
+
+		ssaValue *tag = ssa_emit_struct_ev(proc, value, 1);
+		ssaValue *dst_tag = NULL;
+		for (isize i = 1; i < src->Record.field_count; i++) {
+			Entity *f = src->Record.fields[i];
+			if (are_types_identical(f->type, dst)) {
+				dst_tag = ssa_make_const_int(a, i);
+				break;
+			}
+		}
+		GB_ASSERT(dst_tag != NULL);
+
+		// HACK(bill): This is probably not very efficient
+		ssaValue *union_copy = ssa_add_local_generated(proc, src_type);
+		ssa_emit_store(proc, union_copy, value);
+
+		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
+		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
+		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+		ssa_emit_if(proc, cond, ok_block, end_block);
+		proc->curr_block = ok_block;
+
+		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
+		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
+
+		ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr));
+		ssa_emit_store(proc, gep0, data);
+		ssa_emit_store(proc, gep1, v_true);
+
+		ssa_emit_jump(proc, end_block);
+		proc->curr_block = end_block;
+
+	}
+	return ssa_emit_load(proc, v);
+}
+
+
+isize ssa_type_info_index(CheckerInfo *info, Type *type) {
+	type = default_type(type);
+
+	isize entry_index = -1;
+	HashKey key = hash_pointer(type);
+	auto *found_entry_index = map_get(&info->type_info_map, key);
+	if (found_entry_index) {
+		entry_index = *found_entry_index;
+	}
+	if (entry_index < 0) {
+		// NOTE(bill): Do manual search
+		// TODO(bill): This is O(n) and can be very slow
+		for_array(i, info->type_info_map.entries){
+			auto *e = &info->type_info_map.entries[i];
+			Type *prev_type = cast(Type *)e->key.ptr;
+			if (are_types_identical(prev_type, type)) {
+				entry_index = e->value;
+				// NOTE(bill): Add it to the search map
+				map_set(&info->type_info_map, key, entry_index);
+				break;
+			}
+		}
+	}
+
+	if (entry_index < 0) {
+		compiler_error("Type_Info for `%s` could not be found", type_to_string(type));
+	}
+	return entry_index;
+}
+
+ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) {
+	ssaValue **found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME)));
+	GB_ASSERT(found != NULL);
+	ssaValue *type_info_data = *found;
+	CheckerInfo *info = proc->module->info;
+
+	type = default_type(type);
+
+	i32 entry_index = ssa_type_info_index(info, type);
+
+	// gb_printf_err("%d %s\n", entry_index, type_to_string(type));
+
+	return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index));
+}
+
+
+
+ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
+	ast_node(be, BinaryExpr, expr);
+#if 0
+	ssaBlock *true_   = ssa_add_block(proc, NULL, "logical.cmp.true");
+	ssaBlock *false_  = ssa_add_block(proc, NULL, "logical.cmp.false");
+	ssaBlock *done  = ssa_add_block(proc, NULL, "logical.cmp.done");
+
+	ssaValue *result = ssa_add_local_generated(proc, t_bool);
+	ssa_build_cond(proc, expr, true_, false_);
+
+	proc->curr_block = true_;
+	ssa_emit_store(proc, result, v_true);
+	ssa_emit_jump(proc, done);
+
+	proc->curr_block = false_;
+	ssa_emit_store(proc, result, v_false);
+	ssa_emit_jump(proc, done);
+
+	proc->curr_block = done;
+
+	return ssa_emit_load(proc, result);
+#else
+	ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs");
+	ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done");
+
+	Type *type = type_of_expr(proc->module->info, expr);
+	type = default_type(type);
+
+	ssaValue *short_circuit = NULL;
+	if (be->op.kind == Token_CmpAnd) {
+		ssa_build_cond(proc, be->left, rhs, done);
+		short_circuit = v_false;
+	} else if (be->op.kind == Token_CmpOr) {
+		ssa_build_cond(proc, be->left, done, rhs);
+		short_circuit = v_true;
+	}
+
+	if (rhs->preds.count == 0) {
+		proc->curr_block = done;
+		return short_circuit;
+	}
+
+	if (done->preds.count == 0) {
+		proc->curr_block = rhs;
+		return ssa_build_expr(proc, be->right);
+	}
+
+	Array<ssaValue *> edges = {};
+	array_init(&edges, proc->module->allocator, done->preds.count+1);
+	for_array(i, done->preds) {
+		array_add(&edges, short_circuit);
+	}
+
+	proc->curr_block = rhs;
+	array_add(&edges, ssa_build_expr(proc, be->right));
+	ssa_emit_jump(proc, done);
+	proc->curr_block = done;
+
+	return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type));
+#endif
+}
+
+
+void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) {
+	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
+		return;
+	}
+
+	index = ssa_emit_conv(proc, index, t_int);
+	len = ssa_emit_conv(proc, len, t_int);
+
+	ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len));
+
+	// gbAllocator a = proc->module->allocator;
+	// ssaValue **args = gb_alloc_array(a, ssaValue *, 5);
+	// args[0] = ssa_emit_global_string(proc, token.pos.file);
+	// args[1] = ssa_make_const_int(a, token.pos.line);
+	// args[2] = ssa_make_const_int(a, token.pos.column);
+	// args[3] = ssa_emit_conv(proc, index, t_int);
+	// args[4] = ssa_emit_conv(proc, len, t_int);
+
+	// ssa_emit_global_call(proc, "__bounds_check_error", args, 5);
+}
+
+void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) {
+	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
+		return;
+	}
+
+
+	low  = ssa_emit_conv(proc, low,  t_int);
+	high = ssa_emit_conv(proc, high, t_int);
+	max  = ssa_emit_conv(proc, max,  t_int);
+
+	ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
+
+	// gbAllocator a = proc->module->allocator;
+	// ssaValue **args = gb_alloc_array(a, ssaValue *, 6);
+	// args[0] = ssa_emit_global_string(proc, token.pos.file);
+	// args[1] = ssa_make_const_int(a, token.pos.line);
+	// args[2] = ssa_make_const_int(a, token.pos.column);
+	// args[3] = ssa_emit_conv(proc, low, t_int);
+	// args[4] = ssa_emit_conv(proc, high, t_int);
+	// args[5] = ssa_emit_conv(proc, max, t_int);
+
+	// if (!is_substring) {
+	// 	ssa_emit_global_call(proc, "__slice_expr_error", args, 6);
+	// } else {
+	// 	ssa_emit_global_call(proc, "__substring_expr_error", args, 5);
+	// }
+}
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Build
+//
+////////////////////////////////////////////////////////////////
+
+
+void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
+	ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList);
+	tl->prev          = proc->target_list;
+	tl->break_        = break_;
+	tl->continue_     = continue_;
+	tl->fallthrough_  = fallthrough_;
+	proc->target_list = tl;
+}
+
+void ssa_pop_target_list(ssaProcedure *proc) {
+	proc->target_list = proc->target_list->prev;
+}
+
+
+void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) {
+	if (field->kind != Entity_TypeName) {
+		return;
+	}
+	String cn = field->token.string;
+
+	isize len = parent.len + 1 + cn.len;
+	String child = {NULL, len};
+	child.text = gb_alloc_array(m->allocator, u8, len);
+
+	isize i = 0;
+	gb_memmove(child.text+i, parent.text, parent.len);
+	i += parent.len;
+	child.text[i++] = '.';
+	gb_memmove(child.text+i, cn.text, cn.len);
+
+	map_set(&m->type_names, hash_pointer(field->type), child);
+	ssa_gen_global_type_name(m, field, child);
+}
+
+void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
+	ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type);
+	ssa_module_add_value(m, e, t);
+	map_set(&m->members, hash_string(name), t);
+
+	Type *bt = base_type(e->type);
+	if (bt->kind == Type_Record) {
+		auto *s = &bt->Record;
+		for (isize j = 0; j < s->other_field_count; j++) {
+			ssa_mangle_sub_type_name(m, s->other_fields[j], name);
+		}
+	}
+
+	if (is_type_union(bt)) {
+		auto *s = &bt->Record;
+		// NOTE(bill): Zeroth entry is null (for `match type` stmts)
+		for (isize j = 1; j < s->field_count; j++) {
+			ssa_mangle_sub_type_name(m, s->fields[j], name);
+		}
+	}
+}
+
+
+
+
+void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) {
+	ssaBlock *b = ssa_add_block(proc, NULL, "defer");
+	// NOTE(bill): The prev block may defer injection before it's terminator
+	ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block);
+	if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) {
+		ssa_emit_jump(proc, b);
+	}
+	proc->curr_block = b;
+	ssa_emit_comment(proc, make_string("defer"));
+	if (d.kind == ssaDefer_Node) {
+		ssa_build_stmt(proc, d.stmt);
+	} else if (d.kind == ssaDefer_Instr) {
+		// NOTE(bill): Need to make a new copy
+		ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue));
+		ssa_emit(proc, instr);
+	}
+}
+
+
+
+ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) {
+	ssaValue **value = map_get(&proc->module->members, hash_string(name));
+	GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name));
+	return *value;
+}
+
+ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) {
+	Entity *e = proc->module->info->implicit_values[id];
+	GB_ASSERT(e->kind == Entity_ImplicitValue);
+	Entity *backing = e->ImplicitValue.backing;
+	ssaValue **value = map_get(&proc->module->values, hash_pointer(backing));
+	GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string));
+	return *value;
+}
+
+
+
+ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
+	expr = unparen_expr(expr);
+	switch (expr->kind) {
+	case_ast_node(bl, BasicLit, expr);
+		GB_PANIC("Non-constant basic literal");
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr));
+		if (e->kind == Entity_Builtin) {
+			Token token = ast_node_token(expr);
+			GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n"
+			         "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name),
+			         LIT(token.pos.file), token.pos.line, token.pos.column);
+			return NULL;
+		} else if (e->kind == Entity_Nil) {
+			return ssa_make_value_nil(proc->module->allocator, tv->type);
+		} else if (e->kind == Entity_ImplicitValue) {
+			return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id));
+		}
+
+		auto *found = map_get(&proc->module->values, hash_pointer(e));
+		if (found) {
+			ssaValue *v = *found;
+			if (v->kind == ssaValue_Proc) {
+				return v;
+			}
+			// if (e->kind == Entity_Variable && e->Variable.param) {
+				// return v;
+			// }
+			return ssa_emit_load(proc, v);
+		}
+		return NULL;
+	case_end;
+
+	case_ast_node(re, RunExpr, expr);
+		// TODO(bill): Run Expression
+		return ssa_build_single_expr(proc, re->expr, tv);
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		TypeAndValue *tav = map_get(&proc->module->info->types, hash_pointer(expr));
+		GB_ASSERT(tav != NULL);
+		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		switch (ue->op.kind) {
+		case Token_Pointer:
+			return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer
+
+		case Token_Maybe:
+			return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr));
+
+		case Token_Add:
+			return ssa_build_expr(proc, ue->expr);
+
+		case Token_Sub: // NOTE(bill): -`x` == 0 - `x`
+			return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type);
+
+		case Token_Not:   // Boolean not
+		case Token_Xor: { // Bitwise not
+			// NOTE(bill): "not" `x` == `x` "xor" `-1`
+			ssaValue *left = ssa_build_expr(proc, ue->expr);
+			ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1));
+			return ssa_emit_arith(proc, ue->op.kind,
+			                      left, right,
+			                      tv->type);
+		} break;
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, expr);
+		switch (be->op.kind) {
+		case Token_Add:
+		case Token_Sub:
+		case Token_Mul:
+		case Token_Quo:
+		case Token_Mod:
+		case Token_And:
+		case Token_Or:
+		case Token_Xor:
+		case Token_AndNot:
+		case Token_Shl:
+		case Token_Shr:
+			return ssa_emit_arith(proc, be->op.kind,
+			                      ssa_build_expr(proc, be->left),
+			                      ssa_build_expr(proc, be->right),
+			                      tv->type);
+
+
+		case Token_CmpEq:
+		case Token_NotEq:
+		case Token_Lt:
+		case Token_LtEq:
+		case Token_Gt:
+		case Token_GtEq: {
+			ssaValue *left  = ssa_build_expr(proc, be->left);
+			ssaValue *right = ssa_build_expr(proc, be->right);
+
+			ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right);
+			return ssa_emit_conv(proc, cmp, default_type(tv->type));
+		} break;
+
+		case Token_CmpAnd:
+		case Token_CmpOr:
+			return ssa_emit_logical_binary_expr(proc, expr);
+
+		case Token_as:
+			ssa_emit_comment(proc, make_string("cast - as"));
+			return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type);
+
+		case Token_transmute:
+			ssa_emit_comment(proc, make_string("cast - transmute"));
+			return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type);
+
+		case Token_down_cast:
+			ssa_emit_comment(proc, make_string("cast - down_cast"));
+			return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type);
+
+		case Token_union_cast:
+			ssa_emit_comment(proc, make_string("cast - union_cast"));
+			return ssa_emit_union_cast(proc, ssa_build_expr(proc, be->left), tv->type);
+
+		default:
+			GB_PANIC("Invalid binary expression");
+			break;
+		}
+	case_end;
+
+	case_ast_node(pl, ProcLit, expr);
+		// NOTE(bill): Generate a new name
+		// parent$count
+		isize name_len = proc->name.len + 1 + 8 + 1;
+		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
+		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count);
+		String name = make_string(name_text, name_len-1);
+
+		Type *type = type_of_expr(proc->module->info, expr);
+		ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
+		                                           proc->module, NULL, type, pl->type, pl->body, name);
+
+		value->Proc.tags = pl->tags;
+
+		array_add(&proc->children, &value->Proc);
+		ssa_build_proc(value, proc);
+
+		return value;
+	case_end;
+
+
+	case_ast_node(cl, CompoundLit, expr);
+		ssa_emit_comment(proc, make_string("CompoundLit"));
+		Type *type = type_of_expr(proc->module->info, expr);
+		Type *bt = base_type(type);
+		ssaValue *v = ssa_add_local_generated(proc, type);
+
+		Type *et = NULL;
+		switch (bt->kind) {
+		case Type_Vector: et = bt->Vector.elem; break;
+		case Type_Array:  et = bt->Array.elem;  break;
+		case Type_Slice:  et = bt->Slice.elem;  break;
+		}
+
+		auto is_elem_const = [](ssaModule *m, AstNode *elem, Type *elem_type) -> b32 {
+			if (base_type(elem_type) == t_any) {
+				return false;
+			}
+			if (elem->kind == AstNode_FieldValue) {
+				elem = elem->FieldValue.value;
+			}
+			TypeAndValue *tav = type_and_value_of_expression(m->info, elem);
+			GB_ASSERT(tav != NULL);
+			return tav->value.kind != ExactValue_Invalid;
+		};
+
+		switch (bt->kind) {
+		default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+
+		case Type_Vector: {
+			ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
+			for_array(index, cl->elems) {
+				AstNode *elem = cl->elems[index];
+				if (is_elem_const(proc->module, elem, et)) {
+					continue;
+				}
+				ssaValue *field_elem = ssa_build_expr(proc, elem);
+				Type *t = ssa_type(field_elem);
+				GB_ASSERT(t->kind != Type_Tuple);
+				ssaValue *ev = ssa_emit_conv(proc, field_elem, et);
+				ssaValue *i = ssa_make_const_int(proc->module->allocator, index);
+				result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i));
+			}
+
+			if (cl->elems.count == 1 && bt->Vector.count > 1) {
+				isize index_count = bt->Vector.count;
+				i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
+				for (isize i = 0; i < index_count; i++) {
+					indices[i] = 0;
+				}
+				ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count));
+				ssa_emit_store(proc, v, sv);
+				return ssa_emit_load(proc, v);
+			}
+			return result;
+		} break;
+
+		case Type_Record: {
+			GB_ASSERT(is_type_struct(bt));
+			auto *st = &bt->Record;
+			if (cl->elems.count > 0) {
+				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
+				for_array(field_index, cl->elems) {
+					AstNode *elem = cl->elems[field_index];
+
+					ssaValue *field_expr = NULL;
+					Entity *field = NULL;
+					isize index = field_index;
+
+					if (elem->kind == AstNode_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+						Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false);
+						index = sel.index[0];
+						elem = fv->value;
+					} else {
+						TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem);
+						Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false);
+						index = sel.index[0];
+					}
+
+					field = st->fields[index];
+					if (is_elem_const(proc->module, elem, field->type)) {
+						continue;
+					}
+
+					field_expr = ssa_build_expr(proc, elem);
+
+					GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple);
+
+
+
+					Type *ft = field->type;
+					ssaValue *fv = ssa_emit_conv(proc, field_expr, ft);
+					ssaValue *gep = ssa_emit_struct_ep(proc, v, index);
+					ssa_emit_store(proc, gep, fv);
+				}
+			}
+		} break;
+		case Type_Array: {
+			if (cl->elems.count > 0) {
+				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
+				for_array(i, cl->elems) {
+					AstNode *elem = cl->elems[i];
+					if (is_elem_const(proc->module, elem, et)) {
+						continue;
+					}
+					ssaValue *field_expr = ssa_build_expr(proc, elem);
+					Type *t = ssa_type(field_expr);
+					GB_ASSERT(t->kind != Type_Tuple);
+					ssaValue *ev = ssa_emit_conv(proc, field_expr, et);
+					ssaValue *gep = ssa_emit_array_ep(proc, v, i);
+					ssa_emit_store(proc, gep, ev);
+				}
+			}
+		} break;
+		case Type_Slice: {
+			if (cl->elems.count > 0) {
+				Type *elem_type = bt->Slice.elem;
+				Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type);
+				Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type);
+				Type *t_int_ptr = make_type_pointer(proc->module->allocator, t_int);
+				ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
+				GB_ASSERT(slice->kind == ssaValue_ConstantSlice);
+
+				ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
+
+				for_array(i, cl->elems) {
+					AstNode *elem = cl->elems[i];
+					if (is_elem_const(proc->module, elem, et)) {
+						continue;
+					}
+
+					ssaValue *field_expr = ssa_build_expr(proc, elem);
+					Type *t = ssa_type(field_expr);
+					GB_ASSERT(t->kind != Type_Tuple);
+					ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type);
+					ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i));
+					ssa_emit_store(proc, offset, ev);
+				}
+
+				ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
+				ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
+				ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1);
+
+				ssa_emit_store(proc, gep0, data);
+				ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
+				ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
+			}
+		} break;
+		}
+
+		return ssa_emit_load(proc, v);
+	case_end;
+
+
+	case_ast_node(ce, CallExpr, expr);
+		AstNode *p = unparen_expr(ce->proc);
+		if (p->kind == AstNode_Ident) {
+			Entity **found = map_get(&proc->module->info->uses, hash_pointer(p));
+			if (found && (*found)->kind == Entity_Builtin) {
+				Entity *e = *found;
+				switch (e->Builtin.id) {
+				case BuiltinProc_type_info: {
+					Type *t = default_type(type_of_expr(proc->module->info, ce->args[0]));
+					return ssa_type_info(proc, t);
+				} break;
+				case BuiltinProc_type_info_of_val: {
+					Type *t = default_type(type_of_expr(proc->module->info, ce->args[0]));
+					return ssa_type_info(proc, t);
+				} break;
+
+				case BuiltinProc_new: {
+					ssa_emit_comment(proc, make_string("new"));
+					// new :: proc(Type) -> ^Type
+					gbAllocator allocator = proc->module->allocator;
+
+					Type *type = type_of_expr(proc->module->info, ce->args[0]);
+					Type *ptr_type = make_type_pointer(allocator, type);
+
+					i64 s = type_size_of(proc->module->sizes, allocator, type);
+					i64 a = type_align_of(proc->module->sizes, allocator, type);
+
+					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
+					args[0] = ssa_make_const_int(allocator, s);
+					args[1] = ssa_make_const_int(allocator, a);
+					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
+					ssaValue *v = ssa_emit_conv(proc, call, ptr_type);
+					return v;
+				} break;
+
+				case BuiltinProc_new_slice: {
+					ssa_emit_comment(proc, make_string("new_slice"));
+					// new_slice :: proc(Type, len: int[, cap: int]) -> ^Type
+					gbAllocator allocator = proc->module->allocator;
+
+					Type *type = type_of_expr(proc->module->info, ce->args[0]);
+					Type *ptr_type = make_type_pointer(allocator, type);
+					Type *slice_type = make_type_slice(allocator, type);
+
+					i64 s = type_size_of(proc->module->sizes, allocator, type);
+					i64 a = type_align_of(proc->module->sizes, allocator, type);
+
+					ssaValue *elem_size  = ssa_make_const_int(allocator, s);
+					ssaValue *elem_align = ssa_make_const_int(allocator, a);
+
+					ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[1]), t_int);
+					ssaValue *cap = len;
+					if (ce->args.count == 3) {
+						cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[2]), t_int);
+					}
+
+					ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args[1]), v_zero, len, cap, false);
+
+					ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int);
+
+					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
+					args[0] = slice_size;
+					args[1] = elem_align;
+					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
+
+					ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type);
+					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+
+					ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
+					ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
+					ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
+					ssa_emit_store(proc, gep0, ptr);
+					ssa_emit_store(proc, gep1, len);
+					ssa_emit_store(proc, gep2, cap);
+					return ssa_emit_load(proc, slice);
+				} break;
+
+				case BuiltinProc_assert: {
+					ssa_emit_comment(proc, make_string("assert"));
+					ssaValue *cond = ssa_build_expr(proc, ce->args[0]);
+					GB_ASSERT(is_type_boolean(ssa_type(cond)));
+
+					cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false);
+					ssaBlock *err  = ssa_add_block(proc, NULL, "builtin.assert.err");
+					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done");
+
+					ssa_emit_if(proc, cond, err, done);
+					proc->curr_block = err;
+
+					// TODO(bill): Cleanup allocations here
+					Token token = ast_node_token(ce->args[0]);
+					TokenPos pos = token.pos;
+					gbString expr = expr_to_string(ce->args[0]);
+					defer (gb_string_free(expr));
+					isize expr_len = gb_string_length(expr);
+					String expr_str = {};
+					expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1);
+					expr_str.len = expr_len;
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
+					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
+					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
+					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
+					args[3] = ssa_make_const_string(proc->module->allocator, expr_str);
+					ssa_emit_global_call(proc, "__assert", args, 4);
+
+					ssa_emit_jump(proc, done);
+					proc->curr_block = done;
+
+					return NULL;
+				} break;
+
+				case BuiltinProc_panic: {
+					ssa_emit_comment(proc, make_string("panic"));
+					ssaValue *msg = ssa_build_expr(proc, ce->args[0]);
+					GB_ASSERT(is_type_string(ssa_type(msg)));
+
+					Token token = ast_node_token(ce->args[0]);
+					TokenPos pos = token.pos;
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
+					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
+					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
+					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
+					args[3] = msg;
+					ssa_emit_global_call(proc, "__assert", args, 4);
+
+					return NULL;
+				} break;
+
+
+				case BuiltinProc_copy: {
+					ssa_emit_comment(proc, make_string("copy"));
+					// copy :: proc(dst, src: []Type) -> int
+					AstNode *dst_node = ce->args[0];
+					AstNode *src_node = ce->args[1];
+					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
+					ssaValue *src_slice = ssa_build_expr(proc, src_node);
+					Type *slice_type = base_type(ssa_type(dst_slice));
+					GB_ASSERT(slice_type->kind == Type_Slice);
+					Type *elem_type = slice_type->Slice.elem;
+					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
+
+
+					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr);
+					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr);
+
+					ssaValue *len_dst = ssa_slice_len(proc, dst_slice);
+					ssaValue *len_src = ssa_slice_len(proc, src_slice);
+
+					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src);
+					ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src);
+
+					ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem);
+					ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int);
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
+					args[0] = dst;
+					args[1] = src;
+					args[2] = byte_count;
+
+					ssa_emit_global_call(proc, "__mem_copy", args, 3);
+
+					return len;
+				} break;
+				case BuiltinProc_append: {
+					ssa_emit_comment(proc, make_string("append"));
+					// append :: proc(s: ^[]Type, item: Type) -> bool
+					AstNode *sptr_node = ce->args[0];
+					AstNode *item_node = ce->args[1];
+					ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node);
+					ssaValue *slice = ssa_emit_load(proc, slice_ptr);
+
+					ssaValue *elem = ssa_slice_elem(proc, slice);
+					ssaValue *len  = ssa_slice_len(proc,  slice);
+					ssaValue *cap  = ssa_slice_cap(proc,  slice);
+
+					Type *elem_type = type_deref(ssa_type(elem));
+
+					ssaValue *item_value = ssa_build_expr(proc, item_node);
+					item_value = ssa_emit_conv(proc, item_value, elem_type);
+
+					ssaValue *item = ssa_add_local_generated(proc, elem_type);
+					ssa_emit_store(proc, item, item_value);
+
+
+					// NOTE(bill): Check if can append is possible
+					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap);
+					ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able");
+					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done");
+
+					ssa_emit_if(proc, cond, able, done);
+					proc->curr_block = able;
+
+					// Add new slice item
+					i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
+					ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size);
+
+					ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len);
+					offset = ssa_emit_conv(proc, offset, t_rawptr);
+
+					item = ssa_emit_ptr_offset(proc, item, v_zero);
+					item = ssa_emit_conv(proc, item, t_rawptr);
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
+					args[0] = offset;
+					args[1] = item;
+					args[2] = byte_count;
+
+					ssa_emit_global_call(proc, "__mem_copy", args, 3);
+
+					// Increment slice length
+					ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int);
+					ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1);
+					ssa_emit_store(proc, gep, new_len);
+
+					ssa_emit_jump(proc, done);
+					proc->curr_block = done;
+
+					return ssa_emit_conv(proc, cond, t_bool);
+				} break;
+
+				case BuiltinProc_swizzle: {
+					ssa_emit_comment(proc, make_string("swizzle"));
+					ssaValue *vector = ssa_build_expr(proc, ce->args[0]);
+					isize index_count = ce->args.count-1;
+					if (index_count == 0) {
+						return vector;
+					}
+
+					i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
+					isize index = 0;
+					for_array(i, ce->args) {
+						if (i == 0) continue;
+						TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args[i]);
+						GB_ASSERT(is_type_integer(tv->type));
+						GB_ASSERT(tv->value.kind == ExactValue_Integer);
+						indices[index++] = cast(i32)tv->value.value_integer;
+					}
+
+					return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count));
+
+				} break;
+
+#if 0
+				case BuiltinProc_ptr_offset: {
+					ssa_emit_comment(proc, make_string("ptr_offset"));
+					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *offset = ssa_build_expr(proc, ce->args[1]);
+					return ssa_emit_ptr_offset(proc, ptr, offset);
+				} break;
+
+				case BuiltinProc_ptr_sub: {
+					ssa_emit_comment(proc, make_string("ptr_sub"));
+					ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]);
+					Type *ptr_type = base_type(ssa_type(ptr_a));
+					GB_ASSERT(ptr_type->kind == Type_Pointer);
+					isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
+
+					ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int);
+					if (elem_size > 1) {
+						ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size);
+						v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int);
+					}
+
+					return v;
+				} break;
+#endif
+
+				case BuiltinProc_slice_ptr: {
+					ssa_emit_comment(proc, make_string("slice_ptr"));
+					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *len = ssa_build_expr(proc, ce->args[1]);
+					ssaValue *cap = len;
+
+					len = ssa_emit_conv(proc, len, t_int);
+
+					if (ce->args.count == 3) {
+						cap = ssa_build_expr(proc, ce->args[2]);
+						cap = ssa_emit_conv(proc, cap, t_int);
+					}
+
+
+					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr)));
+					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap);
+					return ssa_emit_load(proc, slice);
+				} break;
+
+				case BuiltinProc_min: {
+					ssa_emit_comment(proc, make_string("min"));
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
+					Type *t = base_type(ssa_type(x));
+					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y);
+					return ssa_emit_select(proc, cond, x, y);
+				} break;
+
+				case BuiltinProc_max: {
+					ssa_emit_comment(proc, make_string("max"));
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
+					Type *t = base_type(ssa_type(x));
+					ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y);
+					return ssa_emit_select(proc, cond, x, y);
+				} break;
+
+				case BuiltinProc_abs: {
+					ssa_emit_comment(proc, make_string("abs"));
+
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					Type *t = ssa_type(x);
+
+					ssaValue *neg_x = ssa_emit_arith(proc, Token_Sub, v_zero, x, t);
+					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, v_zero);
+					return ssa_emit_select(proc, cond, neg_x, x);
+				} break;
+
+				case BuiltinProc_enum_to_string: {
+					ssa_emit_comment(proc, make_string("enum_to_string"));
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					Type *t = ssa_type(x);
+					ssaValue *ti = ssa_type_info(proc, t);
+
+
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2);
+					args[0] = ti;
+					args[1] = ssa_emit_conv(proc, x, t_i64);
+					return ssa_emit_global_call(proc, "__enum_to_string", args, 2);
+				} break;
+				}
+			}
+		}
+
+
+		// NOTE(bill): Regular call
+		ssaValue *value = ssa_build_expr(proc, ce->proc);
+		Type *proc_type_ = base_type(ssa_type(value));
+		GB_ASSERT(proc_type_->kind == Type_Proc);
+		auto *type = &proc_type_->Proc;
+
+		isize arg_index = 0;
+
+		isize arg_count = 0;
+		for_array(i, ce->args) {
+			AstNode *a = ce->args[i];
+			Type *at = base_type(type_of_expr(proc->module->info, a));
+			if (at->kind == Type_Tuple) {
+				arg_count += at->Tuple.variable_count;
+			} else {
+				arg_count++;
+			}
+		}
+		ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
+		b32 variadic = proc_type_->Proc.variadic;
+		b32 vari_expand = ce->ellipsis.pos.line != 0;
+
+		for_array(i, ce->args) {
+			ssaValue *a = ssa_build_expr(proc, ce->args[i]);
+			Type *at = ssa_type(a);
+			if (at->kind == Type_Tuple) {
+				for (isize i = 0; i < at->Tuple.variable_count; i++) {
+					Entity *e = at->Tuple.variables[i];
+					ssaValue *v = ssa_emit_struct_ev(proc, a, i);
+					args[arg_index++] = v;
+				}
+			} else {
+				args[arg_index++] = a;
+			}
+		}
+
+		auto *pt = &type->params->Tuple;
+
+		if (variadic) {
+			isize i = 0;
+			for (; i < type->param_count-1; i++) {
+				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type);
+			}
+			if (!vari_expand) {
+				Type *variadic_type = pt->variables[i]->type;
+				GB_ASSERT(is_type_slice(variadic_type));
+				variadic_type = base_type(variadic_type)->Slice.elem;
+				for (; i < arg_count; i++) {
+					args[i] = ssa_emit_conv(proc, args[i], variadic_type);
+				}
+			}
+		} else {
+			for (isize i = 0; i < arg_count; i++) {
+				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type);
+			}
+		}
+
+		if (variadic && !vari_expand) {
+			ssa_emit_comment(proc, make_string("variadic call argument generation"));
+			gbAllocator allocator = proc->module->allocator;
+			Type *slice_type = pt->variables[type->param_count-1]->type;
+			Type *elem_type  = base_type(slice_type)->Slice.elem;
+			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+			isize slice_len = arg_count+1 - type->param_count;
+
+			if (slice_len > 0) {
+				ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len));
+
+				for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) {
+					ssaValue *addr = ssa_emit_array_ep(proc, base_array, j);
+					ssa_emit_store(proc, addr, args[i]);
+				}
+
+				ssaValue *base_elem  = ssa_emit_array_ep(proc, base_array, 0);
+				ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice,      0);
+				ssa_emit_store(proc, slice_elem, base_elem);
+				ssaValue *len = ssa_make_const_int(allocator, slice_len);
+				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
+				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len);
+			}
+
+			if (args[0]->kind == ssaValue_Constant) {
+				auto *c = &args[0]->Constant;
+				gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind);
+			}
+
+			arg_count = type->param_count;
+			args[arg_count-1] = ssa_emit_load(proc, slice);
+		}
+
+		return ssa_emit_call(proc, value, args, arg_count);
+	case_end;
+
+	case_ast_node(de, DemaybeExpr, expr);
+		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
+	case_end;
+	}
+
+	GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind]));
+	return NULL;
+}
+
+
+ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
+	expr = unparen_expr(expr);
+
+	TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr));
+	GB_ASSERT_NOT_NULL(tv);
+
+	if (tv->value.kind != ExactValue_Invalid) {
+		return ssa_add_module_constant(proc->module, tv->type, tv->value);
+	}
+
+	ssaValue *value = NULL;
+	if (tv->mode == Addressing_Variable) {
+		value = ssa_addr_load(proc, ssa_build_addr(proc, expr));
+	} else {
+		value = ssa_build_single_expr(proc, expr, tv);
+	}
+
+	return value;
+}
+
+ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) {
+	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
+	String name = e->token.string;
+	Entity *parent = e->using_parent;
+	Selection sel = lookup_field(proc->module->allocator, parent->type, name, false);
+	GB_ASSERT(sel.entity != NULL);
+	ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent));
+	ssaValue *v = NULL;
+	if (pv != NULL) {
+		v = *pv;
+	} else {
+		v = ssa_build_addr(proc, e->using_expr).addr;
+	}
+	GB_ASSERT(v != NULL);
+	ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel);
+	map_set(&proc->module->values, hash_pointer(e), var);
+	return var;
+}
+
+ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
+	switch (expr->kind) {
+	case_ast_node(i, Ident, expr);
+		if (ssa_is_blank_ident(expr)) {
+			ssaAddr val = {};
+			return val;
+		}
+
+		Entity *e = entity_of_ident(proc->module->info, expr);
+		TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr));
+
+		GB_ASSERT(e->kind != Entity_Constant);
+
+		ssaValue *v = NULL;
+		ssaValue **found = map_get(&proc->module->values, hash_pointer(e));
+		if (found) {
+			v = *found;
+		} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
+			v = ssa_add_using_variable(proc, e);
+		} else if (e->kind == Entity_ImplicitValue) {
+			// TODO(bill): Should a copy be made?
+			v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id);
+		}
+
+		if (v == NULL) {
+			GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind]));
+		}
+
+		return ssa_make_addr(v, expr);
+	case_end;
+
+	case_ast_node(pe, ParenExpr, expr);
+		return ssa_build_addr(proc, unparen_expr(expr));
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		ssa_emit_comment(proc, make_string("SelectorExpr"));
+		String selector = unparen_expr(se->selector)->Ident.string;
+		Type *type = base_type(type_of_expr(proc->module->info, se->expr));
+
+		if (type == t_invalid) {
+			// NOTE(bill): Imports
+			Entity *imp = entity_of_ident(proc->module->info, se->expr);
+			if (imp != NULL) {
+				GB_ASSERT(imp->kind == Entity_ImportName);
+			}
+			return ssa_build_addr(proc, unparen_expr(se->selector));
+		} else {
+			Selection sel = lookup_field(proc->module->allocator, type, selector, false);
+			GB_ASSERT(sel.entity != NULL);
+
+			ssaValue *a = ssa_build_addr(proc, se->expr).addr;
+			a = ssa_emit_deep_field_gep(proc, type, a, sel);
+			return ssa_make_addr(a, expr);
+		}
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		switch (ue->op.kind) {
+		case Token_Pointer: {
+			return ssa_build_addr(proc, ue->expr);
+		}
+		default:
+			GB_PANIC("Invalid unary expression for ssa_build_addr");
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, expr);
+		switch (be->op.kind) {
+		case Token_as: {
+			ssa_emit_comment(proc, make_string("Cast - as"));
+			// NOTE(bill): Needed for dereference of pointer conversion
+			Type *type = type_of_expr(proc->module->info, expr);
+			ssaValue *v = ssa_add_local_generated(proc, type);
+			ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type));
+			return ssa_make_addr(v, expr);
+		}
+		case Token_transmute: {
+			ssa_emit_comment(proc, make_string("Cast - transmute"));
+			// NOTE(bill): Needed for dereference of pointer conversion
+			Type *type = type_of_expr(proc->module->info, expr);
+			ssaValue *v = ssa_add_local_generated(proc, type);
+			ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type));
+			return ssa_make_addr(v, expr);
+		}
+		default:
+			GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string));
+			break;
+		}
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		ssa_emit_comment(proc, make_string("IndexExpr"));
+		Type *t = base_type(type_of_expr(proc->module->info, ie->expr));
+		gbAllocator a = proc->module->allocator;
+
+
+		b32 deref = is_type_pointer(t);
+		t = type_deref(t);
+
+		ssaValue *using_addr = NULL;
+		if (!is_type_indexable(t)) {
+			// Using index expression
+			Entity *using_field = find_using_index_expr(t);
+			if (using_field != NULL) {
+				Selection sel = lookup_field(a, t, using_field->token.string, false);
+				ssaValue *e = ssa_build_addr(proc, ie->expr).addr;
+				using_addr = ssa_emit_deep_field_gep(proc, t, e, sel);
+
+				t = using_field->type;
+			}
+		}
+
+
+		switch (t->kind) {
+		case Type_Vector: {
+			ssaValue *vector = NULL;
+			if (using_addr != NULL) {
+				vector = using_addr;
+			} else {
+				vector = ssa_build_addr(proc, ie->expr).addr;
+				if (deref) {
+					vector = ssa_emit_load(proc, vector);
+				}
+			}
+			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
+			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
+			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
+			return ssa_make_addr_vector(vector, index, expr);
+		} break;
+
+		case Type_Array: {
+			ssaValue *array = NULL;
+			if (using_addr != NULL) {
+				array = using_addr;
+			} else {
+				array = ssa_build_addr(proc, ie->expr).addr;
+				if (deref) {
+					array = ssa_emit_load(proc, array);
+				}
+			}
+			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
+			ssaValue *elem = ssa_emit_array_ep(proc, array, index);
+			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
+			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
+			return ssa_make_addr(elem, expr);
+		} break;
+
+		case Type_Slice: {
+			ssaValue *slice = NULL;
+			if (using_addr != NULL) {
+				slice = ssa_emit_load(proc, using_addr);
+			} else {
+				slice = ssa_build_expr(proc, ie->expr);
+				if (deref) {
+					slice = ssa_emit_load(proc, slice);
+				}
+			}
+			ssaValue *elem = ssa_slice_elem(proc, slice);
+			ssaValue *len = ssa_slice_len(proc, slice);
+			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
+			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
+			ssaValue *v = ssa_emit_ptr_offset(proc, elem, index);
+			return ssa_make_addr(v, expr);
+
+		} break;
+
+		case Type_Basic: { // Basic_string
+			TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(ie->expr));
+			ssaValue *str;
+			ssaValue *elem;
+			ssaValue *len;
+			ssaValue *index;
+
+			if (using_addr != NULL) {
+				str = ssa_emit_load(proc, using_addr);
+			} else {
+				str = ssa_build_expr(proc, ie->expr);
+				if (deref) {
+					str = ssa_emit_load(proc, str);
+				}
+			}
+			elem = ssa_string_elem(proc, str);
+			len = ssa_string_len(proc, str);
+
+			index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
+			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
+
+			return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr);
+		} break;
+		}
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		ssa_emit_comment(proc, make_string("SliceExpr"));
+		gbAllocator a = proc->module->allocator;
+		ssaValue *low  = v_zero;
+		ssaValue *high = NULL;
+		ssaValue *max  = NULL;
+
+		if (se->low  != NULL)    low  = ssa_build_expr(proc, se->low);
+		if (se->high != NULL)    high = ssa_build_expr(proc, se->high);
+		if (se->triple_indexed)  max  = ssa_build_expr(proc, se->max);
+		ssaValue *addr = ssa_build_addr(proc, se->expr).addr;
+		ssaValue *base = ssa_emit_load(proc, addr);
+		Type *type = base_type(ssa_type(base));
+
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			addr = base;
+			base = ssa_emit_load(proc, base);
+		}
+
+		// TODO(bill): Cleanup like mad!
+
+		switch (type->kind) {
+		case Type_Slice: {
+			Type *slice_type = type;
+
+			if (high == NULL) high = ssa_slice_len(proc, base);
+			if (max == NULL)  max  = ssa_slice_cap(proc, base);
+			GB_ASSERT(max != NULL);
+
+			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
+
+			ssaValue *elem = ssa_slice_elem(proc, base);
+			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
+			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
+			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+
+			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
+			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
+			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
+			ssa_emit_store(proc, gep0, elem);
+			ssa_emit_store(proc, gep1, len);
+			ssa_emit_store(proc, gep2, cap);
+
+			return ssa_make_addr(slice, expr);
+		}
+
+		case Type_Array: {
+			Type *slice_type = make_type_slice(a, type->Array.elem);
+
+			if (high == NULL) high = ssa_array_len(proc, base);
+			if (max == NULL)  max  = ssa_array_cap(proc, base);
+			GB_ASSERT(max != NULL);
+
+			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
+
+			ssaValue *elem = ssa_array_elem(proc, addr);
+			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
+			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
+			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+
+			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
+			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
+			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
+			ssa_emit_store(proc, gep0, elem);
+			ssa_emit_store(proc, gep1, len);
+			ssa_emit_store(proc, gep2, cap);
+
+			return ssa_make_addr(slice, expr);
+		}
+
+		case Type_Basic: {
+			GB_ASSERT(type == t_string);
+			if (high == NULL) {
+				high = ssa_string_len(proc, base);
+			}
+
+			ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true);
+
+			ssaValue *elem, *len;
+			len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
+
+			elem = ssa_string_elem(proc, base);
+			elem = ssa_emit_ptr_offset(proc, elem, low);
+
+			ssaValue *str = ssa_add_local_generated(proc, t_string);
+			ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0);
+			ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1);
+			ssa_emit_store(proc, gep0, elem);
+			ssa_emit_store(proc, gep1, len);
+
+			return ssa_make_addr(str, expr);
+		} break;
+		}
+
+		GB_PANIC("Unknown slicable type");
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		// TODO(bill): Is a ptr copy needed?
+		ssaValue *addr = ssa_build_expr(proc, de->expr);
+		addr = ssa_emit_ptr_offset(proc, addr, v_zero);
+		return ssa_make_addr(addr, expr);
+	case_end;
+
+	case_ast_node(de, DemaybeExpr, expr);
+		ssa_emit_comment(proc, make_string("DemaybeExpr"));
+		ssaValue *maybe = ssa_build_expr(proc, de->expr);
+		Type *t = default_type(type_of_expr(proc->module->info, expr));
+		GB_ASSERT(is_type_tuple(t));
+
+		ssaValue *result = ssa_add_local_generated(proc, t);
+		ssa_emit_store(proc, result, maybe);
+
+		return ssa_make_addr(result, expr);
+	case_end;
+
+	case_ast_node(ce, CallExpr, expr);
+		ssaValue *e = ssa_build_expr(proc, expr);
+		ssaValue *v = ssa_add_local_generated(proc, ssa_type(e));
+		ssa_emit_store(proc, v, e);
+		return ssa_make_addr(v, expr);
+	case_end;
+	}
+
+	TokenPos token_pos = ast_node_token(expr).pos;
+	GB_PANIC("Unexpected address expression\n"
+	         "\tAstNode: %.*s @ "
+	         "%.*s(%td:%td)\n",
+	         LIT(ast_node_strings[expr->kind]),
+	         LIT(token_pos.file), token_pos.line, token_pos.column);
+
+
+	return ssa_make_addr(NULL, NULL);
+}
+
+void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) {
+	ssaValue *old_value = ssa_addr_load(proc, lhs);
+	Type *type = ssa_type(old_value);
+
+	ssaValue *change = value;
+	if (is_type_pointer(type) && is_type_integer(ssa_type(value))) {
+		change = ssa_emit_conv(proc, value, default_type(ssa_type(value)));
+	} else {
+		change = ssa_emit_conv(proc, value, type);
+	}
+	ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type);
+	ssa_addr_store(proc, lhs, new_value);
+}
+
+void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) {
+	switch (cond->kind) {
+	case_ast_node(pe, ParenExpr, cond);
+		ssa_build_cond(proc, pe->expr, true_block, false_block);
+		return;
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, cond);
+		if (ue->op.kind == Token_Not) {
+			ssa_build_cond(proc, ue->expr, false_block, true_block);
+			return;
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, cond);
+		if (be->op.kind == Token_CmpAnd) {
+			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and");
+			ssa_build_cond(proc, be->left, block, false_block);
+			proc->curr_block = block;
+			ssa_build_cond(proc, be->right, true_block, false_block);
+			return;
+		} else if (be->op.kind == Token_CmpOr) {
+			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or");
+			ssa_build_cond(proc, be->left, true_block, block);
+			proc->curr_block = block;
+			ssa_build_cond(proc, be->right, true_block, false_block);
+			return;
+		}
+	case_end;
+	}
+
+	ssaValue *expr = ssa_build_expr(proc, cond);
+	expr = ssa_emit_conv(proc, expr, t_bool);
+	ssa_emit_if(proc, expr, true_block, false_block);
+}
+
+
+
+
+void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) {
+	for_array(i, stmts) {
+		ssa_build_stmt(proc, stmts[i]);
+	}
+}
+
+void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
+	u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
+	defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
+
+	if (node->stmt_state_flags != 0) {
+		u32 in = node->stmt_state_flags;
+		u32 out = proc->module->stmt_state_flags;
+		defer (proc->module->stmt_state_flags = out);
+
+		if (in & StmtStateFlag_bounds_check) {
+			out |= StmtStateFlag_bounds_check;
+			out &= ~StmtStateFlag_no_bounds_check;
+		} else if (in & StmtStateFlag_no_bounds_check) {
+			out |= StmtStateFlag_no_bounds_check;
+			out &= ~StmtStateFlag_bounds_check;
+		}
+	}
+
+
+	switch (node->kind) {
+	case_ast_node(bs, EmptyStmt, node);
+	case_end;
+
+	case_ast_node(us, UsingStmt, node);
+		AstNode *decl = unparen_expr(us->node);
+		if (decl->kind == AstNode_VarDecl) {
+			ssa_build_stmt(proc, decl);
+		}
+	case_end;
+
+	case_ast_node(vd, VarDecl, node);
+		ssaModule *m = proc->module;
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+
+		if (vd->values.count == 0) { // declared and zero-initialized
+			for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
+				if (!ssa_is_blank_ident(name)) {
+					ssa_add_local_for_identifier(proc, name, true);
+				}
+			}
+		} else { // Tuple(s)
+			Array<ssaAddr>  lvals;
+			Array<ssaValue *> inits;
+			array_init(&lvals, m->tmp_allocator, vd->names.count);
+			array_init(&inits, m->tmp_allocator, vd->names.count);
+
+			for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
+				ssaAddr lval = ssa_make_addr(NULL, NULL);
+				if (!ssa_is_blank_ident(name)) {
+					ssa_add_local_for_identifier(proc, name, false);
+					lval = ssa_build_addr(proc, name);
+				}
+
+				array_add(&lvals, lval);
+			}
+
+			for_array(i, vd->values) {
+				ssaValue *init = ssa_build_expr(proc, vd->values[i]);
+				Type *t = ssa_type(init);
+				if (t->kind == Type_Tuple) {
+					for (isize i = 0; i < t->Tuple.variable_count; i++) {
+						Entity *e = t->Tuple.variables[i];
+						ssaValue *v = ssa_emit_struct_ev(proc, init, i);
+						array_add(&inits, v);
+					}
+				} else {
+					array_add(&inits, init);
+				}
+			}
+
+
+			for_array(i, inits) {
+				ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
+				ssa_addr_store(proc, lvals[i], v);
+			}
+		}
+	case_end;
+
+	case_ast_node(pd, ProcDecl, node);
+		if (pd->body != NULL) {
+			auto *info = proc->module->info;
+
+			Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
+			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
+			Entity *e = *found;
+
+
+			if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) {
+				// NOTE(bill): Nothing depends upon it so doesn't need to be built
+				break;
+			}
+
+			// NOTE(bill): Generate a new name
+			// parent.name-guid
+			String original_name = pd->name->Ident.string;
+			String pd_name = original_name;
+			if (pd->link_name.len > 0) {
+				pd_name = pd->link_name;
+			}
+
+			isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1;
+			u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
+			i32 guid = cast(i32)proc->children.count;
+			name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
+			String name = make_string(name_text, name_len-1);
+
+
+			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
+			                                           proc->module, e, e->type, pd->type, pd->body, name);
+
+			value->Proc.tags = pd->tags;
+			value->Proc.parent = proc;
+
+			ssa_module_add_value(proc->module, e, value);
+			array_add(&proc->children, &value->Proc);
+			array_add(&proc->module->procs, value);
+		} else {
+			auto *info = proc->module->info;
+
+			Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
+			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
+			Entity *e = *found;
+
+			// FFI - Foreign function interace
+			String original_name = pd->name->Ident.string;
+			String name = original_name;
+			if (pd->foreign_name.len > 0) {
+				name = pd->foreign_name;
+			}
+
+			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
+			                                           proc->module, e, e->type, pd->type, pd->body, name);
+
+			value->Proc.tags = pd->tags;
+
+			ssa_module_add_value(proc->module, e, value);
+			ssa_build_proc(value, proc);
+
+			if (value->Proc.tags & ProcTag_foreign) {
+				HashKey key = hash_string(name);
+				auto *prev_value = map_get(&proc->module->members, key);
+				if (prev_value == NULL) {
+					// NOTE(bill): Don't do mutliple declarations in the IR
+					map_set(&proc->module->members, key, value);
+				}
+			} else {
+				array_add(&proc->children, &value->Proc);
+			}
+		}
+	case_end;
+
+	case_ast_node(td, TypeDecl, node);
+
+		// NOTE(bill): Generate a new name
+		// parent_proc.name-guid
+		String td_name = td->name->Ident.string;
+		isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1;
+		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
+		i32 guid = cast(i32)proc->module->members.entries.count;
+		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid);
+		String name = make_string(name_text, name_len-1);
+
+		Entity **found = map_get(&proc->module->info->definitions, hash_pointer(td->name));
+		GB_ASSERT(found != NULL);
+		Entity *e = *found;
+		ssaValue *value = ssa_make_value_type_name(proc->module->allocator,
+		                                           name, e->type);
+		map_set(&proc->module->type_names, hash_pointer(e->type), name);
+		ssa_gen_global_type_name(proc->module, e, name);
+	case_end;
+
+	case_ast_node(ids, IncDecStmt, node);
+		ssa_emit_comment(proc, make_string("IncDecStmt"));
+		TokenKind op = ids->op.kind;
+		if (op == Token_Increment) {
+			op = Token_Add;
+		} else if (op == Token_Decrement) {
+			op = Token_Sub;
+		}
+		ssaAddr lval = ssa_build_addr(proc, ids->expr);
+		ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval));
+		ssa_build_assign_op(proc, lval, one, op);
+
+	case_end;
+
+	case_ast_node(as, AssignStmt, node);
+		ssa_emit_comment(proc, make_string("AssignStmt"));
+
+		ssaModule *m = proc->module;
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+
+		switch (as->op.kind) {
+		case Token_Eq: {
+			Array<ssaAddr> lvals;
+			array_init(&lvals, m->tmp_allocator);
+
+			for_array(i, as->lhs) {
+				AstNode *lhs = as->lhs[i];
+				ssaAddr lval = {};
+				if (!ssa_is_blank_ident(lhs)) {
+					lval = ssa_build_addr(proc, lhs);
+				}
+				array_add(&lvals, lval);
+			}
+
+			if (as->lhs.count == as->rhs.count) {
+				if (as->lhs.count == 1) {
+					AstNode *rhs = as->rhs[0];
+					ssaValue *init = ssa_build_expr(proc, rhs);
+					ssa_addr_store(proc, lvals[0], init);
+				} else {
+					Array<ssaValue *> inits;
+					array_init(&inits, m->tmp_allocator, lvals.count);
+
+					for_array(i, as->rhs) {
+						ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
+						array_add(&inits, init);
+					}
+
+					for_array(i, inits) {
+						ssa_addr_store(proc, lvals[i], inits[i]);
+					}
+				}
+			} else {
+				Array<ssaValue *> inits;
+				array_init(&inits, m->tmp_allocator, lvals.count);
+
+				for_array(i, as->rhs) {
+					ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
+					Type *t = ssa_type(init);
+					// TODO(bill): refactor for code reuse as this is repeated a bit
+					if (t->kind == Type_Tuple) {
+						for (isize i = 0; i < t->Tuple.variable_count; i++) {
+							Entity *e = t->Tuple.variables[i];
+							ssaValue *v = ssa_emit_struct_ev(proc, init, i);
+							array_add(&inits, v);
+						}
+					} else {
+						array_add(&inits, init);
+					}
+				}
+
+				for_array(i, inits) {
+					ssa_addr_store(proc, lvals[i], inits[i]);
+				}
+			}
+
+		} break;
+
+		default: {
+			// NOTE(bill): Only 1 += 1 is allowed, no tuples
+			// +=, -=, etc
+			i32 op = cast(i32)as->op.kind;
+			op += Token_Add - Token_AddEq; // Convert += to +
+			ssaAddr lhs = ssa_build_addr(proc, as->lhs[0]);
+			ssaValue *value = ssa_build_expr(proc, as->rhs[0]);
+			ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op);
+		} break;
+		}
+	case_end;
+
+	case_ast_node(es, ExprStmt, node);
+		// NOTE(bill): No need to use return value
+		ssa_build_expr(proc, es->expr);
+	case_end;
+
+	case_ast_node(bs, BlockStmt, node);
+		ssa_open_scope(proc);
+		ssa_build_stmt_list(proc, bs->stmts);
+		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
+	case_end;
+
+	case_ast_node(ds, DeferStmt, node);
+		ssa_emit_comment(proc, make_string("DeferStmt"));
+		isize scope_index = proc->scope_index;
+		if (ds->stmt->kind == AstNode_BlockStmt) {
+			scope_index--;
+		}
+		ssa_add_defer_node(proc, scope_index, ds->stmt);
+	case_end;
+
+	case_ast_node(rs, ReturnStmt, node);
+		ssa_emit_comment(proc, make_string("ReturnStmt"));
+		ssaValue *v = NULL;
+		auto *return_type_tuple  = &proc->type->Proc.results->Tuple;
+		isize return_count = proc->type->Proc.result_count;
+		if (return_count == 0) {
+			// No return values
+		} else if (return_count == 1) {
+			Entity *e = return_type_tuple->variables[0];
+			v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[0]), e->type);
+		} else {
+			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
+			defer (gb_temp_arena_memory_end(tmp));
+
+			Array<ssaValue *> results;
+			array_init(&results, proc->module->tmp_allocator, return_count);
+
+			for_array(res_index, rs->results) {
+				ssaValue *res = ssa_build_expr(proc, rs->results[res_index]);
+				Type *t = ssa_type(res);
+				if (t->kind == Type_Tuple) {
+					for (isize i = 0; i < t->Tuple.variable_count; i++) {
+						Entity *e = t->Tuple.variables[i];
+						ssaValue *v = ssa_emit_struct_ev(proc, res, i);
+						array_add(&results, v);
+					}
+				} else {
+					array_add(&results, res);
+				}
+			}
+
+			Type *ret_type = proc->type->Proc.results;
+			v = ssa_add_local_generated(proc, ret_type);
+			for_array(i, results) {
+				Entity *e = return_type_tuple->variables[i];
+				ssaValue *res = ssa_emit_conv(proc, results[i], e->type);
+				ssaValue *field = ssa_emit_struct_ep(proc, v, i);
+				ssa_emit_store(proc, field, res);
+			}
+
+			v = ssa_emit_load(proc, v);
+
+		}
+		ssa_emit_return(proc, v);
+
+	case_end;
+
+	case_ast_node(is, IfStmt, node);
+		ssa_emit_comment(proc, make_string("IfStmt"));
+		if (is->init != NULL) {
+			ssaBlock *init = ssa_add_block(proc, node, "if.init");
+			ssa_emit_jump(proc, init);
+			proc->curr_block = init;
+			ssa_build_stmt(proc, is->init);
+		}
+		ssaBlock *then = ssa_add_block(proc, node, "if.then");
+		ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later
+		ssaBlock *else_ = done;
+		if (is->else_stmt != NULL) {
+			else_ = ssa_add_block(proc, is->else_stmt, "if.else");
+		}
+
+		ssa_build_cond(proc, is->cond, then, else_);
+		proc->curr_block = then;
+
+		ssa_open_scope(proc);
+		ssa_build_stmt(proc, is->body);
+		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
+
+		ssa_emit_jump(proc, done);
+
+		if (is->else_stmt != NULL) {
+			proc->curr_block = else_;
+
+			ssa_open_scope(proc);
+			ssa_build_stmt(proc, is->else_stmt);
+			ssa_close_scope(proc, ssaDeferExit_Default, NULL);
+
+			ssa_emit_jump(proc, done);
+		}
+		proc->curr_block = done;
+	case_end;
+
+	case_ast_node(fs, ForStmt, node);
+		ssa_emit_comment(proc, make_string("ForStmt"));
+		if (fs->init != NULL) {
+			ssaBlock *init = ssa_add_block(proc, node, "for.init");
+			ssa_emit_jump(proc, init);
+			proc->curr_block = init;
+			ssa_build_stmt(proc, fs->init);
+		}
+		ssaBlock *body = ssa_add_block(proc, node, "for.body");
+		ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later
+
+		ssaBlock *loop = body;
+
+		if (fs->cond != NULL) {
+			loop = ssa_add_block(proc, node, "for.loop");
+		}
+		ssaBlock *cont = loop;
+		if (fs->post != NULL) {
+			cont = ssa_add_block(proc, node, "for.post");
+
+		}
+		ssa_emit_jump(proc, loop);
+		proc->curr_block = loop;
+		if (loop != body) {
+			ssa_build_cond(proc, fs->cond, body, done);
+			proc->curr_block = body;
+		}
+
+		ssa_push_target_list(proc, done, cont, NULL);
+
+		ssa_open_scope(proc);
+		ssa_build_stmt(proc, fs->body);
+		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
+
+		ssa_pop_target_list(proc);
+		ssa_emit_jump(proc, cont);
+
+		if (fs->post != NULL) {
+			proc->curr_block = cont;
+			ssa_build_stmt(proc, fs->post);
+			ssa_emit_jump(proc, loop);
+		}
+
+
+		proc->curr_block = done;
+
+	case_end;
+
+	case_ast_node(ms, MatchStmt, node);
+		ssa_emit_comment(proc, make_string("MatchStmt"));
+		if (ms->init != NULL) {
+			ssa_build_stmt(proc, ms->init);
+		}
+		ssaValue *tag = v_true;
+		if (ms->tag != NULL) {
+			tag = ssa_build_expr(proc, ms->tag);
+		}
+		ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later
+
+		ast_node(body, BlockStmt, ms->body);
+
+		AstNodeArray default_stmts = {};
+		ssaBlock *default_fall = NULL;
+		ssaBlock *default_block = NULL;
+
+		ssaBlock *fall = NULL;
+		b32 append_fall = false;
+
+		isize case_count = body->stmts.count;
+		for_array(i, body->stmts) {
+			AstNode *clause = body->stmts[i];
+			ssaBlock *body = fall;
+
+			ast_node(cc, CaseClause, clause);
+
+			if (body == NULL) {
+				if (cc->list.count == 0) {
+					body = ssa_add_block(proc, clause, "match.dflt.body");
+				} else {
+					body = ssa_add_block(proc, clause, "match.case.body");
+				}
+			}
+			if (append_fall && body == fall) {
+				append_fall = false;
+			}
+
+			fall = done;
+			if (i+1 < case_count) {
+				append_fall = true;
+				fall = ssa_add_block(proc, clause, "match.fall.body");
+			}
+
+			if (cc->list.count == 0) {
+				// default case
+				default_stmts = cc->stmts;
+				default_fall  = fall;
+				default_block = body;
+				continue;
+			}
+
+			ssaBlock *next_cond = NULL;
+			for_array(j, cc->list) {
+				AstNode *expr = cc->list[j];
+				next_cond = ssa_add_block(proc, clause, "match.case.next");
+
+				ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr));
+				ssa_emit_if(proc, cond, body, next_cond);
+				proc->curr_block = next_cond;
+			}
+			proc->curr_block = body;
+
+			ssa_push_target_list(proc, done, NULL, fall);
+			ssa_open_scope(proc);
+			ssa_build_stmt_list(proc, cc->stmts);
+			ssa_close_scope(proc, ssaDeferExit_Default, body);
+			ssa_pop_target_list(proc);
+
+			ssa_emit_jump(proc, done);
+			proc->curr_block = next_cond;
+		}
+
+		if (default_block != NULL) {
+			ssa_emit_jump(proc, default_block);
+			proc->curr_block = default_block;
+
+			ssa_push_target_list(proc, done, NULL, default_fall);
+			ssa_open_scope(proc);
+			ssa_build_stmt_list(proc, default_stmts);
+			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
+			ssa_pop_target_list(proc);
+		}
+
+		ssa_emit_jump(proc, done);
+		proc->curr_block = done;
+	case_end;
+
+
+	case_ast_node(ms, TypeMatchStmt, node);
+		ssa_emit_comment(proc, make_string("TypeMatchStmt"));
+		gbAllocator allocator = proc->module->allocator;
+
+		ssaValue *parent = ssa_build_expr(proc, ms->tag);
+		Type *union_type = type_deref(ssa_type(parent));
+		GB_ASSERT(is_type_union(union_type));
+
+		ssa_emit_comment(proc, make_string("get union's tag"));
+		ssaValue *tag_index = ssa_emit_struct_ep(proc, parent, 1);
+		tag_index = ssa_emit_load(proc, tag_index);
+
+		ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
+
+		ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first");
+		ssa_emit_jump(proc, start_block);
+		proc->curr_block = start_block;
+
+		ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later
+
+		ast_node(body, BlockStmt, ms->body);
+
+
+		String tag_var_name = ms->var->Ident.string;
+
+		AstNodeArray default_stmts = {};
+		ssaBlock *default_block = NULL;
+
+		isize case_count = body->stmts.count;
+		for_array(i, body->stmts) {
+			AstNode *clause = body->stmts[i];
+			ast_node(cc, CaseClause, clause);
+
+			if (cc->list.count == 0) {
+				// default case
+				default_stmts = cc->stmts;
+				default_block = ssa_add_block(proc, clause, "type-match.dflt.body");
+				continue;
+			}
+
+
+			ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body");
+
+			Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause));
+			Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
+			GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name));
+			ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
+			ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type);
+			ssa_emit_store(proc, tag_var, data_ptr);
+
+
+
+			Type *bt = type_deref(tag_var_entity->type);
+			ssaValue *index = NULL;
+			Type *ut = base_type(union_type);
+			GB_ASSERT(ut->Record.kind == TypeRecord_Union);
+			for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) {
+				Entity *f = base_type(union_type)->Record.fields[field_index];
+				if (are_types_identical(f->type, bt)) {
+					index = ssa_make_const_int(allocator, field_index);
+					break;
+				}
+			}
+			GB_ASSERT(index != NULL);
+
+			ssaBlock *next_cond = ssa_add_block(proc, clause, "type-match.case.next");
+			ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index);
+			ssa_emit_if(proc, cond, body, next_cond);
+			proc->curr_block = next_cond;
+
+			proc->curr_block = body;
+
+			ssa_push_target_list(proc, done, NULL, NULL);
+			ssa_open_scope(proc);
+			ssa_build_stmt_list(proc, cc->stmts);
+			ssa_close_scope(proc, ssaDeferExit_Default, body);
+			ssa_pop_target_list(proc);
+
+			ssa_emit_jump(proc, done);
+			proc->curr_block = next_cond;
+		}
+
+		if (default_block != NULL) {
+			ssa_emit_jump(proc, default_block);
+			proc->curr_block = default_block;
+
+			ssa_push_target_list(proc, done, NULL, NULL);
+			ssa_open_scope(proc);
+			ssa_build_stmt_list(proc, default_stmts);
+			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
+			ssa_pop_target_list(proc);
+		}
+
+		ssa_emit_jump(proc, done);
+		proc->curr_block = done;
+	case_end;
+
+	case_ast_node(bs, BranchStmt, node);
+		ssaBlock *block = NULL;
+		switch (bs->token.kind) {
+		case Token_break:
+			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+				block = t->break_;
+			}
+			break;
+		case Token_continue:
+			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+				block = t->continue_;
+			}
+			break;
+		case Token_fallthrough:
+			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
+				block = t->fallthrough_;
+			}
+			break;
+		}
+		if (block != NULL) {
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block);
+		}
+		switch (bs->token.kind) {
+		case Token_break:       ssa_emit_comment(proc, make_string("break"));       break;
+		case Token_continue:    ssa_emit_comment(proc, make_string("continue"));    break;
+		case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break;
+		}
+		ssa_emit_jump(proc, block);
+		ssa_emit_unreachable(proc);
+	case_end;
+
+
+
+	case_ast_node(pa, PushAllocator, node);
+		ssa_emit_comment(proc, make_string("PushAllocator"));
+		ssa_open_scope(proc);
+		defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL));
+
+		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
+		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
+		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
+
+		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
+
+		ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1);
+		ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr));
+
+		ssa_build_stmt(proc, pa->body);
+
+	case_end;
+
+
+	case_ast_node(pa, PushContext, node);
+		ssa_emit_comment(proc, make_string("PushContext"));
+		ssa_open_scope(proc);
+		defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL));
+
+		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
+		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
+		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
+
+		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
+
+		ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr));
+
+		ssa_build_stmt(proc, pa->body);
+	case_end;
+
+
+	}
+}
+
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Optimizations
+//
+////////////////////////////////////////////////////////////////
+
+#include "ssa_opt.cpp"
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Procedure
+//
+////////////////////////////////////////////////////////////////
+
+void ssa_begin_procedure_body(ssaProcedure *proc) {
+	array_init(&proc->blocks,      heap_allocator());
+	array_init(&proc->defer_stmts, heap_allocator());
+	array_init(&proc->children,    heap_allocator());
+
+	proc->decl_block  = ssa_add_block(proc, proc->type_expr, "decls");
+	proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry");
+	proc->curr_block  = proc->entry_block;
+
+	if (proc->type->Proc.params != NULL) {
+		auto *params = &proc->type->Proc.params->Tuple;
+		for (isize i = 0; i < params->variable_count; i++) {
+			Entity *e = params->variables[i];
+			ssaValue *param = ssa_add_param(proc, e);
+			array_add(&proc->params, param);
+		}
+	}
+}
+
+
+void ssa_end_procedure_body(ssaProcedure *proc) {
+	if (proc->type->Proc.result_count == 0) {
+		ssa_emit_return(proc, NULL);
+	}
+
+	if (proc->curr_block->instrs.count == 0) {
+		ssa_emit_unreachable(proc);
+	}
+
+	proc->curr_block = proc->decl_block;
+	ssa_emit_jump(proc, proc->entry_block);
+
+	ssa_opt_proc(proc);
+
+// Number registers
+	i32 reg_index = 0;
+	for_array(i, proc->blocks) {
+		ssaBlock *b = proc->blocks[i];
+		b->index = i;
+		for_array(j, b->instrs) {
+			ssaValue *value = b->instrs[j];
+			GB_ASSERT(value->kind == ssaValue_Instr);
+			ssaInstr *instr = &value->Instr;
+			if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions
+				continue;
+			}
+			value->index = reg_index;
+			reg_index++;
+		}
+	}
+}
+
+
+void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) {
+	if (parent == NULL) {
+		if (proc->name == "main") {
+			ssa_emit_startup_runtime(proc);
+		}
+	}
+}
+
+void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
+	ssaProcedure *proc = &value->Proc;
+
+	proc->parent = parent;
+
+	if (proc->entity != NULL) {
+		ssaModule *m = proc->module;
+		CheckerInfo *info = m->info;
+		Entity *e = proc->entity;
+		String filename = e->token.pos.file;
+		AstFile **found = map_get(&info->files, hash_string(filename));
+		GB_ASSERT(found != NULL);
+		AstFile *f = *found;
+		ssaDebugInfo *di_file = NULL;
+
+		ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f));
+		if (di_file_found) {
+			di_file = *di_file_found;
+			GB_ASSERT(di_file->kind == ssaDebugInfo_File);
+		} else {
+			di_file = ssa_add_debug_info_file(proc, f);
+		}
+
+		ssa_add_debug_info_proc(proc, e, proc->name, di_file);
+	}
+
+	if (proc->body != NULL) {
+		u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
+		defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
+
+		if (proc->tags != 0) {
+			u32 in = proc->tags;
+			u32 out = proc->module->stmt_state_flags;
+			defer (proc->module->stmt_state_flags = out);
+
+			if (in & ProcTag_bounds_check) {
+				out |= StmtStateFlag_bounds_check;
+				out &= ~StmtStateFlag_no_bounds_check;
+			} else if (in & ProcTag_no_bounds_check) {
+				out |= StmtStateFlag_no_bounds_check;
+				out &= ~StmtStateFlag_bounds_check;
+			}
+		}
+
+
+		ssa_begin_procedure_body(proc);
+		ssa_insert_code_before_proc(proc, parent);
+		ssa_build_stmt(proc, proc->body);
+		ssa_end_procedure_body(proc);
+	}
+}
+
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Module
+//
+////////////////////////////////////////////////////////////////
+
+
+
+void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
+	map_set(&m->values, hash_pointer(e), v);
+}
+
+void ssa_init_module(ssaModule *m, Checker *c) {
+	// TODO(bill): Determine a decent size for the arena
+	isize token_count = c->parser->total_token_count;
+	isize arena_size = 4 * token_count * gb_size_of(ssaValue);
+	gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size);
+	gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size);
+	m->allocator     = gb_arena_allocator(&m->arena);
+	m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
+	m->info = &c->info;
+	m->sizes = c->sizes;
+
+	map_init(&m->values,     heap_allocator());
+	map_init(&m->members,    heap_allocator());
+	map_init(&m->debug_info, heap_allocator());
+	map_init(&m->type_names, heap_allocator());
+	array_init(&m->procs,  heap_allocator());
+
+	// Default states
+	m->stmt_state_flags = 0;
+	m->stmt_state_flags |= StmtStateFlag_bounds_check;
+
+	{
+		// Add type info data
+		{
+			String name = make_string(SSA_TYPE_INFO_DATA_NAME);
+			isize count = c->info.type_info_map.entries.count;
+			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count));
+			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
+			g->Global.is_private  = true;
+			ssa_module_add_value(m, e, g);
+			map_set(&m->members, hash_string(name), g);
+		}
+
+		// Type info member buffer
+		{
+			// NOTE(bill): Removes need for heap allocation by making it global memory
+			isize count = 0;
+
+			for_array(entry_index, m->info->type_info_map.entries) {
+				auto *entry = &m->info->type_info_map.entries[entry_index];
+				Type *t = cast(Type *)cast(uintptr)entry->key.key;
+
+				switch (t->kind) {
+				case Type_Record:
+					switch (t->Record.kind) {
+					case TypeRecord_Struct:
+					case TypeRecord_RawUnion:
+						count += t->Record.field_count;
+					}
+					break;
+				case Type_Tuple:
+					count += t->Tuple.variable_count;
+					break;
+				}
+			}
+
+			String name = make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME);
+			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name),
+			                                 make_type_array(m->allocator, t_type_info_member, count));
+			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
+			ssa_module_add_value(m, e, g);
+			map_set(&m->members, hash_string(name), g);
+		}
+	}
+
+	{
+		ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit);
+		di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file
+		di->CompileUnit.producer = make_string("odin");
+
+		map_set(&m->debug_info, hash_pointer(m), di);
+	}
+}
+
+void ssa_destroy_module(ssaModule *m) {
+	map_destroy(&m->values);
+	map_destroy(&m->members);
+	map_destroy(&m->type_names);
+	map_destroy(&m->debug_info);
+	array_free(&m->procs);
+	gb_arena_free(&m->arena);
+}
+
+
+
+////////////////////////////////////////////////////////////////
+//
+// @Code Generation
+//
+////////////////////////////////////////////////////////////////
+
+
+
+struct ssaGen {
+	ssaModule module;
+	gbFile output_file;
+};
+
+
+b32 ssa_gen_init(ssaGen *s, Checker *c) {
+	if (global_error_collector.count != 0) {
+		return false;
+	}
+
+	isize tc = c->parser->total_token_count;
+	if (tc < 2) {
+		return false;
+	}
+
+	ssa_init_module(&s->module, c);
+	s->module.generate_debug_info = false;
+
+	// TODO(bill): generate appropriate output name
+	int pos = cast(int)string_extension_position(c->parser->init_fullpath);
+	gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text));
+	if (err != gbFileError_None) {
+		return false;
+	}
+
+	return true;
+}
+
+void ssa_gen_destroy(ssaGen *s) {
+	ssa_destroy_module(&s->module);
+	gb_file_close(&s->output_file);
+}
+
+String ssa_mangle_name(ssaGen *s, String path, String name) {
+	// NOTE(bill): prefix names not in the init scope
+	// TODO(bill): make robust and not just rely on the file's name
+
+	ssaModule *m = &s->module;
+	CheckerInfo *info = m->info;
+	gbAllocator a = m->allocator;
+	AstFile *file = *map_get(&info->files, hash_string(path));
+
+	char *str = gb_alloc_array(a, char, path.len+1);
+	gb_memmove(str, path.text, path.len);
+	str[path.len] = 0;
+	for (isize i = 0; i < path.len; i++) {
+		if (str[i] == '\\') {
+			str[i] = '/';
+		}
+	}
+
+	char const *base = gb_path_base_name(str);
+	char const *ext = gb_path_extension(base);
+	isize base_len = ext-1-base;
+
+	isize max_len = base_len + 1 + 10 + 1 + name.len;
+	u8 *new_name = gb_alloc_array(a, u8, max_len);
+	isize new_name_len = gb_snprintf(
+		cast(char *)new_name, max_len,
+		"%.*s-%u.%.*s",
+		cast(int)base_len, base,
+		file->id,
+		LIT(name));
+
+	return make_string(new_name, new_name_len-1);
+}
+
+
+void ssa_gen_tree(ssaGen *s) {
+	ssaModule *m = &s->module;
+	CheckerInfo *info = m->info;
+	gbAllocator a = m->allocator;
+
+	if (v_zero == NULL) {
+		v_zero   = ssa_make_const_int (m->allocator, 0);
+		v_one    = ssa_make_const_int (m->allocator, 1);
+		v_zero32 = ssa_make_const_i32 (m->allocator, 0);
+		v_one32  = ssa_make_const_i32 (m->allocator, 1);
+		v_two32  = ssa_make_const_i32 (m->allocator, 2);
+		v_false  = ssa_make_const_bool(m->allocator, false);
+		v_true   = ssa_make_const_bool(m->allocator, true);
+	}
+
+	isize global_variable_max_count = 0;
+	Entity *entry_point = NULL;
+
+	for_array(i, info->entities.entries) {
+		auto *entry = &info->entities.entries[i];
+		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
+		String name = e->token.string;
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		} else if (e->kind == Entity_Procedure) {
+			if (e->scope->is_init && name == "main") {
+				entry_point = e;
+			}
+		}
+	}
+
+	struct ssaGlobalVariable {
+		ssaValue *var, *init;
+		DeclInfo *decl;
+	};
+	Array<ssaGlobalVariable> global_variables;
+	array_init(&global_variables, m->tmp_allocator, global_variable_max_count);
+
+	m->min_dep_map = generate_minimum_dependency_map(info, entry_point);
+
+	for_array(i, info->entities.entries) {
+		auto *entry = &info->entities.entries[i];
+		Entity *e = cast(Entity *)entry->key.ptr;
+		String name = e->token.string;
+		DeclInfo *decl = entry->value;
+		Scope *scope = e->scope;
+
+		if (!scope->is_file) {
+			continue;
+		}
+
+		if (map_get(&m->min_dep_map, hash_pointer(e)) == NULL) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+
+		if (!scope->is_global && !scope->is_init) {
+			name = ssa_mangle_name(s, e->token.pos.file, name);
+		}
+
+
+		switch (e->kind) {
+		case Entity_TypeName:
+			GB_ASSERT(e->type->kind == Type_Named);
+			map_set(&m->type_names, hash_pointer(e->type), name);
+			ssa_gen_global_type_name(m, e, name);
+			break;
+
+		case Entity_Variable: {
+			ssaValue *g = ssa_make_value_global(a, e, NULL);
+			if (decl->var_decl_tags & VarDeclTag_thread_local) {
+				g->Global.is_thread_local = true;
+			}
+			ssaGlobalVariable var = {};
+			var.var = g;
+			var.decl = decl;
+
+			if (decl->init_expr != NULL) {
+				TypeAndValue *tav = map_get(&info->types, hash_pointer(decl->init_expr));
+				if (tav != NULL) {
+					if (tav->value.kind != ExactValue_Invalid) {
+						ExactValue v = tav->value;
+						// if (v.kind != ExactValue_String) {
+							g->Global.value = ssa_add_module_constant(m, tav->type, v);
+						// }
+					}
+				}
+			}
+
+			if (g->Global.value == NULL) {
+				array_add(&global_variables, var);
+			}
+
+			map_set(&m->values, hash_pointer(e), g);
+			map_set(&m->members, hash_string(name), g);
+		} break;
+
+		case Entity_Procedure: {
+			auto *pd = &decl->proc_decl->ProcDecl;
+			String original_name = name;
+			AstNode *body = pd->body;
+			if (pd->tags & ProcTag_foreign) {
+				name = pd->name->Ident.string;
+			}
+			if (pd->foreign_name.len > 0) {
+				name = pd->foreign_name;
+			} else if (pd->link_name.len > 0) {
+				name = pd->link_name;
+			}
+
+			ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
+			p->Proc.tags = pd->tags;
+
+			map_set(&m->values, hash_pointer(e), p);
+			HashKey hash_name = hash_string(name);
+			if (map_get(&m->members, hash_name) == NULL) {
+				map_set(&m->members, hash_name, p);
+			}
+		} break;
+		}
+	}
+
+	for_array(i, m->members.entries) {
+		auto *entry = &m->members.entries[i];
+		ssaValue *v = entry->value;
+		if (v->kind == ssaValue_Proc)
+			ssa_build_proc(v, NULL);
+	}
+
+	ssaDebugInfo *compile_unit = m->debug_info.entries[0].value;
+	GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit);
+	ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs);
+
+	isize all_proc_max_count = 0;
+	for_array(i, m->debug_info.entries) {
+		auto *entry = &m->debug_info.entries[i];
+		ssaDebugInfo *di = entry->value;
+		di->id = i;
+		if (di->kind == ssaDebugInfo_Proc) {
+			all_proc_max_count++;
+		}
+	}
+
+	array_init(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count);
+	map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped
+	compile_unit->CompileUnit.all_procs = all_procs;
+
+
+	for_array(i, m->debug_info.entries) {
+		auto *entry = &m->debug_info.entries[i];
+		ssaDebugInfo *di = entry->value;
+		di->id = i;
+		if (di->kind == ssaDebugInfo_Proc) {
+			array_add(&all_procs->AllProcs.procs, di);
+		}
+	}
+
+
+	{ // Startup Runtime
+		// Cleanup(bill): probably better way of doing code insertion
+		String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME);
+		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
+		                                 NULL, 0,
+		                                 NULL, 0, false);
+		AstNode *body = gb_alloc_item(a, AstNode);
+		ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name);
+		Token token = {};
+		token.string = name;
+		Entity *e = make_entity_procedure(a, NULL, token, proc_type);
+
+		map_set(&m->values, hash_pointer(e), p);
+		map_set(&m->members, hash_string(name), p);
+
+		ssaProcedure *proc = &p->Proc;
+		proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea?
+
+		ssa_begin_procedure_body(proc);
+
+		// TODO(bill): Should do a dependency graph do check which order to initialize them in?
+		for_array(i, global_variables) {
+			ssaGlobalVariable *var = &global_variables[i];
+			if (var->decl->init_expr != NULL) {
+				var->init = ssa_build_expr(proc, var->decl->init_expr);
+			}
+		}
+
+		// NOTE(bill): Initialize constants first
+		for_array(i, global_variables) {
+			ssaGlobalVariable *var = &global_variables[i];
+			if (var->init != NULL) {
+				if (var->init->kind == ssaValue_Constant) {
+					ssa_emit_store(proc, var->var, var->init);
+				}
+			}
+		}
+
+		for_array(i, global_variables) {
+			ssaGlobalVariable *var = &global_variables[i];
+			if (var->init != NULL) {
+				if (var->init->kind != ssaValue_Constant) {
+					ssa_emit_store(proc, var->var, var->init);
+				}
+			}
+		}
+
+		{ // NOTE(bill): Setup type_info data
+			// TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR
+			ssaValue *type_info_data = NULL;
+			ssaValue *type_info_member_data = NULL;
+
+			ssaValue **found = NULL;
+			found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME)));
+			GB_ASSERT(found != NULL);
+			type_info_data = *found;
+
+			found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME)));
+			GB_ASSERT(found != NULL);
+			type_info_member_data = *found;
+
+			CheckerInfo *info = proc->module->info;
+
+			// Useful types
+			Type *t_i64_slice_ptr    = make_type_pointer(a, make_type_slice(a, t_i64));
+			Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string));
+
+			auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * {
+				i32 index = cast(i32)ssa_type_info_index(proc->module->info, type);
+				// gb_printf_err("%d %s\n", index, type_to_string(type));
+				return ssa_emit_array_ep(proc, type_info_data, index);
+			};
+
+			i32 type_info_member_index = 0;
+
+			auto type_info_member_offset = [](ssaProcedure *proc, ssaValue *data, isize count, i32 *index) -> ssaValue * {
+				ssaValue *offset = ssa_emit_array_ep(proc, data, *index);
+				*index += count;
+				return offset;
+			};
+
+
+
+			for_array(type_info_map_index, info->type_info_map.entries) {
+				auto *entry = &info->type_info_map.entries[type_info_map_index];
+				Type *t = cast(Type *)cast(uintptr)entry->key.key;
+				t = default_type(t);
+				isize entry_index = entry->value;
+
+				ssaValue *tag = NULL;
+
+				switch (t->kind) {
+				case Type_Named: {
+					tag = ssa_add_local_generated(proc, t_type_info_named);
+
+					// TODO(bill): Which is better? The mangled name or actual name?
+					ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string);
+					ssaValue *gtip = get_type_info_ptr(proc, type_info_data, t->Named.base);
+
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip);
+				} break;
+
+				case Type_Basic:
+					switch (t->Basic.kind) {
+					case Basic_bool:
+						tag = ssa_add_local_generated(proc, t_type_info_boolean);
+						break;
+					case Basic_i8:
+					case Basic_i16:
+					case Basic_i32:
+					case Basic_i64:
+					case Basic_u8:
+					case Basic_u16:
+					case Basic_u32:
+					case Basic_u64:
+					case Basic_int:
+					case Basic_uint: {
+						tag = ssa_add_local_generated(proc, t_type_info_integer);
+						b32 is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0;
+						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
+						ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned);
+						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
+						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed);
+					} break;
+
+					case Basic_f32:
+					case Basic_f64: {
+						tag = ssa_add_local_generated(proc, t_type_info_float);
+						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
+						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
+					} break;
+
+					case Basic_rawptr:
+						tag = ssa_add_local_generated(proc, t_type_info_pointer);
+						break;
+
+					case Basic_string:
+						tag = ssa_add_local_generated(proc, t_type_info_string);
+						break;
+
+					case Basic_any:
+						tag = ssa_add_local_generated(proc, t_type_info_any);
+						break;
+					}
+					break;
+
+				case Type_Pointer: {
+					tag = ssa_add_local_generated(proc, t_type_info_pointer);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
+				} break;
+				case Type_Maybe: {
+					tag = ssa_add_local_generated(proc, t_type_info_maybe);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Maybe.elem);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
+				} break;
+				case Type_Array: {
+					tag = ssa_add_local_generated(proc, t_type_info_array);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
+
+					isize ez = type_size_of(m->sizes, a, t->Array.elem);
+					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
+					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
+
+					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
+					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count));
+
+				} break;
+				case Type_Slice: {
+					tag = ssa_add_local_generated(proc, t_type_info_slice);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
+
+					isize ez = type_size_of(m->sizes, a, t->Slice.elem);
+					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
+					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
+
+				} break;
+				case Type_Vector: {
+					tag = ssa_add_local_generated(proc, t_type_info_vector);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem);
+					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
+
+					isize ez = type_size_of(m->sizes, a, t->Vector.elem);
+					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
+					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
+
+					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
+					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count));
+
+				} break;
+				case Type_Record: {
+					switch (t->Record.kind) {
+					case TypeRecord_Struct: {
+						tag = ssa_add_local_generated(proc, t_type_info_struct);
+
+						{
+							ssaValue *packed  = ssa_make_const_bool(a, t->Record.struct_is_packed);
+							ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered);
+							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
+							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered);
+						}
+
+						ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
+
+						type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet
+						for (isize source_index = 0; source_index < t->Record.field_count; source_index++) {
+							// TODO(bill): Order fields in source order not layout order
+							Entity *f = t->Record.fields_in_src_order[source_index];
+							ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
+							i64 foffset = t->Record.struct_offsets[f->Variable.field_index];
+							GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
+
+							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index));
+							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
+							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
+							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
+
+							if (f->token.string.len > 0) {
+								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
+							}
+							ssa_emit_store(proc, type_info, tip);
+							ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset));
+						}
+
+						Type *slice_type = make_type_slice(a, t_type_info_member);
+						Type *slice_type_ptr = make_type_pointer(a, slice_type);
+						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
+						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
+
+						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
+						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
+						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
+
+						ssa_emit_store(proc, elem, memory);
+						ssa_emit_store(proc, len, field_count);
+						ssa_emit_store(proc, cap, field_count);
+					} break;
+					case TypeRecord_Union:
+						tag = ssa_add_local_generated(proc, t_type_info_union);
+						{
+							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
+							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
+						}
+						break;
+					case TypeRecord_RawUnion: {
+						tag = ssa_add_local_generated(proc, t_type_info_raw_union);
+						{
+							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
+							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
+						}
+
+						ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
+
+						for (isize i = 0; i < t->Record.field_count; i++) {
+							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
+							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
+							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
+							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
+
+							Entity *f = t->Record.fields[i];
+							ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
+
+							if (f->token.string.len > 0) {
+								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
+							}
+							ssa_emit_store(proc, type_info, tip);
+							ssa_emit_store(proc, offset, ssa_make_const_int(a, 0));
+						}
+
+						Type *slice_type = make_type_slice(a, t_type_info_member);
+						Type *slice_type_ptr = make_type_pointer(a, slice_type);
+						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
+						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
+
+						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
+						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
+						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
+
+						ssa_emit_store(proc, elem, memory);
+						ssa_emit_store(proc, len, field_count);
+						ssa_emit_store(proc, cap, field_count);
+					} break;
+					case TypeRecord_Enum: {
+						tag = ssa_add_local_generated(proc, t_type_info_enum);
+						Type *enum_base = t->Record.enum_base;
+						if (enum_base == NULL) {
+							enum_base = t_int;
+						}
+						ssaValue *base = ssa_emit_struct_ep(proc, tag, 0);
+						ssa_emit_store(proc, base, get_type_info_ptr(proc, type_info_data, enum_base));
+
+						if (t->Record.other_field_count > 0) {
+							Entity **fields = t->Record.other_fields;
+							isize count = t->Record.other_field_count;
+							ssaValue *value_array = NULL;
+							ssaValue *name_array = NULL;
+
+
+							{
+								Token token = {Token_Identifier};
+								i32 id = cast(i32)entry_index;
+								char name_base[] = "__$enum_values";
+								isize name_len = gb_size_of(name_base) + 10;
+								token.string.text = gb_alloc_array(a, u8, name_len);
+								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+								                               "%s-%d", name_base, id)-1;
+								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count));
+								value_array = ssa_make_value_global(a, e, NULL);
+								value_array->Global.is_private = true;
+								ssa_module_add_value(m, e, value_array);
+								map_set(&m->members, hash_string(token.string), value_array);
+							}
+							{
+								Token token = {Token_Identifier};
+								i32 id = cast(i32)entry_index;
+								char name_base[] = "__$enum_names";
+								isize name_len = gb_size_of(name_base) + 10;
+								token.string.text = gb_alloc_array(a, u8, name_len);
+								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
+								                               "%s-%d", name_base, id)-1;
+								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count));
+								name_array = ssa_make_value_global(a, e, NULL);
+								name_array->Global.is_private = true;
+								ssa_module_add_value(m, e, name_array);
+								map_set(&m->members, hash_string(token.string), name_array);
+							}
+
+							for (isize i = 0; i < count; i++) {
+								ssaValue *value_gep = ssa_emit_struct_ep(proc, value_array, i);
+								ssaValue *name_gep  = ssa_emit_struct_ep(proc, name_array, i);
+
+								ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer));
+								ssa_emit_store(proc, name_gep,  ssa_make_const_string(a, fields[i]->token.string));
+							}
+
+							ssaValue *v_count = ssa_make_const_int(a, count);
+
+
+							ssaValue *values = ssa_emit_struct_ep(proc, tag, 1);
+							ssaValue *names  = ssa_emit_struct_ep(proc, tag, 2);
+							ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr));
+							ssaValue *name_slice  = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr));
+
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array));
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count);
+
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array));
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count);
+							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count);
+
+							ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice));
+							ssa_emit_store(proc, names,  ssa_emit_load(proc, name_slice));
+						}
+					} break;
+					}
+				} break;
+
+				case Type_Tuple: {
+					tag = ssa_add_local_generated(proc, t_type_info_tuple);
+
+					{
+						ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
+						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
+					}
+
+					ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index);
+
+					for (isize i = 0; i < t->Tuple.variable_count; i++) {
+						ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
+						ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
+						ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
+						// NOTE(bill): offset is not used for tuples
+
+						Entity *f = t->Tuple.variables[i];
+						ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
+
+						if (f->token.string.len > 0) {
+							ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
+						}
+						ssa_emit_store(proc, type_info, tip);
+					}
+
+					Type *slice_type = make_type_slice(a, t_type_info_member);
+					Type *slice_type_ptr = make_type_pointer(a, slice_type);
+					ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
+					ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count);
+
+					ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
+					ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
+					ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
+
+					ssa_emit_store(proc, elem, memory);
+					ssa_emit_store(proc, len, variable_count);
+					ssa_emit_store(proc, cap, variable_count);
+				} break;
+
+				case Type_Proc: {
+					tag = ssa_add_local_generated(proc, t_type_info_procedure);
+
+					ssaValue *params   = ssa_emit_struct_ep(proc, tag, 0);
+					ssaValue *results  = ssa_emit_struct_ep(proc, tag, 1);
+					ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2);
+
+					if (t->Proc.params) {
+						ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params));
+					}
+					if (t->Proc.results) {
+						ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results));
+					}
+					ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic));
+
+					// TODO(bill): Type_Info for procedures
+				} break;
+				}
+
+				if (tag != NULL) {
+					ssaValue *gep = ssa_emit_array_ep(proc, type_info_data, entry_index);
+					ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info);
+					ssa_emit_store(proc, gep, val);
+				}
+			}
+		}
+
+		ssa_end_procedure_body(proc);
+	}
+
+	for_array(i, m->procs) {
+		ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent);
+	}
+
+
+	// m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
+}
+

+ 0 - 1965
src/ssa/build.cpp

@@ -1,1965 +0,0 @@
-void    ssa_emit_jump           (ssaProcedure *proc, ssaBlock *block);
-void    ssa_build_stmt          (ssaProcedure *proc, AstNode *s);
-ssaAddr ssa_build_addr          (ssaProcedure *proc, AstNode *expr);
-void    ssa_build_proc          (ssaValue *value, ssaProcedure *parent);
-void    ssa_gen_global_type_name(ssaModule *m, Entity *e, String name);
-
-
-
-
-void ssa_push_target_list(ssaProcedure *proc, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
-	ssaTargetList *tl = gb_alloc_item(proc->module->allocator, ssaTargetList);
-	tl->prev          = proc->target_list;
-	tl->break_        = break_;
-	tl->continue_     = continue_;
-	tl->fallthrough_  = fallthrough_;
-	proc->target_list = tl;
-}
-
-void ssa_pop_target_list(ssaProcedure *proc) {
-	proc->target_list = proc->target_list->prev;
-}
-
-
-void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) {
-	if (field->kind != Entity_TypeName) {
-		return;
-	}
-	String cn = field->token.string;
-
-	isize len = parent.len + 1 + cn.len;
-	String child = {NULL, len};
-	child.text = gb_alloc_array(m->allocator, u8, len);
-
-	isize i = 0;
-	gb_memmove(child.text+i, parent.text, parent.len);
-	i += parent.len;
-	child.text[i++] = '.';
-	gb_memmove(child.text+i, cn.text, cn.len);
-
-	map_set(&m->type_names, hash_pointer(field->type), child);
-	ssa_gen_global_type_name(m, field, child);
-}
-
-void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
-	ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type);
-	ssa_module_add_value(m, e, t);
-	map_set(&m->members, hash_string(name), t);
-
-	Type *bt = base_type(e->type);
-	if (bt->kind == Type_Record) {
-		auto *s = &bt->Record;
-		for (isize j = 0; j < s->other_field_count; j++) {
-			ssa_mangle_sub_type_name(m, s->other_fields[j], name);
-		}
-	}
-
-	if (is_type_union(bt)) {
-		auto *s = &bt->Record;
-		// NOTE(bill): Zeroth entry is null (for `match type` stmts)
-		for (isize j = 1; j < s->field_count; j++) {
-			ssa_mangle_sub_type_name(m, s->fields[j], name);
-		}
-	}
-}
-
-
-
-
-void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) {
-	ssaBlock *b = ssa_add_block(proc, NULL, "defer");
-	// NOTE(bill): The prev block may defer injection before it's terminator
-	ssaInstr *last_instr = ssa_get_last_instr(proc->curr_block);
-	if (last_instr == NULL || !ssa_is_instr_terminating(last_instr)) {
-		ssa_emit_jump(proc, b);
-	}
-	proc->curr_block = b;
-	ssa_emit_comment(proc, make_string("defer"));
-	if (d.kind == ssaDefer_Node) {
-		ssa_build_stmt(proc, d.stmt);
-	} else if (d.kind == ssaDefer_Instr) {
-		// NOTE(bill): Need to make a new copy
-		ssaValue *instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, d.instr, gb_size_of(ssaValue));
-		ssa_emit(proc, instr);
-	}
-}
-
-
-
-ssaValue *ssa_find_global_variable(ssaProcedure *proc, String name) {
-	ssaValue **value = map_get(&proc->module->members, hash_string(name));
-	GB_ASSERT_MSG(value != NULL, "Unable to find global variable `%.*s`", LIT(name));
-	return *value;
-}
-
-ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id) {
-	Entity *e = proc->module->info->implicit_values[id];
-	GB_ASSERT(e->kind == Entity_ImplicitValue);
-	Entity *backing = e->ImplicitValue.backing;
-	ssaValue **value = map_get(&proc->module->values, hash_pointer(backing));
-	GB_ASSERT_MSG(value != NULL, "Unable to find implicit value backing `%.*s`", LIT(backing->token.string));
-	return *value;
-}
-
-
-
-ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
-	expr = unparen_expr(expr);
-	switch (expr->kind) {
-	case_ast_node(bl, BasicLit, expr);
-		GB_PANIC("Non-constant basic literal");
-	case_end;
-
-	case_ast_node(i, Ident, expr);
-		Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr));
-		if (e->kind == Entity_Builtin) {
-			Token token = ast_node_token(expr);
-			GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`\n"
-			         "\t at %.*s(%td:%td)", LIT(builtin_procs[e->Builtin.id].name),
-			         LIT(token.pos.file), token.pos.line, token.pos.column);
-			return NULL;
-		} else if (e->kind == Entity_Nil) {
-			return ssa_make_value_nil(proc->module->allocator, tv->type);
-		} else if (e->kind == Entity_ImplicitValue) {
-			return ssa_emit_load(proc, ssa_find_implicit_value_backing(proc, e->ImplicitValue.id));
-		}
-
-		auto *found = map_get(&proc->module->values, hash_pointer(e));
-		if (found) {
-			ssaValue *v = *found;
-			if (v->kind == ssaValue_Proc) {
-				return v;
-			}
-			// if (e->kind == Entity_Variable && e->Variable.param) {
-				// return v;
-			// }
-			return ssa_emit_load(proc, v);
-		}
-		return NULL;
-	case_end;
-
-	case_ast_node(re, RunExpr, expr);
-		// TODO(bill): Run Expression
-		return ssa_build_single_expr(proc, re->expr, tv);
-	case_end;
-
-	case_ast_node(de, DerefExpr, expr);
-		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	case_end;
-
-	case_ast_node(se, SelectorExpr, expr);
-		TypeAndValue *tav = map_get(&proc->module->info->types, hash_pointer(expr));
-		GB_ASSERT(tav != NULL);
-		return ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, expr);
-		switch (ue->op.kind) {
-		case Token_Pointer:
-			return ssa_emit_ptr_offset(proc, ssa_build_addr(proc, ue->expr).addr, v_zero); // Make a copy of the pointer
-
-		case Token_Maybe:
-			return ssa_emit_conv(proc, ssa_build_expr(proc, ue->expr), type_of_expr(proc->module->info, expr));
-
-		case Token_Add:
-			return ssa_build_expr(proc, ue->expr);
-
-		case Token_Sub: // NOTE(bill): -`x` == 0 - `x`
-			return ssa_emit_arith(proc, ue->op.kind, v_zero, ssa_build_expr(proc, ue->expr), tv->type);
-
-		case Token_Not:   // Boolean not
-		case Token_Xor: { // Bitwise not
-			// NOTE(bill): "not" `x` == `x` "xor" `-1`
-			ssaValue *left = ssa_build_expr(proc, ue->expr);
-			ssaValue *right = ssa_add_module_constant(proc->module, tv->type, make_exact_value_integer(-1));
-			return ssa_emit_arith(proc, ue->op.kind,
-			                      left, right,
-			                      tv->type);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, expr);
-		switch (be->op.kind) {
-		case Token_Add:
-		case Token_Sub:
-		case Token_Mul:
-		case Token_Quo:
-		case Token_Mod:
-		case Token_And:
-		case Token_Or:
-		case Token_Xor:
-		case Token_AndNot:
-		case Token_Shl:
-		case Token_Shr:
-			return ssa_emit_arith(proc, be->op.kind,
-			                      ssa_build_expr(proc, be->left),
-			                      ssa_build_expr(proc, be->right),
-			                      tv->type);
-
-
-		case Token_CmpEq:
-		case Token_NotEq:
-		case Token_Lt:
-		case Token_LtEq:
-		case Token_Gt:
-		case Token_GtEq: {
-			ssaValue *left  = ssa_build_expr(proc, be->left);
-			ssaValue *right = ssa_build_expr(proc, be->right);
-
-			ssaValue *cmp = ssa_emit_comp(proc, be->op.kind, left, right);
-			return ssa_emit_conv(proc, cmp, default_type(tv->type));
-		} break;
-
-		case Token_CmpAnd:
-		case Token_CmpOr:
-			return ssa_emit_logical_binary_expr(proc, expr);
-
-		case Token_as:
-			ssa_emit_comment(proc, make_string("cast - as"));
-			return ssa_emit_conv(proc, ssa_build_expr(proc, be->left), tv->type);
-
-		case Token_transmute:
-			ssa_emit_comment(proc, make_string("cast - transmute"));
-			return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type);
-
-		case Token_down_cast:
-			ssa_emit_comment(proc, make_string("cast - down_cast"));
-			return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type);
-
-		case Token_union_cast:
-			ssa_emit_comment(proc, make_string("cast - union_cast"));
-			return ssa_emit_union_cast(proc, ssa_build_expr(proc, be->left), tv->type);
-
-		default:
-			GB_PANIC("Invalid binary expression");
-			break;
-		}
-	case_end;
-
-	case_ast_node(pl, ProcLit, expr);
-		// NOTE(bill): Generate a new name
-		// parent$count
-		isize name_len = proc->name.len + 1 + 8 + 1;
-		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
-		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count);
-		String name = make_string(name_text, name_len-1);
-
-		Type *type = type_of_expr(proc->module->info, expr);
-		ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-		                                           proc->module, NULL, type, pl->type, pl->body, name);
-
-		value->Proc.tags = pl->tags;
-
-		array_add(&proc->children, &value->Proc);
-		ssa_build_proc(value, proc);
-
-		return value;
-	case_end;
-
-
-	case_ast_node(cl, CompoundLit, expr);
-		ssa_emit_comment(proc, make_string("CompoundLit"));
-		Type *type = type_of_expr(proc->module->info, expr);
-		Type *bt = base_type(type);
-		ssaValue *v = ssa_add_local_generated(proc, type);
-
-		Type *et = NULL;
-		switch (bt->kind) {
-		case Type_Vector: et = bt->Vector.elem; break;
-		case Type_Array:  et = bt->Array.elem;  break;
-		case Type_Slice:  et = bt->Slice.elem;  break;
-		}
-
-		auto is_elem_const = [](ssaModule *m, AstNode *elem, Type *elem_type) -> b32 {
-			if (base_type(elem_type) == t_any) {
-				return false;
-			}
-			if (elem->kind == AstNode_FieldValue) {
-				elem = elem->FieldValue.value;
-			}
-			TypeAndValue *tav = type_and_value_of_expression(m->info, elem);
-			GB_ASSERT(tav != NULL);
-			return tav->value.kind != ExactValue_Invalid;
-		};
-
-		switch (bt->kind) {
-		default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
-
-		case Type_Vector: {
-			ssaValue *result = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
-			for_array(index, cl->elems) {
-				AstNode *elem = cl->elems[index];
-				if (is_elem_const(proc->module, elem, et)) {
-					continue;
-				}
-				ssaValue *field_elem = ssa_build_expr(proc, elem);
-				Type *t = ssa_type(field_elem);
-				GB_ASSERT(t->kind != Type_Tuple);
-				ssaValue *ev = ssa_emit_conv(proc, field_elem, et);
-				ssaValue *i = ssa_make_const_int(proc->module->allocator, index);
-				result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i));
-			}
-
-			if (cl->elems.count == 1 && bt->Vector.count > 1) {
-				isize index_count = bt->Vector.count;
-				i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-				for (isize i = 0; i < index_count; i++) {
-					indices[i] = 0;
-				}
-				ssaValue *sv = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, result, indices, index_count));
-				ssa_emit_store(proc, v, sv);
-				return ssa_emit_load(proc, v);
-			}
-			return result;
-		} break;
-
-		case Type_Record: {
-			GB_ASSERT(is_type_struct(bt));
-			auto *st = &bt->Record;
-			if (cl->elems.count > 0) {
-				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
-				for_array(field_index, cl->elems) {
-					AstNode *elem = cl->elems[field_index];
-
-					ssaValue *field_expr = NULL;
-					Entity *field = NULL;
-					isize index = field_index;
-
-					if (elem->kind == AstNode_FieldValue) {
-						ast_node(fv, FieldValue, elem);
-						Selection sel = lookup_field(proc->module->allocator, bt, fv->field->Ident.string, false);
-						index = sel.index[0];
-						elem = fv->value;
-					} else {
-						TypeAndValue *tav = type_and_value_of_expression(proc->module->info, elem);
-						Selection sel = lookup_field(proc->module->allocator, bt, st->fields_in_src_order[field_index]->token.string, false);
-						index = sel.index[0];
-					}
-
-					field = st->fields[index];
-					if (is_elem_const(proc->module, elem, field->type)) {
-						continue;
-					}
-
-					field_expr = ssa_build_expr(proc, elem);
-
-					GB_ASSERT(ssa_type(field_expr)->kind != Type_Tuple);
-
-
-
-					Type *ft = field->type;
-					ssaValue *fv = ssa_emit_conv(proc, field_expr, ft);
-					ssaValue *gep = ssa_emit_struct_ep(proc, v, index);
-					ssa_emit_store(proc, gep, fv);
-				}
-			}
-		} break;
-		case Type_Array: {
-			if (cl->elems.count > 0) {
-				ssa_emit_store(proc, v, ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr)));
-				for_array(i, cl->elems) {
-					AstNode *elem = cl->elems[i];
-					if (is_elem_const(proc->module, elem, et)) {
-						continue;
-					}
-					ssaValue *field_expr = ssa_build_expr(proc, elem);
-					Type *t = ssa_type(field_expr);
-					GB_ASSERT(t->kind != Type_Tuple);
-					ssaValue *ev = ssa_emit_conv(proc, field_expr, et);
-					ssaValue *gep = ssa_emit_array_ep(proc, v, i);
-					ssa_emit_store(proc, gep, ev);
-				}
-			}
-		} break;
-		case Type_Slice: {
-			if (cl->elems.count > 0) {
-				Type *elem_type = bt->Slice.elem;
-				Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type);
-				Type *elem_ptr_ptr_type = make_type_pointer(proc->module->allocator, elem_ptr_type);
-				Type *t_int_ptr = make_type_pointer(proc->module->allocator, t_int);
-				ssaValue *slice = ssa_add_module_constant(proc->module, type, make_exact_value_compound(expr));
-				GB_ASSERT(slice->kind == ssaValue_ConstantSlice);
-
-				ssaValue *data = ssa_emit_array_ep(proc, slice->ConstantSlice.backing_array, v_zero32);
-
-				for_array(i, cl->elems) {
-					AstNode *elem = cl->elems[i];
-					if (is_elem_const(proc->module, elem, et)) {
-						continue;
-					}
-
-					ssaValue *field_expr = ssa_build_expr(proc, elem);
-					Type *t = ssa_type(field_expr);
-					GB_ASSERT(t->kind != Type_Tuple);
-					ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type);
-					ssaValue *offset = ssa_emit_ptr_offset(proc, data, ssa_make_const_int(proc->module->allocator, i));
-					ssa_emit_store(proc, offset, ev);
-				}
-
-				ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-				ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-				ssaValue *gep2 = ssa_emit_struct_ep(proc, v, 1);
-
-				ssa_emit_store(proc, gep0, data);
-				ssa_emit_store(proc, gep1, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
-				ssa_emit_store(proc, gep2, ssa_make_const_int(proc->module->allocator, slice->ConstantSlice.count));
-			}
-		} break;
-		}
-
-		return ssa_emit_load(proc, v);
-	case_end;
-
-
-	case_ast_node(ce, CallExpr, expr);
-		AstNode *p = unparen_expr(ce->proc);
-		if (p->kind == AstNode_Ident) {
-			Entity **found = map_get(&proc->module->info->uses, hash_pointer(p));
-			if (found && (*found)->kind == Entity_Builtin) {
-				Entity *e = *found;
-				switch (e->Builtin.id) {
-				case BuiltinProc_type_info: {
-					Type *t = default_type(type_of_expr(proc->module->info, ce->args[0]));
-					return ssa_type_info(proc, t);
-				} break;
-				case BuiltinProc_type_info_of_val: {
-					Type *t = default_type(type_of_expr(proc->module->info, ce->args[0]));
-					return ssa_type_info(proc, t);
-				} break;
-
-				case BuiltinProc_new: {
-					ssa_emit_comment(proc, make_string("new"));
-					// new :: proc(Type) -> ^Type
-					gbAllocator allocator = proc->module->allocator;
-
-					Type *type = type_of_expr(proc->module->info, ce->args[0]);
-					Type *ptr_type = make_type_pointer(allocator, type);
-
-					i64 s = type_size_of(proc->module->sizes, allocator, type);
-					i64 a = type_align_of(proc->module->sizes, allocator, type);
-
-					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
-					args[0] = ssa_make_const_int(allocator, s);
-					args[1] = ssa_make_const_int(allocator, a);
-					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
-					ssaValue *v = ssa_emit_conv(proc, call, ptr_type);
-					return v;
-				} break;
-
-				case BuiltinProc_new_slice: {
-					ssa_emit_comment(proc, make_string("new_slice"));
-					// new_slice :: proc(Type, len: int[, cap: int]) -> ^Type
-					gbAllocator allocator = proc->module->allocator;
-
-					Type *type = type_of_expr(proc->module->info, ce->args[0]);
-					Type *ptr_type = make_type_pointer(allocator, type);
-					Type *slice_type = make_type_slice(allocator, type);
-
-					i64 s = type_size_of(proc->module->sizes, allocator, type);
-					i64 a = type_align_of(proc->module->sizes, allocator, type);
-
-					ssaValue *elem_size  = ssa_make_const_int(allocator, s);
-					ssaValue *elem_align = ssa_make_const_int(allocator, a);
-
-					ssaValue *len = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[1]), t_int);
-					ssaValue *cap = len;
-					if (ce->args.count == 3) {
-						cap = ssa_emit_conv(proc, ssa_build_expr(proc, ce->args[2]), t_int);
-					}
-
-					ssa_emit_slice_bounds_check(proc, ast_node_token(ce->args[1]), v_zero, len, cap, false);
-
-					ssaValue *slice_size = ssa_emit_arith(proc, Token_Mul, elem_size, cap, t_int);
-
-					ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
-					args[0] = slice_size;
-					args[1] = elem_align;
-					ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
-
-					ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type, true);
-					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-					ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-					ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-					ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-					ssa_emit_store(proc, gep0, ptr);
-					ssa_emit_store(proc, gep1, len);
-					ssa_emit_store(proc, gep2, cap);
-					return ssa_emit_load(proc, slice);
-				} break;
-
-				case BuiltinProc_assert: {
-					ssa_emit_comment(proc, make_string("assert"));
-					ssaValue *cond = ssa_build_expr(proc, ce->args[0]);
-					GB_ASSERT(is_type_boolean(ssa_type(cond)));
-
-					cond = ssa_emit_comp(proc, Token_CmpEq, cond, v_false);
-					ssaBlock *err  = ssa_add_block(proc, NULL, "builtin.assert.err");
-					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.assert.done");
-
-					ssa_emit_if(proc, cond, err, done);
-					proc->curr_block = err;
-
-					// TODO(bill): Cleanup allocations here
-					Token token = ast_node_token(ce->args[0]);
-					TokenPos pos = token.pos;
-					gbString expr = expr_to_string(ce->args[0]);
-					defer (gb_string_free(expr));
-					isize expr_len = gb_string_length(expr);
-					String expr_str = {};
-					expr_str.text = cast(u8 *)gb_alloc_copy_align(proc->module->allocator, expr, expr_len, 1);
-					expr_str.len = expr_len;
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
-					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
-					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
-					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
-					args[3] = ssa_make_const_string(proc->module->allocator, expr_str);
-					ssa_emit_global_call(proc, "__assert", args, 4);
-
-					ssa_emit_jump(proc, done);
-					proc->curr_block = done;
-
-					return NULL;
-				} break;
-
-				case BuiltinProc_panic: {
-					ssa_emit_comment(proc, make_string("panic"));
-					ssaValue *msg = ssa_build_expr(proc, ce->args[0]);
-					GB_ASSERT(is_type_string(ssa_type(msg)));
-
-					Token token = ast_node_token(ce->args[0]);
-					TokenPos pos = token.pos;
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 4);
-					args[0] = ssa_make_const_string(proc->module->allocator, pos.file);
-					args[1] = ssa_make_const_int(proc->module->allocator, pos.line);
-					args[2] = ssa_make_const_int(proc->module->allocator, pos.column);
-					args[3] = msg;
-					ssa_emit_global_call(proc, "__assert", args, 4);
-
-					return NULL;
-				} break;
-
-
-				case BuiltinProc_copy: {
-					ssa_emit_comment(proc, make_string("copy"));
-					// copy :: proc(dst, src: []Type) -> int
-					AstNode *dst_node = ce->args[0];
-					AstNode *src_node = ce->args[1];
-					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
-					ssaValue *src_slice = ssa_build_expr(proc, src_node);
-					Type *slice_type = base_type(ssa_type(dst_slice));
-					GB_ASSERT(slice_type->kind == Type_Slice);
-					Type *elem_type = slice_type->Slice.elem;
-					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
-
-
-					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr, true);
-					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr, true);
-
-					ssaValue *len_dst = ssa_slice_len(proc, dst_slice);
-					ssaValue *len_src = ssa_slice_len(proc, src_slice);
-
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len_dst, len_src);
-					ssaValue *len = ssa_emit_select(proc, cond, len_dst, len_src);
-
-					ssaValue *elem_size = ssa_make_const_int(proc->module->allocator, size_of_elem);
-					ssaValue *byte_count = ssa_emit_arith(proc, Token_Mul, len, elem_size, t_int);
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
-					args[0] = dst;
-					args[1] = src;
-					args[2] = byte_count;
-
-					ssa_emit_global_call(proc, "__mem_copy", args, 3);
-
-					return len;
-				} break;
-				case BuiltinProc_append: {
-					ssa_emit_comment(proc, make_string("append"));
-					// append :: proc(s: ^[]Type, item: Type) -> bool
-					AstNode *sptr_node = ce->args[0];
-					AstNode *item_node = ce->args[1];
-					ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node);
-					ssaValue *slice = ssa_emit_load(proc, slice_ptr);
-
-					ssaValue *elem = ssa_slice_elem(proc, slice);
-					ssaValue *len  = ssa_slice_len(proc,  slice);
-					ssaValue *cap  = ssa_slice_cap(proc,  slice);
-
-					Type *elem_type = type_deref(ssa_type(elem));
-
-					ssaValue *item_value = ssa_build_expr(proc, item_node);
-					item_value = ssa_emit_conv(proc, item_value, elem_type, true);
-
-					ssaValue *item = ssa_add_local_generated(proc, elem_type);
-					ssa_emit_store(proc, item, item_value);
-
-
-					// NOTE(bill): Check if can append is possible
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, len, cap);
-					ssaBlock *able = ssa_add_block(proc, NULL, "builtin.append.able");
-					ssaBlock *done = ssa_add_block(proc, NULL, "builtin.append.done");
-
-					ssa_emit_if(proc, cond, able, done);
-					proc->curr_block = able;
-
-					// Add new slice item
-					i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
-					ssaValue *byte_count = ssa_make_const_int(proc->module->allocator, item_size);
-
-					ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len);
-					offset = ssa_emit_conv(proc, offset, t_rawptr, true);
-
-					item = ssa_emit_ptr_offset(proc, item, v_zero);
-					item = ssa_emit_conv(proc, item, t_rawptr, true);
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
-					args[0] = offset;
-					args[1] = item;
-					args[2] = byte_count;
-
-					ssa_emit_global_call(proc, "__mem_copy", args, 3);
-
-					// Increment slice length
-					ssaValue *new_len = ssa_emit_arith(proc, Token_Add, len, v_one, t_int);
-					ssaValue *gep = ssa_emit_struct_ep(proc, slice_ptr, 1);
-					ssa_emit_store(proc, gep, new_len);
-
-					ssa_emit_jump(proc, done);
-					proc->curr_block = done;
-
-					return ssa_emit_conv(proc, cond, t_bool, true);
-				} break;
-
-				case BuiltinProc_swizzle: {
-					ssa_emit_comment(proc, make_string("swizzle"));
-					ssaValue *vector = ssa_build_expr(proc, ce->args[0]);
-					isize index_count = ce->args.count-1;
-					if (index_count == 0) {
-						return vector;
-					}
-
-					i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-					isize index = 0;
-					for_array(i, ce->args) {
-						if (i == 0) continue;
-						TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args[i]);
-						GB_ASSERT(is_type_integer(tv->type));
-						GB_ASSERT(tv->value.kind == ExactValue_Integer);
-						indices[index++] = cast(i32)tv->value.value_integer;
-					}
-
-					return ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, vector, indices, index_count));
-
-				} break;
-
-#if 0
-				case BuiltinProc_ptr_offset: {
-					ssa_emit_comment(proc, make_string("ptr_offset"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
-					ssaValue *offset = ssa_build_expr(proc, ce->args[1]);
-					return ssa_emit_ptr_offset(proc, ptr, offset);
-				} break;
-
-				case BuiltinProc_ptr_sub: {
-					ssa_emit_comment(proc, make_string("ptr_sub"));
-					ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]);
-					ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]);
-					Type *ptr_type = base_type(ssa_type(ptr_a));
-					GB_ASSERT(ptr_type->kind == Type_Pointer);
-					isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
-
-					ssaValue *v = ssa_emit_arith(proc, Token_Sub, ptr_a, ptr_b, t_int);
-					if (elem_size > 1) {
-						ssaValue *ez = ssa_make_const_int(proc->module->allocator, elem_size);
-						v = ssa_emit_arith(proc, Token_Quo, v, ez, t_int);
-					}
-
-					return v;
-				} break;
-#endif
-
-				case BuiltinProc_slice_ptr: {
-					ssa_emit_comment(proc, make_string("slice_ptr"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
-					ssaValue *len = ssa_build_expr(proc, ce->args[1]);
-					ssaValue *cap = len;
-
-					len = ssa_emit_conv(proc, len, t_int, true);
-
-					if (ce->args.count == 3) {
-						cap = ssa_build_expr(proc, ce->args[2]);
-						cap = ssa_emit_conv(proc, cap, t_int, true);
-					}
-
-
-					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr)));
-					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 0), ptr);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), cap);
-					return ssa_emit_load(proc, slice);
-				} break;
-
-				case BuiltinProc_min: {
-					ssa_emit_comment(proc, make_string("min"));
-					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
-					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
-					Type *t = base_type(ssa_type(x));
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y);
-					return ssa_emit_select(proc, cond, x, y);
-				} break;
-
-				case BuiltinProc_max: {
-					ssa_emit_comment(proc, make_string("max"));
-					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
-					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
-					Type *t = base_type(ssa_type(x));
-					ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y);
-					return ssa_emit_select(proc, cond, x, y);
-				} break;
-
-				case BuiltinProc_abs: {
-					ssa_emit_comment(proc, make_string("abs"));
-
-					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
-					Type *t = ssa_type(x);
-
-					ssaValue *neg_x = ssa_emit_arith(proc, Token_Sub, v_zero, x, t);
-					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, v_zero);
-					return ssa_emit_select(proc, cond, neg_x, x);
-				} break;
-
-				case BuiltinProc_enum_to_string: {
-					ssa_emit_comment(proc, make_string("enum_to_string"));
-					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
-					Type *t = ssa_type(x);
-					ssaValue *ti = ssa_type_info(proc, t);
-
-
-					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 2);
-					args[0] = ti;
-					args[1] = ssa_emit_conv(proc, x, t_i64);
-					return ssa_emit_global_call(proc, "__enum_to_string", args, 2);
-				} break;
-				}
-			}
-		}
-
-
-		// NOTE(bill): Regular call
-		ssaValue *value = ssa_build_expr(proc, ce->proc);
-		Type *proc_type_ = base_type(ssa_type(value));
-		GB_ASSERT(proc_type_->kind == Type_Proc);
-		auto *type = &proc_type_->Proc;
-
-		isize arg_index = 0;
-
-		isize arg_count = 0;
-		for_array(i, ce->args) {
-			AstNode *a = ce->args[i];
-			Type *at = base_type(type_of_expr(proc->module->info, a));
-			if (at->kind == Type_Tuple) {
-				arg_count += at->Tuple.variable_count;
-			} else {
-				arg_count++;
-			}
-		}
-		ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
-		b32 variadic = proc_type_->Proc.variadic;
-		b32 vari_expand = ce->ellipsis.pos.line != 0;
-
-		for_array(i, ce->args) {
-			ssaValue *a = ssa_build_expr(proc, ce->args[i]);
-			Type *at = ssa_type(a);
-			if (at->kind == Type_Tuple) {
-				for (isize i = 0; i < at->Tuple.variable_count; i++) {
-					Entity *e = at->Tuple.variables[i];
-					ssaValue *v = ssa_emit_struct_ev(proc, a, i);
-					args[arg_index++] = v;
-				}
-			} else {
-				args[arg_index++] = a;
-			}
-		}
-
-		auto *pt = &type->params->Tuple;
-
-		if (variadic) {
-			isize i = 0;
-			for (; i < type->param_count-1; i++) {
-				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
-			}
-			if (!vari_expand) {
-				Type *variadic_type = pt->variables[i]->type;
-				GB_ASSERT(is_type_slice(variadic_type));
-				variadic_type = base_type(variadic_type)->Slice.elem;
-				for (; i < arg_count; i++) {
-					args[i] = ssa_emit_conv(proc, args[i], variadic_type, true);
-				}
-			}
-		} else {
-			for (isize i = 0; i < arg_count; i++) {
-				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
-			}
-		}
-
-		if (variadic && !vari_expand) {
-			ssa_emit_comment(proc, make_string("variadic call argument generation"));
-			gbAllocator allocator = proc->module->allocator;
-			Type *slice_type = pt->variables[type->param_count-1]->type;
-			Type *elem_type  = base_type(slice_type)->Slice.elem;
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-			isize slice_len = arg_count+1 - type->param_count;
-
-			if (slice_len > 0) {
-				ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len));
-
-				for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) {
-					ssaValue *addr = ssa_emit_array_ep(proc, base_array, j);
-					ssa_emit_store(proc, addr, args[i]);
-				}
-
-				ssaValue *base_elem  = ssa_emit_array_ep(proc, base_array, 0);
-				ssaValue *slice_elem = ssa_emit_struct_ep(proc, slice,      0);
-				ssa_emit_store(proc, slice_elem, base_elem);
-				ssaValue *len = ssa_make_const_int(allocator, slice_len);
-				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 1), len);
-				ssa_emit_store(proc, ssa_emit_struct_ep(proc, slice, 2), len);
-			}
-
-			if (args[0]->kind == ssaValue_Constant) {
-				auto *c = &args[0]->Constant;
-				gb_printf_err("%s %d\n", type_to_string(c->type), c->value.kind);
-			}
-
-			arg_count = type->param_count;
-			args[arg_count-1] = ssa_emit_load(proc, slice);
-		}
-
-		return ssa_emit_call(proc, value, args, arg_count);
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-
-	case_ast_node(se, SliceExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-
-	case_ast_node(ie, IndexExpr, expr);
-		return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr);
-	case_end;
-	}
-
-	GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind]));
-	return NULL;
-}
-
-
-ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
-	expr = unparen_expr(expr);
-
-	TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr));
-	GB_ASSERT_NOT_NULL(tv);
-
-	if (tv->value.kind != ExactValue_Invalid) {
-		return ssa_add_module_constant(proc->module, tv->type, tv->value);
-	}
-
-	ssaValue *value = NULL;
-	if (tv->mode == Addressing_Variable) {
-		value = ssa_addr_load(proc, ssa_build_addr(proc, expr));
-	} else {
-		value = ssa_build_single_expr(proc, expr, tv);
-	}
-
-	return value;
-}
-
-ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) {
-	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
-	String name = e->token.string;
-	Entity *parent = e->using_parent;
-	Selection sel = lookup_field(proc->module->allocator, parent->type, name, false);
-	GB_ASSERT(sel.entity != NULL);
-	ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent));
-	ssaValue *v = NULL;
-	if (pv != NULL) {
-		v = *pv;
-	} else {
-		v = ssa_build_addr(proc, e->using_expr).addr;
-	}
-	GB_ASSERT(v != NULL);
-	ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel);
-	map_set(&proc->module->values, hash_pointer(e), var);
-	return var;
-}
-
-ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
-	switch (expr->kind) {
-	case_ast_node(i, Ident, expr);
-		if (ssa_is_blank_ident(expr)) {
-			ssaAddr val = {};
-			return val;
-		}
-
-		Entity *e = entity_of_ident(proc->module->info, expr);
-		TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(expr));
-
-		GB_ASSERT(e->kind != Entity_Constant);
-
-		ssaValue *v = NULL;
-		ssaValue **found = map_get(&proc->module->values, hash_pointer(e));
-		if (found) {
-			v = *found;
-		} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
-			v = ssa_add_using_variable(proc, e);
-		} else if (e->kind == Entity_ImplicitValue) {
-			// TODO(bill): Should a copy be made?
-			v = ssa_find_implicit_value_backing(proc, e->ImplicitValue.id);
-		}
-
-		if (v == NULL) {
-			GB_PANIC("Unknown value: %s, entity: %p %.*s\n", expr_to_string(expr), e, LIT(entity_strings[e->kind]));
-		}
-
-		return ssa_make_addr(v, expr);
-	case_end;
-
-	case_ast_node(pe, ParenExpr, expr);
-		return ssa_build_addr(proc, unparen_expr(expr));
-	case_end;
-
-	case_ast_node(se, SelectorExpr, expr);
-		ssa_emit_comment(proc, make_string("SelectorExpr"));
-		String selector = unparen_expr(se->selector)->Ident.string;
-		Type *type = base_type(type_of_expr(proc->module->info, se->expr));
-
-		if (type == t_invalid) {
-			// NOTE(bill): Imports
-			Entity *imp = entity_of_ident(proc->module->info, se->expr);
-			if (imp != NULL) {
-				GB_ASSERT(imp->kind == Entity_ImportName);
-			}
-			return ssa_build_addr(proc, unparen_expr(se->selector));
-		} else {
-			Selection sel = lookup_field(proc->module->allocator, type, selector, false);
-			GB_ASSERT(sel.entity != NULL);
-
-			ssaValue *a = ssa_build_addr(proc, se->expr).addr;
-			a = ssa_emit_deep_field_gep(proc, type, a, sel);
-			return ssa_make_addr(a, expr);
-		}
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, expr);
-		switch (ue->op.kind) {
-		case Token_Pointer: {
-			return ssa_build_addr(proc, ue->expr);
-		}
-		default:
-			GB_PANIC("Invalid unary expression for ssa_build_addr");
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, expr);
-		switch (be->op.kind) {
-		case Token_as: {
-			ssa_emit_comment(proc, make_string("Cast - as"));
-			// NOTE(bill): Needed for dereference of pointer conversion
-			Type *type = type_of_expr(proc->module->info, expr);
-			ssaValue *v = ssa_add_local_generated(proc, type);
-			ssa_emit_store(proc, v, ssa_emit_conv(proc, ssa_build_expr(proc, be->left), type));
-			return ssa_make_addr(v, expr);
-		}
-		case Token_transmute: {
-			ssa_emit_comment(proc, make_string("Cast - transmute"));
-			// NOTE(bill): Needed for dereference of pointer conversion
-			Type *type = type_of_expr(proc->module->info, expr);
-			ssaValue *v = ssa_add_local_generated(proc, type);
-			ssa_emit_store(proc, v, ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), type));
-			return ssa_make_addr(v, expr);
-		}
-		default:
-			GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string));
-			break;
-		}
-	case_end;
-
-	case_ast_node(ie, IndexExpr, expr);
-		ssa_emit_comment(proc, make_string("IndexExpr"));
-		Type *t = base_type(type_of_expr(proc->module->info, ie->expr));
-		gbAllocator a = proc->module->allocator;
-
-
-		b32 deref = is_type_pointer(t);
-		t = type_deref(t);
-
-		ssaValue *using_addr = NULL;
-		if (!is_type_indexable(t)) {
-			// Using index expression
-			Entity *using_field = find_using_index_expr(t);
-			if (using_field != NULL) {
-				Selection sel = lookup_field(a, t, using_field->token.string, false);
-				ssaValue *e = ssa_build_addr(proc, ie->expr).addr;
-				using_addr = ssa_emit_deep_field_gep(proc, t, e, sel);
-
-				t = using_field->type;
-			}
-		}
-
-
-		switch (t->kind) {
-		case Type_Vector: {
-			ssaValue *vector = NULL;
-			if (using_addr != NULL) {
-				vector = using_addr;
-			} else {
-				vector = ssa_build_addr(proc, ie->expr).addr;
-				if (deref) {
-					vector = ssa_emit_load(proc, vector);
-				}
-			}
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			return ssa_make_addr_vector(vector, index, expr);
-		} break;
-
-		case Type_Array: {
-			ssaValue *array = NULL;
-			if (using_addr != NULL) {
-				array = using_addr;
-			} else {
-				array = ssa_build_addr(proc, ie->expr).addr;
-				if (deref) {
-					array = ssa_emit_load(proc, array);
-				}
-			}
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssaValue *elem = ssa_emit_array_ep(proc, array, index);
-			ssaValue *len = ssa_make_const_int(a, t->Vector.count);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			return ssa_make_addr(elem, expr);
-		} break;
-
-		case Type_Slice: {
-			ssaValue *slice = NULL;
-			if (using_addr != NULL) {
-				slice = ssa_emit_load(proc, using_addr);
-			} else {
-				slice = ssa_build_expr(proc, ie->expr);
-				if (deref) {
-					slice = ssa_emit_load(proc, slice);
-				}
-			}
-			ssaValue *elem = ssa_slice_elem(proc, slice);
-			ssaValue *len = ssa_slice_len(proc, slice);
-			ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-			ssaValue *v = ssa_emit_ptr_offset(proc, elem, index);
-			return ssa_make_addr(v, expr);
-
-		} break;
-
-		case Type_Basic: { // Basic_string
-			TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(ie->expr));
-			ssaValue *str;
-			ssaValue *elem;
-			ssaValue *len;
-			ssaValue *index;
-
-			if (using_addr != NULL) {
-				str = ssa_emit_load(proc, using_addr);
-			} else {
-				str = ssa_build_expr(proc, ie->expr);
-				if (deref) {
-					str = ssa_emit_load(proc, str);
-				}
-			}
-			elem = ssa_string_elem(proc, str);
-			len = ssa_string_len(proc, str);
-
-			index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
-			ssa_emit_bounds_check(proc, ast_node_token(ie->index), index, len);
-
-			return ssa_make_addr(ssa_emit_ptr_offset(proc, elem, index), expr);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(se, SliceExpr, expr);
-		ssa_emit_comment(proc, make_string("SliceExpr"));
-		gbAllocator a = proc->module->allocator;
-		ssaValue *low  = v_zero;
-		ssaValue *high = NULL;
-		ssaValue *max  = NULL;
-
-		if (se->low  != NULL)    low  = ssa_build_expr(proc, se->low);
-		if (se->high != NULL)    high = ssa_build_expr(proc, se->high);
-		if (se->triple_indexed)  max  = ssa_build_expr(proc, se->max);
-		ssaValue *addr = ssa_build_addr(proc, se->expr).addr;
-		ssaValue *base = ssa_emit_load(proc, addr);
-		Type *type = base_type(ssa_type(base));
-
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			addr = base;
-			base = ssa_emit_load(proc, base);
-		}
-
-		// TODO(bill): Cleanup like mad!
-
-		switch (type->kind) {
-		case Type_Slice: {
-			Type *slice_type = type;
-
-			if (high == NULL) high = ssa_slice_len(proc, base);
-			if (max == NULL)  max  = ssa_slice_cap(proc, base);
-			GB_ASSERT(max != NULL);
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
-
-			ssaValue *elem = ssa_slice_elem(proc, base);
-			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-			ssa_emit_store(proc, gep2, cap);
-
-			return ssa_make_addr(slice, expr);
-		}
-
-		case Type_Array: {
-			Type *slice_type = make_type_slice(a, type->Array.elem);
-
-			if (high == NULL) high = ssa_array_len(proc, base);
-			if (max == NULL)  max  = ssa_array_cap(proc, base);
-			GB_ASSERT(max != NULL);
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, max, false);
-
-			ssaValue *elem = ssa_array_elem(proc, addr);
-			ssaValue *len  = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-			ssaValue *cap  = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, slice, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, slice, 1);
-			ssaValue *gep2 = ssa_emit_struct_ep(proc, slice, 2);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-			ssa_emit_store(proc, gep2, cap);
-
-			return ssa_make_addr(slice, expr);
-		}
-
-		case Type_Basic: {
-			GB_ASSERT(type == t_string);
-			if (high == NULL) {
-				high = ssa_string_len(proc, base);
-			}
-
-			ssa_emit_slice_bounds_check(proc, se->open, low, high, high, true);
-
-			ssaValue *elem, *len;
-			len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-
-			elem = ssa_string_elem(proc, base);
-			elem = ssa_emit_ptr_offset(proc, elem, low);
-
-			ssaValue *str = ssa_add_local_generated(proc, t_string);
-			ssaValue *gep0 = ssa_emit_struct_ep(proc, str, 0);
-			ssaValue *gep1 = ssa_emit_struct_ep(proc, str, 1);
-			ssa_emit_store(proc, gep0, elem);
-			ssa_emit_store(proc, gep1, len);
-
-			return ssa_make_addr(str, expr);
-		} break;
-		}
-
-		GB_PANIC("Unknown slicable type");
-	case_end;
-
-	case_ast_node(de, DerefExpr, expr);
-		// TODO(bill): Is a ptr copy needed?
-		ssaValue *addr = ssa_build_expr(proc, de->expr);
-		addr = ssa_emit_ptr_offset(proc, addr, v_zero);
-		return ssa_make_addr(addr, expr);
-	case_end;
-
-	case_ast_node(de, DemaybeExpr, expr);
-		ssa_emit_comment(proc, make_string("DemaybeExpr"));
-		ssaValue *maybe = ssa_build_expr(proc, de->expr);
-		Type *t = default_type(type_of_expr(proc->module->info, expr));
-		GB_ASSERT(is_type_tuple(t));
-
-		ssaValue *result = ssa_add_local_generated(proc, t);
-		ssa_emit_store(proc, result, maybe);
-
-		return ssa_make_addr(result, expr);
-	case_end;
-
-	case_ast_node(ce, CallExpr, expr);
-		ssaValue *e = ssa_build_expr(proc, expr);
-		ssaValue *v = ssa_add_local_generated(proc, ssa_type(e));
-		ssa_emit_store(proc, v, e);
-		return ssa_make_addr(v, expr);
-	case_end;
-	}
-
-	TokenPos token_pos = ast_node_token(expr).pos;
-	GB_PANIC("Unexpected address expression\n"
-	         "\tAstNode: %.*s @ "
-	         "%.*s(%td:%td)\n",
-	         LIT(ast_node_strings[expr->kind]),
-	         LIT(token_pos.file), token_pos.line, token_pos.column);
-
-
-	return ssa_make_addr(NULL, NULL);
-}
-
-void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, TokenKind op) {
-	ssaValue *old_value = ssa_addr_load(proc, lhs);
-	Type *type = ssa_type(old_value);
-
-	ssaValue *change = value;
-	if (is_type_pointer(type) && is_type_integer(ssa_type(value))) {
-		change = ssa_emit_conv(proc, value, default_type(ssa_type(value)));
-	} else {
-		change = ssa_emit_conv(proc, value, type);
-	}
-	ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, type);
-	ssa_addr_store(proc, lhs, new_value);
-}
-
-void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	switch (cond->kind) {
-	case_ast_node(pe, ParenExpr, cond);
-		ssa_build_cond(proc, pe->expr, true_block, false_block);
-		return;
-	case_end;
-
-	case_ast_node(ue, UnaryExpr, cond);
-		if (ue->op.kind == Token_Not) {
-			ssa_build_cond(proc, ue->expr, false_block, true_block);
-			return;
-		}
-	case_end;
-
-	case_ast_node(be, BinaryExpr, cond);
-		if (be->op.kind == Token_CmpAnd) {
-			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and");
-			ssa_build_cond(proc, be->left, block, false_block);
-			proc->curr_block = block;
-			ssa_build_cond(proc, be->right, true_block, false_block);
-			return;
-		} else if (be->op.kind == Token_CmpOr) {
-			ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or");
-			ssa_build_cond(proc, be->left, true_block, block);
-			proc->curr_block = block;
-			ssa_build_cond(proc, be->right, true_block, false_block);
-			return;
-		}
-	case_end;
-	}
-
-	ssaValue *expr = ssa_build_expr(proc, cond);
-	expr = ssa_emit_conv(proc, expr, t_bool);
-	ssa_emit_if(proc, expr, true_block, false_block);
-}
-
-
-
-
-void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) {
-	for_array(i, stmts) {
-		ssa_build_stmt(proc, stmts[i]);
-	}
-}
-
-void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
-	u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
-	defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
-
-	if (node->stmt_state_flags != 0) {
-		u32 in = node->stmt_state_flags;
-		u32 out = proc->module->stmt_state_flags;
-		defer (proc->module->stmt_state_flags = out);
-
-		if (in & StmtStateFlag_bounds_check) {
-			out |= StmtStateFlag_bounds_check;
-			out &= ~StmtStateFlag_no_bounds_check;
-		} else if (in & StmtStateFlag_no_bounds_check) {
-			out |= StmtStateFlag_no_bounds_check;
-			out &= ~StmtStateFlag_bounds_check;
-		}
-	}
-
-
-	switch (node->kind) {
-	case_ast_node(bs, EmptyStmt, node);
-	case_end;
-
-	case_ast_node(us, UsingStmt, node);
-		AstNode *decl = unparen_expr(us->node);
-		if (decl->kind == AstNode_VarDecl) {
-			ssa_build_stmt(proc, decl);
-		}
-	case_end;
-
-	case_ast_node(vd, VarDecl, node);
-		ssaModule *m = proc->module;
-		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-		defer (gb_temp_arena_memory_end(tmp));
-
-		if (vd->values.count == 0) { // declared and zero-initialized
-			for_array(i, vd->names) {
-				AstNode *name = vd->names[i];
-				if (!ssa_is_blank_ident(name)) {
-					ssa_add_local_for_identifier(proc, name, true);
-				}
-			}
-		} else { // Tuple(s)
-			Array<ssaAddr>  lvals;
-			Array<ssaValue *> inits;
-			array_init(&lvals, m->tmp_allocator, vd->names.count);
-			array_init(&inits, m->tmp_allocator, vd->names.count);
-
-			for_array(i, vd->names) {
-				AstNode *name = vd->names[i];
-				ssaAddr lval = ssa_make_addr(NULL, NULL);
-				if (!ssa_is_blank_ident(name)) {
-					ssa_add_local_for_identifier(proc, name, false);
-					lval = ssa_build_addr(proc, name);
-				}
-
-				array_add(&lvals, lval);
-			}
-
-			for_array(i, vd->values) {
-				ssaValue *init = ssa_build_expr(proc, vd->values[i]);
-				Type *t = ssa_type(init);
-				if (t->kind == Type_Tuple) {
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						Entity *e = t->Tuple.variables[i];
-						ssaValue *v = ssa_emit_struct_ev(proc, init, i);
-						array_add(&inits, v);
-					}
-				} else {
-					array_add(&inits, init);
-				}
-			}
-
-
-			for_array(i, inits) {
-				ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
-				ssa_addr_store(proc, lvals[i], v);
-			}
-		}
-	case_end;
-
-	case_ast_node(pd, ProcDecl, node);
-		if (pd->body != NULL) {
-			auto *info = proc->module->info;
-
-			Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
-			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
-			Entity *e = *found;
-
-
-			if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == NULL) {
-				// NOTE(bill): Nothing depends upon it so doesn't need to be built
-				break;
-			}
-
-			// NOTE(bill): Generate a new name
-			// parent.name-guid
-			String original_name = pd->name->Ident.string;
-			String pd_name = original_name;
-			if (pd->link_name.len > 0) {
-				pd_name = pd->link_name;
-			}
-
-			isize name_len = proc->name.len + 1 + pd_name.len + 1 + 10 + 1;
-			u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
-			i32 guid = cast(i32)proc->children.count;
-			name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(pd_name), guid);
-			String name = make_string(name_text, name_len-1);
-
-
-			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-			                                           proc->module, e, e->type, pd->type, pd->body, name);
-
-			value->Proc.tags = pd->tags;
-			value->Proc.parent = proc;
-
-			ssa_module_add_value(proc->module, e, value);
-			array_add(&proc->children, &value->Proc);
-			array_add(&proc->module->procs, value);
-		} else {
-			auto *info = proc->module->info;
-
-			Entity **found = map_get(&info->definitions, hash_pointer(pd->name));
-			GB_ASSERT_MSG(found != NULL, "Unable to find: %.*s", LIT(pd->name->Ident.string));
-			Entity *e = *found;
-
-			// FFI - Foreign function interace
-			String original_name = pd->name->Ident.string;
-			String name = original_name;
-			if (pd->foreign_name.len > 0) {
-				name = pd->foreign_name;
-			}
-
-			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
-			                                           proc->module, e, e->type, pd->type, pd->body, name);
-
-			value->Proc.tags = pd->tags;
-
-			ssa_module_add_value(proc->module, e, value);
-			ssa_build_proc(value, proc);
-
-			if (value->Proc.tags & ProcTag_foreign) {
-				HashKey key = hash_string(name);
-				auto *prev_value = map_get(&proc->module->members, key);
-				if (prev_value == NULL) {
-					// NOTE(bill): Don't do mutliple declarations in the IR
-					map_set(&proc->module->members, key, value);
-				}
-			} else {
-				array_add(&proc->children, &value->Proc);
-			}
-		}
-	case_end;
-
-	case_ast_node(td, TypeDecl, node);
-
-		// NOTE(bill): Generate a new name
-		// parent_proc.name-guid
-		String td_name = td->name->Ident.string;
-		isize name_len = proc->name.len + 1 + td_name.len + 1 + 10 + 1;
-		u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
-		i32 guid = cast(i32)proc->module->members.entries.count;
-		name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(td_name), guid);
-		String name = make_string(name_text, name_len-1);
-
-		Entity **found = map_get(&proc->module->info->definitions, hash_pointer(td->name));
-		GB_ASSERT(found != NULL);
-		Entity *e = *found;
-		ssaValue *value = ssa_make_value_type_name(proc->module->allocator,
-		                                           name, e->type);
-		map_set(&proc->module->type_names, hash_pointer(e->type), name);
-		ssa_gen_global_type_name(proc->module, e, name);
-	case_end;
-
-	case_ast_node(ids, IncDecStmt, node);
-		ssa_emit_comment(proc, make_string("IncDecStmt"));
-		TokenKind op = ids->op.kind;
-		if (op == Token_Increment) {
-			op = Token_Add;
-		} else if (op == Token_Decrement) {
-			op = Token_Sub;
-		}
-		ssaAddr lval = ssa_build_addr(proc, ids->expr);
-		ssaValue *one = ssa_emit_conv(proc, v_one, ssa_addr_type(lval));
-		ssa_build_assign_op(proc, lval, one, op);
-
-	case_end;
-
-	case_ast_node(as, AssignStmt, node);
-		ssa_emit_comment(proc, make_string("AssignStmt"));
-
-		ssaModule *m = proc->module;
-		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
-		defer (gb_temp_arena_memory_end(tmp));
-
-		switch (as->op.kind) {
-		case Token_Eq: {
-			Array<ssaAddr> lvals;
-			array_init(&lvals, m->tmp_allocator);
-
-			for_array(i, as->lhs) {
-				AstNode *lhs = as->lhs[i];
-				ssaAddr lval = {};
-				if (!ssa_is_blank_ident(lhs)) {
-					lval = ssa_build_addr(proc, lhs);
-				}
-				array_add(&lvals, lval);
-			}
-
-			if (as->lhs.count == as->rhs.count) {
-				if (as->lhs.count == 1) {
-					AstNode *rhs = as->rhs[0];
-					ssaValue *init = ssa_build_expr(proc, rhs);
-					ssa_addr_store(proc, lvals[0], init);
-				} else {
-					Array<ssaValue *> inits;
-					array_init(&inits, m->tmp_allocator, lvals.count);
-
-					for_array(i, as->rhs) {
-						ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
-						array_add(&inits, init);
-					}
-
-					for_array(i, inits) {
-						ssa_addr_store(proc, lvals[i], inits[i]);
-					}
-				}
-			} else {
-				Array<ssaValue *> inits;
-				array_init(&inits, m->tmp_allocator, lvals.count);
-
-				for_array(i, as->rhs) {
-					ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
-					Type *t = ssa_type(init);
-					// TODO(bill): refactor for code reuse as this is repeated a bit
-					if (t->kind == Type_Tuple) {
-						for (isize i = 0; i < t->Tuple.variable_count; i++) {
-							Entity *e = t->Tuple.variables[i];
-							ssaValue *v = ssa_emit_struct_ev(proc, init, i);
-							array_add(&inits, v);
-						}
-					} else {
-						array_add(&inits, init);
-					}
-				}
-
-				for_array(i, inits) {
-					ssa_addr_store(proc, lvals[i], inits[i]);
-				}
-			}
-
-		} break;
-
-		default: {
-			// NOTE(bill): Only 1 += 1 is allowed, no tuples
-			// +=, -=, etc
-			i32 op = cast(i32)as->op.kind;
-			op += Token_Add - Token_AddEq; // Convert += to +
-			ssaAddr lhs = ssa_build_addr(proc, as->lhs[0]);
-			ssaValue *value = ssa_build_expr(proc, as->rhs[0]);
-			ssa_build_assign_op(proc, lhs, value, cast(TokenKind)op);
-		} break;
-		}
-	case_end;
-
-	case_ast_node(es, ExprStmt, node);
-		// NOTE(bill): No need to use return value
-		ssa_build_expr(proc, es->expr);
-	case_end;
-
-	case_ast_node(bs, BlockStmt, node);
-		ssa_open_scope(proc);
-		ssa_build_stmt_list(proc, bs->stmts);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-	case_end;
-
-	case_ast_node(ds, DeferStmt, node);
-		ssa_emit_comment(proc, make_string("DeferStmt"));
-		isize scope_index = proc->scope_index;
-		if (ds->stmt->kind == AstNode_BlockStmt) {
-			scope_index--;
-		}
-		ssa_add_defer_node(proc, scope_index, ds->stmt);
-	case_end;
-
-	case_ast_node(rs, ReturnStmt, node);
-		ssa_emit_comment(proc, make_string("ReturnStmt"));
-		ssaValue *v = NULL;
-		auto *return_type_tuple  = &proc->type->Proc.results->Tuple;
-		isize return_count = proc->type->Proc.result_count;
-		if (return_count == 0) {
-			// No return values
-		} else if (return_count == 1) {
-			Entity *e = return_type_tuple->variables[0];
-			v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[0]), e->type);
-		} else {
-			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
-			defer (gb_temp_arena_memory_end(tmp));
-
-			Array<ssaValue *> results;
-			array_init(&results, proc->module->tmp_allocator, return_count);
-
-			for_array(res_index, rs->results) {
-				ssaValue *res = ssa_build_expr(proc, rs->results[res_index]);
-				Type *t = ssa_type(res);
-				if (t->kind == Type_Tuple) {
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						Entity *e = t->Tuple.variables[i];
-						ssaValue *v = ssa_emit_struct_ev(proc, res, i);
-						array_add(&results, v);
-					}
-				} else {
-					array_add(&results, res);
-				}
-			}
-
-			Type *ret_type = proc->type->Proc.results;
-			v = ssa_add_local_generated(proc, ret_type);
-			for_array(i, results) {
-				Entity *e = return_type_tuple->variables[i];
-				ssaValue *res = ssa_emit_conv(proc, results[i], e->type);
-				ssaValue *field = ssa_emit_struct_ep(proc, v, i);
-				ssa_emit_store(proc, field, res);
-			}
-
-			v = ssa_emit_load(proc, v);
-
-		}
-		ssa_emit_return(proc, v);
-
-	case_end;
-
-	case_ast_node(is, IfStmt, node);
-		ssa_emit_comment(proc, make_string("IfStmt"));
-		if (is->init != NULL) {
-			ssaBlock *init = ssa_add_block(proc, node, "if.init");
-			ssa_emit_jump(proc, init);
-			proc->curr_block = init;
-			ssa_build_stmt(proc, is->init);
-		}
-		ssaBlock *then = ssa_add_block(proc, node, "if.then");
-		ssaBlock *done = ssa_add_block(proc, node, "if.done"); // NOTE(bill): Append later
-		ssaBlock *else_ = done;
-		if (is->else_stmt != NULL) {
-			else_ = ssa_add_block(proc, is->else_stmt, "if.else");
-		}
-
-		ssa_build_cond(proc, is->cond, then, else_);
-		proc->curr_block = then;
-
-		ssa_open_scope(proc);
-		ssa_build_stmt(proc, is->body);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-		ssa_emit_jump(proc, done);
-
-		if (is->else_stmt != NULL) {
-			proc->curr_block = else_;
-
-			ssa_open_scope(proc);
-			ssa_build_stmt(proc, is->else_stmt);
-			ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-			ssa_emit_jump(proc, done);
-		}
-		proc->curr_block = done;
-	case_end;
-
-	case_ast_node(fs, ForStmt, node);
-		ssa_emit_comment(proc, make_string("ForStmt"));
-		if (fs->init != NULL) {
-			ssaBlock *init = ssa_add_block(proc, node, "for.init");
-			ssa_emit_jump(proc, init);
-			proc->curr_block = init;
-			ssa_build_stmt(proc, fs->init);
-		}
-		ssaBlock *body = ssa_add_block(proc, node, "for.body");
-		ssaBlock *done = ssa_add_block(proc, node, "for.done"); // NOTE(bill): Append later
-
-		ssaBlock *loop = body;
-
-		if (fs->cond != NULL) {
-			loop = ssa_add_block(proc, node, "for.loop");
-		}
-		ssaBlock *cont = loop;
-		if (fs->post != NULL) {
-			cont = ssa_add_block(proc, node, "for.post");
-
-		}
-		ssa_emit_jump(proc, loop);
-		proc->curr_block = loop;
-		if (loop != body) {
-			ssa_build_cond(proc, fs->cond, body, done);
-			proc->curr_block = body;
-		}
-
-		ssa_push_target_list(proc, done, cont, NULL);
-
-		ssa_open_scope(proc);
-		ssa_build_stmt(proc, fs->body);
-		ssa_close_scope(proc, ssaDeferExit_Default, NULL);
-
-		ssa_pop_target_list(proc);
-		ssa_emit_jump(proc, cont);
-
-		if (fs->post != NULL) {
-			proc->curr_block = cont;
-			ssa_build_stmt(proc, fs->post);
-			ssa_emit_jump(proc, loop);
-		}
-
-
-		proc->curr_block = done;
-
-	case_end;
-
-	case_ast_node(ms, MatchStmt, node);
-		ssa_emit_comment(proc, make_string("MatchStmt"));
-		if (ms->init != NULL) {
-			ssa_build_stmt(proc, ms->init);
-		}
-		ssaValue *tag = v_true;
-		if (ms->tag != NULL) {
-			tag = ssa_build_expr(proc, ms->tag);
-		}
-		ssaBlock *done = ssa_add_block(proc, node, "match.done"); // NOTE(bill): Append later
-
-		ast_node(body, BlockStmt, ms->body);
-
-		AstNodeArray default_stmts = {};
-		ssaBlock *default_fall = NULL;
-		ssaBlock *default_block = NULL;
-
-		ssaBlock *fall = NULL;
-		b32 append_fall = false;
-
-		isize case_count = body->stmts.count;
-		for_array(i, body->stmts) {
-			AstNode *clause = body->stmts[i];
-			ssaBlock *body = fall;
-
-			ast_node(cc, CaseClause, clause);
-
-			if (body == NULL) {
-				if (cc->list.count == 0) {
-					body = ssa_add_block(proc, clause, "match.dflt.body");
-				} else {
-					body = ssa_add_block(proc, clause, "match.case.body");
-				}
-			}
-			if (append_fall && body == fall) {
-				append_fall = false;
-			}
-
-			fall = done;
-			if (i+1 < case_count) {
-				append_fall = true;
-				fall = ssa_add_block(proc, clause, "match.fall.body");
-			}
-
-			if (cc->list.count == 0) {
-				// default case
-				default_stmts = cc->stmts;
-				default_fall  = fall;
-				default_block = body;
-				continue;
-			}
-
-			ssaBlock *next_cond = NULL;
-			for_array(j, cc->list) {
-				AstNode *expr = cc->list[j];
-				next_cond = ssa_add_block(proc, clause, "match.case.next");
-
-				ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, ssa_build_expr(proc, expr));
-				ssa_emit_if(proc, cond, body, next_cond);
-				proc->curr_block = next_cond;
-			}
-			proc->curr_block = body;
-
-			ssa_push_target_list(proc, done, NULL, fall);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, body);
-			ssa_pop_target_list(proc);
-
-			ssa_emit_jump(proc, done);
-			proc->curr_block = next_cond;
-		}
-
-		if (default_block != NULL) {
-			ssa_emit_jump(proc, default_block);
-			proc->curr_block = default_block;
-
-			ssa_push_target_list(proc, done, NULL, default_fall);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, default_stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
-			ssa_pop_target_list(proc);
-		}
-
-		ssa_emit_jump(proc, done);
-		proc->curr_block = done;
-	case_end;
-
-
-	case_ast_node(ms, TypeMatchStmt, node);
-		ssa_emit_comment(proc, make_string("TypeMatchStmt"));
-		gbAllocator allocator = proc->module->allocator;
-
-		ssaValue *parent = ssa_build_expr(proc, ms->tag);
-		Type *union_type = type_deref(ssa_type(parent));
-		GB_ASSERT(is_type_union(union_type));
-
-		ssa_emit_comment(proc, make_string("get union's tag"));
-		ssaValue *tag_index = ssa_emit_struct_ep(proc, parent, 1);
-		tag_index = ssa_emit_load(proc, tag_index);
-
-		ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
-
-		ssaBlock *start_block = ssa_add_block(proc, node, "type-match.case.first");
-		ssa_emit_jump(proc, start_block);
-		proc->curr_block = start_block;
-
-		ssaBlock *done = ssa_add_block(proc, node, "type-match.done"); // NOTE(bill): Append later
-
-		ast_node(body, BlockStmt, ms->body);
-
-
-		String tag_var_name = ms->var->Ident.string;
-
-		AstNodeArray default_stmts = {};
-		ssaBlock *default_block = NULL;
-
-		isize case_count = body->stmts.count;
-		for_array(i, body->stmts) {
-			AstNode *clause = body->stmts[i];
-			ast_node(cc, CaseClause, clause);
-
-			if (cc->list.count == 0) {
-				// default case
-				default_stmts = cc->stmts;
-				default_block = ssa_add_block(proc, clause, "type-match.dflt.body");
-				continue;
-			}
-
-
-			ssaBlock *body = ssa_add_block(proc, clause, "type-match.case.body");
-
-			Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause));
-			Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
-			GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name));
-			ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
-			ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type);
-			ssa_emit_store(proc, tag_var, data_ptr);
-
-
-
-			Type *bt = type_deref(tag_var_entity->type);
-			ssaValue *index = NULL;
-			Type *ut = base_type(union_type);
-			GB_ASSERT(ut->Record.kind == TypeRecord_Union);
-			for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) {
-				Entity *f = base_type(union_type)->Record.fields[field_index];
-				if (are_types_identical(f->type, bt)) {
-					index = ssa_make_const_int(allocator, field_index);
-					break;
-				}
-			}
-			GB_ASSERT(index != NULL);
-
-			ssaBlock *next_cond = ssa_add_block(proc, clause, "type-match.case.next");
-			ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag_index, index);
-			ssa_emit_if(proc, cond, body, next_cond);
-			proc->curr_block = next_cond;
-
-			proc->curr_block = body;
-
-			ssa_push_target_list(proc, done, NULL, NULL);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, body);
-			ssa_pop_target_list(proc);
-
-			ssa_emit_jump(proc, done);
-			proc->curr_block = next_cond;
-		}
-
-		if (default_block != NULL) {
-			ssa_emit_jump(proc, default_block);
-			proc->curr_block = default_block;
-
-			ssa_push_target_list(proc, done, NULL, NULL);
-			ssa_open_scope(proc);
-			ssa_build_stmt_list(proc, default_stmts);
-			ssa_close_scope(proc, ssaDeferExit_Default, default_block);
-			ssa_pop_target_list(proc);
-		}
-
-		ssa_emit_jump(proc, done);
-		proc->curr_block = done;
-	case_end;
-
-	case_ast_node(bs, BranchStmt, node);
-		ssaBlock *block = NULL;
-		switch (bs->token.kind) {
-		case Token_break:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->break_;
-			}
-			break;
-		case Token_continue:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->continue_;
-			}
-			break;
-		case Token_fallthrough:
-			for (ssaTargetList *t = proc->target_list; t != NULL && block == NULL; t = t->prev) {
-				block = t->fallthrough_;
-			}
-			break;
-		}
-		if (block != NULL) {
-			ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block);
-		}
-		switch (bs->token.kind) {
-		case Token_break:       ssa_emit_comment(proc, make_string("break"));       break;
-		case Token_continue:    ssa_emit_comment(proc, make_string("continue"));    break;
-		case Token_fallthrough: ssa_emit_comment(proc, make_string("fallthrough")); break;
-		}
-		ssa_emit_jump(proc, block);
-		ssa_emit_unreachable(proc);
-	case_end;
-
-
-
-	case_ast_node(pa, PushAllocator, node);
-		ssa_emit_comment(proc, make_string("PushAllocator"));
-		ssa_open_scope(proc);
-		defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL));
-
-		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
-		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
-		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-
-		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
-
-		ssaValue *gep = ssa_emit_struct_ep(proc, context_ptr, 1);
-		ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr));
-
-		ssa_build_stmt(proc, pa->body);
-
-	case_end;
-
-
-	case_ast_node(pa, PushContext, node);
-		ssa_emit_comment(proc, make_string("PushContext"));
-		ssa_open_scope(proc);
-		defer (ssa_close_scope(proc, ssaDeferExit_Default, NULL));
-
-		ssaValue *context_ptr = ssa_find_implicit_value_backing(proc, ImplicitValue_context);
-		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
-		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-
-		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
-
-		ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr));
-
-		ssa_build_stmt(proc, pa->body);
-	case_end;
-
-
-	}
-}
-
-

+ 0 - 671
src/ssa/codegen.cpp

@@ -1,671 +0,0 @@
-struct ssaGen {
-	ssaModule module;
-	gbFile output_file;
-};
-
-b32 ssa_gen_init(ssaGen *s, Checker *c) {
-	if (global_error_collector.count != 0) {
-		return false;
-	}
-
-	isize tc = c->parser->total_token_count;
-	if (tc < 2) {
-		return false;
-	}
-
-	ssa_init_module(&s->module, c);
-	s->module.generate_debug_info = false;
-
-	// TODO(bill): generate appropriate output name
-	int pos = cast(int)string_extension_position(c->parser->init_fullpath);
-	gbFileError err = gb_file_create(&s->output_file, gb_bprintf("%.*s.ll", pos, c->parser->init_fullpath.text));
-	if (err != gbFileError_None) {
-		return false;
-	}
-
-	return true;
-}
-
-void ssa_gen_destroy(ssaGen *s) {
-	ssa_destroy_module(&s->module);
-	gb_file_close(&s->output_file);
-}
-
-String ssa_mangle_name(ssaGen *s, String path, String name) {
-	// NOTE(bill): prefix names not in the init scope
-	// TODO(bill): make robust and not just rely on the file's name
-
-	ssaModule *m = &s->module;
-	CheckerInfo *info = m->info;
-	gbAllocator a = m->allocator;
-	AstFile *file = *map_get(&info->files, hash_string(path));
-
-	char *str = gb_alloc_array(a, char, path.len+1);
-	gb_memmove(str, path.text, path.len);
-	str[path.len] = 0;
-	for (isize i = 0; i < path.len; i++) {
-		if (str[i] == '\\') {
-			str[i] = '/';
-		}
-	}
-
-	char const *base = gb_path_base_name(str);
-	char const *ext = gb_path_extension(base);
-	isize base_len = ext-1-base;
-
-	isize max_len = base_len + 1 + 10 + 1 + name.len;
-	u8 *new_name = gb_alloc_array(a, u8, max_len);
-	isize new_name_len = gb_snprintf(
-		cast(char *)new_name, max_len,
-		"%.*s-%u.%.*s",
-		cast(int)base_len, base,
-		file->id,
-		LIT(name));
-
-	return make_string(new_name, new_name_len-1);
-}
-
-
-void ssa_gen_tree(ssaGen *s) {
-	ssaModule *m = &s->module;
-	CheckerInfo *info = m->info;
-	gbAllocator a = m->allocator;
-
-	if (v_zero == NULL) {
-		v_zero   = ssa_make_const_int (m->allocator, 0);
-		v_one    = ssa_make_const_int (m->allocator, 1);
-		v_zero32 = ssa_make_const_i32 (m->allocator, 0);
-		v_one32  = ssa_make_const_i32 (m->allocator, 1);
-		v_two32  = ssa_make_const_i32 (m->allocator, 2);
-		v_false  = ssa_make_const_bool(m->allocator, false);
-		v_true   = ssa_make_const_bool(m->allocator, true);
-	}
-
-	isize global_variable_max_count = 0;
-	Entity *entry_point = NULL;
-
-	for_array(i, info->entities.entries) {
-		auto *entry = &info->entities.entries[i];
-		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
-		String name = e->token.string;
-		if (e->kind == Entity_Variable) {
-			global_variable_max_count++;
-		} else if (e->kind == Entity_Procedure) {
-			if (e->scope->is_init && name == "main") {
-				entry_point = e;
-			}
-		}
-	}
-
-	struct ssaGlobalVariable {
-		ssaValue *var, *init;
-		DeclInfo *decl;
-	};
-	Array<ssaGlobalVariable> global_variables;
-	array_init(&global_variables, m->tmp_allocator, global_variable_max_count);
-
-	m->min_dep_map = generate_minimum_dependency_map(info, entry_point);
-
-	for_array(i, info->entities.entries) {
-		auto *entry = &info->entities.entries[i];
-		Entity *e = cast(Entity *)entry->key.ptr;
-		String name = e->token.string;
-		DeclInfo *decl = entry->value;
-		Scope *scope = e->scope;
-
-		if (!scope->is_file) {
-			continue;
-		}
-
-		if (map_get(&m->min_dep_map, hash_pointer(e)) == NULL) {
-			// NOTE(bill): Nothing depends upon it so doesn't need to be built
-			continue;
-		}
-
-		if (!scope->is_global && !scope->is_init) {
-			name = ssa_mangle_name(s, e->token.pos.file, name);
-		}
-
-
-		switch (e->kind) {
-		case Entity_TypeName:
-			GB_ASSERT(e->type->kind == Type_Named);
-			map_set(&m->type_names, hash_pointer(e->type), name);
-			ssa_gen_global_type_name(m, e, name);
-			break;
-
-		case Entity_Variable: {
-			ssaValue *g = ssa_make_value_global(a, e, NULL);
-			if (decl->var_decl_tags & VarDeclTag_thread_local) {
-				g->Global.is_thread_local = true;
-			}
-			ssaGlobalVariable var = {};
-			var.var = g;
-			var.decl = decl;
-
-			if (decl->init_expr != NULL) {
-				TypeAndValue *tav = map_get(&info->types, hash_pointer(decl->init_expr));
-				if (tav != NULL) {
-					if (tav->value.kind != ExactValue_Invalid) {
-						ExactValue v = tav->value;
-						// if (v.kind != ExactValue_String) {
-							g->Global.value = ssa_add_module_constant(m, tav->type, v);
-						// }
-					}
-				}
-			}
-
-			if (g->Global.value == NULL) {
-				array_add(&global_variables, var);
-			}
-
-			map_set(&m->values, hash_pointer(e), g);
-			map_set(&m->members, hash_string(name), g);
-		} break;
-
-		case Entity_Procedure: {
-			auto *pd = &decl->proc_decl->ProcDecl;
-			String original_name = name;
-			AstNode *body = pd->body;
-			if (pd->tags & ProcTag_foreign) {
-				name = pd->name->Ident.string;
-			}
-			if (pd->foreign_name.len > 0) {
-				name = pd->foreign_name;
-			} else if (pd->link_name.len > 0) {
-				name = pd->link_name;
-			}
-
-			ssaValue *p = ssa_make_value_procedure(a, m, e, e->type, decl->type_expr, body, name);
-			p->Proc.tags = pd->tags;
-
-			map_set(&m->values, hash_pointer(e), p);
-			HashKey hash_name = hash_string(name);
-			if (map_get(&m->members, hash_name) == NULL) {
-				map_set(&m->members, hash_name, p);
-			}
-		} break;
-		}
-	}
-
-	for_array(i, m->members.entries) {
-		auto *entry = &m->members.entries[i];
-		ssaValue *v = entry->value;
-		if (v->kind == ssaValue_Proc)
-			ssa_build_proc(v, NULL);
-	}
-
-	ssaDebugInfo *compile_unit = m->debug_info.entries[0].value;
-	GB_ASSERT(compile_unit->kind == ssaDebugInfo_CompileUnit);
-	ssaDebugInfo *all_procs = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_AllProcs);
-
-	isize all_proc_max_count = 0;
-	for_array(i, m->debug_info.entries) {
-		auto *entry = &m->debug_info.entries[i];
-		ssaDebugInfo *di = entry->value;
-		di->id = i;
-		if (di->kind == ssaDebugInfo_Proc) {
-			all_proc_max_count++;
-		}
-	}
-
-	array_init(&all_procs->AllProcs.procs, m->allocator, all_proc_max_count);
-	map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped
-	compile_unit->CompileUnit.all_procs = all_procs;
-
-
-	for_array(i, m->debug_info.entries) {
-		auto *entry = &m->debug_info.entries[i];
-		ssaDebugInfo *di = entry->value;
-		di->id = i;
-		if (di->kind == ssaDebugInfo_Proc) {
-			array_add(&all_procs->AllProcs.procs, di);
-		}
-	}
-
-
-	{ // Startup Runtime
-		// Cleanup(bill): probably better way of doing code insertion
-		String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME);
-		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
-		                                 NULL, 0,
-		                                 NULL, 0, false);
-		AstNode *body = gb_alloc_item(a, AstNode);
-		ssaValue *p = ssa_make_value_procedure(a, m, NULL, proc_type, NULL, body, name);
-		Token token = {};
-		token.string = name;
-		Entity *e = make_entity_procedure(a, NULL, token, proc_type);
-
-		map_set(&m->values, hash_pointer(e), p);
-		map_set(&m->members, hash_string(name), p);
-
-		ssaProcedure *proc = &p->Proc;
-		proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea?
-
-		ssa_begin_procedure_body(proc);
-
-		// TODO(bill): Should do a dependency graph do check which order to initialize them in?
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables[i];
-			if (var->decl->init_expr != NULL) {
-				var->init = ssa_build_expr(proc, var->decl->init_expr);
-			}
-		}
-
-		// NOTE(bill): Initialize constants first
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables[i];
-			if (var->init != NULL) {
-				if (var->init->kind == ssaValue_Constant) {
-					ssa_emit_store(proc, var->var, var->init);
-				}
-			}
-		}
-
-		for_array(i, global_variables) {
-			ssaGlobalVariable *var = &global_variables[i];
-			if (var->init != NULL) {
-				if (var->init->kind != ssaValue_Constant) {
-					ssa_emit_store(proc, var->var, var->init);
-				}
-			}
-		}
-
-		{ // NOTE(bill): Setup type_info data
-			// TODO(bill): Try and make a lot of this constant aggregate literals in LLVM IR
-			ssaValue *type_info_data = NULL;
-			ssaValue *type_info_member_data = NULL;
-
-			ssaValue **found = NULL;
-			found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME)));
-			GB_ASSERT(found != NULL);
-			type_info_data = *found;
-
-			found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME)));
-			GB_ASSERT(found != NULL);
-			type_info_member_data = *found;
-
-			CheckerInfo *info = proc->module->info;
-
-			// Useful types
-			Type *t_i64_slice_ptr    = make_type_pointer(a, make_type_slice(a, t_i64));
-			Type *t_string_slice_ptr = make_type_pointer(a, make_type_slice(a, t_string));
-
-			auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * {
-				i32 index = cast(i32)ssa_type_info_index(proc->module->info, type);
-				// gb_printf_err("%d %s\n", index, type_to_string(type));
-				return ssa_emit_array_ep(proc, type_info_data, index);
-			};
-
-			i32 type_info_member_index = 0;
-
-			auto type_info_member_offset = [](ssaProcedure *proc, ssaValue *data, isize count, i32 *index) -> ssaValue * {
-				ssaValue *offset = ssa_emit_array_ep(proc, data, *index);
-				*index += count;
-				return offset;
-			};
-
-
-
-			for_array(type_info_map_index, info->type_info_map.entries) {
-				auto *entry = &info->type_info_map.entries[type_info_map_index];
-				Type *t = cast(Type *)cast(uintptr)entry->key.key;
-				t = default_type(t);
-				isize entry_index = entry->value;
-
-				ssaValue *tag = NULL;
-
-				switch (t->kind) {
-				case Type_Named: {
-					tag = ssa_add_local_generated(proc, t_type_info_named);
-
-					// TODO(bill): Which is better? The mangled name or actual name?
-					ssaValue *name = ssa_make_const_string(a, t->Named.type_name->token.string);
-					ssaValue *gtip = get_type_info_ptr(proc, type_info_data, t->Named.base);
-
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), name);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), gtip);
-				} break;
-
-				case Type_Basic:
-					switch (t->Basic.kind) {
-					case Basic_bool:
-						tag = ssa_add_local_generated(proc, t_type_info_boolean);
-						break;
-					case Basic_i8:
-					case Basic_i16:
-					case Basic_i32:
-					case Basic_i64:
-					case Basic_u8:
-					case Basic_u16:
-					case Basic_u32:
-					case Basic_u64:
-					case Basic_int:
-					case Basic_uint: {
-						tag = ssa_add_local_generated(proc, t_type_info_integer);
-						b32 is_unsigned = (t->Basic.flags & BasicFlag_Unsigned) != 0;
-						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-						ssaValue *is_signed = ssa_make_const_bool(a, !is_unsigned);
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), is_signed);
-					} break;
-
-					case Basic_f32:
-					case Basic_f64: {
-						tag = ssa_add_local_generated(proc, t_type_info_float);
-						ssaValue *bits = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), bits);
-					} break;
-
-					case Basic_rawptr:
-						tag = ssa_add_local_generated(proc, t_type_info_pointer);
-						break;
-
-					case Basic_string:
-						tag = ssa_add_local_generated(proc, t_type_info_string);
-						break;
-
-					case Basic_any:
-						tag = ssa_add_local_generated(proc, t_type_info_any);
-						break;
-					}
-					break;
-
-				case Type_Pointer: {
-					tag = ssa_add_local_generated(proc, t_type_info_pointer);
-					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-				} break;
-				case Type_Maybe: {
-					tag = ssa_add_local_generated(proc, t_type_info_maybe);
-					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Maybe.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-				} break;
-				case Type_Array: {
-					tag = ssa_add_local_generated(proc, t_type_info_array);
-					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Array.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
-					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Array.count));
-
-				} break;
-				case Type_Slice: {
-					tag = ssa_add_local_generated(proc, t_type_info_slice);
-					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Slice.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-				} break;
-				case Type_Vector: {
-					tag = ssa_add_local_generated(proc, t_type_info_vector);
-					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem);
-					ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 0), gep);
-
-					isize ez = type_size_of(m->sizes, a, t->Vector.elem);
-					ssaValue *elem_size = ssa_emit_struct_ep(proc, tag, 1);
-					ssa_emit_store(proc, elem_size, ssa_make_const_int(a, ez));
-
-					ssaValue *count = ssa_emit_struct_ep(proc, tag, 2);
-					ssa_emit_store(proc, count, ssa_make_const_int(a, t->Vector.count));
-
-				} break;
-				case Type_Record: {
-					switch (t->Record.kind) {
-					case TypeRecord_Struct: {
-						tag = ssa_add_local_generated(proc, t_type_info_struct);
-
-						{
-							ssaValue *packed  = ssa_make_const_bool(a, t->Record.struct_is_packed);
-							ssaValue *ordered = ssa_make_const_bool(a, t->Record.struct_is_ordered);
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1), size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 3), packed);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 4), ordered);
-						}
-
-						ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
-
-						type_set_offsets(m->sizes, a, t); // NOTE(bill): Just incase the offsets have not been set yet
-						for (isize source_index = 0; source_index < t->Record.field_count; source_index++) {
-							// TODO(bill): Order fields in source order not layout order
-							Entity *f = t->Record.fields_in_src_order[source_index];
-							ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
-							i64 foffset = t->Record.struct_offsets[f->Variable.field_index];
-							GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
-
-							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, source_index));
-							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
-
-							if (f->token.string.len > 0) {
-								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-							}
-							ssa_emit_store(proc, type_info, tip);
-							ssa_emit_store(proc, offset, ssa_make_const_int(a, foffset));
-						}
-
-						Type *slice_type = make_type_slice(a, t_type_info_member);
-						Type *slice_type_ptr = make_type_pointer(a, slice_type);
-						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
-
-						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-						ssa_emit_store(proc, elem, memory);
-						ssa_emit_store(proc, len, field_count);
-						ssa_emit_store(proc, cap, field_count);
-					} break;
-					case TypeRecord_Union:
-						tag = ssa_add_local_generated(proc, t_type_info_union);
-						{
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
-						}
-						break;
-					case TypeRecord_RawUnion: {
-						tag = ssa_add_local_generated(proc, t_type_info_raw_union);
-						{
-							ssaValue *size    = ssa_make_const_int(a, type_size_of(m->sizes, a, t));
-							ssaValue *align   = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 1),  size);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2),  align);
-						}
-
-						ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Record.field_count, &type_info_member_index);
-
-						for (isize i = 0; i < t->Record.field_count; i++) {
-							ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
-							ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-							ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-							ssaValue *offset    = ssa_emit_struct_ep(proc, field, 2);
-
-							Entity *f = t->Record.fields[i];
-							ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
-
-							if (f->token.string.len > 0) {
-								ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-							}
-							ssa_emit_store(proc, type_info, tip);
-							ssa_emit_store(proc, offset, ssa_make_const_int(a, 0));
-						}
-
-						Type *slice_type = make_type_slice(a, t_type_info_member);
-						Type *slice_type_ptr = make_type_pointer(a, slice_type);
-						ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-						ssaValue *field_count = ssa_make_const_int(a, t->Record.field_count);
-
-						ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-						ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-						ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-						ssa_emit_store(proc, elem, memory);
-						ssa_emit_store(proc, len, field_count);
-						ssa_emit_store(proc, cap, field_count);
-					} break;
-					case TypeRecord_Enum: {
-						tag = ssa_add_local_generated(proc, t_type_info_enum);
-						Type *enum_base = t->Record.enum_base;
-						if (enum_base == NULL) {
-							enum_base = t_int;
-						}
-						ssaValue *base = ssa_emit_struct_ep(proc, tag, 0);
-						ssa_emit_store(proc, base, get_type_info_ptr(proc, type_info_data, enum_base));
-
-						if (t->Record.other_field_count > 0) {
-							Entity **fields = t->Record.other_fields;
-							isize count = t->Record.other_field_count;
-							ssaValue *value_array = NULL;
-							ssaValue *name_array = NULL;
-
-
-							{
-								Token token = {Token_Identifier};
-								i32 id = cast(i32)entry_index;
-								char name_base[] = "__$enum_values";
-								isize name_len = gb_size_of(name_base) + 10;
-								token.string.text = gb_alloc_array(a, u8, name_len);
-								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
-								                               "%s-%d", name_base, id)-1;
-								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_i64, count));
-								value_array = ssa_make_value_global(a, e, NULL);
-								value_array->Global.is_private = true;
-								ssa_module_add_value(m, e, value_array);
-								map_set(&m->members, hash_string(token.string), value_array);
-							}
-							{
-								Token token = {Token_Identifier};
-								i32 id = cast(i32)entry_index;
-								char name_base[] = "__$enum_names";
-								isize name_len = gb_size_of(name_base) + 10;
-								token.string.text = gb_alloc_array(a, u8, name_len);
-								token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
-								                               "%s-%d", name_base, id)-1;
-								Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count));
-								name_array = ssa_make_value_global(a, e, NULL);
-								name_array->Global.is_private = true;
-								ssa_module_add_value(m, e, name_array);
-								map_set(&m->members, hash_string(token.string), name_array);
-							}
-
-							for (isize i = 0; i < count; i++) {
-								ssaValue *value_gep = ssa_emit_struct_ep(proc, value_array, i);
-								ssaValue *name_gep  = ssa_emit_struct_ep(proc, name_array, i);
-
-								ssa_emit_store(proc, value_gep, ssa_make_const_i64(a, fields[i]->Constant.value.value_integer));
-								ssa_emit_store(proc, name_gep,  ssa_make_const_string(a, fields[i]->token.string));
-							}
-
-							ssaValue *v_count = ssa_make_const_int(a, count);
-
-
-							ssaValue *values = ssa_emit_struct_ep(proc, tag, 1);
-							ssaValue *names  = ssa_emit_struct_ep(proc, tag, 2);
-							ssaValue *value_slice = ssa_add_local_generated(proc, type_deref(t_i64_slice_ptr));
-							ssaValue *name_slice  = ssa_add_local_generated(proc, type_deref(t_string_slice_ptr));
-
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 0), ssa_array_elem(proc, value_array));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 1), v_count);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, value_slice, 2), v_count);
-
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 0), ssa_array_elem(proc, name_array));
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 1), v_count);
-							ssa_emit_store(proc, ssa_emit_struct_ep(proc, name_slice, 2), v_count);
-
-							ssa_emit_store(proc, values, ssa_emit_load(proc, value_slice));
-							ssa_emit_store(proc, names,  ssa_emit_load(proc, name_slice));
-						}
-					} break;
-					}
-				} break;
-
-				case Type_Tuple: {
-					tag = ssa_add_local_generated(proc, t_type_info_tuple);
-
-					{
-						ssaValue *align = ssa_make_const_int(a, type_align_of(m->sizes, a, t));
-						ssa_emit_store(proc, ssa_emit_struct_ep(proc, tag, 2), align);
-					}
-
-					ssaValue *memory = type_info_member_offset(proc, type_info_member_data, t->Tuple.variable_count, &type_info_member_index);
-
-					for (isize i = 0; i < t->Tuple.variable_count; i++) {
-						ssaValue *field     = ssa_emit_ptr_offset(proc, memory, ssa_make_const_int(a, i));
-						ssaValue *name      = ssa_emit_struct_ep(proc, field, 0);
-						ssaValue *type_info = ssa_emit_struct_ep(proc, field, 1);
-						// NOTE(bill): offset is not used for tuples
-
-						Entity *f = t->Tuple.variables[i];
-						ssaValue *tip = get_type_info_ptr(proc, type_info_data, f->type);
-
-						if (f->token.string.len > 0) {
-							ssa_emit_store(proc, name, ssa_make_const_string(a, f->token.string));
-						}
-						ssa_emit_store(proc, type_info, tip);
-					}
-
-					Type *slice_type = make_type_slice(a, t_type_info_member);
-					Type *slice_type_ptr = make_type_pointer(a, slice_type);
-					ssaValue *slice = ssa_emit_struct_ep(proc, tag, 0);
-					ssaValue *variable_count = ssa_make_const_int(a, t->Tuple.variable_count);
-
-					ssaValue *elem = ssa_emit_struct_ep(proc, slice, 0);
-					ssaValue *len  = ssa_emit_struct_ep(proc, slice, 1);
-					ssaValue *cap  = ssa_emit_struct_ep(proc, slice, 2);
-
-					ssa_emit_store(proc, elem, memory);
-					ssa_emit_store(proc, len, variable_count);
-					ssa_emit_store(proc, cap, variable_count);
-				} break;
-
-				case Type_Proc: {
-					tag = ssa_add_local_generated(proc, t_type_info_procedure);
-
-					ssaValue *params   = ssa_emit_struct_ep(proc, tag, 0);
-					ssaValue *results  = ssa_emit_struct_ep(proc, tag, 1);
-					ssaValue *variadic = ssa_emit_struct_ep(proc, tag, 2);
-
-					if (t->Proc.params) {
-						ssa_emit_store(proc, params, get_type_info_ptr(proc, type_info_data, t->Proc.params));
-					}
-					if (t->Proc.results) {
-						ssa_emit_store(proc, results, get_type_info_ptr(proc, type_info_data, t->Proc.results));
-					}
-					ssa_emit_store(proc, variadic, ssa_make_const_bool(a, t->Proc.variadic));
-
-					// TODO(bill): Type_Info for procedures
-				} break;
-				}
-
-				if (tag != NULL) {
-					ssaValue *gep = ssa_emit_array_ep(proc, type_info_data, entry_index);
-					ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info);
-					ssa_emit_store(proc, gep, val);
-				}
-			}
-		}
-
-		ssa_end_procedure_body(proc);
-	}
-
-	for_array(i, m->procs) {
-		ssa_build_proc(m->procs[i], m->procs[i]->Proc.parent);
-	}
-
-
-	// m->layout = make_string("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
-}

+ 0 - 48
src/ssa/debug.cpp

@@ -1,48 +0,0 @@
-ssaDebugInfo *ssa_add_debug_info_file(ssaProcedure *proc, AstFile *file) {
-	if (!proc->module->generate_debug_info) {
-		return NULL;
-	}
-
-	GB_ASSERT(file != NULL);
-	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_File);
-	di->File.file = file;
-
-	String filename = file->tokenizer.fullpath;
-	String directory = filename;
-	isize slash_index = 0;
-	for (isize i = filename.len-1; i >= 0; i--) {
-		if (filename.text[i] == '\\' ||
-		    filename.text[i] == '/') {
-			break;
-		}
-		slash_index = i;
-	}
-	directory.len = slash_index-1;
-	filename.text = filename.text + slash_index;
-	filename.len -= slash_index;
-
-
-	di->File.filename = filename;
-	di->File.directory = directory;
-
-	map_set(&proc->module->debug_info, hash_pointer(file), di);
-	return di;
-}
-
-
-ssaDebugInfo *ssa_add_debug_info_proc(ssaProcedure *proc, Entity *entity, String name, ssaDebugInfo *file) {
-	if (!proc->module->generate_debug_info) {
-		return NULL;
-	}
-
-	GB_ASSERT(entity != NULL);
-	ssaDebugInfo *di = ssa_alloc_debug_info(proc->module->allocator, ssaDebugInfo_Proc);
-	di->Proc.entity = entity;
-	di->Proc.name = name;
-	di->Proc.file = file;
-	di->Proc.pos = entity->token.pos;
-
-	map_set(&proc->module->debug_info, hash_pointer(entity), di);
-	return di;
-}
-

+ 0 - 1150
src/ssa/emit.cpp

@@ -1,1150 +0,0 @@
-ssaValue *ssa_type_info       (ssaProcedure *proc, Type *type);
-ssaValue *ssa_emit_conv       (ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument = false);
-ssaValue *ssa_build_expr      (ssaProcedure *proc, AstNode *expr);
-void      ssa_build_stmt      (ssaProcedure *proc, AstNode *node);
-void      ssa_build_cond      (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block);
-void      ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d);
-
-
-ssaValue *ssa_emit(ssaProcedure *proc, ssaValue *instr) {
-	GB_ASSERT(instr->kind == ssaValue_Instr);
-	ssaBlock *b = proc->curr_block;
-	instr->Instr.parent = b;
-	if (b != NULL) {
-		ssaInstr *i = ssa_get_last_instr(b);
-		if (!ssa_is_instr_terminating(i)) {
-			array_add(&b->instrs, instr);
-		}
-	}
-	return instr;
-}
-ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
-	return ssa_emit(p, ssa_make_instr_store(p, address, value));
-}
-ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
-	return ssa_emit(p, ssa_make_instr_load(p, address));
-}
-ssaValue *ssa_emit_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
-	return ssa_emit(p, ssa_make_instr_select(p, cond, t, f));
-}
-
-ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address)  {
-	return ssa_emit(p, ssa_make_instr_zero_init(p, address));
-}
-
-ssaValue *ssa_emit_comment(ssaProcedure *p, String text) {
-	return ssa_emit(p, ssa_make_instr_comment(p, text));
-}
-
-
-ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
-	Type *pt = base_type(ssa_type(value));
-	GB_ASSERT(pt->kind == Type_Proc);
-	Type *results = pt->Proc.results;
-	return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
-}
-
-ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) {
-	String name = make_string(name_);
-	ssaValue **found = map_get(&proc->module->members, hash_string(name));
-	GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name));
-	ssaValue *gp = *found;
-	return ssa_emit_call(proc, gp, args, arg_count);
-}
-
-
-
-void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
-	isize count = proc->defer_stmts.count;
-	isize i = count;
-	while (i --> 0) {
-		ssaDefer d = proc->defer_stmts[i];
-		if (kind == ssaDeferExit_Default) {
-			if (proc->scope_index == d.scope_index &&
-			    d.scope_index > 1) {
-				ssa_build_defer_stmt(proc, d);
-				array_pop(&proc->defer_stmts);
-				continue;
-			} else {
-				break;
-			}
-		} else if (kind == ssaDeferExit_Return) {
-			ssa_build_defer_stmt(proc, d);
-		} else if (kind == ssaDeferExit_Branch) {
-			GB_ASSERT(block != NULL);
-			isize lower_limit = block->scope_index+1;
-			if (lower_limit < d.scope_index) {
-				ssa_build_defer_stmt(proc, d);
-			}
-		}
-	}
-}
-
-
-void ssa_open_scope(ssaProcedure *proc) {
-	proc->scope_index++;
-}
-
-void ssa_close_scope(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
-	ssa_emit_defer_stmts(proc, kind, block);
-	GB_ASSERT(proc->scope_index > 0);
-	proc->scope_index--;
-}
-
-
-
-void ssa_emit_unreachable(ssaProcedure *proc) {
-	ssa_emit(proc, ssa_make_instr_unreachable(proc));
-}
-
-void ssa_emit_return(ssaProcedure *proc, ssaValue *v) {
-	ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL);
-	ssa_emit(proc, ssa_make_instr_return(proc, v));
-}
-
-void ssa_emit_jump(ssaProcedure *proc, ssaBlock *target_block) {
-	ssaBlock *b = proc->curr_block;
-	if (b == NULL) {
-		return;
-	}
-	ssa_emit(proc, ssa_make_instr_jump(proc, target_block));
-	ssa_add_edge(b, target_block);
-	proc->curr_block = NULL;
-}
-
-void ssa_emit_if(ssaProcedure *proc, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	ssaBlock *b = proc->curr_block;
-	if (b == NULL) {
-		return;
-	}
-	ssa_emit(proc, ssa_make_instr_if(proc, cond, true_block, false_block));
-	ssa_add_edge(b, true_block);
-	ssa_add_edge(b, false_block);
-	proc->curr_block = NULL;
-}
-
-void ssa_emit_startup_runtime(ssaProcedure *proc) {
-	GB_ASSERT(proc->parent == NULL && proc->name == "main");
-	ssa_emit(proc, ssa_alloc_instr(proc, ssaInstr_StartupRuntime));
-}
-
-
-
-
-ssaValue *ssa_addr_store(ssaProcedure *proc, ssaAddr addr, ssaValue *value) {
-	if (addr.addr == NULL) {
-		return NULL;
-	}
-
-	if (addr.kind == ssaAddr_Vector) {
-		ssaValue *v = ssa_emit_load(proc, addr.addr);
-		Type *elem_type = base_type(ssa_type(v))->Vector.elem;
-		ssaValue *elem = ssa_emit_conv(proc, value, elem_type);
-		ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, addr.Vector.index));
-		return ssa_emit_store(proc, addr.addr, out);
-	} else {
-		ssaValue *v = ssa_emit_conv(proc, value, ssa_addr_type(addr));
-		return ssa_emit_store(proc, addr.addr, v);
-	}
-}
-ssaValue *ssa_addr_load(ssaProcedure *proc, ssaAddr addr) {
-	if (addr.addr == NULL) {
-		GB_PANIC("Illegal addr load");
-		return NULL;
-	}
-
-	if (addr.kind == ssaAddr_Vector) {
-		ssaValue *v = ssa_emit_load(proc, addr.addr);
-		return ssa_emit(proc, ssa_make_instr_extract_element(proc, v, addr.Vector.index));
-	}
-	Type *t = base_type(ssa_type(addr.addr));
-	if (t->kind == Type_Proc) {
-		// NOTE(bill): Imported procedures don't require a load as they are pointers
-		return addr.addr;
-	}
-	return ssa_emit_load(proc, addr.addr);
-}
-
-
-
-
-ssaValue *ssa_emit_ptr_offset(ssaProcedure *proc, ssaValue *ptr, ssaValue *offset) {
-	offset = ssa_emit_conv(proc, offset, t_int);
-	return ssa_emit(proc, ssa_make_instr_ptr_offset(proc, ptr, offset));
-}
-
-ssaValue *ssa_emit_arith(ssaProcedure *proc, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
-	Type *t_left = ssa_type(left);
-	Type *t_right = ssa_type(right);
-
-	if (op == Token_Add) {
-		if (is_type_pointer(t_left)) {
-			ssaValue *ptr = ssa_emit_conv(proc, left, type);
-			ssaValue *offset = right;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		} else if (is_type_pointer(ssa_type(right))) {
-			ssaValue *ptr = ssa_emit_conv(proc, right, type);
-			ssaValue *offset = left;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		}
-	} else if (op == Token_Sub) {
-		if (is_type_pointer(t_left) && is_type_integer(t_right)) {
-			// ptr - int
-			ssaValue *ptr = ssa_emit_conv(proc, left, type);
-			ssaValue *offset = right;
-			return ssa_emit_ptr_offset(proc, ptr, offset);
-		} else if (is_type_pointer(t_left) && is_type_pointer(t_right)) {
-			GB_ASSERT(is_type_integer(type));
-			Type *ptr_type = t_left;
-			ssaModule *m = proc->module;
-			ssaValue *x = ssa_emit_conv(proc, left, type);
-			ssaValue *y = ssa_emit_conv(proc, right, type);
-			ssaValue *diff = ssa_emit_arith(proc, op, x, y, type);
-			ssaValue *elem_size = ssa_make_const_int(m->allocator, type_size_of(m->sizes, m->allocator, ptr_type));
-			return ssa_emit_arith(proc, Token_Quo, diff, elem_size, type);
-		}
-	}
-
-
-	switch (op) {
-	case Token_AndNot: {
-		// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
-		// NOTE(bill): "not" `x` == `x` "xor" `-1`
-		ssaValue *neg = ssa_add_module_constant(proc->module, type, make_exact_value_integer(-1));
-		op = Token_Xor;
-		right = ssa_emit_arith(proc, op, right, neg, type);
-		GB_ASSERT(right->Instr.kind == ssaInstr_BinaryOp);
-		right->Instr.BinaryOp.type = type;
-		op = Token_And;
-	} /* fallthrough */
-	case Token_Add:
-	case Token_Sub:
-	case Token_Mul:
-	case Token_Quo:
-	case Token_Mod:
-	case Token_And:
-	case Token_Or:
-	case Token_Xor:
-		left  = ssa_emit_conv(proc, left, type);
-		right = ssa_emit_conv(proc, right, type);
-		break;
-	}
-
-	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right, type));
-}
-
-ssaValue *ssa_emit_comp(ssaProcedure *proc, TokenKind op_kind, ssaValue *left, ssaValue *right) {
-	Type *a = base_type(ssa_type(left));
-	Type *b = base_type(ssa_type(right));
-
-	if (are_types_identical(a, b)) {
-		// NOTE(bill): No need for a conversion
-	} else if (left->kind == ssaValue_Constant || left->kind == ssaValue_Nil) {
-		left = ssa_emit_conv(proc, left, ssa_type(right));
-	} else if (right->kind == ssaValue_Constant || right->kind == ssaValue_Nil) {
-		right = ssa_emit_conv(proc, right, ssa_type(left));
-	}
-
-	Type *result = t_bool;
-	if (is_type_vector(a)) {
-		result = make_type_vector(proc->module->allocator, t_bool, a->Vector.count);
-	}
-	return ssa_emit(proc, ssa_make_instr_binary_op(proc, op_kind, left, right, result));
-}
-
-ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, ssaValue *index) {
-	Type *st = base_type(type_deref(ssa_type(s)));
-	GB_ASSERT(is_type_array(st));
-
-	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
-	index = ssa_emit_conv(proc, index, t_i32);
-	return ssa_emit(proc, ssa_make_instr_array_element_ptr(proc, s, index));
-}
-
-ssaValue *ssa_emit_array_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
-	return ssa_emit_array_ep(proc, s, ssa_make_const_i32(proc->module->allocator, index));
-}
-
-
-ssaValue *ssa_emit_struct_ep(ssaProcedure *proc, ssaValue *s, i32 index) {
-	gbAllocator a = proc->module->allocator;
-	Type *t = base_type(type_deref(ssa_type(s)));
-	Type *result_type = NULL;
-	ssaValue *gep = NULL;
-
-	if (is_type_struct(t)) {
-		GB_ASSERT(t->Record.field_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-		result_type = make_type_pointer(a, t->Record.fields[index]->type);
-	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->Tuple.variable_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
-		result_type = make_type_pointer(a, t->Tuple.variables[index]->type);
-	} else if (is_type_slice(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break;
-		case 1: result_type = make_type_pointer(a, t_int); break;
-		case 2: result_type = make_type_pointer(a, t_int); break;
-		}
-	} else if (is_type_string(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t_u8_ptr); break;
-		case 1: result_type = make_type_pointer(a, t_int);    break;
-		}
-	} else if (is_type_any(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t_type_info_ptr); break;
-		case 1: result_type = make_type_pointer(a, t_rawptr);        break;
-		}
-	} else if (is_type_maybe(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t->Maybe.elem); break;
-		case 1: result_type = make_type_pointer(a, t_bool);        break;
-		}
-	} else if (is_type_union(t)) {
-		switch (index) {
-		case 1: result_type = make_type_pointer(a, t_int); break;
-
-		case 0:
-		default:
-			GB_PANIC("TODO(bill): struct_gep 0 for unions");
-			break;
-		}
-	} else {
-		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(ssa_type(s)), index);
-	}
-
-	GB_ASSERT(result_type != NULL);
-
-	gep = ssa_make_instr_struct_element_ptr(proc, s, index, result_type);
-	return ssa_emit(proc, gep);
-}
-
-
-
-ssaValue *ssa_emit_array_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
-	Type *st = base_type(ssa_type(s));
-	GB_ASSERT(is_type_array(st));
-	return ssa_emit(proc, ssa_make_instr_array_extract_value(proc, s, index));
-}
-
-ssaValue *ssa_emit_struct_ev(ssaProcedure *proc, ssaValue *s, i32 index) {
-	// NOTE(bill): For some weird legacy reason in LLVM, structure elements must be accessed as an i32
-
-	gbAllocator a = proc->module->allocator;
-	Type *t = base_type(ssa_type(s));
-	Type *result_type = NULL;
-
-	if (is_type_struct(t)) {
-		GB_ASSERT(t->Record.field_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
-		result_type = t->Record.fields[index]->type;
-	} else if (is_type_tuple(t)) {
-		GB_ASSERT(t->Tuple.variable_count > 0);
-		GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
-		result_type = t->Tuple.variables[index]->type;
-	} else if (is_type_slice(t)) {
-		switch (index) {
-		case 0: result_type = make_type_pointer(a, t->Slice.elem); break;
-		case 1: result_type = t_int; break;
-		case 2: result_type = t_int; break;
-		}
-	} else if (is_type_string(t)) {
-		switch (index) {
-		case 0: result_type = t_u8_ptr; break;
-		case 1: result_type = t_int;    break;
-		}
-	} else if (is_type_any(t)) {
-		switch (index) {
-		case 0: result_type = t_type_info_ptr; break;
-		case 1: result_type = t_rawptr;        break;
-		}
-	} else if (is_type_maybe(t)) {
-		switch (index) {
-		case 0: result_type = t->Maybe.elem; break;
-		case 1: result_type = t_bool;        break;
-		}
-	} else if (is_type_union(t)) {
-		switch (index) {
-		case 1: result_type = t_int; break;
-
-		case 0:
-		default:
-			GB_PANIC("TODO(bill): struct_gep 0 for unions");
-			break;
-		}
-	} else {
-		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(ssa_type(s)), index);
-	}
-
-	GB_ASSERT(result_type != NULL);
-
-	return ssa_emit(proc, ssa_make_instr_struct_extract_value(proc, s, index, result_type));
-}
-
-
-ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
-	GB_ASSERT(sel.index.count > 0);
-
-	for_array(i, sel.index) {
-		i32 index = cast(i32)sel.index[i];
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			e = ssa_emit_load(proc, e);
-			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
-		}
-		type = base_type(type);
-
-
-		if (is_type_raw_union(type)) {
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
-		} else if (type->kind == Type_Record) {
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_struct_ep(proc, e, index);
-		} else if (type->kind == Type_Basic) {
-			switch (type->Basic.kind) {
-			case Basic_any: {
-				if (index == 0) {
-					type = t_type_info_ptr;
-				} else if (index == 1) {
-					type = t_rawptr;
-				}
-				e = ssa_emit_struct_ep(proc, e, index);
-			} break;
-
-			case Basic_string:
-				e = ssa_emit_struct_ep(proc, e, index);
-				break;
-
-			default:
-				GB_PANIC("un-gep-able type");
-				break;
-			}
-		} else if (type->kind == Type_Slice) {
-			e = ssa_emit_struct_ep(proc, e, index);
-		} else {
-			GB_PANIC("un-gep-able type");
-		}
-	}
-
-	return e;
-}
-
-
-ssaValue *ssa_emit_deep_field_ev(ssaProcedure *proc, Type *type, ssaValue *e, Selection sel) {
-	GB_ASSERT(sel.index.count > 0);
-
-	for_array(i, sel.index) {
-		isize index = sel.index[i];
-		if (is_type_pointer(type)) {
-			type = type_deref(type);
-			e = ssa_emit_load(proc, e);
-			e = ssa_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies?
-		}
-		type = base_type(type);
-
-
-		if (is_type_raw_union(type)) {
-			type = type->Record.fields[index]->type;
-			e = ssa_emit_conv(proc, e, make_type_pointer(proc->module->allocator, type));
-		} else {
-			e = ssa_emit_struct_ev(proc, e, index);
-		}
-	}
-
-	return e;
-}
-
-
-
-
-ssaValue *ssa_array_elem(ssaProcedure *proc, ssaValue *array) {
-	return ssa_emit_array_ep(proc, array, v_zero32);
-}
-ssaValue *ssa_array_len(ssaProcedure *proc, ssaValue *array) {
-	Type *t = ssa_type(array);
-	GB_ASSERT(t->kind == Type_Array);
-	return ssa_make_const_int(proc->module->allocator, t->Array.count);
-}
-ssaValue *ssa_array_cap(ssaProcedure *proc, ssaValue *array) {
-	return ssa_array_len(proc, array);
-}
-
-ssaValue *ssa_slice_elem(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 0);
-}
-ssaValue *ssa_slice_len(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 1);
-}
-ssaValue *ssa_slice_cap(ssaProcedure *proc, ssaValue *slice) {
-	Type *t = ssa_type(slice);
-	GB_ASSERT(t->kind == Type_Slice);
-	return ssa_emit_struct_ev(proc, slice, 2);
-}
-
-ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
-	Type *t = ssa_type(string);
-	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
-	return ssa_emit_struct_ev(proc, string, 0);
-}
-ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
-	Type *t = ssa_type(string);
-	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
-	return ssa_emit_struct_ev(proc, string, 1);
-}
-
-
-
-ssaValue *ssa_add_local_slice(ssaProcedure *proc, Type *slice_type, ssaValue *base, ssaValue *low, ssaValue *high, ssaValue *max) {
-	// TODO(bill): array bounds checking for slice creation
-	// TODO(bill): check that low < high <= max
-	gbAllocator a = proc->module->allocator;
-	Type *bt = base_type(ssa_type(base));
-
-	if (low == NULL) {
-		low = v_zero;
-	}
-	if (high == NULL) {
-		switch (bt->kind) {
-		case Type_Array:   high = ssa_array_len(proc, base); break;
-		case Type_Slice:   high = ssa_slice_len(proc, base); break;
-		case Type_Pointer: high = v_one;                     break;
-		}
-	}
-	if (max == NULL) {
-		switch (bt->kind) {
-		case Type_Array:   max = ssa_array_cap(proc, base); break;
-		case Type_Slice:   max = ssa_slice_cap(proc, base); break;
-		case Type_Pointer: max = high;                      break;
-		}
-	}
-	GB_ASSERT(max != NULL);
-
-	ssaValue *len = ssa_emit_arith(proc, Token_Sub, high, low, t_int);
-	ssaValue *cap = ssa_emit_arith(proc, Token_Sub, max,  low, t_int);
-
-	ssaValue *elem = NULL;
-	switch (bt->kind) {
-	case Type_Array:   elem = ssa_array_elem(proc, base); break;
-	case Type_Slice:   elem = ssa_slice_elem(proc, base); break;
-	case Type_Pointer: elem = ssa_emit_load(proc, base);  break;
-	}
-
-	elem = ssa_emit_ptr_offset(proc, elem, low);
-
-	ssaValue *slice = ssa_add_local_generated(proc, slice_type);
-
-	ssaValue *gep = NULL;
-	gep = ssa_emit_struct_ep(proc, slice, 0);
-	ssa_emit_store(proc, gep, elem);
-
-	gep = ssa_emit_struct_ep(proc, slice, 1);
-	ssa_emit_store(proc, gep, len);
-
-	gep = ssa_emit_struct_ep(proc, slice, 2);
-	ssa_emit_store(proc, gep, cap);
-
-	return slice;
-}
-
-ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) {
-	ssaValue *str = ssa_add_local_generated(proc, t_string);
-	ssaValue *str_elem = ssa_emit_struct_ep(proc, str, 0);
-	ssaValue *str_len = ssa_emit_struct_ep(proc, str, 1);
-	ssa_emit_store(proc, str_elem, elem);
-	ssa_emit_store(proc, str_len, len);
-	return ssa_emit_load(proc, str);
-}
-
-
-
-
-String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
-	Type *prev_src = src;
-	// Type *prev_dst = dst;
-	src = base_type(type_deref(src));
-	// dst = base_type(type_deref(dst));
-	b32 src_is_ptr = src != prev_src;
-	// b32 dst_is_ptr = dst != prev_dst;
-
-	GB_ASSERT(is_type_struct(src));
-	for (isize i = 0; i < src->Record.field_count; i++) {
-		Entity *f = src->Record.fields[i];
-		if (f->kind == Entity_Variable && f->flags & EntityFlag_Anonymous) {
-			if (are_types_identical(dst, f->type)) {
-				return f->token.string;
-			}
-			if (src_is_ptr && is_type_pointer(dst)) {
-				if (are_types_identical(type_deref(dst), f->type)) {
-					return f->token.string;
-				}
-			}
-			String name = lookup_polymorphic_field(info, dst, f->type);
-			if (name.len > 0) {
-				return name;
-			}
-		}
-	}
-	return make_string("");
-}
-
-ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) {
-	return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type));
-}
-
-
-ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument) {
-	Type *src_type = ssa_type(value);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-
-	Type *src = get_enum_base_type(base_type(src_type));
-	Type *dst = get_enum_base_type(base_type(t));
-
-	if (value->kind == ssaValue_Constant) {
-		if (is_type_any(dst)) {
-			ssaValue *default_value = ssa_add_local_generated(proc, default_type(src_type));
-			ssa_emit_store(proc, default_value, value);
-			return ssa_emit_conv(proc, ssa_emit_load(proc, default_value), t_any, is_argument);
-		} else if (dst->kind == Type_Basic) {
-			ExactValue ev = value->Constant.value;
-			if (is_type_float(dst)) {
-				ev = exact_value_to_float(ev);
-			} else if (is_type_string(dst)) {
-				// Handled elsewhere
-				GB_ASSERT(ev.kind == ExactValue_String);
-			} else if (is_type_integer(dst)) {
-				ev = exact_value_to_integer(ev);
-			} else if (is_type_pointer(dst)) {
-				// IMPORTANT NOTE(bill): LLVM doesn't support pointer constants expect `null`
-				ssaValue *i = ssa_add_module_constant(proc->module, t_uint, ev);
-				return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, i, t_uint, dst));
-			}
-			return ssa_add_module_constant(proc->module, t, ev);
-		}
-	}
-
-	if (are_types_identical(src, dst)) {
-		return value;
-	}
-
-	if (is_type_maybe(dst)) {
-		ssaValue *maybe = ssa_add_local_generated(proc, dst);
-		ssaValue *val = ssa_emit_struct_ep(proc, maybe, 0);
-		ssaValue *set = ssa_emit_struct_ep(proc, maybe, 1);
-		ssa_emit_store(proc, val, value);
-		ssa_emit_store(proc, set, v_true);
-		return ssa_emit_load(proc, maybe);
-	}
-
-	// integer -> integer
-	if (is_type_integer(src) && is_type_integer(dst)) {
-		GB_ASSERT(src->kind == Type_Basic &&
-		          dst->kind == Type_Basic);
-		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
-		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
-		if (sz == dz) {
-			// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
-			return value;
-		}
-
-		ssaConvKind kind = ssaConv_trunc;
-		if (dz >= sz) {
-			kind = ssaConv_zext;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// boolean -> integer
-	if (is_type_boolean(src) && is_type_integer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_zext, value, src, dst));
-	}
-
-	// integer -> boolean
-	if (is_type_integer(src) && is_type_boolean(dst)) {
-		return ssa_emit_comp(proc, Token_NotEq, value, v_zero);
-	}
-
-
-	// float -> float
-	if (is_type_float(src) && is_type_float(dst)) {
-		i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
-		i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
-		ssaConvKind kind = ssaConv_fptrunc;
-		if (dz >= sz) {
-			kind = ssaConv_fpext;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// float <-> integer
-	if (is_type_float(src) && is_type_integer(dst)) {
-		ssaConvKind kind = ssaConv_fptosi;
-		if (is_type_unsigned(dst)) {
-			kind = ssaConv_fptoui;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-	if (is_type_integer(src) && is_type_float(dst)) {
-		ssaConvKind kind = ssaConv_sitofp;
-		if (is_type_unsigned(src)) {
-			kind = ssaConv_uitofp;
-		}
-		return ssa_emit(proc, ssa_make_instr_conv(proc, kind, value, src, dst));
-	}
-
-	// Pointer <-> int
-	if (is_type_pointer(src) && is_type_int_or_uint(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_ptrtoint, value, src, dst));
-	}
-	if (is_type_int_or_uint(src) && is_type_pointer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_inttoptr, value, src, dst));
-	}
-
-	if (is_type_union(dst)) {
-		for (isize i = 0; i < dst->Record.field_count; i++) {
-			Entity *f = dst->Record.fields[i];
-			if (are_types_identical(f->type, src_type)) {
-				ssa_emit_comment(proc, make_string("union - child to parent"));
-				gbAllocator allocator = proc->module->allocator;
-				ssaValue *parent = ssa_add_local_generated(proc, t);
-				ssaValue *tag = ssa_make_const_int(allocator, i);
-				ssa_emit_store(proc, ssa_emit_struct_ep(proc, parent, 1), tag);
-
-				ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
-
-				Type *tag_type = src_type;
-				Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
-				ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr);
-				ssa_emit_store(proc, underlying, value);
-
-				return ssa_emit_load(proc, parent);
-			}
-		}
-	}
-
-	// NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's
-	// subtype polymorphism casting
-	if (true || is_argument) {
-		Type *sb = base_type(type_deref(src));
-		b32 src_is_ptr = src != sb;
-		if (is_type_struct(sb)) {
-			String field_name = lookup_polymorphic_field(proc->module->info, t, src);
-			// gb_printf("field_name: %.*s\n", LIT(field_name));
-			if (field_name.len > 0) {
-				// NOTE(bill): It can be casted
-				Selection sel = lookup_field(proc->module->allocator, sb, field_name, false);
-				if (sel.entity != NULL) {
-					ssa_emit_comment(proc, make_string("cast - polymorphism"));
-					if (src_is_ptr) {
-						value = ssa_emit_load(proc, value);
-					}
-					return ssa_emit_deep_field_ev(proc, sb, value, sel);
-				}
-			}
-		}
-	}
-
-
-
-	// Pointer <-> Pointer
-	if (is_type_pointer(src) && is_type_pointer(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-
-
-	// proc <-> proc
-	if (is_type_proc(src) && is_type_proc(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-	// pointer -> proc
-	if (is_type_pointer(src) && is_type_proc(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-	// proc -> pointer
-	if (is_type_proc(src) && is_type_pointer(dst)) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-
-
-	// []byte/[]u8 <-> string
-	if (is_type_u8_slice(src) && is_type_string(dst)) {
-		ssaValue *elem = ssa_slice_elem(proc, value);
-		ssaValue *len  = ssa_slice_len(proc, value);
-		return ssa_emit_string(proc, elem, len);
-	}
-	if (is_type_string(src) && is_type_u8_slice(dst)) {
-		ssaValue *elem = ssa_string_elem(proc, value);
-		ssaValue *elem_ptr = ssa_add_local_generated(proc, ssa_type(elem));
-		ssa_emit_store(proc, elem_ptr, elem);
-
-		ssaValue *len  = ssa_string_len(proc, value);
-		ssaValue *slice = ssa_add_local_slice(proc, dst, elem_ptr, v_zero, len, len);
-		return ssa_emit_load(proc, slice);
-	}
-
-	if (is_type_vector(dst)) {
-		Type *dst_elem = dst->Vector.elem;
-		value = ssa_emit_conv(proc, value, dst_elem);
-		ssaValue *v = ssa_add_local_generated(proc, t);
-		v = ssa_emit_load(proc, v);
-		v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32));
-		// NOTE(bill): Broadcast lowest value to all values
-		isize index_count = dst->Vector.count;
-		i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
-		for (isize i = 0; i < index_count; i++) {
-			indices[i] = 0;
-		}
-
-		v = ssa_emit(proc, ssa_make_instr_vector_shuffle(proc, v, indices, index_count));
-		return v;
-	}
-
-	if (is_type_any(dst)) {
-		ssaValue *result = ssa_add_local_generated(proc, t_any);
-
-		if (is_type_untyped_nil(src)) {
-			return ssa_emit_load(proc, result);
-		}
-
-		ssaValue *data = NULL;
-		if (value->kind == ssaValue_Instr &&
-		    value->Instr.kind == ssaInstr_Load) {
-			// NOTE(bill): Addressable value
-			data = value->Instr.Load.address;
-		} else {
-			// NOTE(bill): Non-addressable value
-			data = ssa_add_local_generated(proc, src_type);
-			ssa_emit_store(proc, data, value);
-		}
-		GB_ASSERT(is_type_pointer(ssa_type(data)));
-		GB_ASSERT(is_type_typed(src_type));
-		data = ssa_emit_conv(proc, data, t_rawptr);
-
-
-		ssaValue *ti = ssa_type_info(proc, src_type);
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, result, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, result, 1);
-		ssa_emit_store(proc, gep0, ti);
-		ssa_emit_store(proc, gep1, data);
-
-		return ssa_emit_load(proc, result);
-	}
-
-	if (is_type_untyped_nil(src) && type_has_nil(dst)) {
-		return ssa_make_value_nil(proc->module->allocator, t);
-	}
-
-
-	gb_printf_err("ssa_emit_conv: src -> dst\n");
-	gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t));
-	gb_printf_err("Not Identical %s != %s\n", type_to_string(src), type_to_string(dst));
-
-
-	GB_PANIC("Invalid type conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
-
-	return NULL;
-}
-
-
-ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
-	Type *src_type = ssa_type(value);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-	Type *src = base_type(src_type);
-	Type *dst = base_type(t);
-	if (are_types_identical(t, src_type)) {
-		return value;
-	}
-
-	i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
-	i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
-
-	if (sz == dz) {
-		return ssa_emit_bitcast(proc, value, dst);
-	}
-
-
-	GB_PANIC("Invalid transmute conversion: `%s` to `%s`", type_to_string(src_type), type_to_string(t));
-
-	return NULL;
-}
-
-ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
-	GB_ASSERT(is_type_pointer(ssa_type(value)));
-	gbAllocator allocator = proc->module->allocator;
-
-	String field_name = check_down_cast_name(t, type_deref(ssa_type(value)));
-	GB_ASSERT(field_name.len > 0);
-	Selection sel = lookup_field(proc->module->allocator, t, field_name, false);
-	Type *t_u8_ptr = make_type_pointer(allocator, t_u8);
-	ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr);
-
-	i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel);
-	ssaValue *offset = ssa_make_const_int(allocator, -offset_);
-	ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset);
-	return ssa_emit_conv(proc, head, t);
-}
-
-ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) {
-	GB_ASSERT(tuple->kind == Type_Tuple);
-	gbAllocator a = proc->module->allocator;
-
-	Type *src_type = ssa_type(value);
-	b32 is_ptr = is_type_pointer(src_type);
-
-	ssaValue *v = ssa_add_local_generated(proc, tuple);
-
-	if (is_ptr) {
-		Type *src = base_type(type_deref(src_type));
-		Type *src_ptr = src_type;
-		GB_ASSERT(is_type_union(src));
-		Type *dst_ptr = tuple->Tuple.variables[0]->type;
-		Type *dst = type_deref(dst_ptr);
-
-		ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_ep(proc, value, 1));
-		ssaValue *dst_tag = NULL;
-		for (isize i = 1; i < src->Record.field_count; i++) {
-			Entity *f = src->Record.fields[i];
-			if (are_types_identical(f->type, dst)) {
-				dst_tag = ssa_make_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != NULL);
-
-		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
-		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
-		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ssa_emit_if(proc, cond, ok_block, end_block);
-		proc->curr_block = ok_block;
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-
-		ssaValue *data = ssa_emit_conv(proc, value, dst_ptr);
-		ssa_emit_store(proc, gep0, data);
-		ssa_emit_store(proc, gep1, v_true);
-
-		ssa_emit_jump(proc, end_block);
-		proc->curr_block = end_block;
-
-	} else {
-		Type *src = base_type(src_type);
-		GB_ASSERT(is_type_union(src));
-		Type *dst = tuple->Tuple.variables[0]->type;
-		Type *dst_ptr = make_type_pointer(a, dst);
-
-		ssaValue *tag = ssa_emit_struct_ev(proc, value, 1);
-		ssaValue *dst_tag = NULL;
-		for (isize i = 1; i < src->Record.field_count; i++) {
-			Entity *f = src->Record.fields[i];
-			if (are_types_identical(f->type, dst)) {
-				dst_tag = ssa_make_const_int(a, i);
-				break;
-			}
-		}
-		GB_ASSERT(dst_tag != NULL);
-
-		// HACK(bill): This is probably not very efficient
-		ssaValue *union_copy = ssa_add_local_generated(proc, src_type);
-		ssa_emit_store(proc, union_copy, value);
-
-		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
-		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
-		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
-		ssa_emit_if(proc, cond, ok_block, end_block);
-		proc->curr_block = ok_block;
-
-		ssaValue *gep0 = ssa_emit_struct_ep(proc, v, 0);
-		ssaValue *gep1 = ssa_emit_struct_ep(proc, v, 1);
-
-		ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr));
-		ssa_emit_store(proc, gep0, data);
-		ssa_emit_store(proc, gep1, v_true);
-
-		ssa_emit_jump(proc, end_block);
-		proc->curr_block = end_block;
-
-	}
-	return ssa_emit_load(proc, v);
-}
-
-
-isize ssa_type_info_index(CheckerInfo *info, Type *type) {
-	type = default_type(type);
-
-	isize entry_index = -1;
-	HashKey key = hash_pointer(type);
-	auto *found_entry_index = map_get(&info->type_info_map, key);
-	if (found_entry_index) {
-		entry_index = *found_entry_index;
-	}
-	if (entry_index < 0) {
-		// NOTE(bill): Do manual search
-		// TODO(bill): This is O(n) and can be very slow
-		for_array(i, info->type_info_map.entries){
-			auto *e = &info->type_info_map.entries[i];
-			Type *prev_type = cast(Type *)e->key.ptr;
-			if (are_types_identical(prev_type, type)) {
-				entry_index = e->value;
-				// NOTE(bill): Add it to the search map
-				map_set(&info->type_info_map, key, entry_index);
-				break;
-			}
-		}
-	}
-
-	if (entry_index < 0) {
-		compiler_error("Type_Info for `%s` could not be found", type_to_string(type));
-	}
-	return entry_index;
-}
-
-ssaValue *ssa_type_info(ssaProcedure *proc, Type *type) {
-	ssaValue **found = map_get(&proc->module->members, hash_string(make_string(SSA_TYPE_INFO_DATA_NAME)));
-	GB_ASSERT(found != NULL);
-	ssaValue *type_info_data = *found;
-	CheckerInfo *info = proc->module->info;
-
-	type = default_type(type);
-
-	i32 entry_index = ssa_type_info_index(info, type);
-
-	// gb_printf_err("%d %s\n", entry_index, type_to_string(type));
-
-	return ssa_emit_array_ep(proc, type_info_data, ssa_make_const_i32(proc->module->allocator, entry_index));
-}
-
-
-
-ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
-	ast_node(be, BinaryExpr, expr);
-#if 0
-	ssaBlock *true_   = ssa_add_block(proc, NULL, "logical.cmp.true");
-	ssaBlock *false_  = ssa_add_block(proc, NULL, "logical.cmp.false");
-	ssaBlock *done  = ssa_add_block(proc, NULL, "logical.cmp.done");
-
-	ssaValue *result = ssa_add_local_generated(proc, t_bool);
-	ssa_build_cond(proc, expr, true_, false_);
-
-	proc->curr_block = true_;
-	ssa_emit_store(proc, result, v_true);
-	ssa_emit_jump(proc, done);
-
-	proc->curr_block = false_;
-	ssa_emit_store(proc, result, v_false);
-	ssa_emit_jump(proc, done);
-
-	proc->curr_block = done;
-
-	return ssa_emit_load(proc, result);
-#else
-	ssaBlock *rhs = ssa_add_block(proc, NULL, "logical.cmp.rhs");
-	ssaBlock *done = ssa_add_block(proc, NULL, "logical.cmp.done");
-
-	Type *type = type_of_expr(proc->module->info, expr);
-	type = default_type(type);
-
-	ssaValue *short_circuit = NULL;
-	if (be->op.kind == Token_CmpAnd) {
-		ssa_build_cond(proc, be->left, rhs, done);
-		short_circuit = v_false;
-	} else if (be->op.kind == Token_CmpOr) {
-		ssa_build_cond(proc, be->left, done, rhs);
-		short_circuit = v_true;
-	}
-
-	if (rhs->preds.count == 0) {
-		proc->curr_block = done;
-		return short_circuit;
-	}
-
-	if (done->preds.count == 0) {
-		proc->curr_block = rhs;
-		return ssa_build_expr(proc, be->right);
-	}
-
-	Array<ssaValue *> edges = {};
-	array_init(&edges, proc->module->allocator, done->preds.count+1);
-	for_array(i, done->preds) {
-		array_add(&edges, short_circuit);
-	}
-
-	proc->curr_block = rhs;
-	array_add(&edges, ssa_build_expr(proc, be->right));
-	ssa_emit_jump(proc, done);
-	proc->curr_block = done;
-
-	return ssa_emit(proc, ssa_make_instr_phi(proc, edges, type));
-#endif
-}
-
-
-void ssa_emit_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) {
-	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-		return;
-	}
-
-	index = ssa_emit_conv(proc, index, t_int);
-	len = ssa_emit_conv(proc, len, t_int);
-
-	ssa_emit(proc, ssa_make_instr_bounds_check(proc, token.pos, index, len));
-
-	// gbAllocator a = proc->module->allocator;
-	// ssaValue **args = gb_alloc_array(a, ssaValue *, 5);
-	// args[0] = ssa_emit_global_string(proc, token.pos.file);
-	// args[1] = ssa_make_const_int(a, token.pos.line);
-	// args[2] = ssa_make_const_int(a, token.pos.column);
-	// args[3] = ssa_emit_conv(proc, index, t_int);
-	// args[4] = ssa_emit_conv(proc, len, t_int);
-
-	// ssa_emit_global_call(proc, "__bounds_check_error", args, 5);
-}
-
-void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) {
-	if ((proc->module->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-		return;
-	}
-
-
-	low  = ssa_emit_conv(proc, low,  t_int);
-	high = ssa_emit_conv(proc, high, t_int);
-	max  = ssa_emit_conv(proc, max,  t_int);
-
-	ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
-
-	// gbAllocator a = proc->module->allocator;
-	// ssaValue **args = gb_alloc_array(a, ssaValue *, 6);
-	// args[0] = ssa_emit_global_string(proc, token.pos.file);
-	// args[1] = ssa_make_const_int(a, token.pos.line);
-	// args[2] = ssa_make_const_int(a, token.pos.column);
-	// args[3] = ssa_emit_conv(proc, low, t_int);
-	// args[4] = ssa_emit_conv(proc, high, t_int);
-	// args[5] = ssa_emit_conv(proc, max, t_int);
-
-	// if (!is_substring) {
-	// 	ssa_emit_global_call(proc, "__slice_expr_error", args, 6);
-	// } else {
-	// 	ssa_emit_global_call(proc, "__substring_expr_error", args, 5);
-	// }
-}
-
-
-
-
-
-

+ 0 - 495
src/ssa/make.cpp

@@ -1,495 +0,0 @@
-void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v);
-ssaValue *ssa_emit_zero_init(ssaProcedure *p, ssaValue *address);
-ssaValue *ssa_emit_comment(ssaProcedure *p, String text);
-ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value);
-ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address);
-
-
-ssaValue *ssa_alloc_value(gbAllocator a, ssaValueKind kind) {
-	ssaValue *v = gb_alloc_item(a, ssaValue);
-	v->kind = kind;
-	return v;
-}
-ssaValue *ssa_alloc_instr(ssaProcedure *proc, ssaInstrKind kind) {
-	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Instr);
-	v->Instr.kind = kind;
-	proc->instr_count++;
-	return v;
-}
-ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) {
-	ssaDebugInfo *di = gb_alloc_item(a, ssaDebugInfo);
-	di->kind = kind;
-	return di;
-}
-
-
-
-
-ssaValue *ssa_make_value_type_name(gbAllocator a, String name, Type *type) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName);
-	v->TypeName.name = name;
-	v->TypeName.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Global);
-	v->Global.entity = e;
-	v->Global.type = make_type_pointer(a, e->type);
-	v->Global.value = value;
-	array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	return v;
-}
-ssaValue *ssa_make_value_param(gbAllocator a, ssaProcedure *parent, Entity *e) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Param);
-	v->Param.parent = parent;
-	v->Param.entity = e;
-	v->Param.type   = e->type;
-	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	return v;
-}
-ssaValue *ssa_make_value_nil(gbAllocator a, Type *type) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Nil);
-	v->Nil.type = type;
-	return v;
-}
-
-
-
-ssaValue *ssa_make_instr_local(ssaProcedure *p, Entity *e, b32 zero_initialized) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Local);
-	ssaInstr *i = &v->Instr;
-	i->Local.entity = e;
-	i->Local.type = make_type_pointer(p->module->allocator, e->type);
-	i->Local.zero_initialized = zero_initialized;
-	array_init(&i->Local.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
-	ssa_module_add_value(p->module, e, v);
-	return v;
-}
-
-
-ssaValue *ssa_make_instr_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Store);
-	ssaInstr *i = &v->Instr;
-	i->Store.address = address;
-	i->Store.value = value;
-	return v;
-}
-
-ssaValue *ssa_make_instr_zero_init(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ZeroInit);
-	ssaInstr *i = &v->Instr;
-	i->ZeroInit.address = address;
-	return v;
-}
-
-ssaValue *ssa_make_instr_load(ssaProcedure *p, ssaValue *address) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Load);
-	ssaInstr *i = &v->Instr;
-	i->Load.address = address;
-	i->Load.type = type_deref(ssa_type(address));
-	return v;
-}
-
-ssaValue *ssa_make_instr_array_element_ptr(ssaProcedure *p, ssaValue *address, ssaValue *elem_index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayElementPtr);
-	ssaInstr *i = &v->Instr;
-	Type *t = ssa_type(address);
-	GB_ASSERT(is_type_pointer(t));
-	t = base_type(type_deref(t));
-	GB_ASSERT(is_type_array(t));
-
-	Type *result_type = make_type_pointer(p->module->allocator, t->Array.elem);
-
-	i->ArrayElementPtr.address = address;
-	i->ArrayElementPtr.elem_index = elem_index;
-	i->ArrayElementPtr.result_type = result_type;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	return v;
-}
-ssaValue *ssa_make_instr_struct_element_ptr(ssaProcedure *p, ssaValue *address, i32 elem_index, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructElementPtr);
-	ssaInstr *i = &v->Instr;
-	i->StructElementPtr.address     = address;
-	i->StructElementPtr.elem_index  = elem_index;
-	i->StructElementPtr.result_type = result_type;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	return v;
-}
-ssaValue *ssa_make_instr_ptr_offset(ssaProcedure *p, ssaValue *address, ssaValue *offset) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_PtrOffset);
-	ssaInstr *i = &v->Instr;
-	i->PtrOffset.address = address;
-	i->PtrOffset.offset  = offset;
-
-	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
-	              "%s", type_to_string(ssa_type(address)));
-	GB_ASSERT_MSG(is_type_integer(ssa_type(offset)),
-	              "%s", type_to_string(ssa_type(address)));
-
-	return v;
-}
-
-
-
-ssaValue *ssa_make_instr_array_extract_value(ssaProcedure *p, ssaValue *address, i32 index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_ArrayExtractValue);
-	ssaInstr *i = &v->Instr;
-	i->ArrayExtractValue.address = address;
-	i->ArrayExtractValue.index = index;
-	Type *t = base_type(ssa_type(address));
-	GB_ASSERT(is_type_array(t));
-	i->ArrayExtractValue.result_type = t->Array.elem;
-	return v;
-}
-
-ssaValue *ssa_make_instr_struct_extract_value(ssaProcedure *p, ssaValue *address, i32 index, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_StructExtractValue);
-	ssaInstr *i = &v->Instr;
-	i->StructExtractValue.address = address;
-	i->StructExtractValue.index = index;
-	i->StructExtractValue.result_type = result_type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_binary_op(ssaProcedure *p, TokenKind op, ssaValue *left, ssaValue *right, Type *type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BinaryOp);
-	ssaInstr *i = &v->Instr;
-	i->BinaryOp.op = op;
-	i->BinaryOp.left = left;
-	i->BinaryOp.right = right;
-	i->BinaryOp.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_jump(ssaProcedure *p, ssaBlock *block) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Jump);
-	ssaInstr *i = &v->Instr;
-	i->Jump.block = block;
-	return v;
-}
-ssaValue *ssa_make_instr_if(ssaProcedure *p, ssaValue *cond, ssaBlock *true_block, ssaBlock *false_block) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_If);
-	ssaInstr *i = &v->Instr;
-	i->If.cond = cond;
-	i->If.true_block = true_block;
-	i->If.false_block = false_block;
-	return v;
-}
-
-
-ssaValue *ssa_make_instr_phi(ssaProcedure *p, Array<ssaValue *> edges, Type *type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Phi);
-	ssaInstr *i = &v->Instr;
-	i->Phi.edges = edges;
-	i->Phi.type = type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_unreachable(ssaProcedure *p) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Unreachable);
-	return v;
-}
-
-ssaValue *ssa_make_instr_return(ssaProcedure *p, ssaValue *value) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Return);
-	v->Instr.Return.value = value;
-	return v;
-}
-
-ssaValue *ssa_make_instr_select(ssaProcedure *p, ssaValue *cond, ssaValue *t, ssaValue *f) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Select);
-	v->Instr.Select.cond = cond;
-	v->Instr.Select.true_value = t;
-	v->Instr.Select.false_value = f;
-	return v;
-}
-
-ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count, Type *result_type) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Call);
-	v->Instr.Call.value = value;
-	v->Instr.Call.args = args;
-	v->Instr.Call.arg_count = arg_count;
-	v->Instr.Call.type = result_type;
-	return v;
-}
-
-ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv);
-	v->Instr.Conv.kind = kind;
-	v->Instr.Conv.value = value;
-	v->Instr.Conv.from = from;
-	v->Instr.Conv.to = to;
-	return v;
-}
-
-ssaValue *ssa_make_instr_extract_element(ssaProcedure *p, ssaValue *vector, ssaValue *index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorExtractElement);
-	v->Instr.VectorExtractElement.vector = vector;
-	v->Instr.VectorExtractElement.index = index;
-	return v;
-}
-
-ssaValue *ssa_make_instr_insert_element(ssaProcedure *p, ssaValue *vector, ssaValue *elem, ssaValue *index) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorInsertElement);
-	v->Instr.VectorInsertElement.vector = vector;
-	v->Instr.VectorInsertElement.elem   = elem;
-	v->Instr.VectorInsertElement.index  = index;
-	return v;
-}
-
-ssaValue *ssa_make_instr_vector_shuffle(ssaProcedure *p, ssaValue *vector, i32 *indices, isize index_count) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_VectorShuffle);
-	v->Instr.VectorShuffle.vector      = vector;
-	v->Instr.VectorShuffle.indices     = indices;
-	v->Instr.VectorShuffle.index_count = index_count;
-
-	Type *vt = base_type(ssa_type(vector));
-	v->Instr.VectorShuffle.type = make_type_vector(p->module->allocator, vt->Vector.elem, index_count);
-
-	return v;
-}
-
-ssaValue *ssa_make_instr_comment(ssaProcedure *p, String text) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Comment);
-	v->Instr.Comment.text = text;
-	return v;
-}
-
-ssaValue *ssa_make_instr_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *index, ssaValue *len) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_BoundsCheck);
-	v->Instr.BoundsCheck.pos   = pos;
-	v->Instr.BoundsCheck.index = index;
-	v->Instr.BoundsCheck.len   = len;
-	return v;
-}
-ssaValue *ssa_make_instr_slice_bounds_check(ssaProcedure *p, TokenPos pos, ssaValue *low, ssaValue *high, ssaValue *max, b32 is_substring) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_SliceBoundsCheck);
-	v->Instr.SliceBoundsCheck.pos  = pos;
-	v->Instr.SliceBoundsCheck.low  = low;
-	v->Instr.SliceBoundsCheck.high = high;
-	v->Instr.SliceBoundsCheck.max  = max;
-	v->Instr.SliceBoundsCheck.is_substring = is_substring;
-	return v;
-}
-
-
-
-ssaValue *ssa_make_value_constant(gbAllocator a, Type *type, ExactValue value) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Constant);
-	v->Constant.type  = type;
-	v->Constant.value = value;
-	return v;
-}
-
-
-ssaValue *ssa_make_value_constant_slice(gbAllocator a, Type *type, ssaValue *backing_array, i64 count) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_ConstantSlice);
-	v->ConstantSlice.type = type;
-	v->ConstantSlice.backing_array = backing_array;
-	v->ConstantSlice.count = count;
-	return v;
-}
-
-ssaValue *ssa_make_const_int(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_int, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_i32(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_i32, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_i64(gbAllocator a, i64 i) {
-	return ssa_make_value_constant(a, t_i64, make_exact_value_integer(i));
-}
-ssaValue *ssa_make_const_bool(gbAllocator a, b32 b) {
-	return ssa_make_value_constant(a, t_bool, make_exact_value_bool(b != 0));
-}
-ssaValue *ssa_make_const_string(gbAllocator a, String s) {
-	return ssa_make_value_constant(a, t_string, make_exact_value_string(s));
-}
-
-ssaValue *ssa_make_value_procedure(gbAllocator a, ssaModule *m, Entity *entity, Type *type, AstNode *type_expr, AstNode *body, String name) {
-	ssaValue *v = ssa_alloc_value(a, ssaValue_Proc);
-	v->Proc.module = m;
-	v->Proc.entity = entity;
-	v->Proc.type   = type;
-	v->Proc.type_expr = type_expr;
-	v->Proc.body   = body;
-	v->Proc.name   = name;
-	array_init(&v->Proc.referrers, heap_allocator(), 0); // TODO(bill): replace heap allocator
-
-	Type *t = base_type(type);
-	GB_ASSERT(is_type_proc(t));
-	array_init(&v->Proc.params, heap_allocator(), t->Proc.param_count);
-
-	return v;
-}
-
-ssaBlock *ssa_add_block(ssaProcedure *proc, AstNode *node, char *label) {
-	Scope *scope = NULL;
-	if (node != NULL) {
-		Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node));
-		if (found) {
-			scope = *found;
-		} else {
-			GB_PANIC("Block scope not found for %.*s", LIT(ast_node_strings[node->kind]));
-		}
-	}
-
-	ssaValue *v = ssa_alloc_value(proc->module->allocator, ssaValue_Block);
-	v->Block.label  = make_string(label);
-	v->Block.node   = node;
-	v->Block.scope  = scope;
-	v->Block.parent = proc;
-
-	array_init(&v->Block.instrs, heap_allocator());
-	array_init(&v->Block.locals, heap_allocator());
-
-	array_init(&v->Block.preds,  heap_allocator());
-	array_init(&v->Block.succs,  heap_allocator());
-
-	ssaBlock *block = &v->Block;
-
-	array_add(&proc->blocks, block);
-	proc->block_count++;
-
-	return block;
-}
-
-
-
-
-
-ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) {
-	ssaDefer d = {ssaDefer_Node};
-	d.scope_index = scope_index;
-	d.block = proc->curr_block;
-	d.stmt = stmt;
-	array_add(&proc->defer_stmts, d);
-	return d;
-}
-
-
-ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) {
-	ssaDefer d = {ssaDefer_Instr};
-	d.scope_index = proc->scope_index;
-	d.block = proc->curr_block;
-	d.instr = instr; // NOTE(bill): It will make a copy everytime it is called
-	array_add(&proc->defer_stmts, d);
-	return d;
-}
-
-
-
-ssaValue *ssa_add_module_constant(ssaModule *m, Type *type, ExactValue value) {
-	if (is_type_slice(type)) {
-		ast_node(cl, CompoundLit, value.value_compound);
-		gbAllocator a = m->allocator;
-
-		isize count = cl->elems.count;
-		if (count == 0) {
-			return ssa_make_value_nil(a, type);
-		}
-		Type *elem = base_type(type)->Slice.elem;
-		Type *t = make_type_array(a, elem, count);
-		ssaValue *backing_array = ssa_add_module_constant(m, t, value);
-
-
-		isize max_len = 7+8+1;
-		u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
-		isize len = gb_snprintf(cast(char *)str, max_len, "__csba$%x", m->global_array_index);
-		m->global_array_index++;
-
-		String name = make_string(str, len-1);
-
-		Entity *e = make_entity_constant(a, NULL, make_token_ident(name), t, value);
-		ssaValue *g = ssa_make_value_global(a, e, backing_array);
-		ssa_module_add_value(m, e, g);
-		map_set(&m->members, hash_string(name), g);
-
-		return ssa_make_value_constant_slice(a, type, g, count);
-	}
-
-	return ssa_make_value_constant(m->allocator, type, value);
-}
-
-ssaValue *ssa_add_global_string_array(ssaModule *m, String string) {
-	gbAllocator a = m->allocator;
-
-	isize max_len = 6+8+1;
-	u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
-	isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", m->global_string_index);
-	m->global_string_index++;
-
-	String name = make_string(str, len-1);
-	Token token = {Token_String};
-	token.string = name;
-	Type *type = make_type_array(a, t_u8, string.len);
-	ExactValue ev = make_exact_value_string(string);
-	Entity *entity = make_entity_constant(a, NULL, token, type, ev);
-	ssaValue *g = ssa_make_value_global(a, entity, ssa_add_module_constant(m, type, ev));
-	g->Global.is_private      = true;
-	// g->Global.is_unnamed_addr = true;
-	// g->Global.is_constant = true;
-
-	ssa_module_add_value(m, entity, g);
-	map_set(&m->members, hash_string(name), g);
-
-	return g;
-}
-
-
-
-
-ssaValue *ssa_add_local(ssaProcedure *proc, Entity *e, b32 zero_initialized = true) {
-	ssaBlock *b = proc->decl_block; // all variables must be in the first block
-	ssaValue *instr = ssa_make_instr_local(proc, e, zero_initialized);
-	instr->Instr.parent = b;
-	array_add(&b->instrs, instr);
-	array_add(&b->locals, instr);
-	proc->local_count++;
-
-	// if (zero_initialized) {
-		ssa_emit_zero_init(proc, instr);
-	// }
-
-	return instr;
-}
-
-ssaValue *ssa_add_local_for_identifier(ssaProcedure *proc, AstNode *name, b32 zero_initialized) {
-	Entity **found = map_get(&proc->module->info->definitions, hash_pointer(name));
-	if (found) {
-		Entity *e = *found;
-		ssa_emit_comment(proc, e->token.string);
-		return ssa_add_local(proc, e, zero_initialized);
-	}
-	return NULL;
-}
-
-ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
-	GB_ASSERT(type != NULL);
-
-	Scope *scope = NULL;
-	if (proc->curr_block) {
-		scope = proc->curr_block->scope;
-	}
-	Entity *e = make_entity_variable(proc->module->allocator,
-	                                 scope,
-	                                 empty_token,
-	                                 type);
-	return ssa_add_local(proc, e, true);
-}
-
-ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
-	ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e);
-#if 1
-	ssaValue *l = ssa_add_local(proc, e);
-	ssa_emit_store(proc, l, v);
-#else
-	ssa_module_add_value(proc->module, e, v);
-#endif
-	return v;
-}

+ 0 - 0
src/ssa/module.cpp


+ 0 - 114
src/ssa/proc.cpp

@@ -1,114 +0,0 @@
-void ssa_begin_procedure_body(ssaProcedure *proc) {
-	array_init(&proc->blocks,      heap_allocator());
-	array_init(&proc->defer_stmts, heap_allocator());
-	array_init(&proc->children,    heap_allocator());
-
-	proc->decl_block  = ssa_add_block(proc, proc->type_expr, "decls");
-	proc->entry_block = ssa_add_block(proc, proc->type_expr, "entry");
-	proc->curr_block  = proc->entry_block;
-
-	if (proc->type->Proc.params != NULL) {
-		auto *params = &proc->type->Proc.params->Tuple;
-		for (isize i = 0; i < params->variable_count; i++) {
-			Entity *e = params->variables[i];
-			ssaValue *param = ssa_add_param(proc, e);
-			array_add(&proc->params, param);
-		}
-	}
-}
-
-
-void ssa_end_procedure_body(ssaProcedure *proc) {
-	if (proc->type->Proc.result_count == 0) {
-		ssa_emit_return(proc, NULL);
-	}
-
-	if (proc->curr_block->instrs.count == 0) {
-		ssa_emit_unreachable(proc);
-	}
-
-	proc->curr_block = proc->decl_block;
-	ssa_emit_jump(proc, proc->entry_block);
-
-	ssa_opt_proc(proc);
-
-// Number registers
-	i32 reg_index = 0;
-	for_array(i, proc->blocks) {
-		ssaBlock *b = proc->blocks[i];
-		b->index = i;
-		for_array(j, b->instrs) {
-			ssaValue *value = b->instrs[j];
-			GB_ASSERT(value->kind == ssaValue_Instr);
-			ssaInstr *instr = &value->Instr;
-			if (ssa_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions
-				continue;
-			}
-			value->index = reg_index;
-			reg_index++;
-		}
-	}
-}
-
-
-void ssa_insert_code_before_proc(ssaProcedure* proc, ssaProcedure *parent) {
-	if (parent == NULL) {
-		if (proc->name == "main") {
-			ssa_emit_startup_runtime(proc);
-		}
-	}
-}
-
-void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
-	ssaProcedure *proc = &value->Proc;
-
-	proc->parent = parent;
-
-	if (proc->entity != NULL) {
-		ssaModule *m = proc->module;
-		CheckerInfo *info = m->info;
-		Entity *e = proc->entity;
-		String filename = e->token.pos.file;
-		AstFile **found = map_get(&info->files, hash_string(filename));
-		GB_ASSERT(found != NULL);
-		AstFile *f = *found;
-		ssaDebugInfo *di_file = NULL;
-
-		ssaDebugInfo **di_file_found = map_get(&m->debug_info, hash_pointer(f));
-		if (di_file_found) {
-			di_file = *di_file_found;
-			GB_ASSERT(di_file->kind == ssaDebugInfo_File);
-		} else {
-			di_file = ssa_add_debug_info_file(proc, f);
-		}
-
-		ssa_add_debug_info_proc(proc, e, proc->name, di_file);
-	}
-
-	if (proc->body != NULL) {
-		u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
-		defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
-
-		if (proc->tags != 0) {
-			u32 in = proc->tags;
-			u32 out = proc->module->stmt_state_flags;
-			defer (proc->module->stmt_state_flags = out);
-
-			if (in & ProcTag_bounds_check) {
-				out |= StmtStateFlag_bounds_check;
-				out &= ~StmtStateFlag_no_bounds_check;
-			} else if (in & ProcTag_no_bounds_check) {
-				out |= StmtStateFlag_no_bounds_check;
-				out &= ~StmtStateFlag_bounds_check;
-			}
-		}
-
-
-		ssa_begin_procedure_body(proc);
-		ssa_insert_code_before_proc(proc, parent);
-		ssa_build_stmt(proc, proc->body);
-		ssa_end_procedure_body(proc);
-	}
-}
-
-

+ 0 - 0
src/ssa/procedure.cpp


+ 0 - 846
src/ssa/ssa.cpp

@@ -1,846 +0,0 @@
-struct ssaProcedure;
-struct ssaBlock;
-struct ssaValue;
-struct ssaDebugInfo;
-
-struct ssaModule {
-	CheckerInfo * info;
-	BaseTypeSizes sizes;
-	gbArena       arena;
-	gbArena       tmp_arena;
-	gbAllocator   allocator;
-	gbAllocator   tmp_allocator;
-	b32 generate_debug_info;
-
-	u32 stmt_state_flags;
-
-	// String source_filename;
-	String layout;
-	// String triple;
-
-
-	Map<Entity *>       min_dep_map; // Key: Entity *
-	Map<ssaValue *>     values;      // Key: Entity *
-	Map<ssaValue *>     members;     // Key: String
-	Map<String>         type_names;  // Key: Type *
-	Map<ssaDebugInfo *> debug_info;  // Key: Unique pointer
-	i32                 global_string_index;
-	i32                 global_array_index; // For ConstantSlice
-
-	Array<ssaValue *> procs; // NOTE(bill): Procedures to generate
-};
-
-// NOTE(bill): For more info, see https://en.wikipedia.org/wiki/Dominator_(graph_theory)
-struct ssaDomNode {
-	ssaBlock *        idom; // Parent (Immediate Dominator)
-	Array<ssaBlock *> children;
-	i32               pre, post; // Ordering in tree
-};
-
-
-struct ssaBlock {
-	i32           index;
-	String        label;
-	ssaProcedure *parent;
-	AstNode *     node; // Can be NULL
-	Scope *       scope;
-	isize         scope_index;
-	ssaDomNode    dom;
-	i32           gaps;
-
-	Array<ssaValue *> instrs;
-	Array<ssaValue *> locals;
-
-	Array<ssaBlock *> preds;
-	Array<ssaBlock *> succs;
-};
-
-struct ssaTargetList {
-	ssaTargetList *prev;
-	ssaBlock *     break_;
-	ssaBlock *     continue_;
-	ssaBlock *     fallthrough_;
-};
-
-enum ssaDeferExitKind {
-	ssaDeferExit_Default,
-	ssaDeferExit_Return,
-	ssaDeferExit_Branch,
-};
-enum ssaDeferKind {
-	ssaDefer_Node,
-	ssaDefer_Instr,
-};
-
-struct ssaDefer {
-	ssaDeferKind kind;
-	isize        scope_index;
-	ssaBlock *   block;
-	union {
-		AstNode *stmt;
-		// NOTE(bill): `instr` will be copied every time to create a new one
-		ssaValue *instr;
-	};
-};
-
-struct ssaProcedure {
-	ssaProcedure *        parent;
-	Array<ssaProcedure *> children;
-
-	Entity *              entity;
-	ssaModule *           module;
-	String                name;
-	Type *                type;
-	AstNode *             type_expr;
-	AstNode *             body;
-	u64                   tags;
-
-	Array<ssaValue *>     params;
-	Array<ssaDefer>       defer_stmts;
-	Array<ssaBlock *>     blocks;
-	i32                   scope_index;
-	ssaBlock *            decl_block;
-	ssaBlock *            entry_block;
-	ssaBlock *            curr_block;
-	ssaTargetList *       target_list;
-	Array<ssaValue *>     referrers;
-
-	i32                   local_count;
-	i32                   instr_count;
-	i32                   block_count;
-};
-
-#define SSA_STARTUP_RUNTIME_PROC_NAME  "__$startup_runtime"
-#define SSA_TYPE_INFO_DATA_NAME        "__$type_info_data"
-#define SSA_TYPE_INFO_DATA_MEMBER_NAME "__$type_info_data_member"
-
-
-#define SSA_INSTR_KINDS \
-	SSA_INSTR_KIND(Invalid), \
-	SSA_INSTR_KIND(Comment), \
-	SSA_INSTR_KIND(Local), \
-	SSA_INSTR_KIND(ZeroInit), \
-	SSA_INSTR_KIND(Store), \
-	SSA_INSTR_KIND(Load), \
-	SSA_INSTR_KIND(PtrOffset), \
-	SSA_INSTR_KIND(ArrayElementPtr), \
-	SSA_INSTR_KIND(StructElementPtr), \
-	SSA_INSTR_KIND(ArrayExtractValue), \
-	SSA_INSTR_KIND(StructExtractValue), \
-	SSA_INSTR_KIND(Conv), \
-	SSA_INSTR_KIND(Jump), \
-	SSA_INSTR_KIND(If), \
-	SSA_INSTR_KIND(Return), \
-	SSA_INSTR_KIND(Select), \
-	SSA_INSTR_KIND(Phi), \
-	SSA_INSTR_KIND(Unreachable), \
-	SSA_INSTR_KIND(BinaryOp), \
-	SSA_INSTR_KIND(Call), \
-	SSA_INSTR_KIND(VectorExtractElement), \
-	SSA_INSTR_KIND(VectorInsertElement), \
-	SSA_INSTR_KIND(VectorShuffle), \
-	SSA_INSTR_KIND(StartupRuntime), \
-	SSA_INSTR_KIND(BoundsCheck), \
-	SSA_INSTR_KIND(SliceBoundsCheck), \
-
-#define SSA_CONV_KINDS \
-	SSA_CONV_KIND(Invalid), \
-	SSA_CONV_KIND(trunc), \
-	SSA_CONV_KIND(zext), \
-	SSA_CONV_KIND(fptrunc), \
-	SSA_CONV_KIND(fpext), \
-	SSA_CONV_KIND(fptoui), \
-	SSA_CONV_KIND(fptosi), \
-	SSA_CONV_KIND(uitofp), \
-	SSA_CONV_KIND(sitofp), \
-	SSA_CONV_KIND(ptrtoint), \
-	SSA_CONV_KIND(inttoptr), \
-	SSA_CONV_KIND(bitcast),
-
-enum ssaInstrKind {
-#define SSA_INSTR_KIND(x) GB_JOIN2(ssaInstr_, x)
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-};
-
-String const ssa_instr_strings[] = {
-#define SSA_INSTR_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}
-	SSA_INSTR_KINDS
-#undef SSA_INSTR_KIND
-};
-
-enum ssaConvKind {
-#define SSA_CONV_KIND(x) GB_JOIN2(ssaConv_, x)
-	SSA_CONV_KINDS
-#undef SSA_CONV_KIND
-};
-
-String const ssa_conv_strings[] = {
-#define SSA_CONV_KIND(x) {cast(u8 *)#x, gb_size_of(#x)-1}
-	SSA_CONV_KINDS
-#undef SSA_CONV_KIND
-};
-
-struct ssaInstr {
-	ssaInstrKind kind;
-
-	ssaBlock *parent;
-	Type *type;
-
-	union {
-		struct {
-			String text;
-		} Comment;
-		struct {
-			Entity *          entity;
-			Type *            type;
-			b32               zero_initialized;
-			Array<ssaValue *> referrers;
-		} Local;
-		struct {
-			ssaValue *address;
-		} ZeroInit;
-		struct {
-			ssaValue *address;
-			ssaValue *value;
-		} Store;
-		struct {
-			Type *type;
-			ssaValue *address;
-		} Load;
-		struct {
-			ssaValue *address;
-			Type *    result_type;
-			ssaValue *elem_index;
-		} ArrayElementPtr;
-		struct {
-			ssaValue *address;
-			Type *    result_type;
-			i32       elem_index;
-		} StructElementPtr;
-		struct {
-			ssaValue *address;
-			ssaValue *offset;
-		} PtrOffset;
-		struct {
-			ssaValue *address;
-			Type *    result_type;
-			i32       index;
-		} ArrayExtractValue;
-		struct {
-			ssaValue *address;
-			Type *    result_type;
-			i32       index;
-		} StructExtractValue;
-		struct {
-			ssaValue *value;
-			ssaValue *elem;
-			i32       index;
-		} InsertValue;
-		struct {
-			ssaConvKind kind;
-			ssaValue *value;
-			Type *from, *to;
-		} Conv;
-		struct {
-			ssaBlock *block;
-		} Jump;
-		struct {
-			ssaValue *cond;
-			ssaBlock *true_block;
-			ssaBlock *false_block;
-		} If;
-		struct {
-			ssaValue *value;
-		} Return;
-		struct {} Unreachable;
-		struct {
-			ssaValue *cond;
-			ssaValue *true_value;
-			ssaValue *false_value;
-		} Select;
-		struct {
-			Array<ssaValue *> edges;
-			Type *type;
-		} Phi;
-		struct {
-			Type *type;
-			TokenKind op;
-			ssaValue *left, *right;
-		} BinaryOp;
-		struct {
-			Type *type; // return type
-			ssaValue *value;
-			ssaValue **args;
-			isize arg_count;
-		} Call;
-		struct {
-			ssaValue *vector;
-			ssaValue *index;
-		} VectorExtractElement;
-		struct {
-			ssaValue *vector;
-			ssaValue *elem;
-			ssaValue *index;
-		} VectorInsertElement;
-		struct {
-			ssaValue *vector;
-			i32 *indices;
-			isize index_count;
-			Type *type;
-		} VectorShuffle;
-
-		struct {} StartupRuntime;
-		struct {
-			TokenPos  pos;
-			ssaValue *index;
-			ssaValue *len;
-		} BoundsCheck;
-		struct {
-			TokenPos  pos;
-			ssaValue *low;
-			ssaValue *high;
-			ssaValue *max;
-			b32       is_substring;
-		} SliceBoundsCheck;
-	};
-};
-
-
-enum ssaValueKind {
-	ssaValue_Invalid,
-
-	ssaValue_Constant,
-	ssaValue_ConstantSlice,
-	ssaValue_Nil,
-	ssaValue_TypeName,
-	ssaValue_Global,
-	ssaValue_Param,
-
-	ssaValue_Proc,
-	ssaValue_Block,
-	ssaValue_Instr,
-
-	ssaValue_Count,
-};
-
-struct ssaValue {
-	ssaValueKind kind;
-	i32 index;
-	union {
-		struct {
-			Type *     type;
-			ExactValue value;
-		} Constant;
-		struct {
-			Type *    type;
-			ssaValue *backing_array;
-			i64       count;
-		} ConstantSlice;
-		struct {
-			Type *type;
-		} Nil;
-		struct {
-			Type * type;
-			String name;
-		} TypeName;
-		struct {
-			Entity *          entity;
-			Type *            type;
-			ssaValue *        value;
-			Array<ssaValue *> referrers;
-			b8                is_constant;
-			b8                is_private;
-			b8                is_thread_local;
-			b8                is_unnamed_addr;
-		} Global;
-		struct {
-			ssaProcedure *    parent;
-			Entity *          entity;
-			Type *            type;
-			Array<ssaValue *> referrers;
-		} Param;
-		ssaProcedure Proc;
-		ssaBlock     Block;
-		ssaInstr     Instr;
-	};
-};
-
-gb_global ssaValue *v_zero    = NULL;
-gb_global ssaValue *v_one     = NULL;
-gb_global ssaValue *v_zero32  = NULL;
-gb_global ssaValue *v_one32   = NULL;
-gb_global ssaValue *v_two32   = NULL;
-gb_global ssaValue *v_false   = NULL;
-gb_global ssaValue *v_true    = NULL;
-
-enum ssaAddrKind {
-	ssaAddr_Default,
-	ssaAddr_Vector,
-};
-
-struct ssaAddr {
-	ssaValue *  addr;
-	AstNode *   expr; // NOTE(bill): Just for testing - probably remove later
-	ssaAddrKind kind;
-	union {
-		struct { ssaValue *index; } Vector;
-	};
-};
-
-ssaAddr ssa_make_addr(ssaValue *addr, AstNode *expr) {
-	ssaAddr v = {addr, expr};
-	return v;
-}
-ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) {
-	ssaAddr v = ssa_make_addr(addr, expr);
-	v.kind = ssaAddr_Vector;
-	v.Vector.index = index;
-	return v;
-}
-
-
-
-enum ssaDebugEncoding {
-	ssaDebugBasicEncoding_Invalid       = 0,
-
-	ssaDebugBasicEncoding_address       = 1,
-	ssaDebugBasicEncoding_boolean       = 2,
-	ssaDebugBasicEncoding_float         = 3,
-	ssaDebugBasicEncoding_signed        = 4,
-	ssaDebugBasicEncoding_signed_char   = 5,
-	ssaDebugBasicEncoding_unsigned      = 6,
-	ssaDebugBasicEncoding_unsigned_char = 7,
-
-	ssaDebugBasicEncoding_member       = 13,
-	ssaDebugBasicEncoding_pointer_type = 15,
-	ssaDebugBasicEncoding_typedef      = 22,
-
-	ssaDebugBasicEncoding_array_type       = 1,
-	ssaDebugBasicEncoding_enumeration_type = 4,
-	ssaDebugBasicEncoding_structure_type   = 19,
-	ssaDebugBasicEncoding_union_type       = 23,
-
-};
-
-enum ssaDebugInfoKind {
-	ssaDebugInfo_Invalid,
-
-	ssaDebugInfo_CompileUnit,
-	ssaDebugInfo_File,
-	ssaDebugInfo_Scope,
-	ssaDebugInfo_Proc,
-	ssaDebugInfo_AllProcs,
-
-	ssaDebugInfo_BasicType,      // basic types
-	ssaDebugInfo_ProcType,
-	ssaDebugInfo_DerivedType,    // pointer, typedef
-	ssaDebugInfo_CompositeType,  // array, struct, enum, (raw_)union
-	ssaDebugInfo_Enumerator,     // For ssaDebugInfo_CompositeType if enum
-	ssaDebugInfo_GlobalVariable,
-	ssaDebugInfo_LocalVariable,
-
-
-	ssaDebugInfo_Count,
-};
-
-struct ssaDebugInfo {
-	ssaDebugInfoKind kind;
-	i32 id;
-
-	union {
-		struct {
-			AstFile *     file;
-			String        producer;
-			ssaDebugInfo *all_procs;
-		} CompileUnit;
-		struct {
-			AstFile *file;
-			String   filename;
-			String   directory;
-		} File;
-		struct {
-			ssaDebugInfo *parent;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			Scope *       scope; // Actual scope
-		} Scope;
-		struct {
-			Entity *      entity;
-			String        name;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-		} Proc;
-		struct {
-			Array<ssaDebugInfo *> procs;
-		} AllProcs;
-
-
-		struct {
-			String           name;
-			i32              size;
-			i32              align;
-			ssaDebugEncoding encoding;
-		} BasicType;
-		struct {
-			ssaDebugInfo *        return_type;
-			Array<ssaDebugInfo *> param_types;
-		} ProcType;
-		struct {
-			ssaDebugInfo *   base_type;
-			ssaDebugEncoding encoding;
-		} DerivedType;
-		struct {
-			ssaDebugEncoding      encoding;
-			String                name;
-			String                identifier;
-			ssaDebugInfo *        file;
-			TokenPos              pos;
-			i32                   size;
-			i32                   align;
-			Array<ssaDebugInfo *> elements;
-		} CompositeType;
-		struct {
-			String name;
-			i64    value;
-		} Enumerator;
-		struct {
-			String        name;
-			String        linkage_name;
-			ssaDebugInfo *scope;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			ssaValue     *variable;
-			ssaDebugInfo *declaration;
-		} GlobalVariable;
-		struct {
-			String        name;
-			ssaDebugInfo *scope;
-			ssaDebugInfo *file;
-			TokenPos      pos;
-			i32           arg; // Non-zero if proc parameter
-			ssaDebugInfo *type;
-		} LocalVariable;
-	};
-};
-
-
-
-struct ssaFileBuffer {
-	gbVirtualMemory vm;
-	isize offset;
-	gbFile *output;
-};
-
-void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) {
-	isize size = 8*gb_virtual_memory_page_size(NULL);
-	f->vm = gb_vm_alloc(NULL, size);
-	f->offset = 0;
-	f->output = output;
-}
-
-void ssa_file_buffer_destroy(ssaFileBuffer *f) {
-	if (f->offset > 0) {
-		// NOTE(bill): finish writing buffered data
-		gb_file_write(f->output, f->vm.data, f->offset);
-	}
-
-	gb_vm_free(f->vm);
-}
-
-void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) {
-	if (len > f->vm.size) {
-		gb_file_write(f->output, data, len);
-		return;
-	}
-
-	if ((f->vm.size - f->offset) < len) {
-		gb_file_write(f->output, f->vm.data, f->offset);
-		f->offset = 0;
-	}
-	u8 *cursor = cast(u8 *)f->vm.data + f->offset;
-	gb_memmove(cursor, data, len);
-	f->offset += len;
-}
-
-
-void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) {
-	va_list va;
-	va_start(va, fmt);
-	char buf[4096] = {};
-	isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
-	ssa_file_buffer_write(f, buf, len-1);
-	va_end(va);
-}
-
-
-void ssa_file_write(ssaFileBuffer *f, void *data, isize len) {
-	ssa_file_buffer_write(f, data, len);
-}
-
-ssaValue *ssa_lookup_member(ssaModule *m, String name) {
-	ssaValue **v = map_get(&m->members, hash_string(name));
-	if (v != NULL) {
-		return *v;
-	}
-	return NULL;
-}
-
-
-Type *ssa_type(ssaValue *value);
-Type *ssa_instr_type(ssaInstr *instr) {
-	switch (instr->kind) {
-	case ssaInstr_Local:
-		return instr->Local.type;
-	case ssaInstr_Load:
-		return instr->Load.type;
-	case ssaInstr_StructElementPtr:
-		return instr->StructElementPtr.result_type;
-	case ssaInstr_ArrayElementPtr:
-		return instr->ArrayElementPtr.result_type;
-	case ssaInstr_PtrOffset:
-		return ssa_type(instr->PtrOffset.address);
-	case ssaInstr_Phi:
-		return instr->Phi.type;
-	case ssaInstr_ArrayExtractValue:
-		return instr->ArrayExtractValue.result_type;
-	case ssaInstr_StructExtractValue:
-		return instr->StructExtractValue.result_type;
-	case ssaInstr_BinaryOp:
-		return instr->BinaryOp.type;
-	case ssaInstr_Conv:
-		return instr->Conv.to;
-	case ssaInstr_Select:
-		return ssa_type(instr->Select.true_value);
-	case ssaInstr_Call: {
-		Type *pt = base_type(instr->Call.type);
-		if (pt != NULL) {
-			if (pt->kind == Type_Tuple && pt->Tuple.variable_count == 1) {
-				return pt->Tuple.variables[0]->type;
-			}
-			return pt;
-		}
-		return NULL;
-	} break;
-	case ssaInstr_VectorExtractElement: {
-		Type *vt = ssa_type(instr->VectorExtractElement.vector);
-		Type *bt = base_vector_type(vt);
-		GB_ASSERT(!is_type_vector(bt));
-		return bt;
-	} break;
-	case ssaInstr_VectorInsertElement:
-		return ssa_type(instr->VectorInsertElement.vector);
-	case ssaInstr_VectorShuffle:
-		return instr->VectorShuffle.type;
-	}
-	return NULL;
-}
-
-Type *ssa_type(ssaValue *value) {
-	switch (value->kind) {
-	case ssaValue_Constant:
-		return value->Constant.type;
-	case ssaValue_ConstantSlice:
-		return value->ConstantSlice.type;
-	case ssaValue_Nil:
-		return value->Nil.type;
-	case ssaValue_TypeName:
-		return value->TypeName.type;
-	case ssaValue_Global:
-		return value->Global.type;
-	case ssaValue_Param:
-		return value->Param.type;
-	case ssaValue_Proc:
-		return value->Proc.type;
-	case ssaValue_Instr:
-		return ssa_instr_type(&value->Instr);
-	}
-	return NULL;
-}
-
-Type *ssa_addr_type(ssaAddr lval) {
-	if (lval.addr != NULL) {
-		Type *t = ssa_type(lval.addr);
-		GB_ASSERT(is_type_pointer(t));
-		return type_deref(t);
-	}
-	return NULL;
-}
-
-
-
-b32 ssa_is_blank_ident(AstNode *node) {
-	if (node->kind == AstNode_Ident) {
-		ast_node(i, Ident, node);
-		return is_blank_ident(i->string);
-	}
-	return false;
-}
-
-
-ssaInstr *ssa_get_last_instr(ssaBlock *block) {
-	if (block != NULL) {
-		isize len = block->instrs.count;
-		if (len > 0) {
-			ssaValue *v = block->instrs[len-1];
-			GB_ASSERT(v->kind == ssaValue_Instr);
-			return &v->Instr;
-		}
-	}
-	return NULL;
-
-}
-
-b32 ssa_is_instr_terminating(ssaInstr *i) {
-	if (i != NULL) {
-		switch (i->kind) {
-		case ssaInstr_Return:
-		case ssaInstr_Unreachable:
-			return true;
-		}
-	}
-
-	return false;
-}
-
-
-void ssa_add_edge(ssaBlock *from, ssaBlock *to) {
-	array_add(&from->succs, to);
-	array_add(&to->preds, from);
-}
-
-void ssa_set_instr_parent(ssaValue *instr, ssaBlock *parent) {
-	if (instr->kind == ssaValue_Instr) {
-		instr->Instr.parent = parent;
-	}
-}
-
-Array<ssaValue *> *ssa_value_referrers(ssaValue *v) {
-	switch (v->kind) {
-	case ssaValue_Global:
-		return &v->Global.referrers;
-	case ssaValue_Param:
-		return &v->Param.referrers;
-	case ssaValue_Proc: {
-		if (v->Proc.parent != NULL) {
-			return &v->Proc.referrers;
-		}
-		return NULL;
-	}
-	case ssaValue_Instr: {
-		ssaInstr *i = &v->Instr;
-		switch (i->kind) {
-		case ssaInstr_Local:
-			return &i->Local.referrers;
-		}
-	} break;
-	}
-
-	return NULL;
-}
-
-
-
-#include "make.cpp"
-#include "debug.cpp"
-#include "emit.cpp"
-#include "build.cpp"
-#include "opt.cpp"
-#include "proc.cpp"
-
-
-
-
-void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
-	map_set(&m->values, hash_pointer(e), v);
-}
-
-void ssa_init_module(ssaModule *m, Checker *c) {
-	// TODO(bill): Determine a decent size for the arena
-	isize token_count = c->parser->total_token_count;
-	isize arena_size = 4 * token_count * gb_size_of(ssaValue);
-	gb_arena_init_from_allocator(&m->arena, heap_allocator(), arena_size);
-	gb_arena_init_from_allocator(&m->tmp_arena, heap_allocator(), arena_size);
-	m->allocator     = gb_arena_allocator(&m->arena);
-	m->tmp_allocator = gb_arena_allocator(&m->tmp_arena);
-	m->info = &c->info;
-	m->sizes = c->sizes;
-
-	map_init(&m->values,     heap_allocator());
-	map_init(&m->members,    heap_allocator());
-	map_init(&m->debug_info, heap_allocator());
-	map_init(&m->type_names, heap_allocator());
-	array_init(&m->procs,  heap_allocator());
-
-	// Default states
-	m->stmt_state_flags = 0;
-	m->stmt_state_flags |= StmtStateFlag_bounds_check;
-
-	{
-		// Add type info data
-		{
-			String name = make_string(SSA_TYPE_INFO_DATA_NAME);
-			isize count = c->info.type_info_map.entries.count;
-			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count));
-			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
-			g->Global.is_private  = true;
-			ssa_module_add_value(m, e, g);
-			map_set(&m->members, hash_string(name), g);
-		}
-
-		// Type info member buffer
-		{
-			// NOTE(bill): Removes need for heap allocation by making it global memory
-			isize count = 0;
-
-			for_array(entry_index, m->info->type_info_map.entries) {
-				auto *entry = &m->info->type_info_map.entries[entry_index];
-				Type *t = cast(Type *)cast(uintptr)entry->key.key;
-
-				switch (t->kind) {
-				case Type_Record:
-					switch (t->Record.kind) {
-					case TypeRecord_Struct:
-					case TypeRecord_RawUnion:
-						count += t->Record.field_count;
-					}
-					break;
-				case Type_Tuple:
-					count += t->Tuple.variable_count;
-					break;
-				}
-			}
-
-			String name = make_string(SSA_TYPE_INFO_DATA_MEMBER_NAME);
-			Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name),
-			                                 make_type_array(m->allocator, t_type_info_member, count));
-			ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
-			ssa_module_add_value(m, e, g);
-			map_set(&m->members, hash_string(name), g);
-		}
-	}
-
-	{
-		ssaDebugInfo *di = ssa_alloc_debug_info(m->allocator, ssaDebugInfo_CompileUnit);
-		di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file
-		di->CompileUnit.producer = make_string("odin");
-
-		map_set(&m->debug_info, hash_pointer(m), di);
-	}
-}
-
-void ssa_destroy_module(ssaModule *m) {
-	map_destroy(&m->values);
-	map_destroy(&m->members);
-	map_destroy(&m->type_names);
-	map_destroy(&m->debug_info);
-	array_free(&m->procs);
-	gb_arena_free(&m->arena);
-}
-
-
-
-
-#include "codegen.cpp"
-
-

+ 11 - 0
src/ssa/opt.cpp → src/ssa_opt.cpp

@@ -79,6 +79,17 @@ void ssa_opt_add_operands(Array<ssaValue *> *ops, ssaInstr *i) {
 		break;
 	case ssaInstr_StartupRuntime:
 		break;
+	case ssaInstr_BoundsCheck:
+		array_add(ops, i->BoundsCheck.index);
+		array_add(ops, i->BoundsCheck.len);
+		break;
+	case ssaInstr_SliceBoundsCheck:
+		array_add(ops, i->SliceBoundsCheck.low);
+		array_add(ops, i->SliceBoundsCheck.high);
+		array_add(ops, i->SliceBoundsCheck.max);
+		break;
+
+
 	}
 }
 

+ 0 - 0
src/llvm/ssa_to_text.cpp → src/ssa_to_llvm.cpp


+ 313 - 67
src/vm/vm.cpp → src/vm.cpp

@@ -1,3 +1,5 @@
+#include "dyncall/include/dyncall.h"
+
 struct VirtualMachine;
 
 struct vmValueProc {
@@ -37,8 +39,9 @@ struct vmFrame {
 	VirtualMachine *  vm;
 	vmFrame *         caller;
 	ssaProcedure *    curr_proc;
+	ssaBlock *        prev_block;
 	ssaBlock *        curr_block;
-	isize             instr_index; // For the current block
+	i32               instr_index; // For the current block
 
 	Map<vmValue>      values; // Key: ssaValue *
 	gbTempArenaMemory temp_arena_memory;
@@ -61,6 +64,14 @@ struct VirtualMachine {
 void    vm_exec_instr   (VirtualMachine *vm, ssaValue *value);
 vmValue vm_operand_value(VirtualMachine *vm, ssaValue *value);
 void    vm_store        (VirtualMachine *vm, void *dst, vmValue val, Type *type);
+void    vm_print_value  (vmValue value, Type *type);
+
+void vm_jump_block(vmFrame *f, ssaBlock *target) {
+	f->prev_block = f->curr_block;
+	f->curr_block = target;
+	f->instr_index = 0;
+}
+
 
 vmFrame *vm_back_frame(VirtualMachine *vm) {
 	if (vm->frame_stack.count > 0) {
@@ -123,6 +134,7 @@ void vm_destroy(VirtualMachine *vm) {
 
 void vm_set_value(vmFrame *f, ssaValue *v, vmValue val) {
 	if (v != NULL) {
+		GB_ASSERT(ssa_type(v) != NULL);
 		map_set(&f->values, hash_pointer(v), val);
 	}
 }
@@ -134,6 +146,7 @@ vmFrame *vm_push_frame(VirtualMachine *vm, ssaProcedure *proc) {
 
 	frame.vm          = vm;
 	frame.curr_proc   = proc;
+	frame.prev_block  = proc->blocks[0];
 	frame.curr_block  = proc->blocks[0];
 	frame.instr_index = 0;
 	frame.caller      = vm_back_frame(vm);
@@ -154,10 +167,10 @@ void vm_pop_frame(VirtualMachine *vm) {
 	map_destroy(&f->values);
 
 	array_pop(&vm->frame_stack);
-
 }
 
-vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue> values) {
+
+vmValue vm_call_proc(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue> values) {
 	Type *type = base_type(proc->type);
 	GB_ASSERT_MSG(type->Proc.param_count == values.count,
 	              "Incorrect number of arguments passed into procedure call!\n"
@@ -169,7 +182,8 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue>
 	vmValue result = {};
 
 	if (proc->body == NULL) {
-		GB_PANIC("TODO(bill): external procedure");
+		// GB_PANIC("TODO(bill): external procedure");
+		gb_printf_err("TODO(bill): external procedure: %.*s\n", LIT(proc->name));
 		return result;
 	}
 	gb_printf("call: %.*s\n", LIT(proc->name));
@@ -179,6 +193,11 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue>
 		vm_set_value(f, proc->params[i], values[i]);
 	}
 
+	if (proc->name == SSA_STARTUP_RUNTIME_PROC_NAME) {
+		ssaBlock *block = proc->curr_block;
+
+	}
+
 	while (f->curr_block != NULL) {
 		ssaValue *curr_instr = f->curr_block->instrs[f->instr_index++];
 		vm_exec_instr(vm, curr_instr);
@@ -195,20 +214,28 @@ vmValue vm_call_procedure(VirtualMachine *vm, ssaProcedure *proc, Array<vmValue>
 			rt = base_type(rt->Tuple.variables[0]->type);
 		}
 
-		if (is_type_string(rt)) {
-			vmValue data  = result.val_comp[0];
-			vmValue count = result.val_comp[1];
-			gb_printf("String: %.*s\n", cast(isize)count.val_int, cast(u8 *)data.val_ptr);
-		} else if (is_type_integer(rt)) {
-			gb_printf("Integer: %lld\n", cast(i64)result.val_int);
-		}
-		// gb_printf("%lld\n", cast(i64)result.val_int);
+		gb_printf("%.*s -> ", LIT(proc->name));
+		vm_print_value(result, rt);
+		gb_printf("\n");
 	}
 
 	vm_pop_frame(vm);
 	return result;
 }
 
+
+ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) {
+	ssaValue *v = ssa_lookup_member(vm->module, name);
+	GB_ASSERT(v->kind == ssaValue_Proc);
+	ssaProcedure *proc = &v->Proc;
+	return proc;
+}
+
+vmValue vm_call_proc_by_name(VirtualMachine *vm, String name, Array<vmValue> args) {
+	ssaProcedure *proc = vm_lookup_procedure(vm, name);
+	return vm_call_proc(vm, proc, args);
+}
+
 vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type *t) {
 	vmValue result = {};
 	Type *original_type = t;
@@ -262,8 +289,7 @@ vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type
 			}
 
 			Type *type = base_type(t);
-			array_init(&result.val_comp, vm->heap_allocator, type->Array.count);
-			array_resize(&result.val_comp, type->Array.count);
+			array_init_count(&result.val_comp, vm->heap_allocator, type->Array.count);
 			for (isize i = 0; i < elem_count; i++) {
 				TypeAndValue *tav = type_and_value_of_expression(vm->module->info, cl->elems[i]);
 				vmValue elem = vm_exact_value(vm, NULL, tav->value, tav->type);
@@ -283,8 +309,7 @@ vmValue vm_exact_value(VirtualMachine *vm, ssaValue *ptr, ExactValue value, Type
 			}
 
 			isize value_count = t->Record.field_count;
-			array_init(&result.val_comp, vm->heap_allocator, value_count);
-			array_resize(&result.val_comp, value_count);
+			array_init_count(&result.val_comp, vm->heap_allocator, value_count);
 
 			if (cl->elems[0]->kind == AstNode_FieldValue) {
 				isize elem_count = cl->elems.count;
@@ -379,6 +404,7 @@ void vm_store_integer(VirtualMachine *vm, void *dst, vmValue val, i64 store_byte
 
 void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) {
 	i64 size = vm_type_size_of(vm, type);
+	Type *original_type = type;
 	type = base_type(get_enum_base_type(type));
 
 	// TODO(bill): I assume little endian here
@@ -431,6 +457,10 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) {
 		}
 		break;
 
+	case Type_Pointer:
+		*cast(void **)dst = val.val_ptr;
+		break;
+
 	case Type_Record: {
 		if (is_type_struct(type)) {
 			u8 *mem = cast(u8 *)dst;
@@ -444,13 +474,41 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) {
 			for (isize i = 0; i < field_count; i++) {
 				Entity *f = type->Record.fields[i];
 				i64 offset = vm_type_offset_of(vm, type, i);
-				void *ptr = mem+offset;
-				vmValue member = val.val_comp[i];
-				vm_store(vm, ptr, member, f->type);
+				vm_store(vm, mem+offset, val.val_comp[i], f->type);
 			}
-
 		} else {
-			gb_printf_err("TODO(bill): records for `vm_store` %s\n", type_to_string(type));
+			// u8 *mem = cast(u8 *)dst;
+			// if (val.val_comp.count == 0) {
+			// 	gb_printf_err("%s\n", type_to_string(original_type));
+			// 	// gb_zero_size(mem, vm_type_size_of(vm, type));
+			// } else {
+			// 	GB_ASSERT(val.val_comp.count == 2);
+			// 	i64 word_size = vm_type_size_of(vm, t_int);
+			// 	i64 size_of_union = vm_type_size_of(vm, type) - word_size;
+			// 	for (isize i = 0; i < size_of_union; i++) {
+			// 		mem[i] = cast(u8)val.val_comp[0].val_comp[i].val_int;
+			// 	}
+			// 	vm_store_integer(vm, mem + size_of_union, val.val_comp[0], word_size);
+			// }
+
+			// gb_printf_err("TODO(bill): records for `vm_store` %s\n", type_to_string(original_type));
+		}
+	} break;
+
+	case Type_Tuple: {
+		u8 *mem = cast(u8 *)dst;
+
+		GB_ASSERT_MSG(type->Tuple.variable_count >= val.val_comp.count,
+		              "%td vs %td",
+		              type->Tuple.variable_count, val.val_comp.count);
+
+		isize variable_count = gb_min(val.val_comp.count, type->Tuple.variable_count);
+
+		for (isize i = 0; i < variable_count; i++) {
+			Entity *f = type->Tuple.variables[i];
+			void *ptr = mem + vm_type_offset_of(vm, type, i);
+			vmValue member = val.val_comp[i];
+			vm_store(vm, ptr, member, f->type);
 		}
 	} break;
 
@@ -463,11 +521,17 @@ void vm_store(VirtualMachine *vm, void *dst, vmValue val, Type *type) {
 		for (i64 i = 0; i < elem_count; i++) {
 			void *ptr = mem + (elem_size*i);
 			vmValue member = val.val_comp[i];
-			*cast(i64 *)ptr = 123;
 			vm_store(vm, ptr, member, elem_type);
 		}
-		gb_printf_err("%lld\n", *cast(i64 *)mem);
+	} break;
+
+	case Type_Slice: {
+		i64 word_size = vm_type_size_of(vm, t_int);
 
+		u8 *mem = cast(u8 *)dst;
+		vm_store(vm, mem+0*word_size, val.val_comp[0], t_rawptr);
+		vm_store(vm, mem+1*word_size, val.val_comp[1], t_int);
+		vm_store(vm, mem+2*word_size, val.val_comp[2], t_int);
 	} break;
 
 	default:
@@ -518,8 +582,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
 
 		case Basic_string: {
 			i64 word_size = vm_type_size_of(vm, t_int);
-			u8 *mem = *cast(u8 **)ptr;
-			array_init(&result.val_comp, vm->heap_allocator, 2);
+			u8 *mem = cast(u8 *)ptr;
+			array_init_count(&result.val_comp, vm->heap_allocator, 2);
 
 			i64 count = 0;
 			u8 *data = mem + 0*word_size;
@@ -530,8 +594,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
 			default: GB_PANIC("Unknown int size");  break;
 			}
 
-			array_add(&result.val_comp, vm_make_value_ptr(mem));
-			array_add(&result.val_comp, vm_make_value_int(count));
+			result.val_comp[0].val_ptr = mem;
+			result.val_comp[1].val_int = count;
 
 		} break;
 
@@ -541,12 +605,44 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
 		}
 		break;
 
+	case Type_Pointer:
+		result.val_ptr = *cast(void **)ptr;
+		break;
+
+	case Type_Array: {
+		i64 count = type->Array.count;
+		Type *elem_type = type->Array.elem;
+		i64 elem_size = vm_type_size_of(vm, elem_type);
+
+		array_init_count(&result.val_comp, vm->heap_allocator, count);
+
+		u8 *mem = cast(u8 *)ptr;
+		for (isize i = 0; i < count; i++) {
+			i64 offset = elem_size*i;
+			vmValue val = vm_load(vm, mem+offset, elem_type);
+			result.val_comp[i] = val;
+		}
+	} break;
+
+	case Type_Slice: {
+		Type *elem_type = type->Slice.elem;
+		i64 elem_size = vm_type_size_of(vm, elem_type);
+		i64 word_size = vm_type_size_of(vm, t_int);
+
+		array_init_count(&result.val_comp, vm->heap_allocator, 3);
+
+		u8 *mem = cast(u8 *)ptr;
+		result.val_comp[0] = vm_load(vm, mem+0*word_size, t_rawptr); // data
+		result.val_comp[1] = vm_load(vm, mem+1*word_size, t_int);    // count
+		result.val_comp[2] = vm_load(vm, mem+2*word_size, t_int);    // capacity
+		return result;
+	} break;
+
 	case Type_Record: {
 		if (is_type_struct(type)) {
 			isize field_count = type->Record.field_count;
 
-			array_init(&result.val_comp, vm->heap_allocator, field_count);
-			array_resize(&result.val_comp, field_count);
+			array_init_count(&result.val_comp, vm->heap_allocator, field_count);
 
 			u8 *mem = cast(u8 *)ptr;
 			for (isize i = 0; i < field_count; i++) {
@@ -558,6 +654,20 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
 		}
 	} break;
 
+	case Type_Tuple: {
+		isize count = type->Tuple.variable_count;
+
+		array_init_count(&result.val_comp, vm->heap_allocator, count);
+
+		u8 *mem = cast(u8 *)ptr;
+		for (isize i = 0; i < count; i++) {
+			Entity *f = type->Tuple.variables[i];
+			i64 offset = vm_type_offset_of(vm, type, i);
+			vmValue val = vm_load(vm, mem+offset, f->type);
+			result.val_comp[i] = val;
+		}
+	} break;
+
 	default:
 		GB_PANIC("TODO(bill): other types for `vm_load` %s", type_to_string(type));
 		break;
@@ -566,14 +676,8 @@ vmValue vm_load(VirtualMachine *vm, void *ptr, Type *type) {
 	return result;
 }
 
-ssaProcedure *vm_lookup_procedure(VirtualMachine *vm, String name) {
-	ssaValue *v = ssa_lookup_member(vm->module, name);
-	GB_ASSERT(v->kind == ssaValue_Proc);
-	ssaProcedure *proc = &v->Proc;
-	return proc;
-}
-
 void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
+	GB_ASSERT(value != NULL);
 	GB_ASSERT(value->kind == ssaValue_Instr);
 	ssaInstr *instr = &value->Instr;
 	vmFrame *f = vm_back_frame(vm);
@@ -586,10 +690,10 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 
 	switch (instr->kind) {
 	case ssaInstr_StartupRuntime: {
-#if 0
+#if 1
 		ssaProcedure *proc = vm_lookup_procedure(vm, make_string(SSA_STARTUP_RUNTIME_PROC_NAME));
 		Array<vmValue> args = {}; // Empty
-		vm_call_procedure(vm, proc, args); // NOTE(bill): No return value
+		vm_call_proc(vm, proc, args); // NOTE(bill): No return value
 #endif
 	} break;
 
@@ -659,7 +763,14 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Phi: {
-		GB_PANIC("TODO(bill): ssaInstr_Phi");
+		for_array(i, f->curr_block->preds) {
+			ssaBlock *pred = f->curr_block->preds[i];
+			if (f->prev_block == pred) {
+				vmValue edge = vm_operand_value(vm, instr->Phi.edges[i]);
+				vm_set_value(f, value, edge);
+				break;
+			}
+		}
 	} break;
 
 	case ssaInstr_ArrayExtractValue: {
@@ -675,18 +786,16 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Jump: {
-		f->curr_block = instr->Jump.block;
-		f->instr_index = 0;
+		vm_jump_block(f, instr->Jump.block);
 	} break;
 
 	case ssaInstr_If: {
 		vmValue cond = vm_operand_value(vm, instr->If.cond);
 		if (cond.val_int != 0) {
-			f->curr_block = instr->If.true_block;
+			vm_jump_block(f, instr->If.true_block);
 		} else {
-			f->curr_block = instr->If.false_block;
+			vm_jump_block(f, instr->If.false_block);
 		}
-		f->instr_index = 0;
 	} break;
 
 	case ssaInstr_Return: {
@@ -700,6 +809,7 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 
 		f->result = result;
 		f->curr_block = NULL;
+		f->instr_index = 0;
 		return;
 	} break;
 
@@ -732,20 +842,20 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 			Type *from = base_type(instr->Conv.from);
 			if (from == t_f64) {
 				u64 u = cast(u64)src.val_f64;
-				gb_memcopy(&dst, &u, to_size);
+				vm_store_integer(vm, &dst, vm_make_value_int(u), to_size);
 			} else {
 				u64 u = cast(u64)src.val_f32;
-				gb_memcopy(&dst, &u, to_size);
+				vm_store_integer(vm, &dst, vm_make_value_int(u), to_size);
 			}
 		} break;
 		case ssaConv_fptosi: {
 			Type *from = base_type(instr->Conv.from);
 			if (from == t_f64) {
 				i64 i = cast(i64)src.val_f64;
-				gb_memcopy(&dst, &i, to_size);
+				vm_store_integer(vm, &dst, vm_make_value_int(i), to_size);
 			} else {
 				i64 i = cast(i64)src.val_f32;
-				gb_memcopy(&dst, &i, to_size);
+				vm_store_integer(vm, &dst, vm_make_value_int(i), to_size);
 			}
 		} break;
 		case ssaConv_uitofp: {
@@ -792,16 +902,56 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 		}
 
 		if (gb_is_between(bo->op, Token__ComparisonBegin+1, Token__ComparisonEnd-1)) {
-			switch (bo->op) {
-			case Token_CmpEq: break;
-			case Token_NotEq: break;
-			case Token_Lt:    break;
-			case Token_Gt:    break;
-			case Token_LtEq:  break;
-			case Token_GtEq:  break;
+			vmValue v = {};
+			vmValue l = vm_operand_value(vm, bo->left);
+			vmValue r = vm_operand_value(vm, bo->right);
+
+			if (is_type_integer(t)) {
+				// TODO(bill): Do I need to take into account the size of the integer?
+				switch (bo->op) {
+				case Token_CmpEq: v.val_int = l.val_int == r.val_int; break;
+				case Token_NotEq: v.val_int = l.val_int != r.val_int; break;
+				case Token_Lt:    v.val_int = l.val_int <  r.val_int; break;
+				case Token_Gt:    v.val_int = l.val_int >  r.val_int; break;
+				case Token_LtEq:  v.val_int = l.val_int <= r.val_int; break;
+				case Token_GtEq:  v.val_int = l.val_int >= r.val_int; break;
+				}
+			} else if (t == t_f32) {
+				switch (bo->op) {
+				case Token_CmpEq: v.val_f32 = l.val_f32 == r.val_f32; break;
+				case Token_NotEq: v.val_f32 = l.val_f32 != r.val_f32; break;
+				case Token_Lt:    v.val_f32 = l.val_f32 <  r.val_f32; break;
+				case Token_Gt:    v.val_f32 = l.val_f32 >  r.val_f32; break;
+				case Token_LtEq:  v.val_f32 = l.val_f32 <= r.val_f32; break;
+				case Token_GtEq:  v.val_f32 = l.val_f32 >= r.val_f32; break;
+				}
+			} else if (t == t_f64) {
+				switch (bo->op) {
+				case Token_CmpEq: v.val_f64 = l.val_f64 == r.val_f64; break;
+				case Token_NotEq: v.val_f64 = l.val_f64 != r.val_f64; break;
+				case Token_Lt:    v.val_f64 = l.val_f64 <  r.val_f64; break;
+				case Token_Gt:    v.val_f64 = l.val_f64 >  r.val_f64; break;
+				case Token_LtEq:  v.val_f64 = l.val_f64 <= r.val_f64; break;
+				case Token_GtEq:  v.val_f64 = l.val_f64 >= r.val_f64; break;
+				}
+			} else if (is_type_string(t)) {
+				Array<vmValue> args = {};
+				array_init(&args, vm->stack_allocator, 2);
+				array_add(&args, l);
+				array_add(&args, r);
+				switch (bo->op) {
+				case Token_CmpEq: v = vm_call_proc_by_name(vm, make_string("__string_eq"), args); break;
+				case Token_NotEq: v = vm_call_proc_by_name(vm, make_string("__string_ne"), args); break;
+				case Token_Lt:    v = vm_call_proc_by_name(vm, make_string("__string_lt"), args); break;
+				case Token_Gt:    v = vm_call_proc_by_name(vm, make_string("__string_gt"), args); break;
+				case Token_LtEq:  v = vm_call_proc_by_name(vm, make_string("__string_le"), args); break;
+				case Token_GtEq:  v = vm_call_proc_by_name(vm, make_string("__string_ge"), args); break;
+				}
+			} else {
+				GB_PANIC("TODO(bill): Vector BinaryOp");
 			}
-			gb_printf_err("TODO(bill): Comparison operations %.*s\n", LIT(token_strings[bo->op]));
-			vm_set_value(f, value, vm_make_value_int(1)); // HACK(bill): always true
+
+			vm_set_value(f, value, v);
 		} else {
 			vmValue v = {};
 			vmValue l = vm_operand_value(vm, bo->left);
@@ -862,7 +1012,7 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 		}
 		vmValue proc = vm_operand_value(vm, instr->Call.value);
 		if (proc.val_proc.proc != NULL) {
-			vmValue result = vm_call_procedure(vm, proc.val_proc.proc, args);
+			vmValue result = vm_call_proc(vm, proc.val_proc.proc, args);
 			vm_set_value(f, value, result);
 		} else {
 			GB_PANIC("TODO(bill): external procedure calls");
@@ -883,15 +1033,30 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 	} break;
 
 	case ssaInstr_VectorExtractElement: {
-
+		vmValue vector = vm_operand_value(vm, instr->VectorExtractElement.vector);
+		vmValue index  = vm_operand_value(vm, instr->VectorExtractElement.index);
+		vmValue v = vector.val_comp[index.val_int];
+		vm_set_value(f, value, v);
 	} break;
 
 	case ssaInstr_VectorInsertElement: {
-
+		vmValue vector = vm_operand_value(vm, instr->VectorInsertElement.vector);
+		vmValue elem   = vm_operand_value(vm, instr->VectorInsertElement.elem);
+		vmValue index  = vm_operand_value(vm, instr->VectorInsertElement.index);
+		vector.val_comp[index.val_int] = elem;
 	} break;
 
 	case ssaInstr_VectorShuffle: {
+		auto *vs = &instr->VectorShuffle;
+		vmValue old_vector = vm_operand_value(vm, instr->VectorShuffle.vector);
+		vmValue new_vector = {};
+		array_init_count(&new_vector.val_comp, vm->stack_allocator, vs->index_count);
+
+		for (i32 i = 0; i < vs->index_count; i++) {
+			new_vector.val_comp[i] = old_vector.val_comp[vs->indices[i]];
+		}
 
+		vm_set_value(f, value, new_vector);
 	} break;
 
 	case ssaInstr_BoundsCheck: {
@@ -904,14 +1069,12 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 		array_add(&args, vm_operand_value(vm, bc->index));
 		array_add(&args, vm_operand_value(vm, bc->len));
 
-		ssaProcedure *proc = vm_lookup_procedure(vm, make_string("__bounds_check_error"));
-		vm_call_procedure(vm, proc, args);
+		vm_call_proc_by_name(vm, make_string("__bounds_check_error"), args);
 	} break;
 
 	case ssaInstr_SliceBoundsCheck: {
 		auto *bc = &instr->SliceBoundsCheck;
 		Array<vmValue> args = {};
-		ssaProcedure *proc;
 
 		array_init(&args, vm->stack_allocator, 7);
 		array_add(&args, vm_exact_value(vm, NULL, make_exact_value_string(bc->pos.file), t_string));
@@ -921,12 +1084,10 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 		array_add(&args, vm_operand_value(vm, bc->high));
 		if (!bc->is_substring) {
 			array_add(&args, vm_operand_value(vm, bc->max));
-			proc = vm_lookup_procedure(vm, make_string("__slice_expr_error"));
+			vm_call_proc_by_name(vm, make_string("__slice_expr_error"), args);
 		} else {
-			proc = vm_lookup_procedure(vm, make_string("__substring_expr_error"));
+			vm_call_proc_by_name(vm, make_string("__substring_expr_error"), args);
 		}
-
-		vm_call_procedure(vm, proc, args);
 	} break;
 
 	default: {
@@ -934,3 +1095,88 @@ void vm_exec_instr(VirtualMachine *vm, ssaValue *value) {
 	} break;
 	}
 }
+
+
+
+void vm_print_value(vmValue value, Type *type) {
+	type = base_type(type);
+	if (is_type_string(type)) {
+		vmValue data  = value.val_comp[0];
+		vmValue count = value.val_comp[1];
+		gb_printf("`%.*s`", cast(isize)count.val_int, cast(u8 *)data.val_ptr);
+	} else if (is_type_boolean(type)) {
+		if (value.val_int != 0) {
+			gb_printf("true");
+		} else {
+			gb_printf("false");
+		}
+	} else if (is_type_integer(type)) {
+		gb_printf("%lld", cast(i64)value.val_int);
+	} else if (type == t_f32) {
+		gb_printf("%f", value.val_f32);
+	} else if (type == t_f64) {
+		gb_printf("%f", value.val_f64);
+	} else if (is_type_pointer(type)) {
+		gb_printf("0x%08x", value.val_ptr);
+	} else if (is_type_array(type)) {
+		gb_printf("[");
+		for_array(i, value.val_comp) {
+			if (i > 0) {
+				gb_printf(", ");
+			}
+			vm_print_value(value.val_comp[i], type->Array.elem);
+		}
+		gb_printf("]");
+	} else if (is_type_vector(type)) {
+		gb_printf("<");
+		for_array(i, value.val_comp) {
+			if (i > 0) {
+				gb_printf(", ");
+			}
+			vm_print_value(value.val_comp[i], type->Vector.elem);
+		}
+		gb_printf(">");
+	} else if (is_type_slice(type)) {
+		gb_printf("[");
+		for_array(i, value.val_comp) {
+			if (i > 0) {
+				gb_printf(", ");
+			}
+			vm_print_value(value.val_comp[i], type->Slice.elem);
+		}
+		gb_printf("]");
+	} else if (is_type_maybe(type)) {
+		if (value.val_comp[1].val_int != 0) {
+			gb_printf("?");
+			vm_print_value(value.val_comp[0], type->Maybe.elem);
+		} else {
+			gb_printf("nil");
+		}
+	} else if (is_type_struct(type)) {
+		if (value.val_comp.count == 0) {
+			gb_printf("nil");
+		} else {
+			gb_printf("{");
+			for_array(i, value.val_comp) {
+				if (i > 0) {
+					gb_printf(", ");
+				}
+				vm_print_value(value.val_comp[i], type->Record.fields[i]->type);
+			}
+			gb_printf("}");
+		}
+	} else if (is_type_tuple(type)) {
+		if (value.val_comp.count != 1) {
+			gb_printf("(");
+		}
+		for_array(i, value.val_comp) {
+			if (i > 0) {
+				gb_printf(", ");
+			}
+			vm_print_value(value.val_comp[i], type->Tuple.variables[i]->type);
+		}
+		if (value.val_comp.count != 1) {
+			gb_printf(")");
+		}
+	}
+}