Sfoglia il codice sorgente

Merge pull request #595 from odin-lang/llvm-integration

LLVM C API Integration
gingerBill 5 anni fa
parent
commit
6bbecbe895
62 ha cambiato i file con 22147 aggiunte e 461 eliminazioni
  1. BIN
      LLVM-C.dll
  2. BIN
      bin/llvm/windows/LLVM-C.lib
  3. 10 4
      build.bat
  4. 6 6
      core/fmt/fmt.odin
  5. 16 6
      core/mem/mem.odin
  6. 1 1
      core/os/os_windows.odin
  7. 7 7
      core/runtime/core.odin
  8. 47 17
      core/runtime/internal.odin
  9. 9 9
      core/runtime/procs_windows_amd64.odin
  10. 2 1
      examples/demo/demo.odin
  11. 7 0
      examples/llvm-demo/demo.odin
  12. 2 0
      src/build_settings.cpp
  13. 1 0
      src/check_decl.cpp
  14. 12 1
      src/check_expr.cpp
  15. 6 2
      src/checker.cpp
  16. 3 0
      src/checker.hpp
  17. 1 2
      src/ir_print.cpp
  18. 65 0
      src/llvm-c/Analysis.h
  19. 85 0
      src/llvm-c/BitReader.h
  20. 59 0
      src/llvm-c/BitWriter.h
  21. 75 0
      src/llvm-c/Comdat.h
  22. 44 0
      src/llvm-c/Config/AsmParsers.def
  23. 46 0
      src/llvm-c/Config/AsmPrinters.def
  24. 45 0
      src/llvm-c/Config/Disassemblers.def
  25. 45 0
      src/llvm-c/Config/Targets.def
  26. 62 0
      src/llvm-c/Config/abi-breaking.h
  27. 85 0
      src/llvm-c/Config/llvm-config.h
  28. 4079 0
      src/llvm-c/Core.h
  29. 90 0
      src/llvm-c/DataTypes.h
  30. 1315 0
      src/llvm-c/DebugInfo.h
  31. 113 0
      src/llvm-c/Disassembler.h
  32. 160 0
      src/llvm-c/DisassemblerTypes.h
  33. 69 0
      src/llvm-c/Error.h
  34. 49 0
      src/llvm-c/ErrorHandling.h
  35. 200 0
      src/llvm-c/ExecutionEngine.h
  36. 40 0
      src/llvm-c/IRReader.h
  37. 56 0
      src/llvm-c/Initialization.h
  38. 68 0
      src/llvm-c/LinkTimeOptimizer.h
  39. 41 0
      src/llvm-c/Linker.h
  40. 233 0
      src/llvm-c/Object.h
  41. 172 0
      src/llvm-c/OrcBindings.h
  42. 329 0
      src/llvm-c/Remarks.h
  43. 65 0
      src/llvm-c/Support.h
  44. 295 0
      src/llvm-c/Target.h
  45. 163 0
      src/llvm-c/TargetMachine.h
  46. 43 0
      src/llvm-c/Transforms/AggressiveInstCombine.h
  47. 55 0
      src/llvm-c/Transforms/Coroutines.h
  48. 84 0
      src/llvm-c/Transforms/IPO.h
  49. 43 0
      src/llvm-c/Transforms/InstCombine.h
  50. 90 0
      src/llvm-c/Transforms/PassManagerBuilder.h
  51. 167 0
      src/llvm-c/Transforms/Scalar.h
  52. 53 0
      src/llvm-c/Transforms/Utils.h
  53. 50 0
      src/llvm-c/Transforms/Vectorize.h
  54. 179 0
      src/llvm-c/Types.h
  55. 899 0
      src/llvm-c/lto.h
  56. 11024 0
      src/llvm_backend.cpp
  57. 407 0
      src/llvm_backend.hpp
  58. 754 399
      src/main.cpp
  59. 2 0
      src/map.cpp
  60. 6 5
      src/parser.hpp
  61. 8 0
      src/string.cpp
  62. 5 1
      src/tokenizer.cpp

BIN
LLVM-C.dll


BIN
bin/llvm/windows/LLVM-C.lib


+ 10 - 4
build.bat

@@ -13,12 +13,14 @@ if "%1" == "1" (
 )
 
 set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
+set compiler_defines= -DLLVM_BACKEND_SUPPORT
+
 
 if %release_mode% EQU 0 ( rem Debug
 	set compiler_flags=%compiler_flags% -Od -MDd -Z7
-	rem -DDISPLAY_TIMING
 ) else ( rem Release
-	set compiler_flags=%compiler_flags% -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
+	set compiler_flags=%compiler_flags% -O2 -MT -Z7
+	set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
 )
 
 set compiler_warnings= ^
@@ -30,7 +32,8 @@ set compiler_warnings= ^
 
 set compiler_includes=
 set libs= ^
-	kernel32.lib
+	kernel32.lib ^
+	bin\llvm\windows\LLVM-C.lib
 
 set linker_flags= -incremental:no -opt:ref -subsystem:console
 
@@ -40,7 +43,7 @@ if %release_mode% EQU 0 ( rem Debug
 	set linker_flags=%linker_flags% -debug
 )
 
-set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
+set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
 set linker_settings=%libs% %linker_flags%
 
 del *.pdb > NUL 2> NUL
@@ -49,6 +52,9 @@ del *.ilk > NUL 2> NUL
 cl %compiler_settings% "src\main.cpp" ^
 	/link %linker_settings% -OUT:%exe_name% ^
 	&& odin run examples/demo/demo.odin
+if %errorlevel% neq 0 (
+	goto end_of_build
+)
 
 del *.obj > NUL 2> NUL
 

+ 6 - 6
core/fmt/fmt.odin

@@ -59,13 +59,13 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
 
 
 // print* procedures return the number of bytes written
-print   :: proc(args: ..any)              -> int { return fprint(context.stdout, ..args); }
-println :: proc(args: ..any)              -> int { return fprintln(context.stdout, ..args); }
-printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); }
+print   :: proc(args: ..any)              -> int { return fprint(os.stdout, ..args); }
+println :: proc(args: ..any)              -> int { return fprintln(os.stdout, ..args); }
+printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args); }
 
-eprint   :: proc(args: ..any)              -> int { return fprint(context.stderr, ..args); }
-eprintln :: proc(args: ..any)              -> int { return fprintln(context.stderr, ..args); }
-eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); }
+eprint   :: proc(args: ..any)              -> int { return fprint(os.stderr, ..args); }
+eprintln :: proc(args: ..any)              -> int { return fprintln(os.stderr, ..args); }
+eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args); }
 
 
 @(deprecated="prefer eprint")   print_err   :: proc(args: ..any)              -> int { return eprint(..args); }

+ 16 - 6
core/mem/mem.odin

@@ -13,16 +13,26 @@ swap :: proc{swap16, swap32, swap64};
 
 set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
 	foreign _ {
-		when size_of(rawptr) == 8 {
-			@(link_name="llvm.memset.p0i8.i64")
-			llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
+		when ODIN_USE_LLVM_API {
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memset.p0i8.i64")
+				memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memset.p0i8.i32")
+				memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) ---;
+			}
 		} else {
-			@(link_name="llvm.memset.p0i8.i32")
-			llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memset.p0i8.i64")
+				memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memset.p0i8.i32")
+				memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			}
 		}
 	}
 
-	llvm_memset(data, value, len, 1, false);
+	memset(data, value, len);
 	return data;
 }
 zero :: inline proc "contextless" (data: rawptr, len: int) -> rawptr {

+ 1 - 1
core/os/os_windows.odin

@@ -201,7 +201,7 @@ stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
 stderr := get_std_handle(win32.STD_ERROR_HANDLE);
 
 
-get_std_handle :: proc(h: int) -> Handle {
+get_std_handle :: proc "contextless" (h: int) -> Handle {
 	fd := win32.get_std_handle(i32(h));
 	win32.set_handle_information(fd, win32.HANDLE_FLAG_INHERIT, 0);
 	return Handle(fd);

+ 7 - 7
core/runtime/core.odin

@@ -288,9 +288,9 @@ Context :: struct {
 	assertion_failure_proc: Assertion_Failure_Proc,
 	logger:                 Logger,
 
-	stdin:  os.Handle,
-	stdout: os.Handle,
-	stderr: os.Handle,
+	// stdin:  os.Handle,
+	// stdout: os.Handle,
+	// stderr: os.Handle,
 
 	thread_id:  int,
 
@@ -463,9 +463,9 @@ __init_context :: proc "contextless" (c: ^Context) {
 	c.logger.procedure = default_logger_proc;
 	c.logger.data = nil;
 
-	c.stdin  = os.stdin;
-	c.stdout = os.stdout;
-	c.stderr = os.stderr;
+	// c.stdin  = os.stdin;
+	// c.stdout = os.stdout;
+	// c.stderr = os.stderr;
 }
 
 @builtin
@@ -474,7 +474,7 @@ init_global_temporary_allocator :: proc(data: []byte, backup_allocator := contex
 }
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
-	fd := context.stderr;
+	fd := os.stderr;
 	print_caller_location(fd, loc);
 	os.write_string(fd, " ");
 	os.write_string(fd, prefix);

+ 47 - 17
core/runtime/internal.odin

@@ -40,12 +40,22 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
 	if len < 0 do return data;
 	when !#defined(memset) {
 		foreign _ {
-			when size_of(rawptr) == 8 {
-				@(link_name="llvm.memset.p0i8.i64")
-				memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			when ODIN_USE_LLVM_API {
+				when size_of(rawptr) == 8 {
+					@(link_name="llvm.memset.p0i8.i64")
+					memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) ---;
+				} else {
+					@(link_name="llvm.memset.p0i8.i32")
+					memset :: proc(dst: rawptr, val: byte, len: int, is_volatile: bool = false) ---;
+				}
 			} else {
-				@(link_name="llvm.memset.p0i8.i32")
-				memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				when size_of(rawptr) == 8 {
+					@(link_name="llvm.memset.p0i8.i64")
+					memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				} else {
+					@(link_name="llvm.memset.p0i8.i32")
+					memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+				}
 			}
 		}
 	}
@@ -57,15 +67,25 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
 	if src == nil do return dst;
 	// NOTE(bill): This _must_ be implemented like C's memmove
 	foreign _ {
-		when size_of(rawptr) == 8 {
-			@(link_name="llvm.memmove.p0i8.p0i8.i64")
-			llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
+		when ODIN_USE_LLVM_API {
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memmove.p0i8.p0i8.i64")
+				llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memmove.p0i8.p0i8.i32")
+				llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+			}
 		} else {
-			@(link_name="llvm.memmove.p0i8.p0i8.i32")
-			llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memmove.p0i8.p0i8.i64")
+				llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memmove.p0i8.p0i8.i32")
+				llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			}
 		}
 	}
-	llvm_memmove(dst, src, len, 1, false);
+	llvm_memmove(dst, src, len);
 	return dst;
 }
 
@@ -73,15 +93,25 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
 	if src == nil do return dst;
 	// NOTE(bill): This _must_ be implemented like C's memcpy
 	foreign _ {
-		when size_of(rawptr) == 8 {
-			@(link_name="llvm.memcpy.p0i8.p0i8.i64")
-			llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
+		when ODIN_USE_LLVM_API {
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memcpy.p0i8.p0i8.i64")
+				llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memcpy.p0i8.p0i8.i32")
+				llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
+			}
 		} else {
-			@(link_name="llvm.memcpy.p0i8.p0i8.i32")
-			llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
+			when size_of(rawptr) == 8 {
+				@(link_name="llvm.memcpy.p0i8.p0i8.i64")
+				llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			} else {
+				@(link_name="llvm.memcpy.p0i8.p0i8.i32")
+				llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
+			}
 		}
 	}
-	llvm_memcpy(dst, src, len, 1, false);
+	llvm_memcpy(dst, src, len);
 	return dst;
 }
 

+ 9 - 9
core/runtime/procs_windows_amd64.odin

@@ -2,15 +2,15 @@ package runtime
 
 foreign import kernel32 "system:Kernel32.lib"
 
-@private
-@(link_name="_tls_index")
-_tls_index: u32;
+// @private
+// @(link_name="_tls_index")
+// _tls_index: u32;
 
-@private
-@(link_name="_fltused")
-_fltused: i32 = 0x9875;
+// @private
+// @(link_name="_fltused")
+// _fltused: i32 = 0x9875;
 
-@(link_name="memcpy")
+// @(link_name="memcpy")
 memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 	foreign kernel32 {
 		RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) ---
@@ -19,7 +19,7 @@ memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 	return dst;
 }
 
-@(link_name="memmove")
+// @(link_name="memmove")
 memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 	foreign kernel32 {
 		RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) ---
@@ -28,7 +28,7 @@ memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 	return dst;
 }
 
-@(link_name="memset")
+// @(link_name="memset")
 memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
 	foreign kernel32 {
 		RtlFillMemory :: proc "c" (dst: rawptr, len: int, fill: byte) ---

+ 2 - 1
examples/demo/demo.odin

@@ -1245,7 +1245,7 @@ implicit_selector_expression :: proc() {
 
 	switch f {
 	case .A:
-		fmt.println("HERE");
+		fmt.println("HITHER");
 	case .B:
 		fmt.println("NEVER");
 	case .C:
@@ -1742,6 +1742,7 @@ range_statements_with_multiple_return_values :: proc() {
 	}
 }
 
+
 soa_struct_layout :: proc() {
 	// IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
 	// NOTE(bill): Most likely #soa [N]T

+ 7 - 0
examples/llvm-demo/demo.odin

@@ -0,0 +1,7 @@
+package demo
+
+import "core:fmt"
+
+main :: proc() {
+	fmt.println("Hellope!", 123, true, 1.3);
+}

+ 2 - 0
src/build_settings.cpp

@@ -128,6 +128,8 @@ struct BuildContext {
 	bool   cross_compiling;
 	bool   use_subsystem_windows;
 
+	bool   use_llvm_api;
+
 	QueryDataSetSettings query_data_set_settings;
 
 	gbAffinity affinity;

+ 1 - 0
src/check_decl.cpp

@@ -625,6 +625,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 
 	check_open_scope(ctx, pl->type);
 	defer (check_close_scope(ctx));
+	ctx->scope->procedure_entity = e;
 
 	Type *decl_type = nullptr;
 

+ 12 - 1
src/check_expr.cpp

@@ -5675,7 +5675,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs
 					c->decl = decl; // will be reset by the 'defer' any way
 					for_array(k, decl->deps.entries) {
 						Entity *dep = decl->deps.entries[k].ptr;
-     						add_declaration_dependency(c, dep); // TODO(bill): Should this be here?
+						add_declaration_dependency(c, dep); // TODO(bill): Should this be here?
 					}
 				}
 			}
@@ -7257,6 +7257,16 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
 	}
 
 	Type *pt = base_type(proc_type);
+
+	#if 0
+	if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
+		init_core_context(c->checker);
+		GB_ASSERT(t_context != nullptr);
+		GB_ASSERT(t_context->kind == Type_Named);
+		add_declaration_dependency(c, t_context->Named.type_name);
+	}
+	#endif
+
 	if (result_type == nullptr) {
 		operand->mode = Addressing_NoValue;
 	} else {
@@ -7680,6 +7690,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 				return kind;
 			}
 
+			pl->decl = decl;
 			check_procedure_later(ctx.checker, ctx.file, empty_token, decl, type, pl->body, pl->tags);
 		}
 		check_close_scope(&ctx);

+ 6 - 2
src/checker.cpp

@@ -711,6 +711,7 @@ void init_universal(void) {
 	add_global_string_constant(str_lit("ODIN_ROOT"),    bc->ODIN_ROOT);
 	add_global_constant(str_lit("ODIN_DEBUG"), t_untyped_bool, exact_value_bool(bc->ODIN_DEBUG));
 	add_global_constant(str_lit("ODIN_DISABLE_ASSERT"), t_untyped_bool, exact_value_bool(bc->ODIN_DISABLE_ASSERT));
+	add_global_constant(str_lit("ODIN_USE_LLVM_API"), t_untyped_bool, exact_value_bool(bc->use_llvm_api));
 
 
 // Builtin Procedures
@@ -1627,6 +1628,7 @@ void add_dependency_to_set(Checker *c, Entity *entity) {
 	if (decl == nullptr) {
 		return;
 	}
+
 	for_array(i, decl->type_info_deps.entries) {
 		Type *type = decl->type_info_deps.entries[i].ptr;
 		add_min_dep_type_info(c, type);
@@ -1672,8 +1674,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("type_table"),
 		str_lit("__type_info_of"),
 		str_lit("default_temp_allocator"),
-		str_lit("default_temp_allocator_init"),
-		str_lit("default_temp_allocator_destroy"),
+		// str_lit("default_temp_allocator_init"),
+		// str_lit("default_temp_allocator_destroy"),
 		str_lit("default_temp_allocator_proc"),
 
 		str_lit("Type_Info"),
@@ -1686,6 +1688,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("udivti3"),
 
 		str_lit("memset"),
+		str_lit("memcpy"),
+		str_lit("memmove"),
 
 		str_lit("memory_compare"),
 		str_lit("memory_compare_zero"),

+ 3 - 0
src/checker.hpp

@@ -182,6 +182,7 @@ struct Scope {
 	union {
 		AstPackage *pkg;
 		AstFile *   file;
+		Entity *    procedure_entity;
 	};
 };
 
@@ -381,3 +382,5 @@ void destroy_checker_poly_path(CheckerPolyPath *);
 
 void  check_poly_path_push(CheckerContext *c, Type *t);
 Type *check_poly_path_pop (CheckerContext *c);
+
+void init_core_context(Checker *c);

+ 1 - 2
src/ir_print.cpp

@@ -1021,8 +1021,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 		} else if (is_type_enumerated_array(type)) {
 			ast_node(cl, CompoundLit, value.value_compound);
 
-			Type *index_type = type->EnumeratedArray.elem;
-			Type *elem_type = type->Array.elem;
+			Type *elem_type = type->EnumeratedArray.elem;
 			isize elem_count = cl->elems.count;
 			if (elem_count == 0) {
 				ir_write_str_lit(f, "zeroinitializer");

+ 65 - 0
src/llvm-c/Analysis.h

@@ -0,0 +1,65 @@
+/*===-- llvm-c/Analysis.h - Analysis Library C Interface --------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMAnalysis.a, which           *|
+|* implements various analyses of the LLVM IR.                                *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_ANALYSIS_H
+#define LLVM_C_ANALYSIS_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCAnalysis Analysis
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+typedef enum {
+  LLVMAbortProcessAction, /* verifier will print to stderr and abort() */
+  LLVMPrintMessageAction, /* verifier will print to stderr and return 1 */
+  LLVMReturnStatusAction  /* verifier will just return 1 */
+} LLVMVerifierFailureAction;
+
+
+/* Verifies that a module is valid, taking the specified action if not.
+   Optionally returns a human-readable description of any invalid constructs.
+   OutMessage must be disposed with LLVMDisposeMessage. */
+LLVMBool LLVMVerifyModule(LLVMModuleRef M, LLVMVerifierFailureAction Action,
+                          char **OutMessage);
+
+/* Verifies that a single function is valid, taking the specified action. Useful
+   for debugging. */
+LLVMBool LLVMVerifyFunction(LLVMValueRef Fn, LLVMVerifierFailureAction Action);
+
+/* Open up a ghostview window that displays the CFG of the current function.
+   Useful for debugging. */
+void LLVMViewFunctionCFG(LLVMValueRef Fn);
+void LLVMViewFunctionCFGOnly(LLVMValueRef Fn);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 85 - 0
src/llvm-c/BitReader.h

@@ -0,0 +1,85 @@
+/*===-- llvm-c/BitReader.h - BitReader Library C Interface ------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMBitReader.a, which          *|
+|* implements input of the LLVM bitcode format.                               *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_BITREADER_H
+#define LLVM_C_BITREADER_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCBitReader Bit Reader
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+/* Builds a module from the bitcode in the specified memory buffer, returning a
+   reference to the module via the OutModule parameter. Returns 0 on success.
+   Optionally returns a human-readable error message via OutMessage.
+
+   This is deprecated. Use LLVMParseBitcode2. */
+LLVMBool LLVMParseBitcode(LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutModule,
+                          char **OutMessage);
+
+/* Builds a module from the bitcode in the specified memory buffer, returning a
+   reference to the module via the OutModule parameter. Returns 0 on success. */
+LLVMBool LLVMParseBitcode2(LLVMMemoryBufferRef MemBuf,
+                           LLVMModuleRef *OutModule);
+
+/* This is deprecated. Use LLVMParseBitcodeInContext2. */
+LLVMBool LLVMParseBitcodeInContext(LLVMContextRef ContextRef,
+                                   LLVMMemoryBufferRef MemBuf,
+                                   LLVMModuleRef *OutModule, char **OutMessage);
+
+LLVMBool LLVMParseBitcodeInContext2(LLVMContextRef ContextRef,
+                                    LLVMMemoryBufferRef MemBuf,
+                                    LLVMModuleRef *OutModule);
+
+/** Reads a module from the specified path, returning via the OutMP parameter
+    a module provider which performs lazy deserialization. Returns 0 on success.
+    Optionally returns a human-readable error message via OutMessage.
+    This is deprecated. Use LLVMGetBitcodeModuleInContext2. */
+LLVMBool LLVMGetBitcodeModuleInContext(LLVMContextRef ContextRef,
+                                       LLVMMemoryBufferRef MemBuf,
+                                       LLVMModuleRef *OutM, char **OutMessage);
+
+/** Reads a module from the specified path, returning via the OutMP parameter a
+ * module provider which performs lazy deserialization. Returns 0 on success. */
+LLVMBool LLVMGetBitcodeModuleInContext2(LLVMContextRef ContextRef,
+                                        LLVMMemoryBufferRef MemBuf,
+                                        LLVMModuleRef *OutM);
+
+/* This is deprecated. Use LLVMGetBitcodeModule2. */
+LLVMBool LLVMGetBitcodeModule(LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM,
+                              char **OutMessage);
+
+LLVMBool LLVMGetBitcodeModule2(LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 59 - 0
src/llvm-c/BitWriter.h

@@ -0,0 +1,59 @@
+/*===-- llvm-c/BitWriter.h - BitWriter Library C Interface ------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMBitWriter.a, which          *|
+|* implements output of the LLVM bitcode format.                              *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_BITWRITER_H
+#define LLVM_C_BITWRITER_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCBitWriter Bit Writer
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+/*===-- Operations on modules ---------------------------------------------===*/
+
+/** Writes a module to the specified path. Returns 0 on success. */
+int LLVMWriteBitcodeToFile(LLVMModuleRef M, const char *Path);
+
+/** Writes a module to an open file descriptor. Returns 0 on success. */
+int LLVMWriteBitcodeToFD(LLVMModuleRef M, int FD, int ShouldClose,
+                         int Unbuffered);
+
+/** Deprecated for LLVMWriteBitcodeToFD. Writes a module to an open file
+    descriptor. Returns 0 on success. Closes the Handle. */
+int LLVMWriteBitcodeToFileHandle(LLVMModuleRef M, int Handle);
+
+/** Writes a module to a new memory buffer and returns it. */
+LLVMMemoryBufferRef LLVMWriteBitcodeToMemoryBuffer(LLVMModuleRef M);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 75 - 0
src/llvm-c/Comdat.h

@@ -0,0 +1,75 @@
+/*===-- llvm-c/Comdat.h - Module Comdat C Interface -------------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to COMDAT.                               *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_COMDAT_H
+#define LLVM_C_COMDAT_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  LLVMAnyComdatSelectionKind,        ///< The linker may choose any COMDAT.
+  LLVMExactMatchComdatSelectionKind, ///< The data referenced by the COMDAT must
+                                     ///< be the same.
+  LLVMLargestComdatSelectionKind,    ///< The linker will choose the largest
+                                     ///< COMDAT.
+  LLVMNoDuplicatesComdatSelectionKind, ///< No other Module may specify this
+                                       ///< COMDAT.
+  LLVMSameSizeComdatSelectionKind ///< The data referenced by the COMDAT must be
+                                  ///< the same size.
+} LLVMComdatSelectionKind;
+
+/**
+ * Return the Comdat in the module with the specified name. It is created
+ * if it didn't already exist.
+ *
+ * @see llvm::Module::getOrInsertComdat()
+ */
+LLVMComdatRef LLVMGetOrInsertComdat(LLVMModuleRef M, const char *Name);
+
+/**
+ * Get the Comdat assigned to the given global object.
+ *
+ * @see llvm::GlobalObject::getComdat()
+ */
+LLVMComdatRef LLVMGetComdat(LLVMValueRef V);
+
+/**
+ * Assign the Comdat to the given global object.
+ *
+ * @see llvm::GlobalObject::setComdat()
+ */
+void LLVMSetComdat(LLVMValueRef V, LLVMComdatRef C);
+
+/*
+ * Get the conflict resolution selection kind for the Comdat.
+ *
+ * @see llvm::Comdat::getSelectionKind()
+ */
+LLVMComdatSelectionKind LLVMGetComdatSelectionKind(LLVMComdatRef C);
+
+/*
+ * Set the conflict resolution selection kind for the Comdat.
+ *
+ * @see llvm::Comdat::setSelectionKind()
+ */
+void LLVMSetComdatSelectionKind(LLVMComdatRef C, LLVMComdatSelectionKind Kind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 44 - 0
src/llvm-c/Config/AsmParsers.def

@@ -0,0 +1,44 @@
+/*===- llvm/Config/AsmParsers.def - LLVM Assembly Parsers -------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file enumerates all of the assembly-language parsers                  *|
+|* supported by this build of LLVM. Clients of this file should define        *|
+|* the LLVM_ASM_PARSER macro to be a function-like macro with a               *|
+|* single parameter (the name of the target whose assembly can be             *|
+|* generated); including this file will then enumerate all of the             *|
+|* targets with assembly parsers.                                             *|
+|*                                                                            *|
+|* The set of targets supported by LLVM is generated at configuration         *|
+|* time, at which point this header is generated. Do not modify this          *|
+|* header directly.                                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_ASM_PARSER
+#  error Please define the macro LLVM_ASM_PARSER(TargetName)
+#endif
+
+LLVM_ASM_PARSER(AArch64)
+LLVM_ASM_PARSER(AMDGPU)
+LLVM_ASM_PARSER(ARM)
+LLVM_ASM_PARSER(BPF)
+LLVM_ASM_PARSER(Hexagon)
+LLVM_ASM_PARSER(Lanai)
+LLVM_ASM_PARSER(Mips)
+LLVM_ASM_PARSER(MSP430)
+LLVM_ASM_PARSER(PowerPC)
+LLVM_ASM_PARSER(RISCV)
+LLVM_ASM_PARSER(Sparc)
+LLVM_ASM_PARSER(SystemZ)
+LLVM_ASM_PARSER(WebAssembly)
+LLVM_ASM_PARSER(X86)
+LLVM_ASM_PARSER(AVR)
+
+
+#undef LLVM_ASM_PARSER

+ 46 - 0
src/llvm-c/Config/AsmPrinters.def

@@ -0,0 +1,46 @@
+/*===- llvm/Config/AsmPrinters.def - LLVM Assembly Printers -----*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file enumerates all of the assembly-language printers                 *|
+|* supported by this build of LLVM. Clients of this file should define        *|
+|* the LLVM_ASM_PRINTER macro to be a function-like macro with a              *|
+|* single parameter (the name of the target whose assembly can be             *|
+|* generated); including this file will then enumerate all of the             *|
+|* targets with assembly printers.                                            *|
+|*                                                                            *|
+|* The set of targets supported by LLVM is generated at configuration         *|
+|* time, at which point this header is generated. Do not modify this          *|
+|* header directly.                                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_ASM_PRINTER
+#  error Please define the macro LLVM_ASM_PRINTER(TargetName)
+#endif
+
+LLVM_ASM_PRINTER(AArch64)
+LLVM_ASM_PRINTER(AMDGPU)
+LLVM_ASM_PRINTER(ARM)
+LLVM_ASM_PRINTER(BPF)
+LLVM_ASM_PRINTER(Hexagon)
+LLVM_ASM_PRINTER(Lanai)
+LLVM_ASM_PRINTER(Mips)
+LLVM_ASM_PRINTER(MSP430)
+LLVM_ASM_PRINTER(NVPTX)
+LLVM_ASM_PRINTER(PowerPC)
+LLVM_ASM_PRINTER(RISCV)
+LLVM_ASM_PRINTER(Sparc)
+LLVM_ASM_PRINTER(SystemZ)
+LLVM_ASM_PRINTER(WebAssembly)
+LLVM_ASM_PRINTER(X86)
+LLVM_ASM_PRINTER(XCore)
+LLVM_ASM_PRINTER(AVR)
+
+
+#undef LLVM_ASM_PRINTER

+ 45 - 0
src/llvm-c/Config/Disassemblers.def

@@ -0,0 +1,45 @@
+/*===- llvm/Config/Disassemblers.def - LLVM Assembly Parsers ----*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file enumerates all of the assembly-language parsers                  *|
+|* supported by this build of LLVM. Clients of this file should define        *|
+|* the LLVM_DISASSEMBLER macro to be a function-like macro with a             *|
+|* single parameter (the name of the target whose assembly can be             *|
+|* generated); including this file will then enumerate all of the             *|
+|* targets with assembly parsers.                                             *|
+|*                                                                            *|
+|* The set of targets supported by LLVM is generated at configuration         *|
+|* time, at which point this header is generated. Do not modify this          *|
+|* header directly.                                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_DISASSEMBLER
+#  error Please define the macro LLVM_DISASSEMBLER(TargetName)
+#endif
+
+LLVM_DISASSEMBLER(AArch64)
+LLVM_DISASSEMBLER(AMDGPU)
+LLVM_DISASSEMBLER(ARM)
+LLVM_DISASSEMBLER(BPF)
+LLVM_DISASSEMBLER(Hexagon)
+LLVM_DISASSEMBLER(Lanai)
+LLVM_DISASSEMBLER(Mips)
+LLVM_DISASSEMBLER(MSP430)
+LLVM_DISASSEMBLER(PowerPC)
+LLVM_DISASSEMBLER(RISCV)
+LLVM_DISASSEMBLER(Sparc)
+LLVM_DISASSEMBLER(SystemZ)
+LLVM_DISASSEMBLER(WebAssembly)
+LLVM_DISASSEMBLER(X86)
+LLVM_DISASSEMBLER(XCore)
+LLVM_DISASSEMBLER(AVR)
+
+
+#undef LLVM_DISASSEMBLER

+ 45 - 0
src/llvm-c/Config/Targets.def

@@ -0,0 +1,45 @@
+/*===- llvm/Config/Targets.def - LLVM Target Architectures ------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file enumerates all of the target architectures supported by          *|
+|* this build of LLVM. Clients of this file should define the                 *|
+|* LLVM_TARGET macro to be a function-like macro with a single                *|
+|* parameter (the name of the target); including this file will then          *|
+|* enumerate all of the targets.                                              *|
+|*                                                                            *|
+|* The set of targets supported by LLVM is generated at configuration         *|
+|* time, at which point this header is generated. Do not modify this          *|
+|* header directly.                                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_TARGET
+#  error Please define the macro LLVM_TARGET(TargetName)
+#endif
+
+LLVM_TARGET(AArch64)
+LLVM_TARGET(AMDGPU)
+LLVM_TARGET(ARM)
+LLVM_TARGET(BPF)
+LLVM_TARGET(Hexagon)
+LLVM_TARGET(Lanai)
+LLVM_TARGET(Mips)
+LLVM_TARGET(MSP430)
+LLVM_TARGET(NVPTX)
+LLVM_TARGET(PowerPC)
+LLVM_TARGET(RISCV)
+LLVM_TARGET(Sparc)
+LLVM_TARGET(SystemZ)
+LLVM_TARGET(WebAssembly)
+LLVM_TARGET(X86)
+LLVM_TARGET(XCore)
+LLVM_TARGET(AVR)
+
+
+#undef LLVM_TARGET

+ 62 - 0
src/llvm-c/Config/abi-breaking.h

@@ -0,0 +1,62 @@
+/*===------- llvm/Config/abi-breaking.h - llvm configuration -------*- C -*-===*/
+/*                                                                            */
+/* Part of the LLVM Project, under the Apache License v2.0 with LLVM          */
+/* Exceptions.                                                                */
+/* See https://llvm.org/LICENSE.txt for license information.                  */
+/* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+
+/* This file controls the C++ ABI break introduced in LLVM public header. */
+
+#ifndef LLVM_ABI_BREAKING_CHECKS_H
+#define LLVM_ABI_BREAKING_CHECKS_H
+
+/* Define to enable checks that alter the LLVM C++ ABI */
+#define LLVM_ENABLE_ABI_BREAKING_CHECKS 0
+
+/* Define to enable reverse iteration of unordered llvm containers */
+#define LLVM_ENABLE_REVERSE_ITERATION 0
+
+/* Allow selectively disabling link-time mismatch checking so that header-only
+   ADT content from LLVM can be used without linking libSupport. */
+#if !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
+
+// ABI_BREAKING_CHECKS protection: provides link-time failure when clients build
+// mismatch with LLVM
+#if defined(_MSC_VER)
+// Use pragma with MSVC
+#define LLVM_XSTR(s) LLVM_STR(s)
+#define LLVM_STR(s) #s
+#pragma detect_mismatch("LLVM_ENABLE_ABI_BREAKING_CHECKS", LLVM_XSTR(LLVM_ENABLE_ABI_BREAKING_CHECKS))
+#undef LLVM_XSTR
+#undef LLVM_STR
+#elif defined(_WIN32) || defined(__CYGWIN__) // Win32 w/o #pragma detect_mismatch
+// FIXME: Implement checks without weak.
+#elif defined(__cplusplus)
+#if !(defined(_AIX) && defined(__GNUC__) && !defined(__clang__))
+#define LLVM_HIDDEN_VISIBILITY __attribute__ ((visibility("hidden")))
+#else
+// GCC on AIX does not support visibility attributes. Symbols are not
+// exported by default on AIX.
+#define LLVM_HIDDEN_VISIBILITY
+#endif
+namespace llvm {
+#if LLVM_ENABLE_ABI_BREAKING_CHECKS
+extern int EnableABIBreakingChecks;
+LLVM_HIDDEN_VISIBILITY
+__attribute__((weak)) int *VerifyEnableABIBreakingChecks =
+    &EnableABIBreakingChecks;
+#else
+extern int DisableABIBreakingChecks;
+LLVM_HIDDEN_VISIBILITY
+__attribute__((weak)) int *VerifyDisableABIBreakingChecks =
+    &DisableABIBreakingChecks;
+#endif
+}
+#undef LLVM_HIDDEN_VISIBILITY
+#endif // _MSC_VER
+
+#endif // LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING
+
+#endif

+ 85 - 0
src/llvm-c/Config/llvm-config.h

@@ -0,0 +1,85 @@
+/*===------- llvm/Config/llvm-config.h - llvm configuration -------*- C -*-===*/
+/*                                                                            */
+/* Part of the LLVM Project, under the Apache License v2.0 with LLVM          */
+/* Exceptions.                                                                */
+/* See https://llvm.org/LICENSE.txt for license information.                  */
+/* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+
+/* This file enumerates variables from the LLVM configuration so that they
+   can be in exported headers and won't override package specific directives.
+   This is a C header that can be included in the llvm-c headers. */
+
+#ifndef LLVM_CONFIG_H
+#define LLVM_CONFIG_H
+
+/* Define if LLVM_ENABLE_DUMP is enabled */
+/* #undef LLVM_ENABLE_DUMP */
+
+/* Define if we link Polly to the tools */
+/* #undef LINK_POLLY_INTO_TOOLS */
+
+/* Target triple LLVM will generate code for by default */
+#define LLVM_DEFAULT_TARGET_TRIPLE "x86_64-pc-windows-msvc"
+
+/* Define if threads enabled */
+#define LLVM_ENABLE_THREADS 1
+
+/* Has gcc/MSVC atomic intrinsics */
+#define LLVM_HAS_ATOMICS 1
+
+/* Host triple LLVM will be executed on */
+#define LLVM_HOST_TRIPLE "x86_64-pc-windows-msvc"
+
+/* LLVM architecture name for the native architecture, if available */
+#define LLVM_NATIVE_ARCH X86
+
+/* LLVM name for the native AsmParser init function, if available */
+#define LLVM_NATIVE_ASMPARSER LLVMInitializeX86AsmParser
+
+/* LLVM name for the native AsmPrinter init function, if available */
+#define LLVM_NATIVE_ASMPRINTER LLVMInitializeX86AsmPrinter
+
+/* LLVM name for the native Disassembler init function, if available */
+#define LLVM_NATIVE_DISASSEMBLER LLVMInitializeX86Disassembler
+
+/* LLVM name for the native Target init function, if available */
+#define LLVM_NATIVE_TARGET LLVMInitializeX86Target
+
+/* LLVM name for the native TargetInfo init function, if available */
+#define LLVM_NATIVE_TARGETINFO LLVMInitializeX86TargetInfo
+
+/* LLVM name for the native target MC init function, if available */
+#define LLVM_NATIVE_TARGETMC LLVMInitializeX86TargetMC
+
+/* Define if this is Unixish platform */
+/* #undef LLVM_ON_UNIX */
+
+/* Define if we have the Intel JIT API runtime support library */
+#define LLVM_USE_INTEL_JITEVENTS 0
+
+/* Define if we have the oprofile JIT-support library */
+#define LLVM_USE_OPROFILE 0
+
+/* Define if we have the perf JIT-support library */
+#define LLVM_USE_PERF 0
+
+/* Major version of the LLVM API */
+#define LLVM_VERSION_MAJOR 9
+
+/* Minor version of the LLVM API */
+#define LLVM_VERSION_MINOR 0
+
+/* Patch version of the LLVM API */
+#define LLVM_VERSION_PATCH 0
+
+/* LLVM version string */
+#define LLVM_VERSION_STRING "9.0.0"
+
+/* Whether LLVM records statistics for use with GetStatistics(),
+ * PrintStatistics() or PrintStatisticsJSON()
+ */
+#define LLVM_FORCE_ENABLE_STATS 0
+
+#endif

+ 4079 - 0
src/llvm-c/Core.h

@@ -0,0 +1,4079 @@
+/*===-- llvm-c/Core.h - Core Library C Interface ------------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMCore.a, which implements    *|
+|* the LLVM intermediate representation.                                      *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_CORE_H
+#define LLVM_C_CORE_H
+
+#include "llvm-c/ErrorHandling.h"
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMC LLVM-C: C interface to LLVM
+ *
+ * This module exposes parts of the LLVM library as a C API.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup LLVMCTransforms Transforms
+ */
+
+/**
+ * @defgroup LLVMCCore Core
+ *
+ * This modules provide an interface to libLLVMCore, which implements
+ * the LLVM intermediate representation as well as other related types
+ * and utilities.
+ *
+ * Many exotic languages can interoperate with C code but have a harder time
+ * with C++ due to name mangling. So in addition to C, this interface enables
+ * tools written in such languages.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup LLVMCCoreTypes Types and Enumerations
+ *
+ * @{
+ */
+
+/// External users depend on the following values being stable. It is not safe
+/// to reorder them.
+typedef enum {
+  /* Terminator Instructions */
+  LLVMRet            = 1,
+  LLVMBr             = 2,
+  LLVMSwitch         = 3,
+  LLVMIndirectBr     = 4,
+  LLVMInvoke         = 5,
+  /* removed 6 due to API changes */
+  LLVMUnreachable    = 7,
+  LLVMCallBr         = 67,
+
+  /* Standard Unary Operators */
+  LLVMFNeg           = 66,
+
+  /* Standard Binary Operators */
+  LLVMAdd            = 8,
+  LLVMFAdd           = 9,
+  LLVMSub            = 10,
+  LLVMFSub           = 11,
+  LLVMMul            = 12,
+  LLVMFMul           = 13,
+  LLVMUDiv           = 14,
+  LLVMSDiv           = 15,
+  LLVMFDiv           = 16,
+  LLVMURem           = 17,
+  LLVMSRem           = 18,
+  LLVMFRem           = 19,
+
+  /* Logical Operators */
+  LLVMShl            = 20,
+  LLVMLShr           = 21,
+  LLVMAShr           = 22,
+  LLVMAnd            = 23,
+  LLVMOr             = 24,
+  LLVMXor            = 25,
+
+  /* Memory Operators */
+  LLVMAlloca         = 26,
+  LLVMLoad           = 27,
+  LLVMStore          = 28,
+  LLVMGetElementPtr  = 29,
+
+  /* Cast Operators */
+  LLVMTrunc          = 30,
+  LLVMZExt           = 31,
+  LLVMSExt           = 32,
+  LLVMFPToUI         = 33,
+  LLVMFPToSI         = 34,
+  LLVMUIToFP         = 35,
+  LLVMSIToFP         = 36,
+  LLVMFPTrunc        = 37,
+  LLVMFPExt          = 38,
+  LLVMPtrToInt       = 39,
+  LLVMIntToPtr       = 40,
+  LLVMBitCast        = 41,
+  LLVMAddrSpaceCast  = 60,
+
+  /* Other Operators */
+  LLVMICmp           = 42,
+  LLVMFCmp           = 43,
+  LLVMPHI            = 44,
+  LLVMCall           = 45,
+  LLVMSelect         = 46,
+  LLVMUserOp1        = 47,
+  LLVMUserOp2        = 48,
+  LLVMVAArg          = 49,
+  LLVMExtractElement = 50,
+  LLVMInsertElement  = 51,
+  LLVMShuffleVector  = 52,
+  LLVMExtractValue   = 53,
+  LLVMInsertValue    = 54,
+
+  /* Atomic operators */
+  LLVMFence          = 55,
+  LLVMAtomicCmpXchg  = 56,
+  LLVMAtomicRMW      = 57,
+
+  /* Exception Handling Operators */
+  LLVMResume         = 58,
+  LLVMLandingPad     = 59,
+  LLVMCleanupRet     = 61,
+  LLVMCatchRet       = 62,
+  LLVMCatchPad       = 63,
+  LLVMCleanupPad     = 64,
+  LLVMCatchSwitch    = 65
+} LLVMOpcode;
+
+typedef enum {
+  LLVMVoidTypeKind,        /**< type with no size */
+  LLVMHalfTypeKind,        /**< 16 bit floating point type */
+  LLVMFloatTypeKind,       /**< 32 bit floating point type */
+  LLVMDoubleTypeKind,      /**< 64 bit floating point type */
+  LLVMX86_FP80TypeKind,    /**< 80 bit floating point type (X87) */
+  LLVMFP128TypeKind,       /**< 128 bit floating point type (112-bit mantissa)*/
+  LLVMPPC_FP128TypeKind,   /**< 128 bit floating point type (two 64-bits) */
+  LLVMLabelTypeKind,       /**< Labels */
+  LLVMIntegerTypeKind,     /**< Arbitrary bit width integers */
+  LLVMFunctionTypeKind,    /**< Functions */
+  LLVMStructTypeKind,      /**< Structures */
+  LLVMArrayTypeKind,       /**< Arrays */
+  LLVMPointerTypeKind,     /**< Pointers */
+  LLVMVectorTypeKind,      /**< SIMD 'packed' format, or other vector type */
+  LLVMMetadataTypeKind,    /**< Metadata */
+  LLVMX86_MMXTypeKind,     /**< X86 MMX */
+  LLVMTokenTypeKind        /**< Tokens */
+} LLVMTypeKind;
+
+typedef enum {
+  LLVMExternalLinkage,    /**< Externally visible function */
+  LLVMAvailableExternallyLinkage,
+  LLVMLinkOnceAnyLinkage, /**< Keep one copy of function when linking (inline)*/
+  LLVMLinkOnceODRLinkage, /**< Same, but only replaced by something
+                            equivalent. */
+  LLVMLinkOnceODRAutoHideLinkage, /**< Obsolete */
+  LLVMWeakAnyLinkage,     /**< Keep one copy of function when linking (weak) */
+  LLVMWeakODRLinkage,     /**< Same, but only replaced by something
+                            equivalent. */
+  LLVMAppendingLinkage,   /**< Special purpose, only applies to global arrays */
+  LLVMInternalLinkage,    /**< Rename collisions when linking (static
+                               functions) */
+  LLVMPrivateLinkage,     /**< Like Internal, but omit from symbol table */
+  LLVMDLLImportLinkage,   /**< Obsolete */
+  LLVMDLLExportLinkage,   /**< Obsolete */
+  LLVMExternalWeakLinkage,/**< ExternalWeak linkage description */
+  LLVMGhostLinkage,       /**< Obsolete */
+  LLVMCommonLinkage,      /**< Tentative definitions */
+  LLVMLinkerPrivateLinkage, /**< Like Private, but linker removes. */
+  LLVMLinkerPrivateWeakLinkage /**< Like LinkerPrivate, but is weak. */
+} LLVMLinkage;
+
+typedef enum {
+  LLVMDefaultVisibility,  /**< The GV is visible */
+  LLVMHiddenVisibility,   /**< The GV is hidden */
+  LLVMProtectedVisibility /**< The GV is protected */
+} LLVMVisibility;
+
+typedef enum {
+  LLVMNoUnnamedAddr,    /**< Address of the GV is significant. */
+  LLVMLocalUnnamedAddr, /**< Address of the GV is locally insignificant. */
+  LLVMGlobalUnnamedAddr /**< Address of the GV is globally insignificant. */
+} LLVMUnnamedAddr;
+
+typedef enum {
+  LLVMDefaultStorageClass   = 0,
+  LLVMDLLImportStorageClass = 1, /**< Function to be imported from DLL. */
+  LLVMDLLExportStorageClass = 2  /**< Function to be accessible from DLL. */
+} LLVMDLLStorageClass;
+
+typedef enum {
+  LLVMCCallConv             = 0,
+  LLVMFastCallConv          = 8,
+  LLVMColdCallConv          = 9,
+  LLVMGHCCallConv           = 10,
+  LLVMHiPECallConv          = 11,
+  LLVMWebKitJSCallConv      = 12,
+  LLVMAnyRegCallConv        = 13,
+  LLVMPreserveMostCallConv  = 14,
+  LLVMPreserveAllCallConv   = 15,
+  LLVMSwiftCallConv         = 16,
+  LLVMCXXFASTTLSCallConv    = 17,
+  LLVMX86StdcallCallConv    = 64,
+  LLVMX86FastcallCallConv   = 65,
+  LLVMARMAPCSCallConv       = 66,
+  LLVMARMAAPCSCallConv      = 67,
+  LLVMARMAAPCSVFPCallConv   = 68,
+  LLVMMSP430INTRCallConv    = 69,
+  LLVMX86ThisCallCallConv   = 70,
+  LLVMPTXKernelCallConv     = 71,
+  LLVMPTXDeviceCallConv     = 72,
+  LLVMSPIRFUNCCallConv      = 75,
+  LLVMSPIRKERNELCallConv    = 76,
+  LLVMIntelOCLBICallConv    = 77,
+  LLVMX8664SysVCallConv     = 78,
+  LLVMWin64CallConv         = 79,
+  LLVMX86VectorCallCallConv = 80,
+  LLVMHHVMCallConv          = 81,
+  LLVMHHVMCCallConv         = 82,
+  LLVMX86INTRCallConv       = 83,
+  LLVMAVRINTRCallConv       = 84,
+  LLVMAVRSIGNALCallConv     = 85,
+  LLVMAVRBUILTINCallConv    = 86,
+  LLVMAMDGPUVSCallConv      = 87,
+  LLVMAMDGPUGSCallConv      = 88,
+  LLVMAMDGPUPSCallConv      = 89,
+  LLVMAMDGPUCSCallConv      = 90,
+  LLVMAMDGPUKERNELCallConv  = 91,
+  LLVMX86RegCallCallConv    = 92,
+  LLVMAMDGPUHSCallConv      = 93,
+  LLVMMSP430BUILTINCallConv = 94,
+  LLVMAMDGPULSCallConv      = 95,
+  LLVMAMDGPUESCallConv      = 96
+} LLVMCallConv;
+
+typedef enum {
+  LLVMArgumentValueKind,
+  LLVMBasicBlockValueKind,
+  LLVMMemoryUseValueKind,
+  LLVMMemoryDefValueKind,
+  LLVMMemoryPhiValueKind,
+
+  LLVMFunctionValueKind,
+  LLVMGlobalAliasValueKind,
+  LLVMGlobalIFuncValueKind,
+  LLVMGlobalVariableValueKind,
+  LLVMBlockAddressValueKind,
+  LLVMConstantExprValueKind,
+  LLVMConstantArrayValueKind,
+  LLVMConstantStructValueKind,
+  LLVMConstantVectorValueKind,
+
+  LLVMUndefValueValueKind,
+  LLVMConstantAggregateZeroValueKind,
+  LLVMConstantDataArrayValueKind,
+  LLVMConstantDataVectorValueKind,
+  LLVMConstantIntValueKind,
+  LLVMConstantFPValueKind,
+  LLVMConstantPointerNullValueKind,
+  LLVMConstantTokenNoneValueKind,
+
+  LLVMMetadataAsValueValueKind,
+  LLVMInlineAsmValueKind,
+
+  LLVMInstructionValueKind,
+} LLVMValueKind;
+
+typedef enum {
+  LLVMIntEQ = 32, /**< equal */
+  LLVMIntNE,      /**< not equal */
+  LLVMIntUGT,     /**< unsigned greater than */
+  LLVMIntUGE,     /**< unsigned greater or equal */
+  LLVMIntULT,     /**< unsigned less than */
+  LLVMIntULE,     /**< unsigned less or equal */
+  LLVMIntSGT,     /**< signed greater than */
+  LLVMIntSGE,     /**< signed greater or equal */
+  LLVMIntSLT,     /**< signed less than */
+  LLVMIntSLE      /**< signed less or equal */
+} LLVMIntPredicate;
+
+typedef enum {
+  LLVMRealPredicateFalse, /**< Always false (always folded) */
+  LLVMRealOEQ,            /**< True if ordered and equal */
+  LLVMRealOGT,            /**< True if ordered and greater than */
+  LLVMRealOGE,            /**< True if ordered and greater than or equal */
+  LLVMRealOLT,            /**< True if ordered and less than */
+  LLVMRealOLE,            /**< True if ordered and less than or equal */
+  LLVMRealONE,            /**< True if ordered and operands are unequal */
+  LLVMRealORD,            /**< True if ordered (no nans) */
+  LLVMRealUNO,            /**< True if unordered: isnan(X) | isnan(Y) */
+  LLVMRealUEQ,            /**< True if unordered or equal */
+  LLVMRealUGT,            /**< True if unordered or greater than */
+  LLVMRealUGE,            /**< True if unordered, greater than, or equal */
+  LLVMRealULT,            /**< True if unordered or less than */
+  LLVMRealULE,            /**< True if unordered, less than, or equal */
+  LLVMRealUNE,            /**< True if unordered or not equal */
+  LLVMRealPredicateTrue   /**< Always true (always folded) */
+} LLVMRealPredicate;
+
+typedef enum {
+  LLVMLandingPadCatch,    /**< A catch clause   */
+  LLVMLandingPadFilter    /**< A filter clause  */
+} LLVMLandingPadClauseTy;
+
+typedef enum {
+  LLVMNotThreadLocal = 0,
+  LLVMGeneralDynamicTLSModel,
+  LLVMLocalDynamicTLSModel,
+  LLVMInitialExecTLSModel,
+  LLVMLocalExecTLSModel
+} LLVMThreadLocalMode;
+
+typedef enum {
+  LLVMAtomicOrderingNotAtomic = 0, /**< A load or store which is not atomic */
+  LLVMAtomicOrderingUnordered = 1, /**< Lowest level of atomicity, guarantees
+                                     somewhat sane results, lock free. */
+  LLVMAtomicOrderingMonotonic = 2, /**< guarantees that if you take all the
+                                     operations affecting a specific address,
+                                     a consistent ordering exists */
+  LLVMAtomicOrderingAcquire = 4, /**< Acquire provides a barrier of the sort
+                                   necessary to acquire a lock to access other
+                                   memory with normal loads and stores. */
+  LLVMAtomicOrderingRelease = 5, /**< Release is similar to Acquire, but with
+                                   a barrier of the sort necessary to release
+                                   a lock. */
+  LLVMAtomicOrderingAcquireRelease = 6, /**< provides both an Acquire and a
+                                          Release barrier (for fences and
+                                          operations which both read and write
+                                           memory). */
+  LLVMAtomicOrderingSequentiallyConsistent = 7 /**< provides Acquire semantics
+                                                 for loads and Release
+                                                 semantics for stores.
+                                                 Additionally, it guarantees
+                                                 that a total ordering exists
+                                                 between all
+                                                 SequentiallyConsistent
+                                                 operations. */
+} LLVMAtomicOrdering;
+
+typedef enum {
+    LLVMAtomicRMWBinOpXchg, /**< Set the new value and return the one old */
+    LLVMAtomicRMWBinOpAdd, /**< Add a value and return the old one */
+    LLVMAtomicRMWBinOpSub, /**< Subtract a value and return the old one */
+    LLVMAtomicRMWBinOpAnd, /**< And a value and return the old one */
+    LLVMAtomicRMWBinOpNand, /**< Not-And a value and return the old one */
+    LLVMAtomicRMWBinOpOr, /**< OR a value and return the old one */
+    LLVMAtomicRMWBinOpXor, /**< Xor a value and return the old one */
+    LLVMAtomicRMWBinOpMax, /**< Sets the value if it's greater than the
+                             original using a signed comparison and return
+                             the old one */
+    LLVMAtomicRMWBinOpMin, /**< Sets the value if it's Smaller than the
+                             original using a signed comparison and return
+                             the old one */
+    LLVMAtomicRMWBinOpUMax, /**< Sets the value if it's greater than the
+                             original using an unsigned comparison and return
+                             the old one */
+    LLVMAtomicRMWBinOpUMin /**< Sets the value if it's greater than the
+                             original using an unsigned comparison  and return
+                             the old one */
+} LLVMAtomicRMWBinOp;
+
+typedef enum {
+    LLVMDSError,
+    LLVMDSWarning,
+    LLVMDSRemark,
+    LLVMDSNote
+} LLVMDiagnosticSeverity;
+
+typedef enum {
+  LLVMInlineAsmDialectATT,
+  LLVMInlineAsmDialectIntel
+} LLVMInlineAsmDialect;
+
+typedef enum {
+  /**
+   * Emits an error if two values disagree, otherwise the resulting value is
+   * that of the operands.
+   *
+   * @see Module::ModFlagBehavior::Error
+   */
+  LLVMModuleFlagBehaviorError,
+  /**
+   * Emits a warning if two values disagree. The result value will be the
+   * operand for the flag from the first module being linked.
+   *
+   * @see Module::ModFlagBehavior::Warning
+   */
+  LLVMModuleFlagBehaviorWarning,
+  /**
+   * Adds a requirement that another module flag be present and have a
+   * specified value after linking is performed. The value must be a metadata
+   * pair, where the first element of the pair is the ID of the module flag
+   * to be restricted, and the second element of the pair is the value the
+   * module flag should be restricted to. This behavior can be used to
+   * restrict the allowable results (via triggering of an error) of linking
+   * IDs with the **Override** behavior.
+   *
+   * @see Module::ModFlagBehavior::Require
+   */
+  LLVMModuleFlagBehaviorRequire,
+  /**
+   * Uses the specified value, regardless of the behavior or value of the
+   * other module. If both modules specify **Override**, but the values
+   * differ, an error will be emitted.
+   *
+   * @see Module::ModFlagBehavior::Override
+   */
+  LLVMModuleFlagBehaviorOverride,
+  /**
+   * Appends the two values, which are required to be metadata nodes.
+   *
+   * @see Module::ModFlagBehavior::Append
+   */
+  LLVMModuleFlagBehaviorAppend,
+  /**
+   * Appends the two values, which are required to be metadata
+   * nodes. However, duplicate entries in the second list are dropped
+   * during the append operation.
+   *
+   * @see Module::ModFlagBehavior::AppendUnique
+   */
+  LLVMModuleFlagBehaviorAppendUnique,
+} LLVMModuleFlagBehavior;
+
+/**
+ * Attribute index are either LLVMAttributeReturnIndex,
+ * LLVMAttributeFunctionIndex or a parameter number from 1 to N.
+ */
+enum {
+  LLVMAttributeReturnIndex = 0U,
+  // ISO C restricts enumerator values to range of 'int'
+  // (4294967295 is too large)
+  // LLVMAttributeFunctionIndex = ~0U,
+  LLVMAttributeFunctionIndex = -1,
+};
+
+typedef unsigned LLVMAttributeIndex;
+
+/**
+ * @}
+ */
+
+void LLVMInitializeCore(LLVMPassRegistryRef R);
+
+/** Deallocate and destroy all ManagedStatic variables.
+    @see llvm::llvm_shutdown
+    @see ManagedStatic */
+void LLVMShutdown(void);
+
+/*===-- Error handling ----------------------------------------------------===*/
+
+char *LLVMCreateMessage(const char *Message);
+void LLVMDisposeMessage(char *Message);
+
+/**
+ * @defgroup LLVMCCoreContext Contexts
+ *
+ * Contexts are execution states for the core LLVM IR system.
+ *
+ * Most types are tied to a context instance. Multiple contexts can
+ * exist simultaneously. A single context is not thread safe. However,
+ * different contexts can execute on different threads simultaneously.
+ *
+ * @{
+ */
+
+typedef void (*LLVMDiagnosticHandler)(LLVMDiagnosticInfoRef, void *);
+typedef void (*LLVMYieldCallback)(LLVMContextRef, void *);
+
+/**
+ * Create a new context.
+ *
+ * Every call to this function should be paired with a call to
+ * LLVMContextDispose() or the context will leak memory.
+ */
+LLVMContextRef LLVMContextCreate(void);
+
+/**
+ * Obtain the global context instance.
+ */
+LLVMContextRef LLVMGetGlobalContext(void);
+
+/**
+ * Set the diagnostic handler for this context.
+ */
+void LLVMContextSetDiagnosticHandler(LLVMContextRef C,
+                                     LLVMDiagnosticHandler Handler,
+                                     void *DiagnosticContext);
+
+/**
+ * Get the diagnostic handler of this context.
+ */
+LLVMDiagnosticHandler LLVMContextGetDiagnosticHandler(LLVMContextRef C);
+
+/**
+ * Get the diagnostic context of this context.
+ */
+void *LLVMContextGetDiagnosticContext(LLVMContextRef C);
+
+/**
+ * Set the yield callback function for this context.
+ *
+ * @see LLVMContext::setYieldCallback()
+ */
+void LLVMContextSetYieldCallback(LLVMContextRef C, LLVMYieldCallback Callback,
+                                 void *OpaqueHandle);
+
+/**
+ * Retrieve whether the given context is set to discard all value names.
+ *
+ * @see LLVMContext::shouldDiscardValueNames()
+ */
+LLVMBool LLVMContextShouldDiscardValueNames(LLVMContextRef C);
+
+/**
+ * Set whether the given context discards all value names.
+ *
+ * If true, only the names of GlobalValue objects will be available in the IR.
+ * This can be used to save memory and runtime, especially in release mode.
+ *
+ * @see LLVMContext::setDiscardValueNames()
+ */
+void LLVMContextSetDiscardValueNames(LLVMContextRef C, LLVMBool Discard);
+
+/**
+ * Destroy a context instance.
+ *
+ * This should be called for every call to LLVMContextCreate() or memory
+ * will be leaked.
+ */
+void LLVMContextDispose(LLVMContextRef C);
+
+/**
+ * Return a string representation of the DiagnosticInfo. Use
+ * LLVMDisposeMessage to free the string.
+ *
+ * @see DiagnosticInfo::print()
+ */
+char *LLVMGetDiagInfoDescription(LLVMDiagnosticInfoRef DI);
+
+/**
+ * Return an enum LLVMDiagnosticSeverity.
+ *
+ * @see DiagnosticInfo::getSeverity()
+ */
+LLVMDiagnosticSeverity LLVMGetDiagInfoSeverity(LLVMDiagnosticInfoRef DI);
+
+unsigned LLVMGetMDKindIDInContext(LLVMContextRef C, const char *Name,
+                                  unsigned SLen);
+unsigned LLVMGetMDKindID(const char *Name, unsigned SLen);
+
+/**
+ * Return an unique id given the name of a enum attribute,
+ * or 0 if no attribute by that name exists.
+ *
+ * See http://llvm.org/docs/LangRef.html#parameter-attributes
+ * and http://llvm.org/docs/LangRef.html#function-attributes
+ * for the list of available attributes.
+ *
+ * NB: Attribute names and/or id are subject to change without
+ * going through the C API deprecation cycle.
+ */
+unsigned LLVMGetEnumAttributeKindForName(const char *Name, size_t SLen);
+unsigned LLVMGetLastEnumAttributeKind(void);
+
+/**
+ * Create an enum attribute.
+ */
+LLVMAttributeRef LLVMCreateEnumAttribute(LLVMContextRef C, unsigned KindID,
+                                         uint64_t Val);
+
+/**
+ * Get the unique id corresponding to the enum attribute
+ * passed as argument.
+ */
+unsigned LLVMGetEnumAttributeKind(LLVMAttributeRef A);
+
+/**
+ * Get the enum attribute's value. 0 is returned if none exists.
+ */
+uint64_t LLVMGetEnumAttributeValue(LLVMAttributeRef A);
+
+/**
+ * Create a string attribute.
+ */
+LLVMAttributeRef LLVMCreateStringAttribute(LLVMContextRef C,
+                                           const char *K, unsigned KLength,
+                                           const char *V, unsigned VLength);
+
+/**
+ * Get the string attribute's kind.
+ */
+const char *LLVMGetStringAttributeKind(LLVMAttributeRef A, unsigned *Length);
+
+/**
+ * Get the string attribute's value.
+ */
+const char *LLVMGetStringAttributeValue(LLVMAttributeRef A, unsigned *Length);
+
+/**
+ * Check for the different types of attributes.
+ */
+LLVMBool LLVMIsEnumAttribute(LLVMAttributeRef A);
+LLVMBool LLVMIsStringAttribute(LLVMAttributeRef A);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreModule Modules
+ *
+ * Modules represent the top-level structure in an LLVM program. An LLVM
+ * module is effectively a translation unit or a collection of
+ * translation units merged together.
+ *
+ * @{
+ */
+
+/**
+ * Create a new, empty module in the global context.
+ *
+ * This is equivalent to calling LLVMModuleCreateWithNameInContext with
+ * LLVMGetGlobalContext() as the context parameter.
+ *
+ * Every invocation should be paired with LLVMDisposeModule() or memory
+ * will be leaked.
+ */
+LLVMModuleRef LLVMModuleCreateWithName(const char *ModuleID);
+
+/**
+ * Create a new, empty module in a specific context.
+ *
+ * Every invocation should be paired with LLVMDisposeModule() or memory
+ * will be leaked.
+ */
+LLVMModuleRef LLVMModuleCreateWithNameInContext(const char *ModuleID,
+                                                LLVMContextRef C);
+/**
+ * Return an exact copy of the specified module.
+ */
+LLVMModuleRef LLVMCloneModule(LLVMModuleRef M);
+
+/**
+ * Destroy a module instance.
+ *
+ * This must be called for every created module or memory will be
+ * leaked.
+ */
+void LLVMDisposeModule(LLVMModuleRef M);
+
+/**
+ * Obtain the identifier of a module.
+ *
+ * @param M Module to obtain identifier of
+ * @param Len Out parameter which holds the length of the returned string.
+ * @return The identifier of M.
+ * @see Module::getModuleIdentifier()
+ */
+const char *LLVMGetModuleIdentifier(LLVMModuleRef M, size_t *Len);
+
+/**
+ * Set the identifier of a module to a string Ident with length Len.
+ *
+ * @param M The module to set identifier
+ * @param Ident The string to set M's identifier to
+ * @param Len Length of Ident
+ * @see Module::setModuleIdentifier()
+ */
+void LLVMSetModuleIdentifier(LLVMModuleRef M, const char *Ident, size_t Len);
+
+/**
+ * Obtain the module's original source file name.
+ *
+ * @param M Module to obtain the name of
+ * @param Len Out parameter which holds the length of the returned string
+ * @return The original source file name of M
+ * @see Module::getSourceFileName()
+ */
+const char *LLVMGetSourceFileName(LLVMModuleRef M, size_t *Len);
+
+/**
+ * Set the original source file name of a module to a string Name with length
+ * Len.
+ *
+ * @param M The module to set the source file name of
+ * @param Name The string to set M's source file name to
+ * @param Len Length of Name
+ * @see Module::setSourceFileName()
+ */
+void LLVMSetSourceFileName(LLVMModuleRef M, const char *Name, size_t Len);
+
+/**
+ * Obtain the data layout for a module.
+ *
+ * @see Module::getDataLayoutStr()
+ *
+ * LLVMGetDataLayout is DEPRECATED, as the name is not only incorrect,
+ * but match the name of another method on the module. Prefer the use
+ * of LLVMGetDataLayoutStr, which is not ambiguous.
+ */
+const char *LLVMGetDataLayoutStr(LLVMModuleRef M);
+const char *LLVMGetDataLayout(LLVMModuleRef M);
+
+/**
+ * Set the data layout for a module.
+ *
+ * @see Module::setDataLayout()
+ */
+void LLVMSetDataLayout(LLVMModuleRef M, const char *DataLayoutStr);
+
+/**
+ * Obtain the target triple for a module.
+ *
+ * @see Module::getTargetTriple()
+ */
+const char *LLVMGetTarget(LLVMModuleRef M);
+
+/**
+ * Set the target triple for a module.
+ *
+ * @see Module::setTargetTriple()
+ */
+void LLVMSetTarget(LLVMModuleRef M, const char *Triple);
+
+/**
+ * Returns the module flags as an array of flag-key-value triples.  The caller
+ * is responsible for freeing this array by calling
+ * \c LLVMDisposeModuleFlagsMetadata.
+ *
+ * @see Module::getModuleFlagsMetadata()
+ */
+LLVMModuleFlagEntry *LLVMCopyModuleFlagsMetadata(LLVMModuleRef M, size_t *Len);
+
+/**
+ * Destroys module flags metadata entries.
+ */
+void LLVMDisposeModuleFlagsMetadata(LLVMModuleFlagEntry *Entries);
+
+/**
+ * Returns the flag behavior for a module flag entry at a specific index.
+ *
+ * @see Module::ModuleFlagEntry::Behavior
+ */
+LLVMModuleFlagBehavior
+LLVMModuleFlagEntriesGetFlagBehavior(LLVMModuleFlagEntry *Entries,
+                                     unsigned Index);
+
+/**
+ * Returns the key for a module flag entry at a specific index.
+ *
+ * @see Module::ModuleFlagEntry::Key
+ */
+const char *LLVMModuleFlagEntriesGetKey(LLVMModuleFlagEntry *Entries,
+                                        unsigned Index, size_t *Len);
+
+/**
+ * Returns the metadata for a module flag entry at a specific index.
+ *
+ * @see Module::ModuleFlagEntry::Val
+ */
+LLVMMetadataRef LLVMModuleFlagEntriesGetMetadata(LLVMModuleFlagEntry *Entries,
+                                                 unsigned Index);
+
+/**
+ * Add a module-level flag to the module-level flags metadata if it doesn't
+ * already exist.
+ *
+ * @see Module::getModuleFlag()
+ */
+LLVMMetadataRef LLVMGetModuleFlag(LLVMModuleRef M,
+                                  const char *Key, size_t KeyLen);
+
+/**
+ * Add a module-level flag to the module-level flags metadata if it doesn't
+ * already exist.
+ *
+ * @see Module::addModuleFlag()
+ */
+void LLVMAddModuleFlag(LLVMModuleRef M, LLVMModuleFlagBehavior Behavior,
+                       const char *Key, size_t KeyLen,
+                       LLVMMetadataRef Val);
+
+/**
+ * Dump a representation of a module to stderr.
+ *
+ * @see Module::dump()
+ */
+void LLVMDumpModule(LLVMModuleRef M);
+
+/**
+ * Print a representation of a module to a file. The ErrorMessage needs to be
+ * disposed with LLVMDisposeMessage. Returns 0 on success, 1 otherwise.
+ *
+ * @see Module::print()
+ */
+LLVMBool LLVMPrintModuleToFile(LLVMModuleRef M, const char *Filename,
+                               char **ErrorMessage);
+
+/**
+ * Return a string representation of the module. Use
+ * LLVMDisposeMessage to free the string.
+ *
+ * @see Module::print()
+ */
+char *LLVMPrintModuleToString(LLVMModuleRef M);
+
+/**
+ * Get inline assembly for a module.
+ *
+ * @see Module::getModuleInlineAsm()
+ */
+const char *LLVMGetModuleInlineAsm(LLVMModuleRef M, size_t *Len);
+
+/**
+ * Set inline assembly for a module.
+ *
+ * @see Module::setModuleInlineAsm()
+ */
+void LLVMSetModuleInlineAsm2(LLVMModuleRef M, const char *Asm, size_t Len);
+
+/**
+ * Append inline assembly to a module.
+ *
+ * @see Module::appendModuleInlineAsm()
+ */
+void LLVMAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, size_t Len);
+
+/**
+ * Create the specified uniqued inline asm string.
+ *
+ * @see InlineAsm::get()
+ */
+LLVMValueRef LLVMGetInlineAsm(LLVMTypeRef Ty,
+                              char *AsmString, size_t AsmStringSize,
+                              char *Constraints, size_t ConstraintsSize,
+                              LLVMBool HasSideEffects, LLVMBool IsAlignStack,
+                              LLVMInlineAsmDialect Dialect);
+
+/**
+ * Obtain the context to which this module is associated.
+ *
+ * @see Module::getContext()
+ */
+LLVMContextRef LLVMGetModuleContext(LLVMModuleRef M);
+
+/**
+ * Obtain a Type from a module by its registered name.
+ */
+LLVMTypeRef LLVMGetTypeByName(LLVMModuleRef M, const char *Name);
+
+/**
+ * Obtain an iterator to the first NamedMDNode in a Module.
+ *
+ * @see llvm::Module::named_metadata_begin()
+ */
+LLVMNamedMDNodeRef LLVMGetFirstNamedMetadata(LLVMModuleRef M);
+
+/**
+ * Obtain an iterator to the last NamedMDNode in a Module.
+ *
+ * @see llvm::Module::named_metadata_end()
+ */
+LLVMNamedMDNodeRef LLVMGetLastNamedMetadata(LLVMModuleRef M);
+
+/**
+ * Advance a NamedMDNode iterator to the next NamedMDNode.
+ *
+ * Returns NULL if the iterator was already at the end and there are no more
+ * named metadata nodes.
+ */
+LLVMNamedMDNodeRef LLVMGetNextNamedMetadata(LLVMNamedMDNodeRef NamedMDNode);
+
+/**
+ * Decrement a NamedMDNode iterator to the previous NamedMDNode.
+ *
+ * Returns NULL if the iterator was already at the beginning and there are
+ * no previous named metadata nodes.
+ */
+LLVMNamedMDNodeRef LLVMGetPreviousNamedMetadata(LLVMNamedMDNodeRef NamedMDNode);
+
+/**
+ * Retrieve a NamedMDNode with the given name, returning NULL if no such
+ * node exists.
+ *
+ * @see llvm::Module::getNamedMetadata()
+ */
+LLVMNamedMDNodeRef LLVMGetNamedMetadata(LLVMModuleRef M,
+                                        const char *Name, size_t NameLen);
+
+/**
+ * Retrieve a NamedMDNode with the given name, creating a new node if no such
+ * node exists.
+ *
+ * @see llvm::Module::getOrInsertNamedMetadata()
+ */
+LLVMNamedMDNodeRef LLVMGetOrInsertNamedMetadata(LLVMModuleRef M,
+                                                const char *Name,
+                                                size_t NameLen);
+
+/**
+ * Retrieve the name of a NamedMDNode.
+ *
+ * @see llvm::NamedMDNode::getName()
+ */
+const char *LLVMGetNamedMetadataName(LLVMNamedMDNodeRef NamedMD,
+                                     size_t *NameLen);
+
+/**
+ * Obtain the number of operands for named metadata in a module.
+ *
+ * @see llvm::Module::getNamedMetadata()
+ */
+unsigned LLVMGetNamedMetadataNumOperands(LLVMModuleRef M, const char *Name);
+
+/**
+ * Obtain the named metadata operands for a module.
+ *
+ * The passed LLVMValueRef pointer should refer to an array of
+ * LLVMValueRef at least LLVMGetNamedMetadataNumOperands long. This
+ * array will be populated with the LLVMValueRef instances. Each
+ * instance corresponds to a llvm::MDNode.
+ *
+ * @see llvm::Module::getNamedMetadata()
+ * @see llvm::MDNode::getOperand()
+ */
+void LLVMGetNamedMetadataOperands(LLVMModuleRef M, const char *Name,
+                                  LLVMValueRef *Dest);
+
+/**
+ * Add an operand to named metadata.
+ *
+ * @see llvm::Module::getNamedMetadata()
+ * @see llvm::MDNode::addOperand()
+ */
+void LLVMAddNamedMetadataOperand(LLVMModuleRef M, const char *Name,
+                                 LLVMValueRef Val);
+
+/**
+ * Return the directory of the debug location for this value, which must be
+ * an llvm::Instruction, llvm::GlobalVariable, or llvm::Function.
+ *
+ * @see llvm::Instruction::getDebugLoc()
+ * @see llvm::GlobalVariable::getDebugInfo()
+ * @see llvm::Function::getSubprogram()
+ */
+const char *LLVMGetDebugLocDirectory(LLVMValueRef Val, unsigned *Length);
+
+/**
+ * Return the filename of the debug location for this value, which must be
+ * an llvm::Instruction, llvm::GlobalVariable, or llvm::Function.
+ *
+ * @see llvm::Instruction::getDebugLoc()
+ * @see llvm::GlobalVariable::getDebugInfo()
+ * @see llvm::Function::getSubprogram()
+ */
+const char *LLVMGetDebugLocFilename(LLVMValueRef Val, unsigned *Length);
+
+/**
+ * Return the line number of the debug location for this value, which must be
+ * an llvm::Instruction, llvm::GlobalVariable, or llvm::Function.
+ *
+ * @see llvm::Instruction::getDebugLoc()
+ * @see llvm::GlobalVariable::getDebugInfo()
+ * @see llvm::Function::getSubprogram()
+ */
+unsigned LLVMGetDebugLocLine(LLVMValueRef Val);
+
+/**
+ * Return the column number of the debug location for this value, which must be
+ * an llvm::Instruction.
+ *
+ * @see llvm::Instruction::getDebugLoc()
+ */
+unsigned LLVMGetDebugLocColumn(LLVMValueRef Val);
+
+/**
+ * Add a function to a module under a specified name.
+ *
+ * @see llvm::Function::Create()
+ */
+LLVMValueRef LLVMAddFunction(LLVMModuleRef M, const char *Name,
+                             LLVMTypeRef FunctionTy);
+
+/**
+ * Obtain a Function value from a Module by its name.
+ *
+ * The returned value corresponds to a llvm::Function value.
+ *
+ * @see llvm::Module::getFunction()
+ */
+LLVMValueRef LLVMGetNamedFunction(LLVMModuleRef M, const char *Name);
+
+/**
+ * Obtain an iterator to the first Function in a Module.
+ *
+ * @see llvm::Module::begin()
+ */
+LLVMValueRef LLVMGetFirstFunction(LLVMModuleRef M);
+
+/**
+ * Obtain an iterator to the last Function in a Module.
+ *
+ * @see llvm::Module::end()
+ */
+LLVMValueRef LLVMGetLastFunction(LLVMModuleRef M);
+
+/**
+ * Advance a Function iterator to the next Function.
+ *
+ * Returns NULL if the iterator was already at the end and there are no more
+ * functions.
+ */
+LLVMValueRef LLVMGetNextFunction(LLVMValueRef Fn);
+
+/**
+ * Decrement a Function iterator to the previous Function.
+ *
+ * Returns NULL if the iterator was already at the beginning and there are
+ * no previous functions.
+ */
+LLVMValueRef LLVMGetPreviousFunction(LLVMValueRef Fn);
+
+/** Deprecated: Use LLVMSetModuleInlineAsm2 instead. */
+void LLVMSetModuleInlineAsm(LLVMModuleRef M, const char *Asm);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreType Types
+ *
+ * Types represent the type of a value.
+ *
+ * Types are associated with a context instance. The context internally
+ * deduplicates types so there is only 1 instance of a specific type
+ * alive at a time. In other words, a unique type is shared among all
+ * consumers within a context.
+ *
+ * A Type in the C API corresponds to llvm::Type.
+ *
+ * Types have the following hierarchy:
+ *
+ *   types:
+ *     integer type
+ *     real type
+ *     function type
+ *     sequence types:
+ *       array type
+ *       pointer type
+ *       vector type
+ *     void type
+ *     label type
+ *     opaque type
+ *
+ * @{
+ */
+
+/**
+ * Obtain the enumerated type of a Type instance.
+ *
+ * @see llvm::Type:getTypeID()
+ */
+LLVMTypeKind LLVMGetTypeKind(LLVMTypeRef Ty);
+
+/**
+ * Whether the type has a known size.
+ *
+ * Things that don't have a size are abstract types, labels, and void.a
+ *
+ * @see llvm::Type::isSized()
+ */
+LLVMBool LLVMTypeIsSized(LLVMTypeRef Ty);
+
+/**
+ * Obtain the context to which this type instance is associated.
+ *
+ * @see llvm::Type::getContext()
+ */
+LLVMContextRef LLVMGetTypeContext(LLVMTypeRef Ty);
+
+/**
+ * Dump a representation of a type to stderr.
+ *
+ * @see llvm::Type::dump()
+ */
+void LLVMDumpType(LLVMTypeRef Val);
+
+/**
+ * Return a string representation of the type. Use
+ * LLVMDisposeMessage to free the string.
+ *
+ * @see llvm::Type::print()
+ */
+char *LLVMPrintTypeToString(LLVMTypeRef Val);
+
+/**
+ * @defgroup LLVMCCoreTypeInt Integer Types
+ *
+ * Functions in this section operate on integer types.
+ *
+ * @{
+ */
+
+/**
+ * Obtain an integer type from a context with specified bit width.
+ */
+LLVMTypeRef LLVMInt1TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMInt8TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMInt16TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMInt32TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMInt64TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMInt128TypeInContext(LLVMContextRef C);
+LLVMTypeRef LLVMIntTypeInContext(LLVMContextRef C, unsigned NumBits);
+
+/**
+ * Obtain an integer type from the global context with a specified bit
+ * width.
+ */
+LLVMTypeRef LLVMInt1Type(void);
+LLVMTypeRef LLVMInt8Type(void);
+LLVMTypeRef LLVMInt16Type(void);
+LLVMTypeRef LLVMInt32Type(void);
+LLVMTypeRef LLVMInt64Type(void);
+LLVMTypeRef LLVMInt128Type(void);
+LLVMTypeRef LLVMIntType(unsigned NumBits);
+unsigned LLVMGetIntTypeWidth(LLVMTypeRef IntegerTy);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreTypeFloat Floating Point Types
+ *
+ * @{
+ */
+
+/**
+ * Obtain a 16-bit floating point type from a context.
+ */
+LLVMTypeRef LLVMHalfTypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a 32-bit floating point type from a context.
+ */
+LLVMTypeRef LLVMFloatTypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a 64-bit floating point type from a context.
+ */
+LLVMTypeRef LLVMDoubleTypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a 80-bit floating point type (X87) from a context.
+ */
+LLVMTypeRef LLVMX86FP80TypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a 128-bit floating point type (112-bit mantissa) from a
+ * context.
+ */
+LLVMTypeRef LLVMFP128TypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a 128-bit floating point type (two 64-bits) from a context.
+ */
+LLVMTypeRef LLVMPPCFP128TypeInContext(LLVMContextRef C);
+
+/**
+ * Obtain a floating point type from the global context.
+ *
+ * These map to the functions in this group of the same name.
+ */
+LLVMTypeRef LLVMHalfType(void);
+LLVMTypeRef LLVMFloatType(void);
+LLVMTypeRef LLVMDoubleType(void);
+LLVMTypeRef LLVMX86FP80Type(void);
+LLVMTypeRef LLVMFP128Type(void);
+LLVMTypeRef LLVMPPCFP128Type(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreTypeFunction Function Types
+ *
+ * @{
+ */
+
+/**
+ * Obtain a function type consisting of a specified signature.
+ *
+ * The function is defined as a tuple of a return Type, a list of
+ * parameter types, and whether the function is variadic.
+ */
+LLVMTypeRef LLVMFunctionType(LLVMTypeRef ReturnType,
+                             LLVMTypeRef *ParamTypes, unsigned ParamCount,
+                             LLVMBool IsVarArg);
+
+/**
+ * Returns whether a function type is variadic.
+ */
+LLVMBool LLVMIsFunctionVarArg(LLVMTypeRef FunctionTy);
+
+/**
+ * Obtain the Type this function Type returns.
+ */
+LLVMTypeRef LLVMGetReturnType(LLVMTypeRef FunctionTy);
+
+/**
+ * Obtain the number of parameters this function accepts.
+ */
+unsigned LLVMCountParamTypes(LLVMTypeRef FunctionTy);
+
+/**
+ * Obtain the types of a function's parameters.
+ *
+ * The Dest parameter should point to a pre-allocated array of
+ * LLVMTypeRef at least LLVMCountParamTypes() large. On return, the
+ * first LLVMCountParamTypes() entries in the array will be populated
+ * with LLVMTypeRef instances.
+ *
+ * @param FunctionTy The function type to operate on.
+ * @param Dest Memory address of an array to be filled with result.
+ */
+void LLVMGetParamTypes(LLVMTypeRef FunctionTy, LLVMTypeRef *Dest);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreTypeStruct Structure Types
+ *
+ * These functions relate to LLVMTypeRef instances.
+ *
+ * @see llvm::StructType
+ *
+ * @{
+ */
+
+/**
+ * Create a new structure type in a context.
+ *
+ * A structure is specified by a list of inner elements/types and
+ * whether these can be packed together.
+ *
+ * @see llvm::StructType::create()
+ */
+LLVMTypeRef LLVMStructTypeInContext(LLVMContextRef C, LLVMTypeRef *ElementTypes,
+                                    unsigned ElementCount, LLVMBool Packed);
+
+/**
+ * Create a new structure type in the global context.
+ *
+ * @see llvm::StructType::create()
+ */
+LLVMTypeRef LLVMStructType(LLVMTypeRef *ElementTypes, unsigned ElementCount,
+                           LLVMBool Packed);
+
+/**
+ * Create an empty structure in a context having a specified name.
+ *
+ * @see llvm::StructType::create()
+ */
+LLVMTypeRef LLVMStructCreateNamed(LLVMContextRef C, const char *Name);
+
+/**
+ * Obtain the name of a structure.
+ *
+ * @see llvm::StructType::getName()
+ */
+const char *LLVMGetStructName(LLVMTypeRef Ty);
+
+/**
+ * Set the contents of a structure type.
+ *
+ * @see llvm::StructType::setBody()
+ */
+void LLVMStructSetBody(LLVMTypeRef StructTy, LLVMTypeRef *ElementTypes,
+                       unsigned ElementCount, LLVMBool Packed);
+
+/**
+ * Get the number of elements defined inside the structure.
+ *
+ * @see llvm::StructType::getNumElements()
+ */
+unsigned LLVMCountStructElementTypes(LLVMTypeRef StructTy);
+
+/**
+ * Get the elements within a structure.
+ *
+ * The function is passed the address of a pre-allocated array of
+ * LLVMTypeRef at least LLVMCountStructElementTypes() long. After
+ * invocation, this array will be populated with the structure's
+ * elements. The objects in the destination array will have a lifetime
+ * of the structure type itself, which is the lifetime of the context it
+ * is contained in.
+ */
+void LLVMGetStructElementTypes(LLVMTypeRef StructTy, LLVMTypeRef *Dest);
+
+/**
+ * Get the type of the element at a given index in the structure.
+ *
+ * @see llvm::StructType::getTypeAtIndex()
+ */
+LLVMTypeRef LLVMStructGetTypeAtIndex(LLVMTypeRef StructTy, unsigned i);
+
+/**
+ * Determine whether a structure is packed.
+ *
+ * @see llvm::StructType::isPacked()
+ */
+LLVMBool LLVMIsPackedStruct(LLVMTypeRef StructTy);
+
+/**
+ * Determine whether a structure is opaque.
+ *
+ * @see llvm::StructType::isOpaque()
+ */
+LLVMBool LLVMIsOpaqueStruct(LLVMTypeRef StructTy);
+
+/**
+ * Determine whether a structure is literal.
+ *
+ * @see llvm::StructType::isLiteral()
+ */
+LLVMBool LLVMIsLiteralStruct(LLVMTypeRef StructTy);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreTypeSequential Sequential Types
+ *
+ * Sequential types represents "arrays" of types. This is a super class
+ * for array, vector, and pointer types.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the type of elements within a sequential type.
+ *
+ * This works on array, vector, and pointer types.
+ *
+ * @see llvm::SequentialType::getElementType()
+ */
+LLVMTypeRef LLVMGetElementType(LLVMTypeRef Ty);
+
+/**
+ * Returns type's subtypes
+ *
+ * @see llvm::Type::subtypes()
+ */
+void LLVMGetSubtypes(LLVMTypeRef Tp, LLVMTypeRef *Arr);
+
+/**
+ *  Return the number of types in the derived type.
+ *
+ * @see llvm::Type::getNumContainedTypes()
+ */
+unsigned LLVMGetNumContainedTypes(LLVMTypeRef Tp);
+
+/**
+ * Create a fixed size array type that refers to a specific type.
+ *
+ * The created type will exist in the context that its element type
+ * exists in.
+ *
+ * @see llvm::ArrayType::get()
+ */
+LLVMTypeRef LLVMArrayType(LLVMTypeRef ElementType, unsigned ElementCount);
+
+/**
+ * Obtain the length of an array type.
+ *
+ * This only works on types that represent arrays.
+ *
+ * @see llvm::ArrayType::getNumElements()
+ */
+unsigned LLVMGetArrayLength(LLVMTypeRef ArrayTy);
+
+/**
+ * Create a pointer type that points to a defined type.
+ *
+ * The created type will exist in the context that its pointee type
+ * exists in.
+ *
+ * @see llvm::PointerType::get()
+ */
+LLVMTypeRef LLVMPointerType(LLVMTypeRef ElementType, unsigned AddressSpace);
+
+/**
+ * Obtain the address space of a pointer type.
+ *
+ * This only works on types that represent pointers.
+ *
+ * @see llvm::PointerType::getAddressSpace()
+ */
+unsigned LLVMGetPointerAddressSpace(LLVMTypeRef PointerTy);
+
+/**
+ * Create a vector type that contains a defined type and has a specific
+ * number of elements.
+ *
+ * The created type will exist in the context thats its element type
+ * exists in.
+ *
+ * @see llvm::VectorType::get()
+ */
+LLVMTypeRef LLVMVectorType(LLVMTypeRef ElementType, unsigned ElementCount);
+
+/**
+ * Obtain the number of elements in a vector type.
+ *
+ * This only works on types that represent vectors.
+ *
+ * @see llvm::VectorType::getNumElements()
+ */
+unsigned LLVMGetVectorSize(LLVMTypeRef VectorTy);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreTypeOther Other Types
+ *
+ * @{
+ */
+
+/**
+ * Create a void type in a context.
+ */
+LLVMTypeRef LLVMVoidTypeInContext(LLVMContextRef C);
+
+/**
+ * Create a label type in a context.
+ */
+LLVMTypeRef LLVMLabelTypeInContext(LLVMContextRef C);
+
+/**
+ * Create a X86 MMX type in a context.
+ */
+LLVMTypeRef LLVMX86MMXTypeInContext(LLVMContextRef C);
+
+/**
+ * Create a token type in a context.
+ */
+LLVMTypeRef LLVMTokenTypeInContext(LLVMContextRef C);
+
+/**
+ * Create a metadata type in a context.
+ */
+LLVMTypeRef LLVMMetadataTypeInContext(LLVMContextRef C);
+
+/**
+ * These are similar to the above functions except they operate on the
+ * global context.
+ */
+LLVMTypeRef LLVMVoidType(void);
+LLVMTypeRef LLVMLabelType(void);
+LLVMTypeRef LLVMX86MMXType(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValues Values
+ *
+ * The bulk of LLVM's object model consists of values, which comprise a very
+ * rich type hierarchy.
+ *
+ * LLVMValueRef essentially represents llvm::Value. There is a rich
+ * hierarchy of classes within this type. Depending on the instance
+ * obtained, not all APIs are available.
+ *
+ * Callers can determine the type of an LLVMValueRef by calling the
+ * LLVMIsA* family of functions (e.g. LLVMIsAArgument()). These
+ * functions are defined by a macro, so it isn't obvious which are
+ * available by looking at the Doxygen source code. Instead, look at the
+ * source definition of LLVM_FOR_EACH_VALUE_SUBCLASS and note the list
+ * of value names given. These value names also correspond to classes in
+ * the llvm::Value hierarchy.
+ *
+ * @{
+ */
+
+#define LLVM_FOR_EACH_VALUE_SUBCLASS(macro) \
+  macro(Argument)                           \
+  macro(BasicBlock)                         \
+  macro(InlineAsm)                          \
+  macro(User)                               \
+    macro(Constant)                         \
+      macro(BlockAddress)                   \
+      macro(ConstantAggregateZero)          \
+      macro(ConstantArray)                  \
+      macro(ConstantDataSequential)         \
+        macro(ConstantDataArray)            \
+        macro(ConstantDataVector)           \
+      macro(ConstantExpr)                   \
+      macro(ConstantFP)                     \
+      macro(ConstantInt)                    \
+      macro(ConstantPointerNull)            \
+      macro(ConstantStruct)                 \
+      macro(ConstantTokenNone)              \
+      macro(ConstantVector)                 \
+      macro(GlobalValue)                    \
+        macro(GlobalAlias)                  \
+        macro(GlobalIFunc)                  \
+        macro(GlobalObject)                 \
+          macro(Function)                   \
+          macro(GlobalVariable)             \
+      macro(UndefValue)                     \
+    macro(Instruction)                      \
+      macro(BinaryOperator)                 \
+      macro(CallInst)                       \
+        macro(IntrinsicInst)                \
+          macro(DbgInfoIntrinsic)           \
+            macro(DbgVariableIntrinsic)     \
+              macro(DbgDeclareInst)         \
+            macro(DbgLabelInst)             \
+          macro(MemIntrinsic)               \
+            macro(MemCpyInst)               \
+            macro(MemMoveInst)              \
+            macro(MemSetInst)               \
+      macro(CmpInst)                        \
+        macro(FCmpInst)                     \
+        macro(ICmpInst)                     \
+      macro(ExtractElementInst)             \
+      macro(GetElementPtrInst)              \
+      macro(InsertElementInst)              \
+      macro(InsertValueInst)                \
+      macro(LandingPadInst)                 \
+      macro(PHINode)                        \
+      macro(SelectInst)                     \
+      macro(ShuffleVectorInst)              \
+      macro(StoreInst)                      \
+      macro(BranchInst)                     \
+      macro(IndirectBrInst)                 \
+      macro(InvokeInst)                     \
+      macro(ReturnInst)                     \
+      macro(SwitchInst)                     \
+      macro(UnreachableInst)                \
+      macro(ResumeInst)                     \
+      macro(CleanupReturnInst)              \
+      macro(CatchReturnInst)                \
+      macro(FuncletPadInst)                 \
+        macro(CatchPadInst)                 \
+        macro(CleanupPadInst)               \
+      macro(UnaryInstruction)               \
+        macro(AllocaInst)                   \
+        macro(CastInst)                     \
+          macro(AddrSpaceCastInst)          \
+          macro(BitCastInst)                \
+          macro(FPExtInst)                  \
+          macro(FPToSIInst)                 \
+          macro(FPToUIInst)                 \
+          macro(FPTruncInst)                \
+          macro(IntToPtrInst)               \
+          macro(PtrToIntInst)               \
+          macro(SExtInst)                   \
+          macro(SIToFPInst)                 \
+          macro(TruncInst)                  \
+          macro(UIToFPInst)                 \
+          macro(ZExtInst)                   \
+        macro(ExtractValueInst)             \
+        macro(LoadInst)                     \
+        macro(VAArgInst)
+
+/**
+ * @defgroup LLVMCCoreValueGeneral General APIs
+ *
+ * Functions in this section work on all LLVMValueRef instances,
+ * regardless of their sub-type. They correspond to functions available
+ * on llvm::Value.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the type of a value.
+ *
+ * @see llvm::Value::getType()
+ */
+LLVMTypeRef LLVMTypeOf(LLVMValueRef Val);
+
+/**
+ * Obtain the enumerated type of a Value instance.
+ *
+ * @see llvm::Value::getValueID()
+ */
+LLVMValueKind LLVMGetValueKind(LLVMValueRef Val);
+
+/**
+ * Obtain the string name of a value.
+ *
+ * @see llvm::Value::getName()
+ */
+const char *LLVMGetValueName2(LLVMValueRef Val, size_t *Length);
+
+/**
+ * Set the string name of a value.
+ *
+ * @see llvm::Value::setName()
+ */
+void LLVMSetValueName2(LLVMValueRef Val, const char *Name, size_t NameLen);
+
+/**
+ * Dump a representation of a value to stderr.
+ *
+ * @see llvm::Value::dump()
+ */
+void LLVMDumpValue(LLVMValueRef Val);
+
+/**
+ * Return a string representation of the value. Use
+ * LLVMDisposeMessage to free the string.
+ *
+ * @see llvm::Value::print()
+ */
+char *LLVMPrintValueToString(LLVMValueRef Val);
+
+/**
+ * Replace all uses of a value with another one.
+ *
+ * @see llvm::Value::replaceAllUsesWith()
+ */
+void LLVMReplaceAllUsesWith(LLVMValueRef OldVal, LLVMValueRef NewVal);
+
+/**
+ * Determine whether the specified value instance is constant.
+ */
+LLVMBool LLVMIsConstant(LLVMValueRef Val);
+
+/**
+ * Determine whether a value instance is undefined.
+ */
+LLVMBool LLVMIsUndef(LLVMValueRef Val);
+
+/**
+ * Convert value instances between types.
+ *
+ * Internally, an LLVMValueRef is "pinned" to a specific type. This
+ * series of functions allows you to cast an instance to a specific
+ * type.
+ *
+ * If the cast is not valid for the specified type, NULL is returned.
+ *
+ * @see llvm::dyn_cast_or_null<>
+ */
+#define LLVM_DECLARE_VALUE_CAST(name) \
+  LLVMValueRef LLVMIsA##name(LLVMValueRef Val);
+LLVM_FOR_EACH_VALUE_SUBCLASS(LLVM_DECLARE_VALUE_CAST)
+
+LLVMValueRef LLVMIsAMDNode(LLVMValueRef Val);
+LLVMValueRef LLVMIsAMDString(LLVMValueRef Val);
+
+/** Deprecated: Use LLVMGetValueName2 instead. */
+const char *LLVMGetValueName(LLVMValueRef Val);
+/** Deprecated: Use LLVMSetValueName2 instead. */
+void LLVMSetValueName(LLVMValueRef Val, const char *Name);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueUses Usage
+ *
+ * This module defines functions that allow you to inspect the uses of a
+ * LLVMValueRef.
+ *
+ * It is possible to obtain an LLVMUseRef for any LLVMValueRef instance.
+ * Each LLVMUseRef (which corresponds to a llvm::Use instance) holds a
+ * llvm::User and llvm::Value.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the first use of a value.
+ *
+ * Uses are obtained in an iterator fashion. First, call this function
+ * to obtain a reference to the first use. Then, call LLVMGetNextUse()
+ * on that instance and all subsequently obtained instances until
+ * LLVMGetNextUse() returns NULL.
+ *
+ * @see llvm::Value::use_begin()
+ */
+LLVMUseRef LLVMGetFirstUse(LLVMValueRef Val);
+
+/**
+ * Obtain the next use of a value.
+ *
+ * This effectively advances the iterator. It returns NULL if you are on
+ * the final use and no more are available.
+ */
+LLVMUseRef LLVMGetNextUse(LLVMUseRef U);
+
+/**
+ * Obtain the user value for a user.
+ *
+ * The returned value corresponds to a llvm::User type.
+ *
+ * @see llvm::Use::getUser()
+ */
+LLVMValueRef LLVMGetUser(LLVMUseRef U);
+
+/**
+ * Obtain the value this use corresponds to.
+ *
+ * @see llvm::Use::get().
+ */
+LLVMValueRef LLVMGetUsedValue(LLVMUseRef U);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueUser User value
+ *
+ * Function in this group pertain to LLVMValueRef instances that descent
+ * from llvm::User. This includes constants, instructions, and
+ * operators.
+ *
+ * @{
+ */
+
+/**
+ * Obtain an operand at a specific index in a llvm::User value.
+ *
+ * @see llvm::User::getOperand()
+ */
+LLVMValueRef LLVMGetOperand(LLVMValueRef Val, unsigned Index);
+
+/**
+ * Obtain the use of an operand at a specific index in a llvm::User value.
+ *
+ * @see llvm::User::getOperandUse()
+ */
+LLVMUseRef LLVMGetOperandUse(LLVMValueRef Val, unsigned Index);
+
+/**
+ * Set an operand at a specific index in a llvm::User value.
+ *
+ * @see llvm::User::setOperand()
+ */
+void LLVMSetOperand(LLVMValueRef User, unsigned Index, LLVMValueRef Val);
+
+/**
+ * Obtain the number of operands in a llvm::User value.
+ *
+ * @see llvm::User::getNumOperands()
+ */
+int LLVMGetNumOperands(LLVMValueRef Val);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueConstant Constants
+ *
+ * This section contains APIs for interacting with LLVMValueRef that
+ * correspond to llvm::Constant instances.
+ *
+ * These functions will work for any LLVMValueRef in the llvm::Constant
+ * class hierarchy.
+ *
+ * @{
+ */
+
+/**
+ * Obtain a constant value referring to the null instance of a type.
+ *
+ * @see llvm::Constant::getNullValue()
+ */
+LLVMValueRef LLVMConstNull(LLVMTypeRef Ty); /* all zeroes */
+
+/**
+ * Obtain a constant value referring to the instance of a type
+ * consisting of all ones.
+ *
+ * This is only valid for integer types.
+ *
+ * @see llvm::Constant::getAllOnesValue()
+ */
+LLVMValueRef LLVMConstAllOnes(LLVMTypeRef Ty);
+
+/**
+ * Obtain a constant value referring to an undefined value of a type.
+ *
+ * @see llvm::UndefValue::get()
+ */
+LLVMValueRef LLVMGetUndef(LLVMTypeRef Ty);
+
+/**
+ * Determine whether a value instance is null.
+ *
+ * @see llvm::Constant::isNullValue()
+ */
+LLVMBool LLVMIsNull(LLVMValueRef Val);
+
+/**
+ * Obtain a constant that is a constant pointer pointing to NULL for a
+ * specified type.
+ */
+LLVMValueRef LLVMConstPointerNull(LLVMTypeRef Ty);
+
+/**
+ * @defgroup LLVMCCoreValueConstantScalar Scalar constants
+ *
+ * Functions in this group model LLVMValueRef instances that correspond
+ * to constants referring to scalar types.
+ *
+ * For integer types, the LLVMTypeRef parameter should correspond to a
+ * llvm::IntegerType instance and the returned LLVMValueRef will
+ * correspond to a llvm::ConstantInt.
+ *
+ * For floating point types, the LLVMTypeRef returned corresponds to a
+ * llvm::ConstantFP.
+ *
+ * @{
+ */
+
+/**
+ * Obtain a constant value for an integer type.
+ *
+ * The returned value corresponds to a llvm::ConstantInt.
+ *
+ * @see llvm::ConstantInt::get()
+ *
+ * @param IntTy Integer type to obtain value of.
+ * @param N The value the returned instance should refer to.
+ * @param SignExtend Whether to sign extend the produced value.
+ */
+LLVMValueRef LLVMConstInt(LLVMTypeRef IntTy, unsigned long long N,
+                          LLVMBool SignExtend);
+
+/**
+ * Obtain a constant value for an integer of arbitrary precision.
+ *
+ * @see llvm::ConstantInt::get()
+ */
+LLVMValueRef LLVMConstIntOfArbitraryPrecision(LLVMTypeRef IntTy,
+                                              unsigned NumWords,
+                                              const uint64_t Words[]);
+
+/**
+ * Obtain a constant value for an integer parsed from a string.
+ *
+ * A similar API, LLVMConstIntOfStringAndSize is also available. If the
+ * string's length is available, it is preferred to call that function
+ * instead.
+ *
+ * @see llvm::ConstantInt::get()
+ */
+LLVMValueRef LLVMConstIntOfString(LLVMTypeRef IntTy, const char *Text,
+                                  uint8_t Radix);
+
+/**
+ * Obtain a constant value for an integer parsed from a string with
+ * specified length.
+ *
+ * @see llvm::ConstantInt::get()
+ */
+LLVMValueRef LLVMConstIntOfStringAndSize(LLVMTypeRef IntTy, const char *Text,
+                                         unsigned SLen, uint8_t Radix);
+
+/**
+ * Obtain a constant value referring to a double floating point value.
+ */
+LLVMValueRef LLVMConstReal(LLVMTypeRef RealTy, double N);
+
+/**
+ * Obtain a constant for a floating point value parsed from a string.
+ *
+ * A similar API, LLVMConstRealOfStringAndSize is also available. It
+ * should be used if the input string's length is known.
+ */
+LLVMValueRef LLVMConstRealOfString(LLVMTypeRef RealTy, const char *Text);
+
+/**
+ * Obtain a constant for a floating point value parsed from a string.
+ */
+LLVMValueRef LLVMConstRealOfStringAndSize(LLVMTypeRef RealTy, const char *Text,
+                                          unsigned SLen);
+
+/**
+ * Obtain the zero extended value for an integer constant value.
+ *
+ * @see llvm::ConstantInt::getZExtValue()
+ */
+unsigned long long LLVMConstIntGetZExtValue(LLVMValueRef ConstantVal);
+
+/**
+ * Obtain the sign extended value for an integer constant value.
+ *
+ * @see llvm::ConstantInt::getSExtValue()
+ */
+long long LLVMConstIntGetSExtValue(LLVMValueRef ConstantVal);
+
+/**
+ * Obtain the double value for an floating point constant value.
+ * losesInfo indicates if some precision was lost in the conversion.
+ *
+ * @see llvm::ConstantFP::getDoubleValue
+ */
+double LLVMConstRealGetDouble(LLVMValueRef ConstantVal, LLVMBool *losesInfo);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueConstantComposite Composite Constants
+ *
+ * Functions in this group operate on composite constants.
+ *
+ * @{
+ */
+
+/**
+ * Create a ConstantDataSequential and initialize it with a string.
+ *
+ * @see llvm::ConstantDataArray::getString()
+ */
+LLVMValueRef LLVMConstStringInContext(LLVMContextRef C, const char *Str,
+                                      unsigned Length, LLVMBool DontNullTerminate);
+
+/**
+ * Create a ConstantDataSequential with string content in the global context.
+ *
+ * This is the same as LLVMConstStringInContext except it operates on the
+ * global context.
+ *
+ * @see LLVMConstStringInContext()
+ * @see llvm::ConstantDataArray::getString()
+ */
+LLVMValueRef LLVMConstString(const char *Str, unsigned Length,
+                             LLVMBool DontNullTerminate);
+
+/**
+ * Returns true if the specified constant is an array of i8.
+ *
+ * @see ConstantDataSequential::getAsString()
+ */
+LLVMBool LLVMIsConstantString(LLVMValueRef c);
+
+/**
+ * Get the given constant data sequential as a string.
+ *
+ * @see ConstantDataSequential::getAsString()
+ */
+const char *LLVMGetAsString(LLVMValueRef c, size_t *Length);
+
+/**
+ * Create an anonymous ConstantStruct with the specified values.
+ *
+ * @see llvm::ConstantStruct::getAnon()
+ */
+LLVMValueRef LLVMConstStructInContext(LLVMContextRef C,
+                                      LLVMValueRef *ConstantVals,
+                                      unsigned Count, LLVMBool Packed);
+
+/**
+ * Create a ConstantStruct in the global Context.
+ *
+ * This is the same as LLVMConstStructInContext except it operates on the
+ * global Context.
+ *
+ * @see LLVMConstStructInContext()
+ */
+LLVMValueRef LLVMConstStruct(LLVMValueRef *ConstantVals, unsigned Count,
+                             LLVMBool Packed);
+
+/**
+ * Create a ConstantArray from values.
+ *
+ * @see llvm::ConstantArray::get()
+ */
+LLVMValueRef LLVMConstArray(LLVMTypeRef ElementTy,
+                            LLVMValueRef *ConstantVals, unsigned Length);
+
+/**
+ * Create a non-anonymous ConstantStruct from values.
+ *
+ * @see llvm::ConstantStruct::get()
+ */
+LLVMValueRef LLVMConstNamedStruct(LLVMTypeRef StructTy,
+                                  LLVMValueRef *ConstantVals,
+                                  unsigned Count);
+
+/**
+ * Get an element at specified index as a constant.
+ *
+ * @see ConstantDataSequential::getElementAsConstant()
+ */
+LLVMValueRef LLVMGetElementAsConstant(LLVMValueRef C, unsigned idx);
+
+/**
+ * Create a ConstantVector from values.
+ *
+ * @see llvm::ConstantVector::get()
+ */
+LLVMValueRef LLVMConstVector(LLVMValueRef *ScalarConstantVals, unsigned Size);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueConstantExpressions Constant Expressions
+ *
+ * Functions in this group correspond to APIs on llvm::ConstantExpr.
+ *
+ * @see llvm::ConstantExpr.
+ *
+ * @{
+ */
+LLVMOpcode LLVMGetConstOpcode(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMAlignOf(LLVMTypeRef Ty);
+LLVMValueRef LLVMSizeOf(LLVMTypeRef Ty);
+LLVMValueRef LLVMConstNeg(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMConstNSWNeg(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMConstNUWNeg(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMConstFNeg(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMConstNot(LLVMValueRef ConstantVal);
+LLVMValueRef LLVMConstAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNSWAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNUWAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFAdd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNSWSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNUWSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFSub(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNSWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstNUWMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFMul(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstUDiv(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstExactUDiv(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstSDiv(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstExactSDiv(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFDiv(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstURem(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstSRem(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFRem(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstAnd(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstOr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstXor(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstICmp(LLVMIntPredicate Predicate,
+                           LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstFCmp(LLVMRealPredicate Predicate,
+                           LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstShl(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstLShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstAShr(LLVMValueRef LHSConstant, LLVMValueRef RHSConstant);
+LLVMValueRef LLVMConstGEP(LLVMValueRef ConstantVal,
+                          LLVMValueRef *ConstantIndices, unsigned NumIndices);
+LLVMValueRef LLVMConstGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
+                           LLVMValueRef *ConstantIndices, unsigned NumIndices);
+LLVMValueRef LLVMConstInBoundsGEP(LLVMValueRef ConstantVal,
+                                  LLVMValueRef *ConstantIndices,
+                                  unsigned NumIndices);
+LLVMValueRef LLVMConstInBoundsGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal,
+                                   LLVMValueRef *ConstantIndices,
+                                   unsigned NumIndices);
+LLVMValueRef LLVMConstTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstSExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstZExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstFPTrunc(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstFPExt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstUIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstSIToFP(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstFPToUI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstFPToSI(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstPtrToInt(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstIntToPtr(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstBitCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstAddrSpaceCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstZExtOrBitCast(LLVMValueRef ConstantVal,
+                                    LLVMTypeRef ToType);
+LLVMValueRef LLVMConstSExtOrBitCast(LLVMValueRef ConstantVal,
+                                    LLVMTypeRef ToType);
+LLVMValueRef LLVMConstTruncOrBitCast(LLVMValueRef ConstantVal,
+                                     LLVMTypeRef ToType);
+LLVMValueRef LLVMConstPointerCast(LLVMValueRef ConstantVal,
+                                  LLVMTypeRef ToType);
+LLVMValueRef LLVMConstIntCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType,
+                              LLVMBool isSigned);
+LLVMValueRef LLVMConstFPCast(LLVMValueRef ConstantVal, LLVMTypeRef ToType);
+LLVMValueRef LLVMConstSelect(LLVMValueRef ConstantCondition,
+                             LLVMValueRef ConstantIfTrue,
+                             LLVMValueRef ConstantIfFalse);
+LLVMValueRef LLVMConstExtractElement(LLVMValueRef VectorConstant,
+                                     LLVMValueRef IndexConstant);
+LLVMValueRef LLVMConstInsertElement(LLVMValueRef VectorConstant,
+                                    LLVMValueRef ElementValueConstant,
+                                    LLVMValueRef IndexConstant);
+LLVMValueRef LLVMConstShuffleVector(LLVMValueRef VectorAConstant,
+                                    LLVMValueRef VectorBConstant,
+                                    LLVMValueRef MaskConstant);
+LLVMValueRef LLVMConstExtractValue(LLVMValueRef AggConstant, unsigned *IdxList,
+                                   unsigned NumIdx);
+LLVMValueRef LLVMConstInsertValue(LLVMValueRef AggConstant,
+                                  LLVMValueRef ElementValueConstant,
+                                  unsigned *IdxList, unsigned NumIdx);
+LLVMValueRef LLVMBlockAddress(LLVMValueRef F, LLVMBasicBlockRef BB);
+
+/** Deprecated: Use LLVMGetInlineAsm instead. */
+LLVMValueRef LLVMConstInlineAsm(LLVMTypeRef Ty,
+                                const char *AsmString, const char *Constraints,
+                                LLVMBool HasSideEffects, LLVMBool IsAlignStack);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueConstantGlobals Global Values
+ *
+ * This group contains functions that operate on global values. Functions in
+ * this group relate to functions in the llvm::GlobalValue class tree.
+ *
+ * @see llvm::GlobalValue
+ *
+ * @{
+ */
+
+LLVMModuleRef LLVMGetGlobalParent(LLVMValueRef Global);
+LLVMBool LLVMIsDeclaration(LLVMValueRef Global);
+LLVMLinkage LLVMGetLinkage(LLVMValueRef Global);
+void LLVMSetLinkage(LLVMValueRef Global, LLVMLinkage Linkage);
+const char *LLVMGetSection(LLVMValueRef Global);
+void LLVMSetSection(LLVMValueRef Global, const char *Section);
+LLVMVisibility LLVMGetVisibility(LLVMValueRef Global);
+void LLVMSetVisibility(LLVMValueRef Global, LLVMVisibility Viz);
+LLVMDLLStorageClass LLVMGetDLLStorageClass(LLVMValueRef Global);
+void LLVMSetDLLStorageClass(LLVMValueRef Global, LLVMDLLStorageClass Class);
+LLVMUnnamedAddr LLVMGetUnnamedAddress(LLVMValueRef Global);
+void LLVMSetUnnamedAddress(LLVMValueRef Global, LLVMUnnamedAddr UnnamedAddr);
+
+/**
+ * Returns the "value type" of a global value.  This differs from the formal
+ * type of a global value which is always a pointer type.
+ *
+ * @see llvm::GlobalValue::getValueType()
+ */
+LLVMTypeRef LLVMGlobalGetValueType(LLVMValueRef Global);
+
+/** Deprecated: Use LLVMGetUnnamedAddress instead. */
+LLVMBool LLVMHasUnnamedAddr(LLVMValueRef Global);
+/** Deprecated: Use LLVMSetUnnamedAddress instead. */
+void LLVMSetUnnamedAddr(LLVMValueRef Global, LLVMBool HasUnnamedAddr);
+
+/**
+ * @defgroup LLVMCCoreValueWithAlignment Values with alignment
+ *
+ * Functions in this group only apply to values with alignment, i.e.
+ * global variables, load and store instructions.
+ */
+
+/**
+ * Obtain the preferred alignment of the value.
+ * @see llvm::AllocaInst::getAlignment()
+ * @see llvm::LoadInst::getAlignment()
+ * @see llvm::StoreInst::getAlignment()
+ * @see llvm::GlobalValue::getAlignment()
+ */
+unsigned LLVMGetAlignment(LLVMValueRef V);
+
+/**
+ * Set the preferred alignment of the value.
+ * @see llvm::AllocaInst::setAlignment()
+ * @see llvm::LoadInst::setAlignment()
+ * @see llvm::StoreInst::setAlignment()
+ * @see llvm::GlobalValue::setAlignment()
+ */
+void LLVMSetAlignment(LLVMValueRef V, unsigned Bytes);
+
+/**
+ * Sets a metadata attachment, erasing the existing metadata attachment if
+ * it already exists for the given kind.
+ *
+ * @see llvm::GlobalObject::setMetadata()
+ */
+void LLVMGlobalSetMetadata(LLVMValueRef Global, unsigned Kind,
+                           LLVMMetadataRef MD);
+
+/**
+ * Erases a metadata attachment of the given kind if it exists.
+ *
+ * @see llvm::GlobalObject::eraseMetadata()
+ */
+void LLVMGlobalEraseMetadata(LLVMValueRef Global, unsigned Kind);
+
+/**
+ * Removes all metadata attachments from this value.
+ *
+ * @see llvm::GlobalObject::clearMetadata()
+ */
+void LLVMGlobalClearMetadata(LLVMValueRef Global);
+
+/**
+ * Retrieves an array of metadata entries representing the metadata attached to
+ * this value. The caller is responsible for freeing this array by calling
+ * \c LLVMDisposeValueMetadataEntries.
+ *
+ * @see llvm::GlobalObject::getAllMetadata()
+ */
+LLVMValueMetadataEntry *LLVMGlobalCopyAllMetadata(LLVMValueRef Value,
+                                                  size_t *NumEntries);
+
+/**
+ * Destroys value metadata entries.
+ */
+void LLVMDisposeValueMetadataEntries(LLVMValueMetadataEntry *Entries);
+
+/**
+ * Returns the kind of a value metadata entry at a specific index.
+ */
+unsigned LLVMValueMetadataEntriesGetKind(LLVMValueMetadataEntry *Entries,
+                                         unsigned Index);
+
+/**
+ * Returns the underlying metadata node of a value metadata entry at a
+ * specific index.
+ */
+LLVMMetadataRef
+LLVMValueMetadataEntriesGetMetadata(LLVMValueMetadataEntry *Entries,
+                                    unsigned Index);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCoreValueConstantGlobalVariable Global Variables
+ *
+ * This group contains functions that operate on global variable values.
+ *
+ * @see llvm::GlobalVariable
+ *
+ * @{
+ */
+LLVMValueRef LLVMAddGlobal(LLVMModuleRef M, LLVMTypeRef Ty, const char *Name);
+LLVMValueRef LLVMAddGlobalInAddressSpace(LLVMModuleRef M, LLVMTypeRef Ty,
+                                         const char *Name,
+                                         unsigned AddressSpace);
+LLVMValueRef LLVMGetNamedGlobal(LLVMModuleRef M, const char *Name);
+LLVMValueRef LLVMGetFirstGlobal(LLVMModuleRef M);
+LLVMValueRef LLVMGetLastGlobal(LLVMModuleRef M);
+LLVMValueRef LLVMGetNextGlobal(LLVMValueRef GlobalVar);
+LLVMValueRef LLVMGetPreviousGlobal(LLVMValueRef GlobalVar);
+void LLVMDeleteGlobal(LLVMValueRef GlobalVar);
+LLVMValueRef LLVMGetInitializer(LLVMValueRef GlobalVar);
+void LLVMSetInitializer(LLVMValueRef GlobalVar, LLVMValueRef ConstantVal);
+LLVMBool LLVMIsThreadLocal(LLVMValueRef GlobalVar);
+void LLVMSetThreadLocal(LLVMValueRef GlobalVar, LLVMBool IsThreadLocal);
+LLVMBool LLVMIsGlobalConstant(LLVMValueRef GlobalVar);
+void LLVMSetGlobalConstant(LLVMValueRef GlobalVar, LLVMBool IsConstant);
+LLVMThreadLocalMode LLVMGetThreadLocalMode(LLVMValueRef GlobalVar);
+void LLVMSetThreadLocalMode(LLVMValueRef GlobalVar, LLVMThreadLocalMode Mode);
+LLVMBool LLVMIsExternallyInitialized(LLVMValueRef GlobalVar);
+void LLVMSetExternallyInitialized(LLVMValueRef GlobalVar, LLVMBool IsExtInit);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCoreValueConstantGlobalAlias Global Aliases
+ *
+ * This group contains function that operate on global alias values.
+ *
+ * @see llvm::GlobalAlias
+ *
+ * @{
+ */
+LLVMValueRef LLVMAddAlias(LLVMModuleRef M, LLVMTypeRef Ty, LLVMValueRef Aliasee,
+                          const char *Name);
+
+/**
+ * Obtain a GlobalAlias value from a Module by its name.
+ *
+ * The returned value corresponds to a llvm::GlobalAlias value.
+ *
+ * @see llvm::Module::getNamedAlias()
+ */
+LLVMValueRef LLVMGetNamedGlobalAlias(LLVMModuleRef M,
+                                     const char *Name, size_t NameLen);
+
+/**
+ * Obtain an iterator to the first GlobalAlias in a Module.
+ *
+ * @see llvm::Module::alias_begin()
+ */
+LLVMValueRef LLVMGetFirstGlobalAlias(LLVMModuleRef M);
+
+/**
+ * Obtain an iterator to the last GlobalAlias in a Module.
+ *
+ * @see llvm::Module::alias_end()
+ */
+LLVMValueRef LLVMGetLastGlobalAlias(LLVMModuleRef M);
+
+/**
+ * Advance a GlobalAlias iterator to the next GlobalAlias.
+ *
+ * Returns NULL if the iterator was already at the end and there are no more
+ * global aliases.
+ */
+LLVMValueRef LLVMGetNextGlobalAlias(LLVMValueRef GA);
+
+/**
+ * Decrement a GlobalAlias iterator to the previous GlobalAlias.
+ *
+ * Returns NULL if the iterator was already at the beginning and there are
+ * no previous global aliases.
+ */
+LLVMValueRef LLVMGetPreviousGlobalAlias(LLVMValueRef GA);
+
+/**
+ * Retrieve the target value of an alias.
+ */
+LLVMValueRef LLVMAliasGetAliasee(LLVMValueRef Alias);
+
+/**
+ * Set the target value of an alias.
+ */
+void LLVMAliasSetAliasee(LLVMValueRef Alias, LLVMValueRef Aliasee);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueFunction Function values
+ *
+ * Functions in this group operate on LLVMValueRef instances that
+ * correspond to llvm::Function instances.
+ *
+ * @see llvm::Function
+ *
+ * @{
+ */
+
+/**
+ * Remove a function from its containing module and deletes it.
+ *
+ * @see llvm::Function::eraseFromParent()
+ */
+void LLVMDeleteFunction(LLVMValueRef Fn);
+
+/**
+ * Check whether the given function has a personality function.
+ *
+ * @see llvm::Function::hasPersonalityFn()
+ */
+LLVMBool LLVMHasPersonalityFn(LLVMValueRef Fn);
+
+/**
+ * Obtain the personality function attached to the function.
+ *
+ * @see llvm::Function::getPersonalityFn()
+ */
+LLVMValueRef LLVMGetPersonalityFn(LLVMValueRef Fn);
+
+/**
+ * Set the personality function attached to the function.
+ *
+ * @see llvm::Function::setPersonalityFn()
+ */
+void LLVMSetPersonalityFn(LLVMValueRef Fn, LLVMValueRef PersonalityFn);
+
+/**
+ * Obtain the intrinsic ID number which matches the given function name.
+ *
+ * @see llvm::Function::lookupIntrinsicID()
+ */
+unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen);
+
+/**
+ * Obtain the ID number from a function instance.
+ *
+ * @see llvm::Function::getIntrinsicID()
+ */
+unsigned LLVMGetIntrinsicID(LLVMValueRef Fn);
+
+/**
+ * Create or insert the declaration of an intrinsic.  For overloaded intrinsics,
+ * parameter types must be provided to uniquely identify an overload.
+ *
+ * @see llvm::Intrinsic::getDeclaration()
+ */
+LLVMValueRef LLVMGetIntrinsicDeclaration(LLVMModuleRef Mod,
+                                         unsigned ID,
+                                         LLVMTypeRef *ParamTypes,
+                                         size_t ParamCount);
+
+/**
+ * Retrieves the type of an intrinsic.  For overloaded intrinsics, parameter
+ * types must be provided to uniquely identify an overload.
+ *
+ * @see llvm::Intrinsic::getType()
+ */
+LLVMTypeRef LLVMIntrinsicGetType(LLVMContextRef Ctx, unsigned ID,
+                                 LLVMTypeRef *ParamTypes, size_t ParamCount);
+
+/**
+ * Retrieves the name of an intrinsic.
+ *
+ * @see llvm::Intrinsic::getName()
+ */
+const char *LLVMIntrinsicGetName(unsigned ID, size_t *NameLength);
+
+/**
+ * Copies the name of an overloaded intrinsic identified by a given list of
+ * parameter types.
+ *
+ * Unlike LLVMIntrinsicGetName, the caller is responsible for freeing the
+ * returned string.
+ *
+ * @see llvm::Intrinsic::getName()
+ */
+const char *LLVMIntrinsicCopyOverloadedName(unsigned ID,
+                                            LLVMTypeRef *ParamTypes,
+                                            size_t ParamCount,
+                                            size_t *NameLength);
+
+/**
+ * Obtain if the intrinsic identified by the given ID is overloaded.
+ *
+ * @see llvm::Intrinsic::isOverloaded()
+ */
+LLVMBool LLVMIntrinsicIsOverloaded(unsigned ID);
+
+/**
+ * Obtain the calling function of a function.
+ *
+ * The returned value corresponds to the LLVMCallConv enumeration.
+ *
+ * @see llvm::Function::getCallingConv()
+ */
+unsigned LLVMGetFunctionCallConv(LLVMValueRef Fn);
+
+/**
+ * Set the calling convention of a function.
+ *
+ * @see llvm::Function::setCallingConv()
+ *
+ * @param Fn Function to operate on
+ * @param CC LLVMCallConv to set calling convention to
+ */
+void LLVMSetFunctionCallConv(LLVMValueRef Fn, unsigned CC);
+
+/**
+ * Obtain the name of the garbage collector to use during code
+ * generation.
+ *
+ * @see llvm::Function::getGC()
+ */
+const char *LLVMGetGC(LLVMValueRef Fn);
+
+/**
+ * Define the garbage collector to use during code generation.
+ *
+ * @see llvm::Function::setGC()
+ */
+void LLVMSetGC(LLVMValueRef Fn, const char *Name);
+
+/**
+ * Add an attribute to a function.
+ *
+ * @see llvm::Function::addAttribute()
+ */
+void LLVMAddAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
+                             LLVMAttributeRef A);
+unsigned LLVMGetAttributeCountAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx);
+void LLVMGetAttributesAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
+                              LLVMAttributeRef *Attrs);
+LLVMAttributeRef LLVMGetEnumAttributeAtIndex(LLVMValueRef F,
+                                             LLVMAttributeIndex Idx,
+                                             unsigned KindID);
+LLVMAttributeRef LLVMGetStringAttributeAtIndex(LLVMValueRef F,
+                                               LLVMAttributeIndex Idx,
+                                               const char *K, unsigned KLen);
+void LLVMRemoveEnumAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
+                                    unsigned KindID);
+void LLVMRemoveStringAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
+                                      const char *K, unsigned KLen);
+
+/**
+ * Add a target-dependent attribute to a function
+ * @see llvm::AttrBuilder::addAttribute()
+ */
+void LLVMAddTargetDependentFunctionAttr(LLVMValueRef Fn, const char *A,
+                                        const char *V);
+
+/**
+ * @defgroup LLVMCCoreValueFunctionParameters Function Parameters
+ *
+ * Functions in this group relate to arguments/parameters on functions.
+ *
+ * Functions in this group expect LLVMValueRef instances that correspond
+ * to llvm::Function instances.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the number of parameters in a function.
+ *
+ * @see llvm::Function::arg_size()
+ */
+unsigned LLVMCountParams(LLVMValueRef Fn);
+
+/**
+ * Obtain the parameters in a function.
+ *
+ * The takes a pointer to a pre-allocated array of LLVMValueRef that is
+ * at least LLVMCountParams() long. This array will be filled with
+ * LLVMValueRef instances which correspond to the parameters the
+ * function receives. Each LLVMValueRef corresponds to a llvm::Argument
+ * instance.
+ *
+ * @see llvm::Function::arg_begin()
+ */
+void LLVMGetParams(LLVMValueRef Fn, LLVMValueRef *Params);
+
+/**
+ * Obtain the parameter at the specified index.
+ *
+ * Parameters are indexed from 0.
+ *
+ * @see llvm::Function::arg_begin()
+ */
+LLVMValueRef LLVMGetParam(LLVMValueRef Fn, unsigned Index);
+
+/**
+ * Obtain the function to which this argument belongs.
+ *
+ * Unlike other functions in this group, this one takes an LLVMValueRef
+ * that corresponds to a llvm::Attribute.
+ *
+ * The returned LLVMValueRef is the llvm::Function to which this
+ * argument belongs.
+ */
+LLVMValueRef LLVMGetParamParent(LLVMValueRef Inst);
+
+/**
+ * Obtain the first parameter to a function.
+ *
+ * @see llvm::Function::arg_begin()
+ */
+LLVMValueRef LLVMGetFirstParam(LLVMValueRef Fn);
+
+/**
+ * Obtain the last parameter to a function.
+ *
+ * @see llvm::Function::arg_end()
+ */
+LLVMValueRef LLVMGetLastParam(LLVMValueRef Fn);
+
+/**
+ * Obtain the next parameter to a function.
+ *
+ * This takes an LLVMValueRef obtained from LLVMGetFirstParam() (which is
+ * actually a wrapped iterator) and obtains the next parameter from the
+ * underlying iterator.
+ */
+LLVMValueRef LLVMGetNextParam(LLVMValueRef Arg);
+
+/**
+ * Obtain the previous parameter to a function.
+ *
+ * This is the opposite of LLVMGetNextParam().
+ */
+LLVMValueRef LLVMGetPreviousParam(LLVMValueRef Arg);
+
+/**
+ * Set the alignment for a function parameter.
+ *
+ * @see llvm::Argument::addAttr()
+ * @see llvm::AttrBuilder::addAlignmentAttr()
+ */
+void LLVMSetParamAlignment(LLVMValueRef Arg, unsigned Align);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueGlobalIFunc IFuncs
+ *
+ * Functions in this group relate to indirect functions.
+ *
+ * Functions in this group expect LLVMValueRef instances that correspond
+ * to llvm::GlobalIFunc instances.
+ *
+ * @{
+ */
+
+/**
+ * Add a global indirect function to a module under a specified name.
+ *
+ * @see llvm::GlobalIFunc::create()
+ */
+LLVMValueRef LLVMAddGlobalIFunc(LLVMModuleRef M,
+                                const char *Name, size_t NameLen,
+                                LLVMTypeRef Ty, unsigned AddrSpace,
+                                LLVMValueRef Resolver);
+
+/**
+ * Obtain a GlobalIFunc value from a Module by its name.
+ *
+ * The returned value corresponds to a llvm::GlobalIFunc value.
+ *
+ * @see llvm::Module::getNamedIFunc()
+ */
+LLVMValueRef LLVMGetNamedGlobalIFunc(LLVMModuleRef M,
+                                     const char *Name, size_t NameLen);
+
+/**
+ * Obtain an iterator to the first GlobalIFunc in a Module.
+ *
+ * @see llvm::Module::ifunc_begin()
+ */
+LLVMValueRef LLVMGetFirstGlobalIFunc(LLVMModuleRef M);
+
+/**
+ * Obtain an iterator to the last GlobalIFunc in a Module.
+ *
+ * @see llvm::Module::ifunc_end()
+ */
+LLVMValueRef LLVMGetLastGlobalIFunc(LLVMModuleRef M);
+
+/**
+ * Advance a GlobalIFunc iterator to the next GlobalIFunc.
+ *
+ * Returns NULL if the iterator was already at the end and there are no more
+ * global aliases.
+ */
+LLVMValueRef LLVMGetNextGlobalIFunc(LLVMValueRef IFunc);
+
+/**
+ * Decrement a GlobalIFunc iterator to the previous GlobalIFunc.
+ *
+ * Returns NULL if the iterator was already at the beginning and there are
+ * no previous global aliases.
+ */
+LLVMValueRef LLVMGetPreviousGlobalIFunc(LLVMValueRef IFunc);
+
+/**
+ * Retrieves the resolver function associated with this indirect function, or
+ * NULL if it doesn't not exist.
+ *
+ * @see llvm::GlobalIFunc::getResolver()
+ */
+LLVMValueRef LLVMGetGlobalIFuncResolver(LLVMValueRef IFunc);
+
+/**
+ * Sets the resolver function associated with this indirect function.
+ *
+ * @see llvm::GlobalIFunc::setResolver()
+ */
+void LLVMSetGlobalIFuncResolver(LLVMValueRef IFunc, LLVMValueRef Resolver);
+
+/**
+ * Remove a global indirect function from its parent module and delete it.
+ *
+ * @see llvm::GlobalIFunc::eraseFromParent()
+ */
+void LLVMEraseGlobalIFunc(LLVMValueRef IFunc);
+
+/**
+ * Remove a global indirect function from its parent module.
+ *
+ * This unlinks the global indirect function from its containing module but
+ * keeps it alive.
+ *
+ * @see llvm::GlobalIFunc::removeFromParent()
+ */
+void LLVMRemoveGlobalIFunc(LLVMValueRef IFunc);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueMetadata Metadata
+ *
+ * @{
+ */
+
+/**
+ * Create an MDString value from a given string value.
+ *
+ * The MDString value does not take ownership of the given string, it remains
+ * the responsibility of the caller to free it.
+ *
+ * @see llvm::MDString::get()
+ */
+LLVMMetadataRef LLVMMDStringInContext2(LLVMContextRef C, const char *Str,
+                                       size_t SLen);
+
+/**
+ * Create an MDNode value with the given array of operands.
+ *
+ * @see llvm::MDNode::get()
+ */
+LLVMMetadataRef LLVMMDNodeInContext2(LLVMContextRef C, LLVMMetadataRef *MDs,
+                                     size_t Count);
+
+/**
+ * Obtain a Metadata as a Value.
+ */
+LLVMValueRef LLVMMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD);
+
+/**
+ * Obtain a Value as a Metadata.
+ */
+LLVMMetadataRef LLVMValueAsMetadata(LLVMValueRef Val);
+
+/**
+ * Obtain the underlying string from a MDString value.
+ *
+ * @param V Instance to obtain string from.
+ * @param Length Memory address which will hold length of returned string.
+ * @return String data in MDString.
+ */
+const char *LLVMGetMDString(LLVMValueRef V, unsigned *Length);
+
+/**
+ * Obtain the number of operands from an MDNode value.
+ *
+ * @param V MDNode to get number of operands from.
+ * @return Number of operands of the MDNode.
+ */
+unsigned LLVMGetMDNodeNumOperands(LLVMValueRef V);
+
+/**
+ * Obtain the given MDNode's operands.
+ *
+ * The passed LLVMValueRef pointer should point to enough memory to hold all of
+ * the operands of the given MDNode (see LLVMGetMDNodeNumOperands) as
+ * LLVMValueRefs. This memory will be populated with the LLVMValueRefs of the
+ * MDNode's operands.
+ *
+ * @param V MDNode to get the operands from.
+ * @param Dest Destination array for operands.
+ */
+void LLVMGetMDNodeOperands(LLVMValueRef V, LLVMValueRef *Dest);
+
+/** Deprecated: Use LLVMMDStringInContext2 instead. */
+LLVMValueRef LLVMMDStringInContext(LLVMContextRef C, const char *Str,
+                                   unsigned SLen);
+/** Deprecated: Use LLVMMDStringInContext2 instead. */
+LLVMValueRef LLVMMDString(const char *Str, unsigned SLen);
+/** Deprecated: Use LLVMMDNodeInContext2 instead. */
+LLVMValueRef LLVMMDNodeInContext(LLVMContextRef C, LLVMValueRef *Vals,
+                                 unsigned Count);
+/** Deprecated: Use LLVMMDNodeInContext2 instead. */
+LLVMValueRef LLVMMDNode(LLVMValueRef *Vals, unsigned Count);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueBasicBlock Basic Block
+ *
+ * A basic block represents a single entry single exit section of code.
+ * Basic blocks contain a list of instructions which form the body of
+ * the block.
+ *
+ * Basic blocks belong to functions. They have the type of label.
+ *
+ * Basic blocks are themselves values. However, the C API models them as
+ * LLVMBasicBlockRef.
+ *
+ * @see llvm::BasicBlock
+ *
+ * @{
+ */
+
+/**
+ * Convert a basic block instance to a value type.
+ */
+LLVMValueRef LLVMBasicBlockAsValue(LLVMBasicBlockRef BB);
+
+/**
+ * Determine whether an LLVMValueRef is itself a basic block.
+ */
+LLVMBool LLVMValueIsBasicBlock(LLVMValueRef Val);
+
+/**
+ * Convert an LLVMValueRef to an LLVMBasicBlockRef instance.
+ */
+LLVMBasicBlockRef LLVMValueAsBasicBlock(LLVMValueRef Val);
+
+/**
+ * Obtain the string name of a basic block.
+ */
+const char *LLVMGetBasicBlockName(LLVMBasicBlockRef BB);
+
+/**
+ * Obtain the function to which a basic block belongs.
+ *
+ * @see llvm::BasicBlock::getParent()
+ */
+LLVMValueRef LLVMGetBasicBlockParent(LLVMBasicBlockRef BB);
+
+/**
+ * Obtain the terminator instruction for a basic block.
+ *
+ * If the basic block does not have a terminator (it is not well-formed
+ * if it doesn't), then NULL is returned.
+ *
+ * The returned LLVMValueRef corresponds to an llvm::Instruction.
+ *
+ * @see llvm::BasicBlock::getTerminator()
+ */
+LLVMValueRef LLVMGetBasicBlockTerminator(LLVMBasicBlockRef BB);
+
+/**
+ * Obtain the number of basic blocks in a function.
+ *
+ * @param Fn Function value to operate on.
+ */
+unsigned LLVMCountBasicBlocks(LLVMValueRef Fn);
+
+/**
+ * Obtain all of the basic blocks in a function.
+ *
+ * This operates on a function value. The BasicBlocks parameter is a
+ * pointer to a pre-allocated array of LLVMBasicBlockRef of at least
+ * LLVMCountBasicBlocks() in length. This array is populated with
+ * LLVMBasicBlockRef instances.
+ */
+void LLVMGetBasicBlocks(LLVMValueRef Fn, LLVMBasicBlockRef *BasicBlocks);
+
+/**
+ * Obtain the first basic block in a function.
+ *
+ * The returned basic block can be used as an iterator. You will likely
+ * eventually call into LLVMGetNextBasicBlock() with it.
+ *
+ * @see llvm::Function::begin()
+ */
+LLVMBasicBlockRef LLVMGetFirstBasicBlock(LLVMValueRef Fn);
+
+/**
+ * Obtain the last basic block in a function.
+ *
+ * @see llvm::Function::end()
+ */
+LLVMBasicBlockRef LLVMGetLastBasicBlock(LLVMValueRef Fn);
+
+/**
+ * Advance a basic block iterator.
+ */
+LLVMBasicBlockRef LLVMGetNextBasicBlock(LLVMBasicBlockRef BB);
+
+/**
+ * Go backwards in a basic block iterator.
+ */
+LLVMBasicBlockRef LLVMGetPreviousBasicBlock(LLVMBasicBlockRef BB);
+
+/**
+ * Obtain the basic block that corresponds to the entry point of a
+ * function.
+ *
+ * @see llvm::Function::getEntryBlock()
+ */
+LLVMBasicBlockRef LLVMGetEntryBasicBlock(LLVMValueRef Fn);
+
+/**
+ * Insert the given basic block after the insertion point of the given builder.
+ *
+ * The insertion point must be valid.
+ *
+ * @see llvm::Function::BasicBlockListType::insertAfter()
+ */
+void LLVMInsertExistingBasicBlockAfterInsertBlock(LLVMBuilderRef Builder,
+                                                  LLVMBasicBlockRef BB);
+
+/**
+ * Append the given basic block to the basic block list of the given function.
+ *
+ * @see llvm::Function::BasicBlockListType::push_back()
+ */
+void LLVMAppendExistingBasicBlock(LLVMValueRef Fn,
+                                  LLVMBasicBlockRef BB);
+
+/**
+ * Create a new basic block without inserting it into a function.
+ *
+ * @see llvm::BasicBlock::Create()
+ */
+LLVMBasicBlockRef LLVMCreateBasicBlockInContext(LLVMContextRef C,
+                                                const char *Name);
+
+/**
+ * Append a basic block to the end of a function.
+ *
+ * @see llvm::BasicBlock::Create()
+ */
+LLVMBasicBlockRef LLVMAppendBasicBlockInContext(LLVMContextRef C,
+                                                LLVMValueRef Fn,
+                                                const char *Name);
+
+/**
+ * Append a basic block to the end of a function using the global
+ * context.
+ *
+ * @see llvm::BasicBlock::Create()
+ */
+LLVMBasicBlockRef LLVMAppendBasicBlock(LLVMValueRef Fn, const char *Name);
+
+/**
+ * Insert a basic block in a function before another basic block.
+ *
+ * The function to add to is determined by the function of the
+ * passed basic block.
+ *
+ * @see llvm::BasicBlock::Create()
+ */
+LLVMBasicBlockRef LLVMInsertBasicBlockInContext(LLVMContextRef C,
+                                                LLVMBasicBlockRef BB,
+                                                const char *Name);
+
+/**
+ * Insert a basic block in a function using the global context.
+ *
+ * @see llvm::BasicBlock::Create()
+ */
+LLVMBasicBlockRef LLVMInsertBasicBlock(LLVMBasicBlockRef InsertBeforeBB,
+                                       const char *Name);
+
+/**
+ * Remove a basic block from a function and delete it.
+ *
+ * This deletes the basic block from its containing function and deletes
+ * the basic block itself.
+ *
+ * @see llvm::BasicBlock::eraseFromParent()
+ */
+void LLVMDeleteBasicBlock(LLVMBasicBlockRef BB);
+
+/**
+ * Remove a basic block from a function.
+ *
+ * This deletes the basic block from its containing function but keep
+ * the basic block alive.
+ *
+ * @see llvm::BasicBlock::removeFromParent()
+ */
+void LLVMRemoveBasicBlockFromParent(LLVMBasicBlockRef BB);
+
+/**
+ * Move a basic block to before another one.
+ *
+ * @see llvm::BasicBlock::moveBefore()
+ */
+void LLVMMoveBasicBlockBefore(LLVMBasicBlockRef BB, LLVMBasicBlockRef MovePos);
+
+/**
+ * Move a basic block to after another one.
+ *
+ * @see llvm::BasicBlock::moveAfter()
+ */
+void LLVMMoveBasicBlockAfter(LLVMBasicBlockRef BB, LLVMBasicBlockRef MovePos);
+
+/**
+ * Obtain the first instruction in a basic block.
+ *
+ * The returned LLVMValueRef corresponds to a llvm::Instruction
+ * instance.
+ */
+LLVMValueRef LLVMGetFirstInstruction(LLVMBasicBlockRef BB);
+
+/**
+ * Obtain the last instruction in a basic block.
+ *
+ * The returned LLVMValueRef corresponds to an LLVM:Instruction.
+ */
+LLVMValueRef LLVMGetLastInstruction(LLVMBasicBlockRef BB);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstruction Instructions
+ *
+ * Functions in this group relate to the inspection and manipulation of
+ * individual instructions.
+ *
+ * In the C++ API, an instruction is modeled by llvm::Instruction. This
+ * class has a large number of descendents. llvm::Instruction is a
+ * llvm::Value and in the C API, instructions are modeled by
+ * LLVMValueRef.
+ *
+ * This group also contains sub-groups which operate on specific
+ * llvm::Instruction types, e.g. llvm::CallInst.
+ *
+ * @{
+ */
+
+/**
+ * Determine whether an instruction has any metadata attached.
+ */
+int LLVMHasMetadata(LLVMValueRef Val);
+
+/**
+ * Return metadata associated with an instruction value.
+ */
+LLVMValueRef LLVMGetMetadata(LLVMValueRef Val, unsigned KindID);
+
+/**
+ * Set metadata associated with an instruction value.
+ */
+void LLVMSetMetadata(LLVMValueRef Val, unsigned KindID, LLVMValueRef Node);
+
+/**
+ * Returns the metadata associated with an instruction value, but filters out
+ * all the debug locations.
+ *
+ * @see llvm::Instruction::getAllMetadataOtherThanDebugLoc()
+ */
+LLVMValueMetadataEntry *
+LLVMInstructionGetAllMetadataOtherThanDebugLoc(LLVMValueRef Instr,
+                                               size_t *NumEntries);
+
+/**
+ * Obtain the basic block to which an instruction belongs.
+ *
+ * @see llvm::Instruction::getParent()
+ */
+LLVMBasicBlockRef LLVMGetInstructionParent(LLVMValueRef Inst);
+
+/**
+ * Obtain the instruction that occurs after the one specified.
+ *
+ * The next instruction will be from the same basic block.
+ *
+ * If this is the last instruction in a basic block, NULL will be
+ * returned.
+ */
+LLVMValueRef LLVMGetNextInstruction(LLVMValueRef Inst);
+
+/**
+ * Obtain the instruction that occurred before this one.
+ *
+ * If the instruction is the first instruction in a basic block, NULL
+ * will be returned.
+ */
+LLVMValueRef LLVMGetPreviousInstruction(LLVMValueRef Inst);
+
+/**
+ * Remove and delete an instruction.
+ *
+ * The instruction specified is removed from its containing building
+ * block but is kept alive.
+ *
+ * @see llvm::Instruction::removeFromParent()
+ */
+void LLVMInstructionRemoveFromParent(LLVMValueRef Inst);
+
+/**
+ * Remove and delete an instruction.
+ *
+ * The instruction specified is removed from its containing building
+ * block and then deleted.
+ *
+ * @see llvm::Instruction::eraseFromParent()
+ */
+void LLVMInstructionEraseFromParent(LLVMValueRef Inst);
+
+/**
+ * Obtain the code opcode for an individual instruction.
+ *
+ * @see llvm::Instruction::getOpCode()
+ */
+LLVMOpcode LLVMGetInstructionOpcode(LLVMValueRef Inst);
+
+/**
+ * Obtain the predicate of an instruction.
+ *
+ * This is only valid for instructions that correspond to llvm::ICmpInst
+ * or llvm::ConstantExpr whose opcode is llvm::Instruction::ICmp.
+ *
+ * @see llvm::ICmpInst::getPredicate()
+ */
+LLVMIntPredicate LLVMGetICmpPredicate(LLVMValueRef Inst);
+
+/**
+ * Obtain the float predicate of an instruction.
+ *
+ * This is only valid for instructions that correspond to llvm::FCmpInst
+ * or llvm::ConstantExpr whose opcode is llvm::Instruction::FCmp.
+ *
+ * @see llvm::FCmpInst::getPredicate()
+ */
+LLVMRealPredicate LLVMGetFCmpPredicate(LLVMValueRef Inst);
+
+/**
+ * Create a copy of 'this' instruction that is identical in all ways
+ * except the following:
+ *   * The instruction has no parent
+ *   * The instruction has no name
+ *
+ * @see llvm::Instruction::clone()
+ */
+LLVMValueRef LLVMInstructionClone(LLVMValueRef Inst);
+
+/**
+ * Determine whether an instruction is a terminator. This routine is named to
+ * be compatible with historical functions that did this by querying the
+ * underlying C++ type.
+ *
+ * @see llvm::Instruction::isTerminator()
+ */
+LLVMValueRef LLVMIsATerminatorInst(LLVMValueRef Inst);
+
+/**
+ * @defgroup LLVMCCoreValueInstructionCall Call Sites and Invocations
+ *
+ * Functions in this group apply to instructions that refer to call
+ * sites and invocations. These correspond to C++ types in the
+ * llvm::CallInst class tree.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the argument count for a call instruction.
+ *
+ * This expects an LLVMValueRef that corresponds to a llvm::CallInst,
+ * llvm::InvokeInst, or llvm:FuncletPadInst.
+ *
+ * @see llvm::CallInst::getNumArgOperands()
+ * @see llvm::InvokeInst::getNumArgOperands()
+ * @see llvm::FuncletPadInst::getNumArgOperands()
+ */
+unsigned LLVMGetNumArgOperands(LLVMValueRef Instr);
+
+/**
+ * Set the calling convention for a call instruction.
+ *
+ * This expects an LLVMValueRef that corresponds to a llvm::CallInst or
+ * llvm::InvokeInst.
+ *
+ * @see llvm::CallInst::setCallingConv()
+ * @see llvm::InvokeInst::setCallingConv()
+ */
+void LLVMSetInstructionCallConv(LLVMValueRef Instr, unsigned CC);
+
+/**
+ * Obtain the calling convention for a call instruction.
+ *
+ * This is the opposite of LLVMSetInstructionCallConv(). Reads its
+ * usage.
+ *
+ * @see LLVMSetInstructionCallConv()
+ */
+unsigned LLVMGetInstructionCallConv(LLVMValueRef Instr);
+
+void LLVMSetInstrParamAlignment(LLVMValueRef Instr, unsigned index,
+                                unsigned Align);
+
+void LLVMAddCallSiteAttribute(LLVMValueRef C, LLVMAttributeIndex Idx,
+                              LLVMAttributeRef A);
+unsigned LLVMGetCallSiteAttributeCount(LLVMValueRef C, LLVMAttributeIndex Idx);
+void LLVMGetCallSiteAttributes(LLVMValueRef C, LLVMAttributeIndex Idx,
+                               LLVMAttributeRef *Attrs);
+LLVMAttributeRef LLVMGetCallSiteEnumAttribute(LLVMValueRef C,
+                                              LLVMAttributeIndex Idx,
+                                              unsigned KindID);
+LLVMAttributeRef LLVMGetCallSiteStringAttribute(LLVMValueRef C,
+                                                LLVMAttributeIndex Idx,
+                                                const char *K, unsigned KLen);
+void LLVMRemoveCallSiteEnumAttribute(LLVMValueRef C, LLVMAttributeIndex Idx,
+                                     unsigned KindID);
+void LLVMRemoveCallSiteStringAttribute(LLVMValueRef C, LLVMAttributeIndex Idx,
+                                       const char *K, unsigned KLen);
+
+/**
+ * Obtain the function type called by this instruction.
+ *
+ * @see llvm::CallBase::getFunctionType()
+ */
+LLVMTypeRef LLVMGetCalledFunctionType(LLVMValueRef C);
+
+/**
+ * Obtain the pointer to the function invoked by this instruction.
+ *
+ * This expects an LLVMValueRef that corresponds to a llvm::CallInst or
+ * llvm::InvokeInst.
+ *
+ * @see llvm::CallInst::getCalledValue()
+ * @see llvm::InvokeInst::getCalledValue()
+ */
+LLVMValueRef LLVMGetCalledValue(LLVMValueRef Instr);
+
+/**
+ * Obtain whether a call instruction is a tail call.
+ *
+ * This only works on llvm::CallInst instructions.
+ *
+ * @see llvm::CallInst::isTailCall()
+ */
+LLVMBool LLVMIsTailCall(LLVMValueRef CallInst);
+
+/**
+ * Set whether a call instruction is a tail call.
+ *
+ * This only works on llvm::CallInst instructions.
+ *
+ * @see llvm::CallInst::setTailCall()
+ */
+void LLVMSetTailCall(LLVMValueRef CallInst, LLVMBool IsTailCall);
+
+/**
+ * Return the normal destination basic block.
+ *
+ * This only works on llvm::InvokeInst instructions.
+ *
+ * @see llvm::InvokeInst::getNormalDest()
+ */
+LLVMBasicBlockRef LLVMGetNormalDest(LLVMValueRef InvokeInst);
+
+/**
+ * Return the unwind destination basic block.
+ *
+ * Works on llvm::InvokeInst, llvm::CleanupReturnInst, and
+ * llvm::CatchSwitchInst instructions.
+ *
+ * @see llvm::InvokeInst::getUnwindDest()
+ * @see llvm::CleanupReturnInst::getUnwindDest()
+ * @see llvm::CatchSwitchInst::getUnwindDest()
+ */
+LLVMBasicBlockRef LLVMGetUnwindDest(LLVMValueRef InvokeInst);
+
+/**
+ * Set the normal destination basic block.
+ *
+ * This only works on llvm::InvokeInst instructions.
+ *
+ * @see llvm::InvokeInst::setNormalDest()
+ */
+void LLVMSetNormalDest(LLVMValueRef InvokeInst, LLVMBasicBlockRef B);
+
+/**
+ * Set the unwind destination basic block.
+ *
+ * Works on llvm::InvokeInst, llvm::CleanupReturnInst, and
+ * llvm::CatchSwitchInst instructions.
+ *
+ * @see llvm::InvokeInst::setUnwindDest()
+ * @see llvm::CleanupReturnInst::setUnwindDest()
+ * @see llvm::CatchSwitchInst::setUnwindDest()
+ */
+void LLVMSetUnwindDest(LLVMValueRef InvokeInst, LLVMBasicBlockRef B);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstructionTerminator Terminators
+ *
+ * Functions in this group only apply to instructions for which
+ * LLVMIsATerminatorInst returns true.
+ *
+ * @{
+ */
+
+/**
+ * Return the number of successors that this terminator has.
+ *
+ * @see llvm::Instruction::getNumSuccessors
+ */
+unsigned LLVMGetNumSuccessors(LLVMValueRef Term);
+
+/**
+ * Return the specified successor.
+ *
+ * @see llvm::Instruction::getSuccessor
+ */
+LLVMBasicBlockRef LLVMGetSuccessor(LLVMValueRef Term, unsigned i);
+
+/**
+ * Update the specified successor to point at the provided block.
+ *
+ * @see llvm::Instruction::setSuccessor
+ */
+void LLVMSetSuccessor(LLVMValueRef Term, unsigned i, LLVMBasicBlockRef block);
+
+/**
+ * Return if a branch is conditional.
+ *
+ * This only works on llvm::BranchInst instructions.
+ *
+ * @see llvm::BranchInst::isConditional
+ */
+LLVMBool LLVMIsConditional(LLVMValueRef Branch);
+
+/**
+ * Return the condition of a branch instruction.
+ *
+ * This only works on llvm::BranchInst instructions.
+ *
+ * @see llvm::BranchInst::getCondition
+ */
+LLVMValueRef LLVMGetCondition(LLVMValueRef Branch);
+
+/**
+ * Set the condition of a branch instruction.
+ *
+ * This only works on llvm::BranchInst instructions.
+ *
+ * @see llvm::BranchInst::setCondition
+ */
+void LLVMSetCondition(LLVMValueRef Branch, LLVMValueRef Cond);
+
+/**
+ * Obtain the default destination basic block of a switch instruction.
+ *
+ * This only works on llvm::SwitchInst instructions.
+ *
+ * @see llvm::SwitchInst::getDefaultDest()
+ */
+LLVMBasicBlockRef LLVMGetSwitchDefaultDest(LLVMValueRef SwitchInstr);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstructionAlloca Allocas
+ *
+ * Functions in this group only apply to instructions that map to
+ * llvm::AllocaInst instances.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the type that is being allocated by the alloca instruction.
+ */
+LLVMTypeRef LLVMGetAllocatedType(LLVMValueRef Alloca);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstructionGetElementPointer GEPs
+ *
+ * Functions in this group only apply to instructions that map to
+ * llvm::GetElementPtrInst instances.
+ *
+ * @{
+ */
+
+/**
+ * Check whether the given GEP instruction is inbounds.
+ */
+LLVMBool LLVMIsInBounds(LLVMValueRef GEP);
+
+/**
+ * Set the given GEP instruction to be inbounds or not.
+ */
+void LLVMSetIsInBounds(LLVMValueRef GEP, LLVMBool InBounds);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstructionPHINode PHI Nodes
+ *
+ * Functions in this group only apply to instructions that map to
+ * llvm::PHINode instances.
+ *
+ * @{
+ */
+
+/**
+ * Add an incoming value to the end of a PHI list.
+ */
+void LLVMAddIncoming(LLVMValueRef PhiNode, LLVMValueRef *IncomingValues,
+                     LLVMBasicBlockRef *IncomingBlocks, unsigned Count);
+
+/**
+ * Obtain the number of incoming basic blocks to a PHI node.
+ */
+unsigned LLVMCountIncoming(LLVMValueRef PhiNode);
+
+/**
+ * Obtain an incoming value to a PHI node as an LLVMValueRef.
+ */
+LLVMValueRef LLVMGetIncomingValue(LLVMValueRef PhiNode, unsigned Index);
+
+/**
+ * Obtain an incoming value to a PHI node as an LLVMBasicBlockRef.
+ */
+LLVMBasicBlockRef LLVMGetIncomingBlock(LLVMValueRef PhiNode, unsigned Index);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreValueInstructionExtractValue ExtractValue
+ * @defgroup LLVMCCoreValueInstructionInsertValue InsertValue
+ *
+ * Functions in this group only apply to instructions that map to
+ * llvm::ExtractValue and llvm::InsertValue instances.
+ *
+ * @{
+ */
+
+/**
+ * Obtain the number of indices.
+ * NB: This also works on GEP.
+ */
+unsigned LLVMGetNumIndices(LLVMValueRef Inst);
+
+/**
+ * Obtain the indices as an array.
+ */
+const unsigned *LLVMGetIndices(LLVMValueRef Inst);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreInstructionBuilder Instruction Builders
+ *
+ * An instruction builder represents a point within a basic block and is
+ * the exclusive means of building instructions using the C interface.
+ *
+ * @{
+ */
+
+LLVMBuilderRef LLVMCreateBuilderInContext(LLVMContextRef C);
+LLVMBuilderRef LLVMCreateBuilder(void);
+void LLVMPositionBuilder(LLVMBuilderRef Builder, LLVMBasicBlockRef Block,
+                         LLVMValueRef Instr);
+void LLVMPositionBuilderBefore(LLVMBuilderRef Builder, LLVMValueRef Instr);
+void LLVMPositionBuilderAtEnd(LLVMBuilderRef Builder, LLVMBasicBlockRef Block);
+LLVMBasicBlockRef LLVMGetInsertBlock(LLVMBuilderRef Builder);
+void LLVMClearInsertionPosition(LLVMBuilderRef Builder);
+void LLVMInsertIntoBuilder(LLVMBuilderRef Builder, LLVMValueRef Instr);
+void LLVMInsertIntoBuilderWithName(LLVMBuilderRef Builder, LLVMValueRef Instr,
+                                   const char *Name);
+void LLVMDisposeBuilder(LLVMBuilderRef Builder);
+
+/* Metadata */
+
+/**
+ * Get location information used by debugging information.
+ *
+ * @see llvm::IRBuilder::getCurrentDebugLocation()
+ */
+LLVMMetadataRef LLVMGetCurrentDebugLocation2(LLVMBuilderRef Builder);
+
+/**
+ * Set location information used by debugging information.
+ *
+ * To clear the location metadata of the given instruction, pass NULL to \p Loc.
+ *
+ * @see llvm::IRBuilder::SetCurrentDebugLocation()
+ */
+void LLVMSetCurrentDebugLocation2(LLVMBuilderRef Builder, LLVMMetadataRef Loc);
+
+/**
+ * Attempts to set the debug location for the given instruction using the
+ * current debug location for the given builder.  If the builder has no current
+ * debug location, this function is a no-op.
+ *
+ * @see llvm::IRBuilder::SetInstDebugLocation()
+ */
+void LLVMSetInstDebugLocation(LLVMBuilderRef Builder, LLVMValueRef Inst);
+
+/**
+ * Get the dafult floating-point math metadata for a given builder.
+ *
+ * @see llvm::IRBuilder::getDefaultFPMathTag()
+ */
+LLVMMetadataRef LLVMBuilderGetDefaultFPMathTag(LLVMBuilderRef Builder);
+
+/**
+ * Set the default floating-point math metadata for the given builder.
+ *
+ * To clear the metadata, pass NULL to \p FPMathTag.
+ *
+ * @see llvm::IRBuilder::setDefaultFPMathTag()
+ */
+void LLVMBuilderSetDefaultFPMathTag(LLVMBuilderRef Builder,
+                                    LLVMMetadataRef FPMathTag);
+
+/**
+ * Deprecated: Passing the NULL location will crash.
+ * Use LLVMGetCurrentDebugLocation2 instead.
+ */
+void LLVMSetCurrentDebugLocation(LLVMBuilderRef Builder, LLVMValueRef L);
+/**
+ * Deprecated: Returning the NULL location will crash.
+ * Use LLVMGetCurrentDebugLocation2 instead.
+ */
+LLVMValueRef LLVMGetCurrentDebugLocation(LLVMBuilderRef Builder);
+
+/* Terminators */
+LLVMValueRef LLVMBuildRetVoid(LLVMBuilderRef);
+LLVMValueRef LLVMBuildRet(LLVMBuilderRef, LLVMValueRef V);
+LLVMValueRef LLVMBuildAggregateRet(LLVMBuilderRef, LLVMValueRef *RetVals,
+                                   unsigned N);
+LLVMValueRef LLVMBuildBr(LLVMBuilderRef, LLVMBasicBlockRef Dest);
+LLVMValueRef LLVMBuildCondBr(LLVMBuilderRef, LLVMValueRef If,
+                             LLVMBasicBlockRef Then, LLVMBasicBlockRef Else);
+LLVMValueRef LLVMBuildSwitch(LLVMBuilderRef, LLVMValueRef V,
+                             LLVMBasicBlockRef Else, unsigned NumCases);
+LLVMValueRef LLVMBuildIndirectBr(LLVMBuilderRef B, LLVMValueRef Addr,
+                                 unsigned NumDests);
+// LLVMBuildInvoke is deprecated in favor of LLVMBuildInvoke2, in preparation
+// for opaque pointer types.
+LLVMValueRef LLVMBuildInvoke(LLVMBuilderRef, LLVMValueRef Fn,
+                             LLVMValueRef *Args, unsigned NumArgs,
+                             LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
+                             const char *Name);
+LLVMValueRef LLVMBuildInvoke2(LLVMBuilderRef, LLVMTypeRef Ty, LLVMValueRef Fn,
+                              LLVMValueRef *Args, unsigned NumArgs,
+                              LLVMBasicBlockRef Then, LLVMBasicBlockRef Catch,
+                              const char *Name);
+LLVMValueRef LLVMBuildUnreachable(LLVMBuilderRef);
+
+/* Exception Handling */
+LLVMValueRef LLVMBuildResume(LLVMBuilderRef B, LLVMValueRef Exn);
+LLVMValueRef LLVMBuildLandingPad(LLVMBuilderRef B, LLVMTypeRef Ty,
+                                 LLVMValueRef PersFn, unsigned NumClauses,
+                                 const char *Name);
+LLVMValueRef LLVMBuildCleanupRet(LLVMBuilderRef B, LLVMValueRef CatchPad,
+                                 LLVMBasicBlockRef BB);
+LLVMValueRef LLVMBuildCatchRet(LLVMBuilderRef B, LLVMValueRef CatchPad,
+                               LLVMBasicBlockRef BB);
+LLVMValueRef LLVMBuildCatchPad(LLVMBuilderRef B, LLVMValueRef ParentPad,
+                               LLVMValueRef *Args, unsigned NumArgs,
+                               const char *Name);
+LLVMValueRef LLVMBuildCleanupPad(LLVMBuilderRef B, LLVMValueRef ParentPad,
+                                 LLVMValueRef *Args, unsigned NumArgs,
+                                 const char *Name);
+LLVMValueRef LLVMBuildCatchSwitch(LLVMBuilderRef B, LLVMValueRef ParentPad,
+                                  LLVMBasicBlockRef UnwindBB,
+                                  unsigned NumHandlers, const char *Name);
+
+/* Add a case to the switch instruction */
+void LLVMAddCase(LLVMValueRef Switch, LLVMValueRef OnVal,
+                 LLVMBasicBlockRef Dest);
+
+/* Add a destination to the indirectbr instruction */
+void LLVMAddDestination(LLVMValueRef IndirectBr, LLVMBasicBlockRef Dest);
+
+/* Get the number of clauses on the landingpad instruction */
+unsigned LLVMGetNumClauses(LLVMValueRef LandingPad);
+
+/* Get the value of the clause at idnex Idx on the landingpad instruction */
+LLVMValueRef LLVMGetClause(LLVMValueRef LandingPad, unsigned Idx);
+
+/* Add a catch or filter clause to the landingpad instruction */
+void LLVMAddClause(LLVMValueRef LandingPad, LLVMValueRef ClauseVal);
+
+/* Get the 'cleanup' flag in the landingpad instruction */
+LLVMBool LLVMIsCleanup(LLVMValueRef LandingPad);
+
+/* Set the 'cleanup' flag in the landingpad instruction */
+void LLVMSetCleanup(LLVMValueRef LandingPad, LLVMBool Val);
+
+/* Add a destination to the catchswitch instruction */
+void LLVMAddHandler(LLVMValueRef CatchSwitch, LLVMBasicBlockRef Dest);
+
+/* Get the number of handlers on the catchswitch instruction */
+unsigned LLVMGetNumHandlers(LLVMValueRef CatchSwitch);
+
+/**
+ * Obtain the basic blocks acting as handlers for a catchswitch instruction.
+ *
+ * The Handlers parameter should point to a pre-allocated array of
+ * LLVMBasicBlockRefs at least LLVMGetNumHandlers() large. On return, the
+ * first LLVMGetNumHandlers() entries in the array will be populated
+ * with LLVMBasicBlockRef instances.
+ *
+ * @param CatchSwitch The catchswitch instruction to operate on.
+ * @param Handlers Memory address of an array to be filled with basic blocks.
+ */
+void LLVMGetHandlers(LLVMValueRef CatchSwitch, LLVMBasicBlockRef *Handlers);
+
+/* Funclets */
+
+/* Get the number of funcletpad arguments. */
+LLVMValueRef LLVMGetArgOperand(LLVMValueRef Funclet, unsigned i);
+
+/* Set a funcletpad argument at the given index. */
+void LLVMSetArgOperand(LLVMValueRef Funclet, unsigned i, LLVMValueRef value);
+
+/**
+ * Get the parent catchswitch instruction of a catchpad instruction.
+ *
+ * This only works on llvm::CatchPadInst instructions.
+ *
+ * @see llvm::CatchPadInst::getCatchSwitch()
+ */
+LLVMValueRef LLVMGetParentCatchSwitch(LLVMValueRef CatchPad);
+
+/**
+ * Set the parent catchswitch instruction of a catchpad instruction.
+ *
+ * This only works on llvm::CatchPadInst instructions.
+ *
+ * @see llvm::CatchPadInst::setCatchSwitch()
+ */
+void LLVMSetParentCatchSwitch(LLVMValueRef CatchPad, LLVMValueRef CatchSwitch);
+
+/* Arithmetic */
+LLVMValueRef LLVMBuildAdd(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildNSWAdd(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildNUWAdd(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildFAdd(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildSub(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildNSWSub(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildNUWSub(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildFSub(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildMul(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildNSWMul(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildNUWMul(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                             const char *Name);
+LLVMValueRef LLVMBuildFMul(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildUDiv(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildExactUDiv(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                                const char *Name);
+LLVMValueRef LLVMBuildSDiv(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildExactSDiv(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                                const char *Name);
+LLVMValueRef LLVMBuildFDiv(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildURem(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildSRem(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildFRem(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildShl(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildLShr(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildAShr(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildAnd(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildOr(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildXor(LLVMBuilderRef, LLVMValueRef LHS, LLVMValueRef RHS,
+                          const char *Name);
+LLVMValueRef LLVMBuildBinOp(LLVMBuilderRef B, LLVMOpcode Op,
+                            LLVMValueRef LHS, LLVMValueRef RHS,
+                            const char *Name);
+LLVMValueRef LLVMBuildNeg(LLVMBuilderRef, LLVMValueRef V, const char *Name);
+LLVMValueRef LLVMBuildNSWNeg(LLVMBuilderRef B, LLVMValueRef V,
+                             const char *Name);
+LLVMValueRef LLVMBuildNUWNeg(LLVMBuilderRef B, LLVMValueRef V,
+                             const char *Name);
+LLVMValueRef LLVMBuildFNeg(LLVMBuilderRef, LLVMValueRef V, const char *Name);
+LLVMValueRef LLVMBuildNot(LLVMBuilderRef, LLVMValueRef V, const char *Name);
+
+/* Memory */
+LLVMValueRef LLVMBuildMalloc(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
+LLVMValueRef LLVMBuildArrayMalloc(LLVMBuilderRef, LLVMTypeRef Ty,
+                                  LLVMValueRef Val, const char *Name);
+
+/**
+ * Creates and inserts a memset to the specified pointer and the
+ * specified value.
+ *
+ * @see llvm::IRRBuilder::CreateMemSet()
+ */
+LLVMValueRef LLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr,
+                             LLVMValueRef Val, LLVMValueRef Len,
+                             unsigned Align);
+/**
+ * Creates and inserts a memcpy between the specified pointers.
+ *
+ * @see llvm::IRRBuilder::CreateMemCpy()
+ */
+LLVMValueRef LLVMBuildMemCpy(LLVMBuilderRef B,
+                             LLVMValueRef Dst, unsigned DstAlign,
+                             LLVMValueRef Src, unsigned SrcAlign,
+                             LLVMValueRef Size);
+/**
+ * Creates and inserts a memmove between the specified pointers.
+ *
+ * @see llvm::IRRBuilder::CreateMemMove()
+ */
+LLVMValueRef LLVMBuildMemMove(LLVMBuilderRef B,
+                              LLVMValueRef Dst, unsigned DstAlign,
+                              LLVMValueRef Src, unsigned SrcAlign,
+                              LLVMValueRef Size);
+
+LLVMValueRef LLVMBuildAlloca(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
+LLVMValueRef LLVMBuildArrayAlloca(LLVMBuilderRef, LLVMTypeRef Ty,
+                                  LLVMValueRef Val, const char *Name);
+LLVMValueRef LLVMBuildFree(LLVMBuilderRef, LLVMValueRef PointerVal);
+// LLVMBuildLoad is deprecated in favor of LLVMBuildLoad2, in preparation for
+// opaque pointer types.
+LLVMValueRef LLVMBuildLoad(LLVMBuilderRef, LLVMValueRef PointerVal,
+                           const char *Name);
+LLVMValueRef LLVMBuildLoad2(LLVMBuilderRef, LLVMTypeRef Ty,
+                            LLVMValueRef PointerVal, const char *Name);
+LLVMValueRef LLVMBuildStore(LLVMBuilderRef, LLVMValueRef Val, LLVMValueRef Ptr);
+// LLVMBuildGEP, LLVMBuildInBoundsGEP, and LLVMBuildStructGEP are deprecated in
+// favor of LLVMBuild*GEP2, in preparation for opaque pointer types.
+LLVMValueRef LLVMBuildGEP(LLVMBuilderRef B, LLVMValueRef Pointer,
+                          LLVMValueRef *Indices, unsigned NumIndices,
+                          const char *Name);
+LLVMValueRef LLVMBuildInBoundsGEP(LLVMBuilderRef B, LLVMValueRef Pointer,
+                                  LLVMValueRef *Indices, unsigned NumIndices,
+                                  const char *Name);
+LLVMValueRef LLVMBuildStructGEP(LLVMBuilderRef B, LLVMValueRef Pointer,
+                                unsigned Idx, const char *Name);
+LLVMValueRef LLVMBuildGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
+                           LLVMValueRef Pointer, LLVMValueRef *Indices,
+                           unsigned NumIndices, const char *Name);
+LLVMValueRef LLVMBuildInBoundsGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
+                                   LLVMValueRef Pointer, LLVMValueRef *Indices,
+                                   unsigned NumIndices, const char *Name);
+LLVMValueRef LLVMBuildStructGEP2(LLVMBuilderRef B, LLVMTypeRef Ty,
+                                 LLVMValueRef Pointer, unsigned Idx,
+                                 const char *Name);
+LLVMValueRef LLVMBuildGlobalString(LLVMBuilderRef B, const char *Str,
+                                   const char *Name);
+LLVMValueRef LLVMBuildGlobalStringPtr(LLVMBuilderRef B, const char *Str,
+                                      const char *Name);
+LLVMBool LLVMGetVolatile(LLVMValueRef MemoryAccessInst);
+void LLVMSetVolatile(LLVMValueRef MemoryAccessInst, LLVMBool IsVolatile);
+LLVMAtomicOrdering LLVMGetOrdering(LLVMValueRef MemoryAccessInst);
+void LLVMSetOrdering(LLVMValueRef MemoryAccessInst, LLVMAtomicOrdering Ordering);
+
+/* Casts */
+LLVMValueRef LLVMBuildTrunc(LLVMBuilderRef, LLVMValueRef Val,
+                            LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildZExt(LLVMBuilderRef, LLVMValueRef Val,
+                           LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildSExt(LLVMBuilderRef, LLVMValueRef Val,
+                           LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildFPToUI(LLVMBuilderRef, LLVMValueRef Val,
+                             LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildFPToSI(LLVMBuilderRef, LLVMValueRef Val,
+                             LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildUIToFP(LLVMBuilderRef, LLVMValueRef Val,
+                             LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildSIToFP(LLVMBuilderRef, LLVMValueRef Val,
+                             LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildFPTrunc(LLVMBuilderRef, LLVMValueRef Val,
+                              LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildFPExt(LLVMBuilderRef, LLVMValueRef Val,
+                            LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildPtrToInt(LLVMBuilderRef, LLVMValueRef Val,
+                               LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildIntToPtr(LLVMBuilderRef, LLVMValueRef Val,
+                               LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildBitCast(LLVMBuilderRef, LLVMValueRef Val,
+                              LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildAddrSpaceCast(LLVMBuilderRef, LLVMValueRef Val,
+                                    LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildZExtOrBitCast(LLVMBuilderRef, LLVMValueRef Val,
+                                    LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildSExtOrBitCast(LLVMBuilderRef, LLVMValueRef Val,
+                                    LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildTruncOrBitCast(LLVMBuilderRef, LLVMValueRef Val,
+                                     LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildCast(LLVMBuilderRef B, LLVMOpcode Op, LLVMValueRef Val,
+                           LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildPointerCast(LLVMBuilderRef, LLVMValueRef Val,
+                                  LLVMTypeRef DestTy, const char *Name);
+LLVMValueRef LLVMBuildIntCast2(LLVMBuilderRef, LLVMValueRef Val,
+                               LLVMTypeRef DestTy, LLVMBool IsSigned,
+                               const char *Name);
+LLVMValueRef LLVMBuildFPCast(LLVMBuilderRef, LLVMValueRef Val,
+                             LLVMTypeRef DestTy, const char *Name);
+
+/** Deprecated: This cast is always signed. Use LLVMBuildIntCast2 instead. */
+LLVMValueRef LLVMBuildIntCast(LLVMBuilderRef, LLVMValueRef Val, /*Signed cast!*/
+                              LLVMTypeRef DestTy, const char *Name);
+
+/* Comparisons */
+LLVMValueRef LLVMBuildICmp(LLVMBuilderRef, LLVMIntPredicate Op,
+                           LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+LLVMValueRef LLVMBuildFCmp(LLVMBuilderRef, LLVMRealPredicate Op,
+                           LLVMValueRef LHS, LLVMValueRef RHS,
+                           const char *Name);
+
+/* Miscellaneous instructions */
+LLVMValueRef LLVMBuildPhi(LLVMBuilderRef, LLVMTypeRef Ty, const char *Name);
+// LLVMBuildCall is deprecated in favor of LLVMBuildCall2, in preparation for
+// opaque pointer types.
+LLVMValueRef LLVMBuildCall(LLVMBuilderRef, LLVMValueRef Fn,
+                           LLVMValueRef *Args, unsigned NumArgs,
+                           const char *Name);
+LLVMValueRef LLVMBuildCall2(LLVMBuilderRef, LLVMTypeRef, LLVMValueRef Fn,
+                            LLVMValueRef *Args, unsigned NumArgs,
+                            const char *Name);
+LLVMValueRef LLVMBuildSelect(LLVMBuilderRef, LLVMValueRef If,
+                             LLVMValueRef Then, LLVMValueRef Else,
+                             const char *Name);
+LLVMValueRef LLVMBuildVAArg(LLVMBuilderRef, LLVMValueRef List, LLVMTypeRef Ty,
+                            const char *Name);
+LLVMValueRef LLVMBuildExtractElement(LLVMBuilderRef, LLVMValueRef VecVal,
+                                     LLVMValueRef Index, const char *Name);
+LLVMValueRef LLVMBuildInsertElement(LLVMBuilderRef, LLVMValueRef VecVal,
+                                    LLVMValueRef EltVal, LLVMValueRef Index,
+                                    const char *Name);
+LLVMValueRef LLVMBuildShuffleVector(LLVMBuilderRef, LLVMValueRef V1,
+                                    LLVMValueRef V2, LLVMValueRef Mask,
+                                    const char *Name);
+LLVMValueRef LLVMBuildExtractValue(LLVMBuilderRef, LLVMValueRef AggVal,
+                                   unsigned Index, const char *Name);
+LLVMValueRef LLVMBuildInsertValue(LLVMBuilderRef, LLVMValueRef AggVal,
+                                  LLVMValueRef EltVal, unsigned Index,
+                                  const char *Name);
+
+LLVMValueRef LLVMBuildIsNull(LLVMBuilderRef, LLVMValueRef Val,
+                             const char *Name);
+LLVMValueRef LLVMBuildIsNotNull(LLVMBuilderRef, LLVMValueRef Val,
+                                const char *Name);
+LLVMValueRef LLVMBuildPtrDiff(LLVMBuilderRef, LLVMValueRef LHS,
+                              LLVMValueRef RHS, const char *Name);
+LLVMValueRef LLVMBuildFence(LLVMBuilderRef B, LLVMAtomicOrdering ordering,
+                            LLVMBool singleThread, const char *Name);
+LLVMValueRef LLVMBuildAtomicRMW(LLVMBuilderRef B, LLVMAtomicRMWBinOp op,
+                                LLVMValueRef PTR, LLVMValueRef Val,
+                                LLVMAtomicOrdering ordering,
+                                LLVMBool singleThread);
+LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, LLVMValueRef Ptr,
+                                    LLVMValueRef Cmp, LLVMValueRef New,
+                                    LLVMAtomicOrdering SuccessOrdering,
+                                    LLVMAtomicOrdering FailureOrdering,
+                                    LLVMBool SingleThread);
+
+LLVMBool LLVMIsAtomicSingleThread(LLVMValueRef AtomicInst);
+void LLVMSetAtomicSingleThread(LLVMValueRef AtomicInst, LLVMBool SingleThread);
+
+LLVMAtomicOrdering LLVMGetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst);
+void LLVMSetCmpXchgSuccessOrdering(LLVMValueRef CmpXchgInst,
+                                   LLVMAtomicOrdering Ordering);
+LLVMAtomicOrdering LLVMGetCmpXchgFailureOrdering(LLVMValueRef CmpXchgInst);
+void LLVMSetCmpXchgFailureOrdering(LLVMValueRef CmpXchgInst,
+                                   LLVMAtomicOrdering Ordering);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreModuleProvider Module Providers
+ *
+ * @{
+ */
+
+/**
+ * Changes the type of M so it can be passed to FunctionPassManagers and the
+ * JIT.  They take ModuleProviders for historical reasons.
+ */
+LLVMModuleProviderRef
+LLVMCreateModuleProviderForExistingModule(LLVMModuleRef M);
+
+/**
+ * Destroys the module M.
+ */
+void LLVMDisposeModuleProvider(LLVMModuleProviderRef M);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreMemoryBuffers Memory Buffers
+ *
+ * @{
+ */
+
+LLVMBool LLVMCreateMemoryBufferWithContentsOfFile(const char *Path,
+                                                  LLVMMemoryBufferRef *OutMemBuf,
+                                                  char **OutMessage);
+LLVMBool LLVMCreateMemoryBufferWithSTDIN(LLVMMemoryBufferRef *OutMemBuf,
+                                         char **OutMessage);
+LLVMMemoryBufferRef LLVMCreateMemoryBufferWithMemoryRange(const char *InputData,
+                                                          size_t InputDataLength,
+                                                          const char *BufferName,
+                                                          LLVMBool RequiresNullTerminator);
+LLVMMemoryBufferRef LLVMCreateMemoryBufferWithMemoryRangeCopy(const char *InputData,
+                                                              size_t InputDataLength,
+                                                              const char *BufferName);
+const char *LLVMGetBufferStart(LLVMMemoryBufferRef MemBuf);
+size_t LLVMGetBufferSize(LLVMMemoryBufferRef MemBuf);
+void LLVMDisposeMemoryBuffer(LLVMMemoryBufferRef MemBuf);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCorePassRegistry Pass Registry
+ *
+ * @{
+ */
+
+/** Return the global pass registry, for use with initialization functions.
+    @see llvm::PassRegistry::getPassRegistry */
+LLVMPassRegistryRef LLVMGetGlobalPassRegistry(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCorePassManagers Pass Managers
+ *
+ * @{
+ */
+
+/** Constructs a new whole-module pass pipeline. This type of pipeline is
+    suitable for link-time optimization and whole-module transformations.
+    @see llvm::PassManager::PassManager */
+LLVMPassManagerRef LLVMCreatePassManager(void);
+
+/** Constructs a new function-by-function pass pipeline over the module
+    provider. It does not take ownership of the module provider. This type of
+    pipeline is suitable for code generation and JIT compilation tasks.
+    @see llvm::FunctionPassManager::FunctionPassManager */
+LLVMPassManagerRef LLVMCreateFunctionPassManagerForModule(LLVMModuleRef M);
+
+/** Deprecated: Use LLVMCreateFunctionPassManagerForModule instead. */
+LLVMPassManagerRef LLVMCreateFunctionPassManager(LLVMModuleProviderRef MP);
+
+/** Initializes, executes on the provided module, and finalizes all of the
+    passes scheduled in the pass manager. Returns 1 if any of the passes
+    modified the module, 0 otherwise.
+    @see llvm::PassManager::run(Module&) */
+LLVMBool LLVMRunPassManager(LLVMPassManagerRef PM, LLVMModuleRef M);
+
+/** Initializes all of the function passes scheduled in the function pass
+    manager. Returns 1 if any of the passes modified the module, 0 otherwise.
+    @see llvm::FunctionPassManager::doInitialization */
+LLVMBool LLVMInitializeFunctionPassManager(LLVMPassManagerRef FPM);
+
+/** Executes all of the function passes scheduled in the function pass manager
+    on the provided function. Returns 1 if any of the passes modified the
+    function, false otherwise.
+    @see llvm::FunctionPassManager::run(Function&) */
+LLVMBool LLVMRunFunctionPassManager(LLVMPassManagerRef FPM, LLVMValueRef F);
+
+/** Finalizes all of the function passes scheduled in the function pass
+    manager. Returns 1 if any of the passes modified the module, 0 otherwise.
+    @see llvm::FunctionPassManager::doFinalization */
+LLVMBool LLVMFinalizeFunctionPassManager(LLVMPassManagerRef FPM);
+
+/** Frees the memory of a pass pipeline. For function pipelines, does not free
+    the module provider.
+    @see llvm::PassManagerBase::~PassManagerBase. */
+void LLVMDisposePassManager(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup LLVMCCoreThreading Threading
+ *
+ * Handle the structures needed to make LLVM safe for multithreading.
+ *
+ * @{
+ */
+
+/** Deprecated: Multi-threading can only be enabled/disabled with the compile
+    time define LLVM_ENABLE_THREADS.  This function always returns
+    LLVMIsMultithreaded(). */
+LLVMBool LLVMStartMultithreaded(void);
+
+/** Deprecated: Multi-threading can only be enabled/disabled with the compile
+    time define LLVM_ENABLE_THREADS. */
+void LLVMStopMultithreaded(void);
+
+/** Check whether LLVM is executing in thread-safe mode or not.
+    @see llvm::llvm_is_multithreaded */
+LLVMBool LLVMIsMultithreaded(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LLVM_C_CORE_H */

+ 90 - 0
src/llvm-c/DataTypes.h

@@ -0,0 +1,90 @@
+/*===-- include/llvm-c/DataTypes.h - Define fixed size types ------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file contains definitions to figure out the size of _HOST_ data types.*|
+|* This file is important because different host OS's define different macros,*|
+|* which makes portability tough.  This file exports the following            *|
+|* definitions:                                                               *|
+|*                                                                            *|
+|*   [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system types*|
+|*   [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values.     *|
+|*                                                                            *|
+|* No library is required when using these functions.                         *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*/
+
+/* Please leave this file C-compatible. */
+
+#ifndef LLVM_C_DATATYPES_H
+#define LLVM_C_DATATYPES_H
+
+#ifdef __cplusplus
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#ifndef _MSC_VER
+
+#if !defined(UINT32_MAX)
+# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
+        "__STDC_LIMIT_MACROS before #including llvm-c/DataTypes.h"
+#endif
+
+#if !defined(UINT32_C)
+# error "The standard header <cstdint> is not C++11 compliant. Must #define "\
+        "__STDC_CONSTANT_MACROS before #including llvm-c/DataTypes.h"
+#endif
+
+/* Note that <inttypes.h> includes <stdint.h>, if this is a C99 system. */
+#include <sys/types.h>
+
+#ifdef _AIX
+// GCC is strict about defining large constants: they must have LL modifier.
+#undef INT64_MAX
+#undef INT64_MIN
+#endif
+
+#else /* _MSC_VER */
+#ifdef __cplusplus
+#include <cstddef>
+#include <cstdlib>
+#else
+#include <stddef.h>
+#include <stdlib.h>
+#endif
+#include <sys/types.h>
+
+#if defined(_WIN64)
+typedef signed __int64 ssize_t;
+#else
+typedef signed int ssize_t;
+#endif /* _WIN64 */
+
+#endif /* _MSC_VER */
+
+/* Set defaults for constants which we cannot find. */
+#if !defined(INT64_MAX)
+# define INT64_MAX 9223372036854775807LL
+#endif
+#if !defined(INT64_MIN)
+# define INT64_MIN ((-INT64_MAX)-1)
+#endif
+#if !defined(UINT64_MAX)
+# define UINT64_MAX 0xffffffffffffffffULL
+#endif
+
+#ifndef HUGE_VALF
+#define HUGE_VALF (float)HUGE_VAL
+#endif
+
+#endif /* LLVM_C_DATATYPES_H */

+ 1315 - 0
src/llvm-c/DebugInfo.h

@@ -0,0 +1,1315 @@
+//===------------ DebugInfo.h - LLVM C API Debug Info API -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file declares the C API endpoints for generating DWARF Debug Info
+///
+/// Note: This interface is experimental. It is *NOT* stable, and may be
+///       changed without warning.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_C_DEBUGINFO_H
+#define LLVM_C_DEBUGINFO_H
+
+#include "llvm-c/Core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Debug info flags.
+ */
+typedef enum {
+  LLVMDIFlagZero = 0,
+  LLVMDIFlagPrivate = 1,
+  LLVMDIFlagProtected = 2,
+  LLVMDIFlagPublic = 3,
+  LLVMDIFlagFwdDecl = 1 << 2,
+  LLVMDIFlagAppleBlock = 1 << 3,
+  LLVMDIFlagBlockByrefStruct = 1 << 4,
+  LLVMDIFlagVirtual = 1 << 5,
+  LLVMDIFlagArtificial = 1 << 6,
+  LLVMDIFlagExplicit = 1 << 7,
+  LLVMDIFlagPrototyped = 1 << 8,
+  LLVMDIFlagObjcClassComplete = 1 << 9,
+  LLVMDIFlagObjectPointer = 1 << 10,
+  LLVMDIFlagVector = 1 << 11,
+  LLVMDIFlagStaticMember = 1 << 12,
+  LLVMDIFlagLValueReference = 1 << 13,
+  LLVMDIFlagRValueReference = 1 << 14,
+  LLVMDIFlagReserved = 1 << 15,
+  LLVMDIFlagSingleInheritance = 1 << 16,
+  LLVMDIFlagMultipleInheritance = 2 << 16,
+  LLVMDIFlagVirtualInheritance = 3 << 16,
+  LLVMDIFlagIntroducedVirtual = 1 << 18,
+  LLVMDIFlagBitField = 1 << 19,
+  LLVMDIFlagNoReturn = 1 << 20,
+  LLVMDIFlagTypePassByValue = 1 << 22,
+  LLVMDIFlagTypePassByReference = 1 << 23,
+  LLVMDIFlagEnumClass = 1 << 24,
+  LLVMDIFlagFixedEnum = LLVMDIFlagEnumClass, // Deprecated.
+  LLVMDIFlagThunk = 1 << 25,
+  LLVMDIFlagNonTrivial = 1 << 26,
+  LLVMDIFlagBigEndian = 1 << 27,
+  LLVMDIFlagLittleEndian = 1 << 28,
+  LLVMDIFlagIndirectVirtualBase = (1 << 2) | (1 << 5),
+  LLVMDIFlagAccessibility = LLVMDIFlagPrivate | LLVMDIFlagProtected |
+                            LLVMDIFlagPublic,
+  LLVMDIFlagPtrToMemberRep = LLVMDIFlagSingleInheritance |
+                             LLVMDIFlagMultipleInheritance |
+                             LLVMDIFlagVirtualInheritance
+} LLVMDIFlags;
+
+/**
+ * Source languages known by DWARF.
+ */
+typedef enum {
+  LLVMDWARFSourceLanguageC89,
+  LLVMDWARFSourceLanguageC,
+  LLVMDWARFSourceLanguageAda83,
+  LLVMDWARFSourceLanguageC_plus_plus,
+  LLVMDWARFSourceLanguageCobol74,
+  LLVMDWARFSourceLanguageCobol85,
+  LLVMDWARFSourceLanguageFortran77,
+  LLVMDWARFSourceLanguageFortran90,
+  LLVMDWARFSourceLanguagePascal83,
+  LLVMDWARFSourceLanguageModula2,
+  // New in DWARF v3:
+  LLVMDWARFSourceLanguageJava,
+  LLVMDWARFSourceLanguageC99,
+  LLVMDWARFSourceLanguageAda95,
+  LLVMDWARFSourceLanguageFortran95,
+  LLVMDWARFSourceLanguagePLI,
+  LLVMDWARFSourceLanguageObjC,
+  LLVMDWARFSourceLanguageObjC_plus_plus,
+  LLVMDWARFSourceLanguageUPC,
+  LLVMDWARFSourceLanguageD,
+  // New in DWARF v4:
+  LLVMDWARFSourceLanguagePython,
+  // New in DWARF v5:
+  LLVMDWARFSourceLanguageOpenCL,
+  LLVMDWARFSourceLanguageGo,
+  LLVMDWARFSourceLanguageModula3,
+  LLVMDWARFSourceLanguageHaskell,
+  LLVMDWARFSourceLanguageC_plus_plus_03,
+  LLVMDWARFSourceLanguageC_plus_plus_11,
+  LLVMDWARFSourceLanguageOCaml,
+  LLVMDWARFSourceLanguageRust,
+  LLVMDWARFSourceLanguageC11,
+  LLVMDWARFSourceLanguageSwift,
+  LLVMDWARFSourceLanguageJulia,
+  LLVMDWARFSourceLanguageDylan,
+  LLVMDWARFSourceLanguageC_plus_plus_14,
+  LLVMDWARFSourceLanguageFortran03,
+  LLVMDWARFSourceLanguageFortran08,
+  LLVMDWARFSourceLanguageRenderScript,
+  LLVMDWARFSourceLanguageBLISS,
+  // Vendor extensions:
+  LLVMDWARFSourceLanguageMips_Assembler,
+  LLVMDWARFSourceLanguageGOOGLE_RenderScript,
+  LLVMDWARFSourceLanguageBORLAND_Delphi
+} LLVMDWARFSourceLanguage;
+
+/**
+ * The amount of debug information to emit.
+ */
+typedef enum {
+    LLVMDWARFEmissionNone = 0,
+    LLVMDWARFEmissionFull,
+    LLVMDWARFEmissionLineTablesOnly
+} LLVMDWARFEmissionKind;
+
+/**
+ * The kind of metadata nodes.
+ */
+enum {
+  LLVMMDStringMetadataKind,
+  LLVMConstantAsMetadataMetadataKind,
+  LLVMLocalAsMetadataMetadataKind,
+  LLVMDistinctMDOperandPlaceholderMetadataKind,
+  LLVMMDTupleMetadataKind,
+  LLVMDILocationMetadataKind,
+  LLVMDIExpressionMetadataKind,
+  LLVMDIGlobalVariableExpressionMetadataKind,
+  LLVMGenericDINodeMetadataKind,
+  LLVMDISubrangeMetadataKind,
+  LLVMDIEnumeratorMetadataKind,
+  LLVMDIBasicTypeMetadataKind,
+  LLVMDIDerivedTypeMetadataKind,
+  LLVMDICompositeTypeMetadataKind,
+  LLVMDISubroutineTypeMetadataKind,
+  LLVMDIFileMetadataKind,
+  LLVMDICompileUnitMetadataKind,
+  LLVMDISubprogramMetadataKind,
+  LLVMDILexicalBlockMetadataKind,
+  LLVMDILexicalBlockFileMetadataKind,
+  LLVMDINamespaceMetadataKind,
+  LLVMDIModuleMetadataKind,
+  LLVMDITemplateTypeParameterMetadataKind,
+  LLVMDITemplateValueParameterMetadataKind,
+  LLVMDIGlobalVariableMetadataKind,
+  LLVMDILocalVariableMetadataKind,
+  LLVMDILabelMetadataKind,
+  LLVMDIObjCPropertyMetadataKind,
+  LLVMDIImportedEntityMetadataKind,
+  LLVMDIMacroMetadataKind,
+  LLVMDIMacroFileMetadataKind,
+  LLVMDICommonBlockMetadataKind
+};
+typedef unsigned LLVMMetadataKind;
+
+/**
+ * An LLVM DWARF type encoding.
+ */
+typedef unsigned LLVMDWARFTypeEncoding;
+
+/**
+ * The current debug metadata version number.
+ */
+unsigned LLVMDebugMetadataVersion(void);
+
+/**
+ * The version of debug metadata that's present in the provided \c Module.
+ */
+unsigned LLVMGetModuleDebugMetadataVersion(LLVMModuleRef Module);
+
+/**
+ * Strip debug info in the module if it exists.
+ * To do this, we remove all calls to the debugger intrinsics and any named
+ * metadata for debugging. We also remove debug locations for instructions.
+ * Return true if module is modified.
+ */
+LLVMBool LLVMStripModuleDebugInfo(LLVMModuleRef Module);
+
+/**
+ * Construct a builder for a module, and do not allow for unresolved nodes
+ * attached to the module.
+ */
+LLVMDIBuilderRef LLVMCreateDIBuilderDisallowUnresolved(LLVMModuleRef M);
+
+/**
+ * Construct a builder for a module and collect unresolved nodes attached
+ * to the module in order to resolve cycles during a call to
+ * \c LLVMDIBuilderFinalize.
+ */
+LLVMDIBuilderRef LLVMCreateDIBuilder(LLVMModuleRef M);
+
+/**
+ * Deallocates the \c DIBuilder and everything it owns.
+ * @note You must call \c LLVMDIBuilderFinalize before this
+ */
+void LLVMDisposeDIBuilder(LLVMDIBuilderRef Builder);
+
+/**
+ * Construct any deferred debug info descriptors.
+ */
+void LLVMDIBuilderFinalize(LLVMDIBuilderRef Builder);
+
+/**
+ * A CompileUnit provides an anchor for all debugging
+ * information generated during this instance of compilation.
+ * \param Lang          Source programming language, eg.
+ *                      \c LLVMDWARFSourceLanguageC99
+ * \param FileRef       File info.
+ * \param Producer      Identify the producer of debugging information
+ *                      and code.  Usually this is a compiler
+ *                      version string.
+ * \param ProducerLen   The length of the C string passed to \c Producer.
+ * \param isOptimized   A boolean flag which indicates whether optimization
+ *                      is enabled or not.
+ * \param Flags         This string lists command line options. This
+ *                      string is directly embedded in debug info
+ *                      output which may be used by a tool
+ *                      analyzing generated debugging information.
+ * \param FlagsLen      The length of the C string passed to \c Flags.
+ * \param RuntimeVer    This indicates runtime version for languages like
+ *                      Objective-C.
+ * \param SplitName     The name of the file that we'll split debug info
+ *                      out into.
+ * \param SplitNameLen  The length of the C string passed to \c SplitName.
+ * \param Kind          The kind of debug information to generate.
+ * \param DWOId         The DWOId if this is a split skeleton compile unit.
+ * \param SplitDebugInlining    Whether to emit inline debug info.
+ * \param DebugInfoForProfiling Whether to emit extra debug info for
+ *                              profile collection.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateCompileUnit(
+    LLVMDIBuilderRef Builder, LLVMDWARFSourceLanguage Lang,
+    LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen,
+    LLVMBool isOptimized, const char *Flags, size_t FlagsLen,
+    unsigned RuntimeVer, const char *SplitName, size_t SplitNameLen,
+    LLVMDWARFEmissionKind Kind, unsigned DWOId, LLVMBool SplitDebugInlining,
+    LLVMBool DebugInfoForProfiling);
+
+/**
+ * Create a file descriptor to hold debugging information for a file.
+ * \param Builder      The \c DIBuilder.
+ * \param Filename     File name.
+ * \param FilenameLen  The length of the C string passed to \c Filename.
+ * \param Directory    Directory.
+ * \param DirectoryLen The length of the C string passed to \c Directory.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename,
+                        size_t FilenameLen, const char *Directory,
+                        size_t DirectoryLen);
+
+/**
+ * Creates a new descriptor for a module with the specified parent scope.
+ * \param Builder         The \c DIBuilder.
+ * \param ParentScope     The parent scope containing this module declaration.
+ * \param Name            Module name.
+ * \param NameLen         The length of the C string passed to \c Name.
+ * \param ConfigMacros    A space-separated shell-quoted list of -D macro
+                          definitions as they would appear on a command line.
+ * \param ConfigMacrosLen The length of the C string passed to \c ConfigMacros.
+ * \param IncludePath     The path to the module map file.
+ * \param IncludePathLen  The length of the C string passed to \c IncludePath.
+ * \param ISysRoot        The Clang system root (value of -isysroot).
+ * \param ISysRootLen     The length of the C string passed to \c ISysRoot.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateModule(LLVMDIBuilderRef Builder, LLVMMetadataRef ParentScope,
+                          const char *Name, size_t NameLen,
+                          const char *ConfigMacros, size_t ConfigMacrosLen,
+                          const char *IncludePath, size_t IncludePathLen,
+                          const char *ISysRoot, size_t ISysRootLen);
+
+/**
+ * Creates a new descriptor for a namespace with the specified parent scope.
+ * \param Builder          The \c DIBuilder.
+ * \param ParentScope      The parent scope containing this module declaration.
+ * \param Name             NameSpace name.
+ * \param NameLen          The length of the C string passed to \c Name.
+ * \param ExportSymbols    Whether or not the namespace exports symbols, e.g.
+ *                         this is true of C++ inline namespaces.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder,
+                             LLVMMetadataRef ParentScope,
+                             const char *Name, size_t NameLen,
+                             LLVMBool ExportSymbols);
+
+/**
+ * Create a new descriptor for the specified subprogram.
+ * \param Builder         The \c DIBuilder.
+ * \param Scope           Function scope.
+ * \param Name            Function name.
+ * \param NameLen         Length of enumeration name.
+ * \param LinkageName     Mangled function name.
+ * \param LinkageNameLen  Length of linkage name.
+ * \param File            File where this variable is defined.
+ * \param LineNo          Line number.
+ * \param Ty              Function type.
+ * \param IsLocalToUnit   True if this function is not externally visible.
+ * \param IsDefinition    True if this is a function definition.
+ * \param ScopeLine       Set to the beginning of the scope this starts
+ * \param Flags           E.g.: \c LLVMDIFlagLValueReference. These flags are
+ *                        used to emit dwarf attributes.
+ * \param IsOptimized     True if optimization is ON.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateFunction(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
+    LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
+    LLVMBool IsLocalToUnit, LLVMBool IsDefinition,
+    unsigned ScopeLine, LLVMDIFlags Flags, LLVMBool IsOptimized);
+
+/**
+ * Create a descriptor for a lexical block with the specified parent context.
+ * \param Builder      The \c DIBuilder.
+ * \param Scope        Parent lexical block.
+ * \param File         Source file.
+ * \param Line         The line in the source file.
+ * \param Column       The column in the source file.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateLexicalBlock(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope,
+    LLVMMetadataRef File, unsigned Line, unsigned Column);
+
+/**
+ * Create a descriptor for a lexical block with a new file attached.
+ * \param Builder        The \c DIBuilder.
+ * \param Scope          Lexical block.
+ * \param File           Source file.
+ * \param Discriminator  DWARF path discriminator value.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateLexicalBlockFile(LLVMDIBuilderRef Builder,
+                                    LLVMMetadataRef Scope,
+                                    LLVMMetadataRef File,
+                                    unsigned Discriminator);
+
+/**
+ * Create a descriptor for an imported namespace. Suitable for e.g. C++
+ * using declarations.
+ * \param Builder    The \c DIBuilder.
+ * \param Scope      The scope this module is imported into
+ * \param File       File where the declaration is located.
+ * \param Line       Line number of the declaration.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateImportedModuleFromNamespace(LLVMDIBuilderRef Builder,
+                                               LLVMMetadataRef Scope,
+                                               LLVMMetadataRef NS,
+                                               LLVMMetadataRef File,
+                                               unsigned Line);
+
+/**
+ * Create a descriptor for an imported module that aliases another
+ * imported entity descriptor.
+ * \param Builder        The \c DIBuilder.
+ * \param Scope          The scope this module is imported into
+ * \param ImportedEntity Previous imported entity to alias.
+ * \param File           File where the declaration is located.
+ * \param Line           Line number of the declaration.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateImportedModuleFromAlias(LLVMDIBuilderRef Builder,
+                                           LLVMMetadataRef Scope,
+                                           LLVMMetadataRef ImportedEntity,
+                                           LLVMMetadataRef File,
+                                           unsigned Line);
+
+/**
+ * Create a descriptor for an imported module.
+ * \param Builder    The \c DIBuilder.
+ * \param Scope      The scope this module is imported into
+ * \param M          The module being imported here
+ * \param File       File where the declaration is located.
+ * \param Line       Line number of the declaration.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateImportedModuleFromModule(LLVMDIBuilderRef Builder,
+                                            LLVMMetadataRef Scope,
+                                            LLVMMetadataRef M,
+                                            LLVMMetadataRef File,
+                                            unsigned Line);
+
+/**
+ * Create a descriptor for an imported function, type, or variable.  Suitable
+ * for e.g. FORTRAN-style USE declarations.
+ * \param Builder    The DIBuilder.
+ * \param Scope      The scope this module is imported into.
+ * \param Decl       The declaration (or definition) of a function, type,
+                     or variable.
+ * \param File       File where the declaration is located.
+ * \param Line       Line number of the declaration.
+ * \param Name       A name that uniquely identifies this imported declaration.
+ * \param NameLen    The length of the C string passed to \c Name.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateImportedDeclaration(LLVMDIBuilderRef Builder,
+                                       LLVMMetadataRef Scope,
+                                       LLVMMetadataRef Decl,
+                                       LLVMMetadataRef File,
+                                       unsigned Line,
+                                       const char *Name, size_t NameLen);
+
+/**
+ * Creates a new DebugLocation that describes a source location.
+ * \param Line The line in the source file.
+ * \param Column The column in the source file.
+ * \param Scope The scope in which the location resides.
+ * \param InlinedAt The scope where this location was inlined, if at all.
+ *                  (optional).
+ * \note If the item to which this location is attached cannot be
+ *       attributed to a source line, pass 0 for the line and column.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateDebugLocation(LLVMContextRef Ctx, unsigned Line,
+                                 unsigned Column, LLVMMetadataRef Scope,
+                                 LLVMMetadataRef InlinedAt);
+
+/**
+ * Get the line number of this debug location.
+ * \param Location     The debug location.
+ *
+ * @see DILocation::getLine()
+ */
+unsigned LLVMDILocationGetLine(LLVMMetadataRef Location);
+
+/**
+ * Get the column number of this debug location.
+ * \param Location     The debug location.
+ *
+ * @see DILocation::getColumn()
+ */
+unsigned LLVMDILocationGetColumn(LLVMMetadataRef Location);
+
+/**
+ * Get the local scope associated with this debug location.
+ * \param Location     The debug location.
+ *
+ * @see DILocation::getScope()
+ */
+LLVMMetadataRef LLVMDILocationGetScope(LLVMMetadataRef Location);
+
+/**
+ * Get the "inline at" location associated with this debug location.
+ * \param Location     The debug location.
+ *
+ * @see DILocation::getInlinedAt()
+ */
+LLVMMetadataRef LLVMDILocationGetInlinedAt(LLVMMetadataRef Location);
+
+/**
+ * Get the metadata of the file associated with a given scope.
+ * \param Scope     The scope object.
+ *
+ * @see DIScope::getFile()
+ */
+LLVMMetadataRef LLVMDIScopeGetFile(LLVMMetadataRef Scope);
+
+/**
+ * Get the directory of a given file.
+ * \param File     The file object.
+ * \param Len      The length of the returned string.
+ *
+ * @see DIFile::getDirectory()
+ */
+const char *LLVMDIFileGetDirectory(LLVMMetadataRef File, unsigned *Len);
+
+/**
+ * Get the name of a given file.
+ * \param File     The file object.
+ * \param Len      The length of the returned string.
+ *
+ * @see DIFile::getFilename()
+ */
+const char *LLVMDIFileGetFilename(LLVMMetadataRef File, unsigned *Len);
+
+/**
+ * Get the source of a given file.
+ * \param File     The file object.
+ * \param Len      The length of the returned string.
+ *
+ * @see DIFile::getSource()
+ */
+const char *LLVMDIFileGetSource(LLVMMetadataRef File, unsigned *Len);
+
+/**
+ * Create a type array.
+ * \param Builder        The DIBuilder.
+ * \param Data           The type elements.
+ * \param NumElements    Number of type elements.
+ */
+LLVMMetadataRef LLVMDIBuilderGetOrCreateTypeArray(LLVMDIBuilderRef Builder,
+                                                  LLVMMetadataRef *Data,
+                                                  size_t NumElements);
+
+/**
+ * Create subroutine type.
+ * \param Builder        The DIBuilder.
+ * \param File            The file in which the subroutine resides.
+ * \param ParameterTypes  An array of subroutine parameter types. This
+ *                        includes return type at 0th index.
+ * \param NumParameterTypes The number of parameter types in \c ParameterTypes
+ * \param Flags           E.g.: \c LLVMDIFlagLValueReference.
+ *                        These flags are used to emit dwarf attributes.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder,
+                                  LLVMMetadataRef File,
+                                  LLVMMetadataRef *ParameterTypes,
+                                  unsigned NumParameterTypes,
+                                  LLVMDIFlags Flags);
+
+/**
+ * Create debugging information entry for an enumerator.
+ * @param Builder        The DIBuilder.
+ * @param Name           Enumerator name.
+ * @param NameLen        Length of enumerator name.
+ * @param Value          Enumerator value.
+ * @param IsUnsigned     True if the value is unsigned.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder,
+                                              const char *Name, size_t NameLen,
+                                              int64_t Value,
+                                              LLVMBool IsUnsigned);
+
+/**
+ * Create debugging information entry for an enumeration.
+ * \param Builder        The DIBuilder.
+ * \param Scope          Scope in which this enumeration is defined.
+ * \param Name           Enumeration name.
+ * \param NameLen        Length of enumeration name.
+ * \param File           File where this member is defined.
+ * \param LineNumber     Line number.
+ * \param SizeInBits     Member size.
+ * \param AlignInBits    Member alignment.
+ * \param Elements       Enumeration elements.
+ * \param NumElements    Number of enumeration elements.
+ * \param ClassTy        Underlying type of a C++11/ObjC fixed enum.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateEnumerationType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef *Elements,
+    unsigned NumElements, LLVMMetadataRef ClassTy);
+
+/**
+ * Create debugging information entry for a union.
+ * \param Builder      The DIBuilder.
+ * \param Scope        Scope in which this union is defined.
+ * \param Name         Union name.
+ * \param NameLen      Length of union name.
+ * \param File         File where this member is defined.
+ * \param LineNumber   Line number.
+ * \param SizeInBits   Member size.
+ * \param AlignInBits  Member alignment.
+ * \param Flags        Flags to encode member attribute, e.g. private
+ * \param Elements     Union elements.
+ * \param NumElements  Number of union elements.
+ * \param RunTimeLang  Optional parameter, Objective-C runtime version.
+ * \param UniqueId     A unique identifier for the union.
+ * \param UniqueIdLen  Length of unique identifier.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateUnionType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags,
+    LLVMMetadataRef *Elements, unsigned NumElements, unsigned RunTimeLang,
+    const char *UniqueId, size_t UniqueIdLen);
+
+
+/**
+ * Create debugging information entry for an array.
+ * \param Builder      The DIBuilder.
+ * \param Size         Array size.
+ * \param AlignInBits  Alignment.
+ * \param Ty           Element type.
+ * \param Subscripts   Subscripts.
+ * \param NumSubscripts Number of subscripts.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size,
+                             uint32_t AlignInBits, LLVMMetadataRef Ty,
+                             LLVMMetadataRef *Subscripts,
+                             unsigned NumSubscripts);
+
+/**
+ * Create debugging information entry for a vector type.
+ * \param Builder      The DIBuilder.
+ * \param Size         Vector size.
+ * \param AlignInBits  Alignment.
+ * \param Ty           Element type.
+ * \param Subscripts   Subscripts.
+ * \param NumSubscripts Number of subscripts.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateVectorType(LLVMDIBuilderRef Builder, uint64_t Size,
+                              uint32_t AlignInBits, LLVMMetadataRef Ty,
+                              LLVMMetadataRef *Subscripts,
+                              unsigned NumSubscripts);
+
+/**
+ * Create a DWARF unspecified type.
+ * \param Builder   The DIBuilder.
+ * \param Name      The unspecified type's name.
+ * \param NameLen   Length of type name.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateUnspecifiedType(LLVMDIBuilderRef Builder, const char *Name,
+                                   size_t NameLen);
+
+/**
+ * Create debugging information entry for a basic
+ * type.
+ * \param Builder     The DIBuilder.
+ * \param Name        Type name.
+ * \param NameLen     Length of type name.
+ * \param SizeInBits  Size of the type.
+ * \param Encoding    DWARF encoding code, e.g. \c LLVMDWARFTypeEncoding_float.
+ * \param Flags       Flags to encode optional attribute like endianity
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name,
+                             size_t NameLen, uint64_t SizeInBits,
+                             LLVMDWARFTypeEncoding Encoding,
+                             LLVMDIFlags Flags);
+
+/**
+ * Create debugging information entry for a pointer.
+ * \param Builder     The DIBuilder.
+ * \param PointeeTy         Type pointed by this pointer.
+ * \param SizeInBits        Size.
+ * \param AlignInBits       Alignment. (optional, pass 0 to ignore)
+ * \param AddressSpace      DWARF address space. (optional, pass 0 to ignore)
+ * \param Name              Pointer type name. (optional)
+ * \param NameLen           Length of pointer type name. (optional)
+ */
+LLVMMetadataRef LLVMDIBuilderCreatePointerType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy,
+    uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace,
+    const char *Name, size_t NameLen);
+
+/**
+ * Create debugging information entry for a struct.
+ * \param Builder     The DIBuilder.
+ * \param Scope        Scope in which this struct is defined.
+ * \param Name         Struct name.
+ * \param NameLen      Struct name length.
+ * \param File         File where this member is defined.
+ * \param LineNumber   Line number.
+ * \param SizeInBits   Member size.
+ * \param AlignInBits  Member alignment.
+ * \param Flags        Flags to encode member attribute, e.g. private
+ * \param Elements     Struct elements.
+ * \param NumElements  Number of struct elements.
+ * \param RunTimeLang  Optional parameter, Objective-C runtime version.
+ * \param VTableHolder The object containing the vtable for the struct.
+ * \param UniqueId     A unique identifier for the struct.
+ * \param UniqueIdLen  Length of the unique identifier for the struct.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateStructType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
+    uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags,
+    LLVMMetadataRef DerivedFrom, LLVMMetadataRef *Elements,
+    unsigned NumElements, unsigned RunTimeLang, LLVMMetadataRef VTableHolder,
+    const char *UniqueId, size_t UniqueIdLen);
+
+/**
+ * Create debugging information entry for a member.
+ * \param Builder      The DIBuilder.
+ * \param Scope        Member scope.
+ * \param Name         Member name.
+ * \param NameLen      Length of member name.
+ * \param File         File where this member is defined.
+ * \param LineNo       Line number.
+ * \param SizeInBits   Member size.
+ * \param AlignInBits  Member alignment.
+ * \param OffsetInBits Member offset.
+ * \param Flags        Flags to encode member attribute, e.g. private
+ * \param Ty           Parent type.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateMemberType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNo,
+    uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits,
+    LLVMDIFlags Flags, LLVMMetadataRef Ty);
+
+/**
+ * Create debugging information entry for a
+ * C++ static data member.
+ * \param Builder      The DIBuilder.
+ * \param Scope        Member scope.
+ * \param Name         Member name.
+ * \param NameLen      Length of member name.
+ * \param File         File where this member is declared.
+ * \param LineNumber   Line number.
+ * \param Type         Type of the static member.
+ * \param Flags        Flags to encode member attribute, e.g. private.
+ * \param ConstantVal  Const initializer of the member.
+ * \param AlignInBits  Member alignment.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateStaticMemberType(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNumber,
+    LLVMMetadataRef Type, LLVMDIFlags Flags, LLVMValueRef ConstantVal,
+    uint32_t AlignInBits);
+
+/**
+ * Create debugging information entry for a pointer to member.
+ * \param Builder      The DIBuilder.
+ * \param PointeeType  Type pointed to by this pointer.
+ * \param ClassType    Type for which this pointer points to members of.
+ * \param SizeInBits   Size.
+ * \param AlignInBits  Alignment.
+ * \param Flags        Flags.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateMemberPointerType(LLVMDIBuilderRef Builder,
+                                     LLVMMetadataRef PointeeType,
+                                     LLVMMetadataRef ClassType,
+                                     uint64_t SizeInBits,
+                                     uint32_t AlignInBits,
+                                     LLVMDIFlags Flags);
+/**
+ * Create debugging information entry for Objective-C instance variable.
+ * \param Builder      The DIBuilder.
+ * \param Name         Member name.
+ * \param NameLen      The length of the C string passed to \c Name.
+ * \param File         File where this member is defined.
+ * \param LineNo       Line number.
+ * \param SizeInBits   Member size.
+ * \param AlignInBits  Member alignment.
+ * \param OffsetInBits Member offset.
+ * \param Flags        Flags to encode member attribute, e.g. private
+ * \param Ty           Parent type.
+ * \param PropertyNode Property associated with this ivar.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateObjCIVar(LLVMDIBuilderRef Builder,
+                            const char *Name, size_t NameLen,
+                            LLVMMetadataRef File, unsigned LineNo,
+                            uint64_t SizeInBits, uint32_t AlignInBits,
+                            uint64_t OffsetInBits, LLVMDIFlags Flags,
+                            LLVMMetadataRef Ty, LLVMMetadataRef PropertyNode);
+
+/**
+ * Create debugging information entry for Objective-C property.
+ * \param Builder            The DIBuilder.
+ * \param Name               Property name.
+ * \param NameLen            The length of the C string passed to \c Name.
+ * \param File               File where this property is defined.
+ * \param LineNo             Line number.
+ * \param GetterName         Name of the Objective C property getter selector.
+ * \param GetterNameLen      The length of the C string passed to \c GetterName.
+ * \param SetterName         Name of the Objective C property setter selector.
+ * \param SetterNameLen      The length of the C string passed to \c SetterName.
+ * \param PropertyAttributes Objective C property attributes.
+ * \param Ty                 Type.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateObjCProperty(LLVMDIBuilderRef Builder,
+                                const char *Name, size_t NameLen,
+                                LLVMMetadataRef File, unsigned LineNo,
+                                const char *GetterName, size_t GetterNameLen,
+                                const char *SetterName, size_t SetterNameLen,
+                                unsigned PropertyAttributes,
+                                LLVMMetadataRef Ty);
+
+/**
+ * Create a uniqued DIType* clone with FlagObjectPointer and FlagArtificial set.
+ * \param Builder   The DIBuilder.
+ * \param Type      The underlying type to which this pointer points.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateObjectPointerType(LLVMDIBuilderRef Builder,
+                                     LLVMMetadataRef Type);
+
+/**
+ * Create debugging information entry for a qualified
+ * type, e.g. 'const int'.
+ * \param Builder     The DIBuilder.
+ * \param Tag         Tag identifying type,
+ *                    e.g. LLVMDWARFTypeQualifier_volatile_type
+ * \param Type        Base Type.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag,
+                                 LLVMMetadataRef Type);
+
+/**
+ * Create debugging information entry for a c++
+ * style reference or rvalue reference type.
+ * \param Builder   The DIBuilder.
+ * \param Tag       Tag identifying type,
+ * \param Type      Base Type.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateReferenceType(LLVMDIBuilderRef Builder, unsigned Tag,
+                                 LLVMMetadataRef Type);
+
+/**
+ * Create C++11 nullptr type.
+ * \param Builder   The DIBuilder.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateNullPtrType(LLVMDIBuilderRef Builder);
+
+/**
+ * Create debugging information entry for a typedef.
+ * \param Builder    The DIBuilder.
+ * \param Type       Original type.
+ * \param Name       Typedef name.
+ * \param File       File where this type is defined.
+ * \param LineNo     Line number.
+ * \param Scope      The surrounding context for the typedef.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type,
+                           const char *Name, size_t NameLen,
+                           LLVMMetadataRef File, unsigned LineNo,
+                           LLVMMetadataRef Scope);
+
+/**
+ * Create debugging information entry to establish inheritance relationship
+ * between two types.
+ * \param Builder       The DIBuilder.
+ * \param Ty            Original type.
+ * \param BaseTy        Base type. Ty is inherits from base.
+ * \param BaseOffset    Base offset.
+ * \param VBPtrOffset  Virtual base pointer offset.
+ * \param Flags         Flags to describe inheritance attribute, e.g. private
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateInheritance(LLVMDIBuilderRef Builder,
+                               LLVMMetadataRef Ty, LLVMMetadataRef BaseTy,
+                               uint64_t BaseOffset, uint32_t VBPtrOffset,
+                               LLVMDIFlags Flags);
+
+/**
+ * Create a permanent forward-declared type.
+ * \param Builder             The DIBuilder.
+ * \param Tag                 A unique tag for this type.
+ * \param Name                Type name.
+ * \param NameLen             Length of type name.
+ * \param Scope               Type scope.
+ * \param File                File where this type is defined.
+ * \param Line                Line number where this type is defined.
+ * \param RuntimeLang         Indicates runtime version for languages like
+ *                            Objective-C.
+ * \param SizeInBits          Member size.
+ * \param AlignInBits         Member alignment.
+ * \param UniqueIdentifier    A unique identifier for the type.
+ * \param UniqueIdentifierLen Length of the unique identifier.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateForwardDecl(
+    LLVMDIBuilderRef Builder, unsigned Tag, const char *Name,
+    size_t NameLen, LLVMMetadataRef Scope, LLVMMetadataRef File, unsigned Line,
+    unsigned RuntimeLang, uint64_t SizeInBits, uint32_t AlignInBits,
+    const char *UniqueIdentifier, size_t UniqueIdentifierLen);
+
+/**
+ * Create a temporary forward-declared type.
+ * \param Builder             The DIBuilder.
+ * \param Tag                 A unique tag for this type.
+ * \param Name                Type name.
+ * \param NameLen             Length of type name.
+ * \param Scope               Type scope.
+ * \param File                File where this type is defined.
+ * \param Line                Line number where this type is defined.
+ * \param RuntimeLang         Indicates runtime version for languages like
+ *                            Objective-C.
+ * \param SizeInBits          Member size.
+ * \param AlignInBits         Member alignment.
+ * \param Flags               Flags.
+ * \param UniqueIdentifier    A unique identifier for the type.
+ * \param UniqueIdentifierLen Length of the unique identifier.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateReplaceableCompositeType(
+    LLVMDIBuilderRef Builder, unsigned Tag, const char *Name,
+    size_t NameLen, LLVMMetadataRef Scope, LLVMMetadataRef File, unsigned Line,
+    unsigned RuntimeLang, uint64_t SizeInBits, uint32_t AlignInBits,
+    LLVMDIFlags Flags, const char *UniqueIdentifier,
+    size_t UniqueIdentifierLen);
+
+/**
+ * Create debugging information entry for a bit field member.
+ * \param Builder             The DIBuilder.
+ * \param Scope               Member scope.
+ * \param Name                Member name.
+ * \param NameLen             Length of member name.
+ * \param File                File where this member is defined.
+ * \param LineNumber          Line number.
+ * \param SizeInBits          Member size.
+ * \param OffsetInBits        Member offset.
+ * \param StorageOffsetInBits Member storage offset.
+ * \param Flags               Flags to encode member attribute.
+ * \param Type                Parent type.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateBitFieldMemberType(LLVMDIBuilderRef Builder,
+                                      LLVMMetadataRef Scope,
+                                      const char *Name, size_t NameLen,
+                                      LLVMMetadataRef File, unsigned LineNumber,
+                                      uint64_t SizeInBits,
+                                      uint64_t OffsetInBits,
+                                      uint64_t StorageOffsetInBits,
+                                      LLVMDIFlags Flags, LLVMMetadataRef Type);
+
+/**
+ * Create debugging information entry for a class.
+ * \param Scope               Scope in which this class is defined.
+ * \param Name                Class name.
+ * \param NameLen             The length of the C string passed to \c Name.
+ * \param File                File where this member is defined.
+ * \param LineNumber          Line number.
+ * \param SizeInBits          Member size.
+ * \param AlignInBits         Member alignment.
+ * \param OffsetInBits        Member offset.
+ * \param Flags               Flags to encode member attribute, e.g. private.
+ * \param DerivedFrom         Debug info of the base class of this type.
+ * \param Elements            Class members.
+ * \param NumElements         Number of class elements.
+ * \param VTableHolder        Debug info of the base class that contains vtable
+ *                            for this type. This is used in
+ *                            DW_AT_containing_type. See DWARF documentation
+ *                            for more info.
+ * \param TemplateParamsNode  Template type parameters.
+ * \param UniqueIdentifier    A unique identifier for the type.
+ * \param UniqueIdentifierLen Length of the unique identifier.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateClassType(LLVMDIBuilderRef Builder,
+    LLVMMetadataRef Scope, const char *Name, size_t NameLen,
+    LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits,
+    uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags,
+    LLVMMetadataRef DerivedFrom,
+    LLVMMetadataRef *Elements, unsigned NumElements,
+    LLVMMetadataRef VTableHolder, LLVMMetadataRef TemplateParamsNode,
+    const char *UniqueIdentifier, size_t UniqueIdentifierLen);
+
+/**
+ * Create a uniqued DIType* clone with FlagArtificial set.
+ * \param Builder     The DIBuilder.
+ * \param Type        The underlying type.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateArtificialType(LLVMDIBuilderRef Builder,
+                                  LLVMMetadataRef Type);
+
+/**
+ * Get the name of this DIType.
+ * \param DType     The DIType.
+ * \param Length    The length of the returned string.
+ *
+ * @see DIType::getName()
+ */
+const char *LLVMDITypeGetName(LLVMMetadataRef DType, size_t *Length);
+
+/**
+ * Get the size of this DIType in bits.
+ * \param DType     The DIType.
+ *
+ * @see DIType::getSizeInBits()
+ */
+uint64_t LLVMDITypeGetSizeInBits(LLVMMetadataRef DType);
+
+/**
+ * Get the offset of this DIType in bits.
+ * \param DType     The DIType.
+ *
+ * @see DIType::getOffsetInBits()
+ */
+uint64_t LLVMDITypeGetOffsetInBits(LLVMMetadataRef DType);
+
+/**
+ * Get the alignment of this DIType in bits.
+ * \param DType     The DIType.
+ *
+ * @see DIType::getAlignInBits()
+ */
+uint32_t LLVMDITypeGetAlignInBits(LLVMMetadataRef DType);
+
+/**
+ * Get the source line where this DIType is declared.
+ * \param DType     The DIType.
+ *
+ * @see DIType::getLine()
+ */
+unsigned LLVMDITypeGetLine(LLVMMetadataRef DType);
+
+/**
+ * Get the flags associated with this DIType.
+ * \param DType     The DIType.
+ *
+ * @see DIType::getFlags()
+ */
+LLVMDIFlags LLVMDITypeGetFlags(LLVMMetadataRef DType);
+
+/**
+ * Create a descriptor for a value range.
+ * \param Builder    The DIBuilder.
+ * \param LowerBound Lower bound of the subrange, e.g. 0 for C, 1 for Fortran.
+ * \param Count      Count of elements in the subrange.
+ */
+LLVMMetadataRef LLVMDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder,
+                                                 int64_t LowerBound,
+                                                 int64_t Count);
+
+/**
+ * Create an array of DI Nodes.
+ * \param Builder        The DIBuilder.
+ * \param Data           The DI Node elements.
+ * \param NumElements    Number of DI Node elements.
+ */
+LLVMMetadataRef LLVMDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder,
+                                              LLVMMetadataRef *Data,
+                                              size_t NumElements);
+
+/**
+ * Create a new descriptor for the specified variable which has a complex
+ * address expression for its address.
+ * \param Builder     The DIBuilder.
+ * \param Addr        An array of complex address operations.
+ * \param Length      Length of the address operation array.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateExpression(LLVMDIBuilderRef Builder,
+                                              int64_t *Addr, size_t Length);
+
+/**
+ * Create a new descriptor for the specified variable that does not have an
+ * address, but does have a constant value.
+ * \param Builder     The DIBuilder.
+ * \param Value       The constant value.
+ */
+LLVMMetadataRef
+LLVMDIBuilderCreateConstantValueExpression(LLVMDIBuilderRef Builder,
+                                           int64_t Value);
+
+/**
+ * Create a new descriptor for the specified variable.
+ * \param Scope       Variable scope.
+ * \param Name        Name of the variable.
+ * \param NameLen     The length of the C string passed to \c Name.
+ * \param Linkage     Mangled  name of the variable.
+ * \param LinkLen     The length of the C string passed to \c Linkage.
+ * \param File        File where this variable is defined.
+ * \param LineNo      Line number.
+ * \param Ty          Variable Type.
+ * \param LocalToUnit Boolean flag indicate whether this variable is
+ *                    externally visible or not.
+ * \param Expr        The location of the global relative to the attached
+ *                    GlobalVariable.
+ * \param Decl        Reference to the corresponding declaration.
+ *                    variables.
+ * \param AlignInBits Variable alignment(or 0 if no alignment attr was
+ *                    specified)
+ */
+LLVMMetadataRef LLVMDIBuilderCreateGlobalVariableExpression(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, const char *Linkage, size_t LinkLen, LLVMMetadataRef File,
+    unsigned LineNo, LLVMMetadataRef Ty, LLVMBool LocalToUnit,
+    LLVMMetadataRef Expr, LLVMMetadataRef Decl, uint32_t AlignInBits);
+
+/**
+ * Retrieves the \c DIVariable associated with this global variable expression.
+ * \param GVE    The global variable expression.
+ *
+ * @see llvm::DIGlobalVariableExpression::getVariable()
+ */
+LLVMMetadataRef LLVMDIGlobalVariableExpressionGetVariable(LLVMMetadataRef GVE);
+
+/**
+ * Retrieves the \c DIExpression associated with this global variable expression.
+ * \param GVE    The global variable expression.
+ *
+ * @see llvm::DIGlobalVariableExpression::getExpression()
+ */
+LLVMMetadataRef LLVMDIGlobalVariableExpressionGetExpression(
+    LLVMMetadataRef GVE);
+
+/**
+ * Get the metadata of the file associated with a given variable.
+ * \param Var     The variable object.
+ *
+ * @see DIVariable::getFile()
+ */
+LLVMMetadataRef LLVMDIVariableGetFile(LLVMMetadataRef Var);
+
+/**
+ * Get the metadata of the scope associated with a given variable.
+ * \param Var     The variable object.
+ *
+ * @see DIVariable::getScope()
+ */
+LLVMMetadataRef LLVMDIVariableGetScope(LLVMMetadataRef Var);
+
+/**
+ * Get the source line where this \c DIVariable is declared.
+ * \param Var     The DIVariable.
+ *
+ * @see DIVariable::getLine()
+ */
+unsigned LLVMDIVariableGetLine(LLVMMetadataRef Var);
+
+/**
+ * Create a new temporary \c MDNode.  Suitable for use in constructing cyclic
+ * \c MDNode structures. A temporary \c MDNode is not uniqued, may be RAUW'd,
+ * and must be manually deleted with \c LLVMDisposeTemporaryMDNode.
+ * \param Ctx            The context in which to construct the temporary node.
+ * \param Data           The metadata elements.
+ * \param NumElements    Number of metadata elements.
+ */
+LLVMMetadataRef LLVMTemporaryMDNode(LLVMContextRef Ctx, LLVMMetadataRef *Data,
+                                    size_t NumElements);
+
+/**
+ * Deallocate a temporary node.
+ *
+ * Calls \c replaceAllUsesWith(nullptr) before deleting, so any remaining
+ * references will be reset.
+ * \param TempNode    The temporary metadata node.
+ */
+void LLVMDisposeTemporaryMDNode(LLVMMetadataRef TempNode);
+
+/**
+ * Replace all uses of temporary metadata.
+ * \param TempTargetMetadata    The temporary metadata node.
+ * \param Replacement           The replacement metadata node.
+ */
+void LLVMMetadataReplaceAllUsesWith(LLVMMetadataRef TempTargetMetadata,
+                                    LLVMMetadataRef Replacement);
+
+/**
+ * Create a new descriptor for the specified global variable that is temporary
+ * and meant to be RAUWed.
+ * \param Scope       Variable scope.
+ * \param Name        Name of the variable.
+ * \param NameLen     The length of the C string passed to \c Name.
+ * \param Linkage     Mangled  name of the variable.
+ * \param LnkLen      The length of the C string passed to \c Linkage.
+ * \param File        File where this variable is defined.
+ * \param LineNo      Line number.
+ * \param Ty          Variable Type.
+ * \param LocalToUnit Boolean flag indicate whether this variable is
+ *                    externally visible or not.
+ * \param Decl        Reference to the corresponding declaration.
+ * \param AlignInBits Variable alignment(or 0 if no alignment attr was
+ *                    specified)
+ */
+LLVMMetadataRef LLVMDIBuilderCreateTempGlobalVariableFwdDecl(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, const char *Linkage, size_t LnkLen, LLVMMetadataRef File,
+    unsigned LineNo, LLVMMetadataRef Ty, LLVMBool LocalToUnit,
+    LLVMMetadataRef Decl, uint32_t AlignInBits);
+
+/**
+ * Insert a new llvm.dbg.declare intrinsic call before the given instruction.
+ * \param Builder     The DIBuilder.
+ * \param Storage     The storage of the variable to declare.
+ * \param VarInfo     The variable's debug info descriptor.
+ * \param Expr        A complex location expression for the variable.
+ * \param DebugLoc    Debug info location.
+ * \param Instr       Instruction acting as a location for the new intrinsic.
+ */
+LLVMValueRef LLVMDIBuilderInsertDeclareBefore(
+  LLVMDIBuilderRef Builder, LLVMValueRef Storage, LLVMMetadataRef VarInfo,
+  LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMValueRef Instr);
+
+/**
+ * Insert a new llvm.dbg.declare intrinsic call at the end of the given basic
+ * block. If the basic block has a terminator instruction, the intrinsic is
+ * inserted before that terminator instruction.
+ * \param Builder     The DIBuilder.
+ * \param Storage     The storage of the variable to declare.
+ * \param VarInfo     The variable's debug info descriptor.
+ * \param Expr        A complex location expression for the variable.
+ * \param DebugLoc    Debug info location.
+ * \param Block       Basic block acting as a location for the new intrinsic.
+ */
+LLVMValueRef LLVMDIBuilderInsertDeclareAtEnd(
+    LLVMDIBuilderRef Builder, LLVMValueRef Storage, LLVMMetadataRef VarInfo,
+    LLVMMetadataRef Expr, LLVMMetadataRef DebugLoc, LLVMBasicBlockRef Block);
+
+/**
+ * Insert a new llvm.dbg.value intrinsic call before the given instruction.
+ * \param Builder     The DIBuilder.
+ * \param Val         The value of the variable.
+ * \param VarInfo     The variable's debug info descriptor.
+ * \param Expr        A complex location expression for the variable.
+ * \param DebugLoc    Debug info location.
+ * \param Instr       Instruction acting as a location for the new intrinsic.
+ */
+LLVMValueRef LLVMDIBuilderInsertDbgValueBefore(LLVMDIBuilderRef Builder,
+                                               LLVMValueRef Val,
+                                               LLVMMetadataRef VarInfo,
+                                               LLVMMetadataRef Expr,
+                                               LLVMMetadataRef DebugLoc,
+                                               LLVMValueRef Instr);
+
+/**
+ * Insert a new llvm.dbg.value intrinsic call at the end of the given basic
+ * block. If the basic block has a terminator instruction, the intrinsic is
+ * inserted before that terminator instruction.
+ * \param Builder     The DIBuilder.
+ * \param Val         The value of the variable.
+ * \param VarInfo     The variable's debug info descriptor.
+ * \param Expr        A complex location expression for the variable.
+ * \param DebugLoc    Debug info location.
+ * \param Block       Basic block acting as a location for the new intrinsic.
+ */
+LLVMValueRef LLVMDIBuilderInsertDbgValueAtEnd(LLVMDIBuilderRef Builder,
+                                              LLVMValueRef Val,
+                                              LLVMMetadataRef VarInfo,
+                                              LLVMMetadataRef Expr,
+                                              LLVMMetadataRef DebugLoc,
+                                              LLVMBasicBlockRef Block);
+
+/**
+ * Create a new descriptor for a local auto variable.
+ * \param Builder         The DIBuilder.
+ * \param Scope           The local scope the variable is declared in.
+ * \param Name            Variable name.
+ * \param NameLen         Length of variable name.
+ * \param File            File where this variable is defined.
+ * \param LineNo          Line number.
+ * \param Ty              Metadata describing the type of the variable.
+ * \param AlwaysPreserve  If true, this descriptor will survive optimizations.
+ * \param Flags           Flags.
+ * \param AlignInBits     Variable alignment.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateAutoVariable(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty,
+    LLVMBool AlwaysPreserve, LLVMDIFlags Flags, uint32_t AlignInBits);
+
+/**
+ * Create a new descriptor for a function parameter variable.
+ * \param Builder         The DIBuilder.
+ * \param Scope           The local scope the variable is declared in.
+ * \param Name            Variable name.
+ * \param NameLen         Length of variable name.
+ * \param ArgNo           Unique argument number for this variable; starts at 1.
+ * \param File            File where this variable is defined.
+ * \param LineNo          Line number.
+ * \param Ty              Metadata describing the type of the variable.
+ * \param AlwaysPreserve  If true, this descriptor will survive optimizations.
+ * \param Flags           Flags.
+ */
+LLVMMetadataRef LLVMDIBuilderCreateParameterVariable(
+    LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name,
+    size_t NameLen, unsigned ArgNo, LLVMMetadataRef File, unsigned LineNo,
+    LLVMMetadataRef Ty, LLVMBool AlwaysPreserve, LLVMDIFlags Flags);
+
+/**
+ * Get the metadata of the subprogram attached to a function.
+ *
+ * @see llvm::Function::getSubprogram()
+ */
+LLVMMetadataRef LLVMGetSubprogram(LLVMValueRef Func);
+
+/**
+ * Set the subprogram attached to a function.
+ *
+ * @see llvm::Function::setSubprogram()
+ */
+void LLVMSetSubprogram(LLVMValueRef Func, LLVMMetadataRef SP);
+
+/**
+ * Get the line associated with a given subprogram.
+ * \param Subprogram     The subprogram object.
+ *
+ * @see DISubprogram::getLine()
+ */
+unsigned LLVMDISubprogramGetLine(LLVMMetadataRef Subprogram);
+
+/**
+ * Get the debug location for the given instruction.
+ *
+ * @see llvm::Instruction::getDebugLoc()
+ */
+LLVMMetadataRef LLVMInstructionGetDebugLoc(LLVMValueRef Inst);
+
+/**
+ * Set the debug location for the given instruction.
+ *
+ * To clear the location metadata of the given instruction, pass NULL to \p Loc.
+ *
+ * @see llvm::Instruction::setDebugLoc()
+ */
+void LLVMInstructionSetDebugLoc(LLVMValueRef Inst, LLVMMetadataRef Loc);
+
+/**
+ * Obtain the enumerated type of a Metadata instance.
+ *
+ * @see llvm::Metadata::getMetadataID()
+ */
+LLVMMetadataKind LLVMGetMetadataKind(LLVMMetadataRef Metadata);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif

+ 113 - 0
src/llvm-c/Disassembler.h

@@ -0,0 +1,113 @@
+/*===-- llvm-c/Disassembler.h - Disassembler Public C Interface ---*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides a public interface to a disassembler library.         *|
+|* LLVM provides an implementation of this interface.                         *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_DISASSEMBLER_H
+#define LLVM_C_DISASSEMBLER_H
+
+#include "llvm-c/DisassemblerTypes.h"
+
+/**
+ * @defgroup LLVMCDisassembler Disassembler
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* !defined(__cplusplus) */
+
+/**
+ * Create a disassembler for the TripleName.  Symbolic disassembly is supported
+ * by passing a block of information in the DisInfo parameter and specifying the
+ * TagType and callback functions as described above.  These can all be passed
+ * as NULL.  If successful, this returns a disassembler context.  If not, it
+ * returns NULL. This function is equivalent to calling
+ * LLVMCreateDisasmCPUFeatures() with an empty CPU name and feature set.
+ */
+LLVMDisasmContextRef LLVMCreateDisasm(const char *TripleName, void *DisInfo,
+                                      int TagType, LLVMOpInfoCallback GetOpInfo,
+                                      LLVMSymbolLookupCallback SymbolLookUp);
+
+/**
+ * Create a disassembler for the TripleName and a specific CPU.  Symbolic
+ * disassembly is supported by passing a block of information in the DisInfo
+ * parameter and specifying the TagType and callback functions as described
+ * above.  These can all be passed * as NULL.  If successful, this returns a
+ * disassembler context.  If not, it returns NULL. This function is equivalent
+ * to calling LLVMCreateDisasmCPUFeatures() with an empty feature set.
+ */
+LLVMDisasmContextRef LLVMCreateDisasmCPU(const char *Triple, const char *CPU,
+                                         void *DisInfo, int TagType,
+                                         LLVMOpInfoCallback GetOpInfo,
+                                         LLVMSymbolLookupCallback SymbolLookUp);
+
+/**
+ * Create a disassembler for the TripleName, a specific CPU and specific feature
+ * string.  Symbolic disassembly is supported by passing a block of information
+ * in the DisInfo parameter and specifying the TagType and callback functions as
+ * described above.  These can all be passed * as NULL.  If successful, this
+ * returns a disassembler context.  If not, it returns NULL.
+ */
+LLVMDisasmContextRef
+LLVMCreateDisasmCPUFeatures(const char *Triple, const char *CPU,
+                            const char *Features, void *DisInfo, int TagType,
+                            LLVMOpInfoCallback GetOpInfo,
+                            LLVMSymbolLookupCallback SymbolLookUp);
+
+/**
+ * Set the disassembler's options.  Returns 1 if it can set the Options and 0
+ * otherwise.
+ */
+int LLVMSetDisasmOptions(LLVMDisasmContextRef DC, uint64_t Options);
+
+/* The option to produce marked up assembly. */
+#define LLVMDisassembler_Option_UseMarkup 1
+/* The option to print immediates as hex. */
+#define LLVMDisassembler_Option_PrintImmHex 2
+/* The option use the other assembler printer variant */
+#define LLVMDisassembler_Option_AsmPrinterVariant 4
+/* The option to set comment on instructions */
+#define LLVMDisassembler_Option_SetInstrComments 8
+  /* The option to print latency information alongside instructions */
+#define LLVMDisassembler_Option_PrintLatency 16
+
+/**
+ * Dispose of a disassembler context.
+ */
+void LLVMDisasmDispose(LLVMDisasmContextRef DC);
+
+/**
+ * Disassemble a single instruction using the disassembler context specified in
+ * the parameter DC.  The bytes of the instruction are specified in the
+ * parameter Bytes, and contains at least BytesSize number of bytes.  The
+ * instruction is at the address specified by the PC parameter.  If a valid
+ * instruction can be disassembled, its string is returned indirectly in
+ * OutString whose size is specified in the parameter OutStringSize.  This
+ * function returns the number of bytes in the instruction or zero if there was
+ * no valid instruction.
+ */
+size_t LLVMDisasmInstruction(LLVMDisasmContextRef DC, uint8_t *Bytes,
+                             uint64_t BytesSize, uint64_t PC,
+                             char *OutString, size_t OutStringSize);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* !defined(__cplusplus) */
+
+#endif /* LLVM_C_DISASSEMBLER_H */

+ 160 - 0
src/llvm-c/DisassemblerTypes.h

@@ -0,0 +1,160 @@
+/*===-- llvm-c/DisassemblerTypedefs.h -----------------------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_DISASSEMBLER_TYPES_H
+#define LLVM_DISASSEMBLER_TYPES_H
+
+#include "llvm-c/DataTypes.h"
+#ifdef __cplusplus
+#include <cstddef>
+#else
+#include <stddef.h>
+#endif
+
+/**
+ * An opaque reference to a disassembler context.
+ */
+typedef void *LLVMDisasmContextRef;
+
+/**
+ * The type for the operand information call back function.  This is called to
+ * get the symbolic information for an operand of an instruction.  Typically
+ * this is from the relocation information, symbol table, etc.  That block of
+ * information is saved when the disassembler context is created and passed to
+ * the call back in the DisInfo parameter.  The instruction containing operand
+ * is at the PC parameter.  For some instruction sets, there can be more than
+ * one operand with symbolic information.  To determine the symbolic operand
+ * information for each operand, the bytes for the specific operand in the
+ * instruction are specified by the Offset parameter and its byte widith is the
+ * size parameter.  For instructions sets with fixed widths and one symbolic
+ * operand per instruction, the Offset parameter will be zero and Size parameter
+ * will be the instruction width.  The information is returned in TagBuf and is
+ * Triple specific with its specific information defined by the value of
+ * TagType for that Triple.  If symbolic information is returned the function
+ * returns 1, otherwise it returns 0.
+ */
+typedef int (*LLVMOpInfoCallback)(void *DisInfo, uint64_t PC,
+                                  uint64_t Offset, uint64_t Size,
+                                  int TagType, void *TagBuf);
+
+/**
+ * The initial support in LLVM MC for the most general form of a relocatable
+ * expression is "AddSymbol - SubtractSymbol + Offset".  For some Darwin targets
+ * this full form is encoded in the relocation information so that AddSymbol and
+ * SubtractSymbol can be link edited independent of each other.  Many other
+ * platforms only allow a relocatable expression of the form AddSymbol + Offset
+ * to be encoded.
+ *
+ * The LLVMOpInfoCallback() for the TagType value of 1 uses the struct
+ * LLVMOpInfo1.  The value of the relocatable expression for the operand,
+ * including any PC adjustment, is passed in to the call back in the Value
+ * field.  The symbolic information about the operand is returned using all
+ * the fields of the structure with the Offset of the relocatable expression
+ * returned in the Value field.  It is possible that some symbols in the
+ * relocatable expression were assembly temporary symbols, for example
+ * "Ldata - LpicBase + constant", and only the Values of the symbols without
+ * symbol names are present in the relocation information.  The VariantKind
+ * type is one of the Target specific #defines below and is used to print
+ * operands like "_foo@GOT", ":lower16:_foo", etc.
+ */
+struct LLVMOpInfoSymbol1 {
+  uint64_t Present;  /* 1 if this symbol is present */
+  const char *Name;  /* symbol name if not NULL */
+  uint64_t Value;    /* symbol value if name is NULL */
+};
+
+struct LLVMOpInfo1 {
+  struct LLVMOpInfoSymbol1 AddSymbol;
+  struct LLVMOpInfoSymbol1 SubtractSymbol;
+  uint64_t Value;
+  uint64_t VariantKind;
+};
+
+/**
+ * The operand VariantKinds for symbolic disassembly.
+ */
+#define LLVMDisassembler_VariantKind_None 0 /* all targets */
+
+/**
+ * The ARM target VariantKinds.
+ */
+#define LLVMDisassembler_VariantKind_ARM_HI16 1 /* :upper16: */
+#define LLVMDisassembler_VariantKind_ARM_LO16 2 /* :lower16: */
+
+/**
+ * The ARM64 target VariantKinds.
+ */
+#define LLVMDisassembler_VariantKind_ARM64_PAGE       1 /* @page */
+#define LLVMDisassembler_VariantKind_ARM64_PAGEOFF    2 /* @pageoff */
+#define LLVMDisassembler_VariantKind_ARM64_GOTPAGE    3 /* @gotpage */
+#define LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF 4 /* @gotpageoff */
+#define LLVMDisassembler_VariantKind_ARM64_TLVP       5 /* @tvlppage */
+#define LLVMDisassembler_VariantKind_ARM64_TLVOFF     6 /* @tvlppageoff */
+
+/**
+ * The type for the symbol lookup function.  This may be called by the
+ * disassembler for things like adding a comment for a PC plus a constant
+ * offset load instruction to use a symbol name instead of a load address value.
+ * It is passed the block information is saved when the disassembler context is
+ * created and the ReferenceValue to look up as a symbol.  If no symbol is found
+ * for the ReferenceValue NULL is returned.  The ReferenceType of the
+ * instruction is passed indirectly as is the PC of the instruction in
+ * ReferencePC.  If the output reference can be determined its type is returned
+ * indirectly in ReferenceType along with ReferenceName if any, or that is set
+ * to NULL.
+ */
+typedef const char *(*LLVMSymbolLookupCallback)(void *DisInfo,
+                                                uint64_t ReferenceValue,
+                                                uint64_t *ReferenceType,
+                                                uint64_t ReferencePC,
+                                                const char **ReferenceName);
+/**
+ * The reference types on input and output.
+ */
+/* No input reference type or no output reference type. */
+#define LLVMDisassembler_ReferenceType_InOut_None 0
+
+/* The input reference is from a branch instruction. */
+#define LLVMDisassembler_ReferenceType_In_Branch 1
+/* The input reference is from a PC relative load instruction. */
+#define LLVMDisassembler_ReferenceType_In_PCrel_Load 2
+
+/* The input reference is from an ARM64::ADRP instruction. */
+#define LLVMDisassembler_ReferenceType_In_ARM64_ADRP 0x100000001
+/* The input reference is from an ARM64::ADDXri instruction. */
+#define LLVMDisassembler_ReferenceType_In_ARM64_ADDXri 0x100000002
+/* The input reference is from an ARM64::LDRXui instruction. */
+#define LLVMDisassembler_ReferenceType_In_ARM64_LDRXui 0x100000003
+/* The input reference is from an ARM64::LDRXl instruction. */
+#define LLVMDisassembler_ReferenceType_In_ARM64_LDRXl 0x100000004
+/* The input reference is from an ARM64::ADR instruction. */
+#define LLVMDisassembler_ReferenceType_In_ARM64_ADR 0x100000005
+
+/* The output reference is to as symbol stub. */
+#define LLVMDisassembler_ReferenceType_Out_SymbolStub 1
+/* The output reference is to a symbol address in a literal pool. */
+#define LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr 2
+/* The output reference is to a cstring address in a literal pool. */
+#define LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr 3
+
+/* The output reference is to a Objective-C CoreFoundation string. */
+#define LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref 4
+/* The output reference is to a Objective-C message. */
+#define LLVMDisassembler_ReferenceType_Out_Objc_Message 5
+/* The output reference is to a Objective-C message ref. */
+#define LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref 6
+/* The output reference is to a Objective-C selector ref. */
+#define LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref 7
+/* The output reference is to a Objective-C class ref. */
+#define LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref 8
+
+/* The output reference is to a C++ symbol name. */
+#define LLVMDisassembler_ReferenceType_DeMangled_Name 9
+
+#endif

+ 69 - 0
src/llvm-c/Error.h

@@ -0,0 +1,69 @@
+/*===------- llvm-c/Error.h - llvm::Error class C Interface -------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to LLVM's Error class.                   *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_ERROR_H
+#define LLVM_C_ERROR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LLVMErrorSuccess 0
+
+/**
+ * Opaque reference to an error instance. Null serves as the 'success' value.
+ */
+typedef struct LLVMOpaqueError *LLVMErrorRef;
+
+/**
+ * Error type identifier.
+ */
+typedef const void *LLVMErrorTypeId;
+
+/**
+ * Returns the type id for the given error instance, which must be a failure
+ * value (i.e. non-null).
+ */
+LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err);
+
+/**
+ * Dispose of the given error without handling it. This operation consumes the
+ * error, and the given LLVMErrorRef value is not usable once this call returns.
+ * Note: This method *only* needs to be called if the error is not being passed
+ * to some other consuming operation, e.g. LLVMGetErrorMessage.
+ */
+void LLVMConsumeError(LLVMErrorRef Err);
+
+/**
+ * Returns the given string's error message. This operation consumes the error,
+ * and the given LLVMErrorRef value is not usable once this call returns.
+ * The caller is responsible for disposing of the string by calling
+ * LLVMDisposeErrorMessage.
+ */
+char *LLVMGetErrorMessage(LLVMErrorRef Err);
+
+/**
+ * Dispose of the given error message.
+ */
+void LLVMDisposeErrorMessage(char *ErrMsg);
+
+/**
+ * Returns the type id for llvm StringError.
+ */
+LLVMErrorTypeId LLVMGetStringErrorTypeId(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 49 - 0
src/llvm-c/ErrorHandling.h

@@ -0,0 +1,49 @@
+/*===-- llvm-c/ErrorHandling.h - Error Handling C Interface -------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to LLVM's error handling mechanism.      *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_ERROR_HANDLING_H
+#define LLVM_C_ERROR_HANDLING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*LLVMFatalErrorHandler)(const char *Reason);
+
+/**
+ * Install a fatal error handler. By default, if LLVM detects a fatal error, it
+ * will call exit(1). This may not be appropriate in many contexts. For example,
+ * doing exit(1) will bypass many crash reporting/tracing system tools. This
+ * function allows you to install a callback that will be invoked prior to the
+ * call to exit(1).
+ */
+void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler);
+
+/**
+ * Reset the fatal error handler. This resets LLVM's fatal error handling
+ * behavior to the default.
+ */
+void LLVMResetFatalErrorHandler(void);
+
+/**
+ * Enable LLVM's built-in stack trace code. This intercepts the OS's crash
+ * signals and prints which component of LLVM you were in at the time if the
+ * crash.
+ */
+void LLVMEnablePrettyStackTrace(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 200 - 0
src/llvm-c/ExecutionEngine.h

@@ -0,0 +1,200 @@
+/*===-- llvm-c/ExecutionEngine.h - ExecutionEngine Lib C Iface --*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMExecutionEngine.o, which    *|
+|* implements various analyses of the LLVM IR.                                *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_EXECUTIONENGINE_H
+#define LLVM_C_EXECUTIONENGINE_H
+
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCExecutionEngine Execution Engine
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+void LLVMLinkInMCJIT(void);
+void LLVMLinkInInterpreter(void);
+
+typedef struct LLVMOpaqueGenericValue *LLVMGenericValueRef;
+typedef struct LLVMOpaqueExecutionEngine *LLVMExecutionEngineRef;
+typedef struct LLVMOpaqueMCJITMemoryManager *LLVMMCJITMemoryManagerRef;
+
+struct LLVMMCJITCompilerOptions {
+  unsigned OptLevel;
+  LLVMCodeModel CodeModel;
+  LLVMBool NoFramePointerElim;
+  LLVMBool EnableFastISel;
+  LLVMMCJITMemoryManagerRef MCJMM;
+};
+
+/*===-- Operations on generic values --------------------------------------===*/
+
+LLVMGenericValueRef LLVMCreateGenericValueOfInt(LLVMTypeRef Ty,
+                                                unsigned long long N,
+                                                LLVMBool IsSigned);
+
+LLVMGenericValueRef LLVMCreateGenericValueOfPointer(void *P);
+
+LLVMGenericValueRef LLVMCreateGenericValueOfFloat(LLVMTypeRef Ty, double N);
+
+unsigned LLVMGenericValueIntWidth(LLVMGenericValueRef GenValRef);
+
+unsigned long long LLVMGenericValueToInt(LLVMGenericValueRef GenVal,
+                                         LLVMBool IsSigned);
+
+void *LLVMGenericValueToPointer(LLVMGenericValueRef GenVal);
+
+double LLVMGenericValueToFloat(LLVMTypeRef TyRef, LLVMGenericValueRef GenVal);
+
+void LLVMDisposeGenericValue(LLVMGenericValueRef GenVal);
+
+/*===-- Operations on execution engines -----------------------------------===*/
+
+LLVMBool LLVMCreateExecutionEngineForModule(LLVMExecutionEngineRef *OutEE,
+                                            LLVMModuleRef M,
+                                            char **OutError);
+
+LLVMBool LLVMCreateInterpreterForModule(LLVMExecutionEngineRef *OutInterp,
+                                        LLVMModuleRef M,
+                                        char **OutError);
+
+LLVMBool LLVMCreateJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
+                                        LLVMModuleRef M,
+                                        unsigned OptLevel,
+                                        char **OutError);
+
+void LLVMInitializeMCJITCompilerOptions(
+  struct LLVMMCJITCompilerOptions *Options, size_t SizeOfOptions);
+
+/**
+ * Create an MCJIT execution engine for a module, with the given options. It is
+ * the responsibility of the caller to ensure that all fields in Options up to
+ * the given SizeOfOptions are initialized. It is correct to pass a smaller
+ * value of SizeOfOptions that omits some fields. The canonical way of using
+ * this is:
+ *
+ * LLVMMCJITCompilerOptions options;
+ * LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
+ * ... fill in those options you care about
+ * LLVMCreateMCJITCompilerForModule(&jit, mod, &options, sizeof(options),
+ *                                  &error);
+ *
+ * Note that this is also correct, though possibly suboptimal:
+ *
+ * LLVMCreateMCJITCompilerForModule(&jit, mod, 0, 0, &error);
+ */
+LLVMBool LLVMCreateMCJITCompilerForModule(
+  LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M,
+  struct LLVMMCJITCompilerOptions *Options, size_t SizeOfOptions,
+  char **OutError);
+
+void LLVMDisposeExecutionEngine(LLVMExecutionEngineRef EE);
+
+void LLVMRunStaticConstructors(LLVMExecutionEngineRef EE);
+
+void LLVMRunStaticDestructors(LLVMExecutionEngineRef EE);
+
+int LLVMRunFunctionAsMain(LLVMExecutionEngineRef EE, LLVMValueRef F,
+                          unsigned ArgC, const char * const *ArgV,
+                          const char * const *EnvP);
+
+LLVMGenericValueRef LLVMRunFunction(LLVMExecutionEngineRef EE, LLVMValueRef F,
+                                    unsigned NumArgs,
+                                    LLVMGenericValueRef *Args);
+
+void LLVMFreeMachineCodeForFunction(LLVMExecutionEngineRef EE, LLVMValueRef F);
+
+void LLVMAddModule(LLVMExecutionEngineRef EE, LLVMModuleRef M);
+
+LLVMBool LLVMRemoveModule(LLVMExecutionEngineRef EE, LLVMModuleRef M,
+                          LLVMModuleRef *OutMod, char **OutError);
+
+LLVMBool LLVMFindFunction(LLVMExecutionEngineRef EE, const char *Name,
+                          LLVMValueRef *OutFn);
+
+void *LLVMRecompileAndRelinkFunction(LLVMExecutionEngineRef EE,
+                                     LLVMValueRef Fn);
+
+LLVMTargetDataRef LLVMGetExecutionEngineTargetData(LLVMExecutionEngineRef EE);
+LLVMTargetMachineRef
+LLVMGetExecutionEngineTargetMachine(LLVMExecutionEngineRef EE);
+
+void LLVMAddGlobalMapping(LLVMExecutionEngineRef EE, LLVMValueRef Global,
+                          void* Addr);
+
+void *LLVMGetPointerToGlobal(LLVMExecutionEngineRef EE, LLVMValueRef Global);
+
+uint64_t LLVMGetGlobalValueAddress(LLVMExecutionEngineRef EE, const char *Name);
+
+uint64_t LLVMGetFunctionAddress(LLVMExecutionEngineRef EE, const char *Name);
+
+/*===-- Operations on memory managers -------------------------------------===*/
+
+typedef uint8_t *(*LLVMMemoryManagerAllocateCodeSectionCallback)(
+  void *Opaque, uintptr_t Size, unsigned Alignment, unsigned SectionID,
+  const char *SectionName);
+typedef uint8_t *(*LLVMMemoryManagerAllocateDataSectionCallback)(
+  void *Opaque, uintptr_t Size, unsigned Alignment, unsigned SectionID,
+  const char *SectionName, LLVMBool IsReadOnly);
+typedef LLVMBool (*LLVMMemoryManagerFinalizeMemoryCallback)(
+  void *Opaque, char **ErrMsg);
+typedef void (*LLVMMemoryManagerDestroyCallback)(void *Opaque);
+
+/**
+ * Create a simple custom MCJIT memory manager. This memory manager can
+ * intercept allocations in a module-oblivious way. This will return NULL
+ * if any of the passed functions are NULL.
+ *
+ * @param Opaque An opaque client object to pass back to the callbacks.
+ * @param AllocateCodeSection Allocate a block of memory for executable code.
+ * @param AllocateDataSection Allocate a block of memory for data.
+ * @param FinalizeMemory Set page permissions and flush cache. Return 0 on
+ *   success, 1 on error.
+ */
+LLVMMCJITMemoryManagerRef LLVMCreateSimpleMCJITMemoryManager(
+  void *Opaque,
+  LLVMMemoryManagerAllocateCodeSectionCallback AllocateCodeSection,
+  LLVMMemoryManagerAllocateDataSectionCallback AllocateDataSection,
+  LLVMMemoryManagerFinalizeMemoryCallback FinalizeMemory,
+  LLVMMemoryManagerDestroyCallback Destroy);
+
+void LLVMDisposeMCJITMemoryManager(LLVMMCJITMemoryManagerRef MM);
+
+/*===-- JIT Event Listener functions -------------------------------------===*/
+
+LLVMJITEventListenerRef LLVMCreateGDBRegistrationListener(void);
+LLVMJITEventListenerRef LLVMCreateIntelJITEventListener(void);
+LLVMJITEventListenerRef LLVMCreateOProfileJITEventListener(void);
+LLVMJITEventListenerRef LLVMCreatePerfJITEventListener(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 40 - 0
src/llvm-c/IRReader.h

@@ -0,0 +1,40 @@
+/*===-- llvm-c/IRReader.h - IR Reader C Interface -----------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to the IR Reader.                        *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_IRREADER_H
+#define LLVM_C_IRREADER_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Read LLVM IR from a memory buffer and convert it into an in-memory Module
+ * object. Returns 0 on success.
+ * Optionally returns a human-readable description of any errors that
+ * occurred during parsing IR. OutMessage must be disposed with
+ * LLVMDisposeMessage.
+ *
+ * @see llvm::ParseIR()
+ */
+LLVMBool LLVMParseIRInContext(LLVMContextRef ContextRef,
+                              LLVMMemoryBufferRef MemBuf, LLVMModuleRef *OutM,
+                              char **OutMessage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 56 - 0
src/llvm-c/Initialization.h

@@ -0,0 +1,56 @@
+/*===-- llvm-c/Initialization.h - Initialization C Interface ------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to LLVM initialization routines,      *|
+|* which must be called before you can use the functionality provided by      *|
+|* the corresponding LLVM library.                                            *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_INITIALIZATION_H
+#define LLVM_C_INITIALIZATION_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCInitialization Initialization Routines
+ * @ingroup LLVMC
+ *
+ * This module contains routines used to initialize the LLVM system.
+ *
+ * @{
+ */
+
+void LLVMInitializeCore(LLVMPassRegistryRef R);
+void LLVMInitializeTransformUtils(LLVMPassRegistryRef R);
+void LLVMInitializeScalarOpts(LLVMPassRegistryRef R);
+void LLVMInitializeObjCARCOpts(LLVMPassRegistryRef R);
+void LLVMInitializeVectorization(LLVMPassRegistryRef R);
+void LLVMInitializeInstCombine(LLVMPassRegistryRef R);
+void LLVMInitializeAggressiveInstCombiner(LLVMPassRegistryRef R);
+void LLVMInitializeIPO(LLVMPassRegistryRef R);
+void LLVMInitializeInstrumentation(LLVMPassRegistryRef R);
+void LLVMInitializeAnalysis(LLVMPassRegistryRef R);
+void LLVMInitializeIPA(LLVMPassRegistryRef R);
+void LLVMInitializeCodeGen(LLVMPassRegistryRef R);
+void LLVMInitializeTarget(LLVMPassRegistryRef R);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 68 - 0
src/llvm-c/LinkTimeOptimizer.h

@@ -0,0 +1,68 @@
+//===-- llvm/LinkTimeOptimizer.h - LTO Public C Interface -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This header provides a C API to use the LLVM link time optimization
+// library. This is intended to be used by linkers which are C-only in
+// their implementation for performing LTO.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_C_LINKTIMEOPTIMIZER_H
+#define LLVM_C_LINKTIMEOPTIMIZER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCLinkTimeOptimizer Link Time Optimization
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+  /// This provides a dummy type for pointers to the LTO object.
+  typedef void* llvm_lto_t;
+
+  /// This provides a C-visible enumerator to manage status codes.
+  /// This should map exactly onto the C++ enumerator LTOStatus.
+  typedef enum llvm_lto_status {
+    LLVM_LTO_UNKNOWN,
+    LLVM_LTO_OPT_SUCCESS,
+    LLVM_LTO_READ_SUCCESS,
+    LLVM_LTO_READ_FAILURE,
+    LLVM_LTO_WRITE_FAILURE,
+    LLVM_LTO_NO_TARGET,
+    LLVM_LTO_NO_WORK,
+    LLVM_LTO_MODULE_MERGE_FAILURE,
+    LLVM_LTO_ASM_FAILURE,
+
+    //  Added C-specific error codes
+    LLVM_LTO_NULL_OBJECT
+  } llvm_lto_status_t;
+
+  /// This provides C interface to initialize link time optimizer. This allows
+  /// linker to use dlopen() interface to dynamically load LinkTimeOptimizer.
+  /// extern "C" helps, because dlopen() interface uses name to find the symbol.
+  extern llvm_lto_t llvm_create_optimizer(void);
+  extern void llvm_destroy_optimizer(llvm_lto_t lto);
+
+  extern llvm_lto_status_t llvm_read_object_file
+    (llvm_lto_t lto, const char* input_filename);
+  extern llvm_lto_status_t llvm_optimize_modules
+    (llvm_lto_t lto, const char* output_filename);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 41 - 0
src/llvm-c/Linker.h

@@ -0,0 +1,41 @@
+/*===-- llvm-c/Linker.h - Module Linker C Interface -------------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to the module/file/archive linker.       *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_LINKER_H
+#define LLVM_C_LINKER_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This enum is provided for backwards-compatibility only. It has no effect. */
+typedef enum {
+  LLVMLinkerDestroySource = 0, /* This is the default behavior. */
+  LLVMLinkerPreserveSource_Removed = 1 /* This option has been deprecated and
+                                          should not be used. */
+} LLVMLinkerMode;
+
+/* Links the source module into the destination module. The source module is
+ * destroyed.
+ * The return value is true if an error occurred, false otherwise.
+ * Use the diagnostic handler to get any diagnostic message.
+*/
+LLVMBool LLVMLinkModules2(LLVMModuleRef Dest, LLVMModuleRef Src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 233 - 0
src/llvm-c/Object.h

@@ -0,0 +1,233 @@
+/*===-- llvm-c/Object.h - Object Lib C Iface --------------------*- C++ -*-===*/
+/*                                                                            */
+/* Part of the LLVM Project, under the Apache License v2.0 with LLVM          */
+/* Exceptions.                                                                */
+/* See https://llvm.org/LICENSE.txt for license information.                  */
+/* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+/*                                                                            */
+/* This header declares the C interface to libLLVMObject.a, which             */
+/* implements object file reading and writing.                                */
+/*                                                                            */
+/* Many exotic languages can interoperate with C code but have a harder time  */
+/* with C++ due to name mangling. So in addition to C, this interface enables */
+/* tools written in such languages.                                           */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_OBJECT_H
+#define LLVM_C_OBJECT_H
+
+#include "llvm-c/Types.h"
+#include "Config/llvm-config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCObject Object file reading and writing
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+// Opaque type wrappers
+typedef struct LLVMOpaqueSectionIterator *LLVMSectionIteratorRef;
+typedef struct LLVMOpaqueSymbolIterator *LLVMSymbolIteratorRef;
+typedef struct LLVMOpaqueRelocationIterator *LLVMRelocationIteratorRef;
+
+typedef enum {
+  LLVMBinaryTypeArchive,                /**< Archive file. */
+  LLVMBinaryTypeMachOUniversalBinary,   /**< Mach-O Universal Binary file. */
+  LLVMBinaryTypeCOFFImportFile,         /**< COFF Import file. */
+  LLVMBinaryTypeIR,                     /**< LLVM IR. */
+  LLVMBinaryTypeWinRes,                 /**< Windows resource (.res) file. */
+  LLVMBinaryTypeCOFF,                   /**< COFF Object file. */
+  LLVMBinaryTypeELF32L,                 /**< ELF 32-bit, little endian. */
+  LLVMBinaryTypeELF32B,                 /**< ELF 32-bit, big endian. */
+  LLVMBinaryTypeELF64L,                 /**< ELF 64-bit, little endian. */
+  LLVMBinaryTypeELF64B,                 /**< ELF 64-bit, big endian. */
+  LLVMBinaryTypeMachO32L,               /**< MachO 32-bit, little endian. */
+  LLVMBinaryTypeMachO32B,               /**< MachO 32-bit, big endian. */
+  LLVMBinaryTypeMachO64L,               /**< MachO 64-bit, little endian. */
+  LLVMBinaryTypeMachO64B,               /**< MachO 64-bit, big endian. */
+  LLVMBinaryTypeWasm,                   /**< Web Assembly. */
+} LLVMBinaryType;
+
+/**
+ * Create a binary file from the given memory buffer.
+ *
+ * The exact type of the binary file will be inferred automatically, and the
+ * appropriate implementation selected.  The context may be NULL except if
+ * the resulting file is an LLVM IR file.
+ *
+ * The memory buffer is not consumed by this function.  It is the responsibilty
+ * of the caller to free it with \c LLVMDisposeMemoryBuffer.
+ *
+ * If NULL is returned, the \p ErrorMessage parameter is populated with the
+ * error's description.  It is then the caller's responsibility to free this
+ * message by calling \c LLVMDisposeMessage.
+ *
+ * @see llvm::object::createBinary
+ */
+LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf,
+                               LLVMContextRef Context,
+                               char **ErrorMessage);
+
+/**
+ * Dispose of a binary file.
+ *
+ * The binary file does not own its backing buffer.  It is the responsibilty
+ * of the caller to free it with \c LLVMDisposeMemoryBuffer.
+ */
+void LLVMDisposeBinary(LLVMBinaryRef BR);
+
+/**
+ * Retrieves a copy of the memory buffer associated with this object file.
+ *
+ * The returned buffer is merely a shallow copy and does not own the actual
+ * backing buffer of the binary. Nevertheless, it is the responsibility of the
+ * caller to free it with \c LLVMDisposeMemoryBuffer.
+ *
+ * @see llvm::object::getMemoryBufferRef
+ */
+LLVMMemoryBufferRef LLVMBinaryCopyMemoryBuffer(LLVMBinaryRef BR);
+
+/**
+ * Retrieve the specific type of a binary.
+ *
+ * @see llvm::object::Binary::getType
+ */
+LLVMBinaryType LLVMBinaryGetType(LLVMBinaryRef BR);
+
+/*
+ * For a Mach-O universal binary file, retrieves the object file corresponding
+ * to the given architecture if it is present as a slice.
+ *
+ * If NULL is returned, the \p ErrorMessage parameter is populated with the
+ * error's description.  It is then the caller's responsibility to free this
+ * message by calling \c LLVMDisposeMessage.
+ *
+ * It is the responsiblity of the caller to free the returned object file by
+ * calling \c LLVMDisposeBinary.
+ */
+LLVMBinaryRef LLVMMachOUniversalBinaryCopyObjectForArch(LLVMBinaryRef BR,
+                                                        const char *Arch,
+                                                        size_t ArchLen,
+                                                        char **ErrorMessage);
+
+/**
+ * Retrieve a copy of the section iterator for this object file.
+ *
+ * If there are no sections, the result is NULL.
+ *
+ * The returned iterator is merely a shallow copy. Nevertheless, it is
+ * the responsibility of the caller to free it with
+ * \c LLVMDisposeSectionIterator.
+ *
+ * @see llvm::object::sections()
+ */
+LLVMSectionIteratorRef LLVMObjectFileCopySectionIterator(LLVMBinaryRef BR);
+
+/**
+ * Returns whether the given section iterator is at the end.
+ *
+ * @see llvm::object::section_end
+ */
+LLVMBool LLVMObjectFileIsSectionIteratorAtEnd(LLVMBinaryRef BR,
+                                              LLVMSectionIteratorRef SI);
+
+/**
+ * Retrieve a copy of the symbol iterator for this object file.
+ *
+ * If there are no symbols, the result is NULL.
+ *
+ * The returned iterator is merely a shallow copy. Nevertheless, it is
+ * the responsibility of the caller to free it with
+ * \c LLVMDisposeSymbolIterator.
+ *
+ * @see llvm::object::symbols()
+ */
+LLVMSymbolIteratorRef LLVMObjectFileCopySymbolIterator(LLVMBinaryRef BR);
+
+/**
+ * Returns whether the given symbol iterator is at the end.
+ *
+ * @see llvm::object::symbol_end
+ */
+LLVMBool LLVMObjectFileIsSymbolIteratorAtEnd(LLVMBinaryRef BR,
+                                             LLVMSymbolIteratorRef SI);
+
+void LLVMDisposeSectionIterator(LLVMSectionIteratorRef SI);
+
+void LLVMMoveToNextSection(LLVMSectionIteratorRef SI);
+void LLVMMoveToContainingSection(LLVMSectionIteratorRef Sect,
+                                 LLVMSymbolIteratorRef Sym);
+
+// ObjectFile Symbol iterators
+void LLVMDisposeSymbolIterator(LLVMSymbolIteratorRef SI);
+void LLVMMoveToNextSymbol(LLVMSymbolIteratorRef SI);
+
+// SectionRef accessors
+const char *LLVMGetSectionName(LLVMSectionIteratorRef SI);
+uint64_t LLVMGetSectionSize(LLVMSectionIteratorRef SI);
+const char *LLVMGetSectionContents(LLVMSectionIteratorRef SI);
+uint64_t LLVMGetSectionAddress(LLVMSectionIteratorRef SI);
+LLVMBool LLVMGetSectionContainsSymbol(LLVMSectionIteratorRef SI,
+                                 LLVMSymbolIteratorRef Sym);
+
+// Section Relocation iterators
+LLVMRelocationIteratorRef LLVMGetRelocations(LLVMSectionIteratorRef Section);
+void LLVMDisposeRelocationIterator(LLVMRelocationIteratorRef RI);
+LLVMBool LLVMIsRelocationIteratorAtEnd(LLVMSectionIteratorRef Section,
+                                       LLVMRelocationIteratorRef RI);
+void LLVMMoveToNextRelocation(LLVMRelocationIteratorRef RI);
+
+
+// SymbolRef accessors
+const char *LLVMGetSymbolName(LLVMSymbolIteratorRef SI);
+uint64_t LLVMGetSymbolAddress(LLVMSymbolIteratorRef SI);
+uint64_t LLVMGetSymbolSize(LLVMSymbolIteratorRef SI);
+
+// RelocationRef accessors
+uint64_t LLVMGetRelocationOffset(LLVMRelocationIteratorRef RI);
+LLVMSymbolIteratorRef LLVMGetRelocationSymbol(LLVMRelocationIteratorRef RI);
+uint64_t LLVMGetRelocationType(LLVMRelocationIteratorRef RI);
+// NOTE: Caller takes ownership of returned string of the two
+// following functions.
+const char *LLVMGetRelocationTypeName(LLVMRelocationIteratorRef RI);
+const char *LLVMGetRelocationValueString(LLVMRelocationIteratorRef RI);
+
+/** Deprecated: Use LLVMBinaryRef instead. */
+typedef struct LLVMOpaqueObjectFile *LLVMObjectFileRef;
+
+/** Deprecated: Use LLVMCreateBinary instead. */
+LLVMObjectFileRef LLVMCreateObjectFile(LLVMMemoryBufferRef MemBuf);
+
+/** Deprecated: Use LLVMDisposeBinary instead. */
+void LLVMDisposeObjectFile(LLVMObjectFileRef ObjectFile);
+
+/** Deprecated: Use LLVMObjectFileCopySectionIterator instead. */
+LLVMSectionIteratorRef LLVMGetSections(LLVMObjectFileRef ObjectFile);
+
+/** Deprecated: Use LLVMObjectFileIsSectionIteratorAtEnd instead. */
+LLVMBool LLVMIsSectionIteratorAtEnd(LLVMObjectFileRef ObjectFile,
+                                    LLVMSectionIteratorRef SI);
+
+/** Deprecated: Use LLVMObjectFileCopySymbolIterator instead. */
+LLVMSymbolIteratorRef LLVMGetSymbols(LLVMObjectFileRef ObjectFile);
+
+/** Deprecated: Use LLVMObjectFileIsSymbolIteratorAtEnd instead. */
+LLVMBool LLVMIsSymbolIteratorAtEnd(LLVMObjectFileRef ObjectFile,
+                                   LLVMSymbolIteratorRef SI);
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 172 - 0
src/llvm-c/OrcBindings.h

@@ -0,0 +1,172 @@
+/*===----------- llvm-c/OrcBindings.h - Orc Lib C Iface ---------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMOrcJIT.a, which implements  *|
+|* JIT compilation of LLVM IR.                                                *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+|* Note: This interface is experimental. It is *NOT* stable, and may be       *|
+|*       changed without warning.                                             *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_ORCBINDINGS_H
+#define LLVM_C_ORCBINDINGS_H
+
+#include "llvm-c/Error.h"
+#include "llvm-c/Object.h"
+#include "llvm-c/TargetMachine.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct LLVMOrcOpaqueJITStack *LLVMOrcJITStackRef;
+typedef uint64_t LLVMOrcModuleHandle;
+typedef uint64_t LLVMOrcTargetAddress;
+typedef uint64_t (*LLVMOrcSymbolResolverFn)(const char *Name, void *LookupCtx);
+typedef uint64_t (*LLVMOrcLazyCompileCallbackFn)(LLVMOrcJITStackRef JITStack,
+                                                 void *CallbackCtx);
+
+/**
+ * Create an ORC JIT stack.
+ *
+ * The client owns the resulting stack, and must call OrcDisposeInstance(...)
+ * to destroy it and free its memory. The JIT stack will take ownership of the
+ * TargetMachine, which will be destroyed when the stack is destroyed. The
+ * client should not attempt to dispose of the Target Machine, or it will result
+ * in a double-free.
+ */
+LLVMOrcJITStackRef LLVMOrcCreateInstance(LLVMTargetMachineRef TM);
+
+/**
+ * Get the error message for the most recent error (if any).
+ *
+ * This message is owned by the ORC JIT Stack and will be freed when the stack
+ * is disposed of by LLVMOrcDisposeInstance.
+ */
+const char *LLVMOrcGetErrorMsg(LLVMOrcJITStackRef JITStack);
+
+/**
+ * Mangle the given symbol.
+ * Memory will be allocated for MangledSymbol to hold the result. The client
+ */
+void LLVMOrcGetMangledSymbol(LLVMOrcJITStackRef JITStack, char **MangledSymbol,
+                             const char *Symbol);
+
+/**
+ * Dispose of a mangled symbol.
+ */
+void LLVMOrcDisposeMangledSymbol(char *MangledSymbol);
+
+/**
+ * Create a lazy compile callback.
+ */
+LLVMErrorRef LLVMOrcCreateLazyCompileCallback(
+    LLVMOrcJITStackRef JITStack, LLVMOrcTargetAddress *RetAddr,
+    LLVMOrcLazyCompileCallbackFn Callback, void *CallbackCtx);
+
+/**
+ * Create a named indirect call stub.
+ */
+LLVMErrorRef LLVMOrcCreateIndirectStub(LLVMOrcJITStackRef JITStack,
+                                       const char *StubName,
+                                       LLVMOrcTargetAddress InitAddr);
+
+/**
+ * Set the pointer for the given indirect stub.
+ */
+LLVMErrorRef LLVMOrcSetIndirectStubPointer(LLVMOrcJITStackRef JITStack,
+                                           const char *StubName,
+                                           LLVMOrcTargetAddress NewAddr);
+
+/**
+ * Add module to be eagerly compiled.
+ */
+LLVMErrorRef LLVMOrcAddEagerlyCompiledIR(LLVMOrcJITStackRef JITStack,
+                                         LLVMOrcModuleHandle *RetHandle,
+                                         LLVMModuleRef Mod,
+                                         LLVMOrcSymbolResolverFn SymbolResolver,
+                                         void *SymbolResolverCtx);
+
+/**
+ * Add module to be lazily compiled one function at a time.
+ */
+LLVMErrorRef LLVMOrcAddLazilyCompiledIR(LLVMOrcJITStackRef JITStack,
+                                        LLVMOrcModuleHandle *RetHandle,
+                                        LLVMModuleRef Mod,
+                                        LLVMOrcSymbolResolverFn SymbolResolver,
+                                        void *SymbolResolverCtx);
+
+/**
+ * Add an object file.
+ *
+ * This method takes ownership of the given memory buffer and attempts to add
+ * it to the JIT as an object file.
+ * Clients should *not* dispose of the 'Obj' argument: the JIT will manage it
+ * from this call onwards.
+ */
+LLVMErrorRef LLVMOrcAddObjectFile(LLVMOrcJITStackRef JITStack,
+                                  LLVMOrcModuleHandle *RetHandle,
+                                  LLVMMemoryBufferRef Obj,
+                                  LLVMOrcSymbolResolverFn SymbolResolver,
+                                  void *SymbolResolverCtx);
+
+/**
+ * Remove a module set from the JIT.
+ *
+ * This works for all modules that can be added via OrcAdd*, including object
+ * files.
+ */
+LLVMErrorRef LLVMOrcRemoveModule(LLVMOrcJITStackRef JITStack,
+                                 LLVMOrcModuleHandle H);
+
+/**
+ * Get symbol address from JIT instance.
+ */
+LLVMErrorRef LLVMOrcGetSymbolAddress(LLVMOrcJITStackRef JITStack,
+                                     LLVMOrcTargetAddress *RetAddr,
+                                     const char *SymbolName);
+
+/**
+ * Get symbol address from JIT instance, searching only the specified
+ * handle.
+ */
+LLVMErrorRef LLVMOrcGetSymbolAddressIn(LLVMOrcJITStackRef JITStack,
+                                       LLVMOrcTargetAddress *RetAddr,
+                                       LLVMOrcModuleHandle H,
+                                       const char *SymbolName);
+
+/**
+ * Dispose of an ORC JIT stack.
+ */
+LLVMErrorRef LLVMOrcDisposeInstance(LLVMOrcJITStackRef JITStack);
+
+/**
+ * Register a JIT Event Listener.
+ *
+ * A NULL listener is ignored.
+ */
+void LLVMOrcRegisterJITEventListener(LLVMOrcJITStackRef JITStack, LLVMJITEventListenerRef L);
+
+/**
+ * Unegister a JIT Event Listener.
+ *
+ * A NULL listener is ignored.
+ */
+void LLVMOrcUnregisterJITEventListener(LLVMOrcJITStackRef JITStack, LLVMJITEventListenerRef L);
+
+#ifdef __cplusplus
+}
+#endif /* extern "C" */
+
+#endif /* LLVM_C_ORCBINDINGS_H */

+ 329 - 0
src/llvm-c/Remarks.h

@@ -0,0 +1,329 @@
+/*===-- llvm-c/Remarks.h - Remarks Public C Interface -------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides a public interface to a remark diagnostics library.   *|
+|* LLVM provides an implementation of this interface.                         *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_REMARKS_H
+#define LLVM_C_REMARKS_H
+
+#include "llvm-c/Types.h"
+#ifdef __cplusplus
+#include <cstddef>
+extern "C" {
+#else
+#include <stddef.h>
+#endif /* !defined(__cplusplus) */
+
+/**
+ * @defgroup LLVMCREMARKS Remarks
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+#define REMARKS_API_VERSION 0
+
+/**
+ * The type of the emitted remark.
+ */
+enum LLVMRemarkType {
+  LLVMRemarkTypeUnknown,
+  LLVMRemarkTypePassed,
+  LLVMRemarkTypeMissed,
+  LLVMRemarkTypeAnalysis,
+  LLVMRemarkTypeAnalysisFPCommute,
+  LLVMRemarkTypeAnalysisAliasing,
+  LLVMRemarkTypeFailure
+};
+
+/**
+ * String containing a buffer and a length. The buffer is not guaranteed to be
+ * zero-terminated.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+typedef struct LLVMRemarkOpaqueString *LLVMRemarkStringRef;
+
+/**
+ * Returns the buffer holding the string.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern const char *LLVMRemarkStringGetData(LLVMRemarkStringRef String);
+
+/**
+ * Returns the size of the string.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint32_t LLVMRemarkStringGetLen(LLVMRemarkStringRef String);
+
+/**
+ * DebugLoc containing File, Line and Column.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+typedef struct LLVMRemarkOpaqueDebugLoc *LLVMRemarkDebugLocRef;
+
+/**
+ * Return the path to the source file for a debug location.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef
+LLVMRemarkDebugLocGetSourceFilePath(LLVMRemarkDebugLocRef DL);
+
+/**
+ * Return the line in the source file for a debug location.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint32_t LLVMRemarkDebugLocGetSourceLine(LLVMRemarkDebugLocRef DL);
+
+/**
+ * Return the column in the source file for a debug location.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint32_t LLVMRemarkDebugLocGetSourceColumn(LLVMRemarkDebugLocRef DL);
+
+/**
+ * Element of the "Args" list. The key might give more information about what
+ * the semantics of the value are, e.g. "Callee" will tell you that the value
+ * is a symbol that names a function.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+typedef struct LLVMRemarkOpaqueArg *LLVMRemarkArgRef;
+
+/**
+ * Returns the key of an argument. The key defines what the value is, and the
+ * same key can appear multiple times in the list of arguments.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef LLVMRemarkArgGetKey(LLVMRemarkArgRef Arg);
+
+/**
+ * Returns the value of an argument. This is a string that can contain newlines.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef LLVMRemarkArgGetValue(LLVMRemarkArgRef Arg);
+
+/**
+ * Returns the debug location that is attached to the value of this argument.
+ *
+ * If there is no debug location, the return value will be `NULL`.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkDebugLocRef LLVMRemarkArgGetDebugLoc(LLVMRemarkArgRef Arg);
+
+/**
+ * A remark emitted by the compiler.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+typedef struct LLVMRemarkOpaqueEntry *LLVMRemarkEntryRef;
+
+/**
+ * Free the resources used by the remark entry.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern void LLVMRemarkEntryDispose(LLVMRemarkEntryRef Remark);
+
+/**
+ * The type of the remark. For example, it can allow users to only keep the
+ * missed optimizations from the compiler.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern enum LLVMRemarkType LLVMRemarkEntryGetType(LLVMRemarkEntryRef Remark);
+
+/**
+ * Get the name of the pass that emitted this remark.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef
+LLVMRemarkEntryGetPassName(LLVMRemarkEntryRef Remark);
+
+/**
+ * Get an identifier of the remark.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef
+LLVMRemarkEntryGetRemarkName(LLVMRemarkEntryRef Remark);
+
+/**
+ * Get the name of the function being processed when the remark was emitted.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkStringRef
+LLVMRemarkEntryGetFunctionName(LLVMRemarkEntryRef Remark);
+
+/**
+ * Returns the debug location that is attached to this remark.
+ *
+ * If there is no debug location, the return value will be `NULL`.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkDebugLocRef
+LLVMRemarkEntryGetDebugLoc(LLVMRemarkEntryRef Remark);
+
+/**
+ * Return the hotness of the remark.
+ *
+ * A hotness of `0` means this value is not set.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint64_t LLVMRemarkEntryGetHotness(LLVMRemarkEntryRef Remark);
+
+/**
+ * The number of arguments the remark holds.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint32_t LLVMRemarkEntryGetNumArgs(LLVMRemarkEntryRef Remark);
+
+/**
+ * Get a new iterator to iterate over a remark's argument.
+ *
+ * If there are no arguments in \p Remark, the return value will be `NULL`.
+ *
+ * The lifetime of the returned value is bound to the lifetime of \p Remark.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkArgRef LLVMRemarkEntryGetFirstArg(LLVMRemarkEntryRef Remark);
+
+/**
+ * Get the next argument in \p Remark from the position of \p It.
+ *
+ * Returns `NULL` if there are no more arguments available.
+ *
+ * The lifetime of the returned value is bound to the lifetime of \p Remark.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkArgRef LLVMRemarkEntryGetNextArg(LLVMRemarkArgRef It,
+                                                  LLVMRemarkEntryRef Remark);
+
+typedef struct LLVMRemarkOpaqueParser *LLVMRemarkParserRef;
+
+/**
+ * Creates a remark parser that can be used to parse the buffer located in \p
+ * Buf of size \p Size bytes.
+ *
+ * \p Buf cannot be `NULL`.
+ *
+ * This function should be paired with LLVMRemarkParserDispose() to avoid
+ * leaking resources.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkParserRef LLVMRemarkParserCreateYAML(const void *Buf,
+                                                      uint64_t Size);
+
+/**
+ * Returns the next remark in the file.
+ *
+ * The value pointed to by the return value needs to be disposed using a call to
+ * LLVMRemarkEntryDispose().
+ *
+ * All the entries in the returned value that are of LLVMRemarkStringRef type
+ * will become invalidated once a call to LLVMRemarkParserDispose is made.
+ *
+ * If the parser reaches the end of the buffer, the return value will be `NULL`.
+ *
+ * In the case of an error, the return value will be `NULL`, and:
+ *
+ * 1) LLVMRemarkParserHasError() will return `1`.
+ *
+ * 2) LLVMRemarkParserGetErrorMessage() will return a descriptive error
+ *    message.
+ *
+ * An error may occur if:
+ *
+ * 1) An argument is invalid.
+ *
+ * 2) There is a parsing error. This can occur on things like malformed YAML.
+ *
+ * 3) There is a Remark semantic error. This can occur on well-formed files with
+ *    missing or extra fields.
+ *
+ * Here is a quick example of the usage:
+ *
+ * ```
+ * LLVMRemarkParserRef Parser = LLVMRemarkParserCreateYAML(Buf, Size);
+ * LLVMRemarkEntryRef Remark = NULL;
+ * while ((Remark = LLVMRemarkParserGetNext(Parser))) {
+ *    // use Remark
+ *    LLVMRemarkEntryDispose(Remark); // Release memory.
+ * }
+ * bool HasError = LLVMRemarkParserHasError(Parser);
+ * LLVMRemarkParserDispose(Parser);
+ * ```
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMRemarkEntryRef LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser);
+
+/**
+ * Returns `1` if the parser encountered an error while parsing the buffer.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser);
+
+/**
+ * Returns a null-terminated string containing an error message.
+ *
+ * In case of no error, the result is `NULL`.
+ *
+ * The memory of the string is bound to the lifetime of \p Parser. If
+ * LLVMRemarkParserDispose() is called, the memory of the string will be
+ * released.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern const char *LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser);
+
+/**
+ * Releases all the resources used by \p Parser.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser);
+
+/**
+ * Returns the version of the remarks library.
+ *
+ * \since REMARKS_API_VERSION=0
+ */
+extern uint32_t LLVMRemarkVersion(void);
+
+/**
+ * @} // endgoup LLVMCREMARKS
+ */
+
+#ifdef __cplusplus
+}
+#endif /* !defined(__cplusplus) */
+
+#endif /* LLVM_C_REMARKS_H */

+ 65 - 0
src/llvm-c/Support.h

@@ -0,0 +1,65 @@
+/*===-- llvm-c/Support.h - Support C Interface --------------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines the C interface to the LLVM support library.             *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_SUPPORT_H
+#define LLVM_C_SUPPORT_H
+
+#include "llvm-c/DataTypes.h"
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This function permanently loads the dynamic library at the given path.
+ * It is safe to call this function multiple times for the same library.
+ *
+ * @see sys::DynamicLibrary::LoadLibraryPermanently()
+  */
+LLVMBool LLVMLoadLibraryPermanently(const char* Filename);
+
+/**
+ * This function parses the given arguments using the LLVM command line parser.
+ * Note that the only stable thing about this function is its signature; you
+ * cannot rely on any particular set of command line arguments being interpreted
+ * the same way across LLVM versions.
+ *
+ * @see llvm::cl::ParseCommandLineOptions()
+ */
+void LLVMParseCommandLineOptions(int argc, const char *const *argv,
+                                 const char *Overview);
+
+/**
+ * This function will search through all previously loaded dynamic
+ * libraries for the symbol \p symbolName. If it is found, the address of
+ * that symbol is returned. If not, null is returned.
+ *
+ * @see sys::DynamicLibrary::SearchForAddressOfSymbol()
+ */
+void *LLVMSearchForAddressOfSymbol(const char *symbolName);
+
+/**
+ * This functions permanently adds the symbol \p symbolName with the
+ * value \p symbolValue.  These symbols are searched before any
+ * libraries.
+ *
+ * @see sys::DynamicLibrary::AddSymbol()
+ */
+void LLVMAddSymbol(const char *symbolName, void *symbolValue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 295 - 0
src/llvm-c/Target.h

@@ -0,0 +1,295 @@
+/*===-- llvm-c/Target.h - Target Lib C Iface --------------------*- C++ -*-===*/
+/*                                                                            */
+/* Part of the LLVM Project, under the Apache License v2.0 with LLVM          */
+/* Exceptions.                                                                */
+/* See https://llvm.org/LICENSE.txt for license information.                  */
+/* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+/*                                                                            */
+/* This header declares the C interface to libLLVMTarget.a, which             */
+/* implements target information.                                             */
+/*                                                                            */
+/* Many exotic languages can interoperate with C code but have a harder time  */
+/* with C++ due to name mangling. So in addition to C, this interface enables */
+/* tools written in such languages.                                           */
+/*                                                                            */
+/*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TARGET_H
+#define LLVM_C_TARGET_H
+
+#include "Types.h"
+#include "Config/llvm-config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTarget Target information
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+enum LLVMByteOrdering { LLVMBigEndian, LLVMLittleEndian };
+
+typedef struct LLVMOpaqueTargetData *LLVMTargetDataRef;
+typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
+
+/* Declare all of the target-initialization functions that are available. */
+#define LLVM_TARGET(TargetName) \
+  void LLVMInitialize##TargetName##TargetInfo(void);
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+
+#define LLVM_TARGET(TargetName) void LLVMInitialize##TargetName##Target(void);
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+
+#define LLVM_TARGET(TargetName) \
+  void LLVMInitialize##TargetName##TargetMC(void);
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+
+/* Declare all of the available assembly printer initialization functions. */
+#define LLVM_ASM_PRINTER(TargetName) \
+  void LLVMInitialize##TargetName##AsmPrinter(void);
+#include "Config/AsmPrinters.def"
+#undef LLVM_ASM_PRINTER  /* Explicit undef to make SWIG happier */
+
+/* Declare all of the available assembly parser initialization functions. */
+#define LLVM_ASM_PARSER(TargetName) \
+  void LLVMInitialize##TargetName##AsmParser(void);
+#include "Config/AsmParsers.def"
+#undef LLVM_ASM_PARSER  /* Explicit undef to make SWIG happier */
+
+/* Declare all of the available disassembler initialization functions. */
+#define LLVM_DISASSEMBLER(TargetName) \
+  void LLVMInitialize##TargetName##Disassembler(void);
+#include "Config/Disassemblers.def"
+#undef LLVM_DISASSEMBLER  /* Explicit undef to make SWIG happier */
+
+/** LLVMInitializeAllTargetInfos - The main program should call this function if
+    it wants access to all available targets that LLVM is configured to
+    support. */
+static inline void LLVMInitializeAllTargetInfos(void) {
+#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetInfo();
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeAllTargets - The main program should call this function if it
+    wants to link in all available targets that LLVM is configured to
+    support. */
+static inline void LLVMInitializeAllTargets(void) {
+#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##Target();
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeAllTargetMCs - The main program should call this function if
+    it wants access to all available target MC that LLVM is configured to
+    support. */
+static inline void LLVMInitializeAllTargetMCs(void) {
+#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetMC();
+#include "Config/Targets.def"
+#undef LLVM_TARGET  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeAllAsmPrinters - The main program should call this function if
+    it wants all asm printers that LLVM is configured to support, to make them
+    available via the TargetRegistry. */
+static inline void LLVMInitializeAllAsmPrinters(void) {
+#define LLVM_ASM_PRINTER(TargetName) LLVMInitialize##TargetName##AsmPrinter();
+#include "Config/AsmPrinters.def"
+#undef LLVM_ASM_PRINTER  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeAllAsmParsers - The main program should call this function if
+    it wants all asm parsers that LLVM is configured to support, to make them
+    available via the TargetRegistry. */
+static inline void LLVMInitializeAllAsmParsers(void) {
+#define LLVM_ASM_PARSER(TargetName) LLVMInitialize##TargetName##AsmParser();
+#include "Config/AsmParsers.def"
+#undef LLVM_ASM_PARSER  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeAllDisassemblers - The main program should call this function
+    if it wants all disassemblers that LLVM is configured to support, to make
+    them available via the TargetRegistry. */
+static inline void LLVMInitializeAllDisassemblers(void) {
+#define LLVM_DISASSEMBLER(TargetName) \
+  LLVMInitialize##TargetName##Disassembler();
+#include "Config/Disassemblers.def"
+#undef LLVM_DISASSEMBLER  /* Explicit undef to make SWIG happier */
+}
+
+/** LLVMInitializeNativeTarget - The main program should call this function to
+    initialize the native target corresponding to the host.  This is useful
+    for JIT applications to ensure that the target gets linked in correctly. */
+static inline LLVMBool LLVMInitializeNativeTarget(void) {
+  /* If we have a native target, initialize it to ensure it is linked in. */
+#ifdef LLVM_NATIVE_TARGET
+  LLVM_NATIVE_TARGETINFO();
+  LLVM_NATIVE_TARGET();
+  LLVM_NATIVE_TARGETMC();
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+/** LLVMInitializeNativeTargetAsmParser - The main program should call this
+    function to initialize the parser for the native target corresponding to the
+    host. */
+static inline LLVMBool LLVMInitializeNativeAsmParser(void) {
+#ifdef LLVM_NATIVE_ASMPARSER
+  LLVM_NATIVE_ASMPARSER();
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+/** LLVMInitializeNativeTargetAsmPrinter - The main program should call this
+    function to initialize the printer for the native target corresponding to
+    the host. */
+static inline LLVMBool LLVMInitializeNativeAsmPrinter(void) {
+#ifdef LLVM_NATIVE_ASMPRINTER
+  LLVM_NATIVE_ASMPRINTER();
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+/** LLVMInitializeNativeTargetDisassembler - The main program should call this
+    function to initialize the disassembler for the native target corresponding
+    to the host. */
+static inline LLVMBool LLVMInitializeNativeDisassembler(void) {
+#ifdef LLVM_NATIVE_DISASSEMBLER
+  LLVM_NATIVE_DISASSEMBLER();
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+/*===-- Target Data -------------------------------------------------------===*/
+
+/**
+ * Obtain the data layout for a module.
+ *
+ * @see Module::getDataLayout()
+ */
+LLVMTargetDataRef LLVMGetModuleDataLayout(LLVMModuleRef M);
+
+/**
+ * Set the data layout for a module.
+ *
+ * @see Module::setDataLayout()
+ */
+void LLVMSetModuleDataLayout(LLVMModuleRef M, LLVMTargetDataRef DL);
+
+/** Creates target data from a target layout string.
+    See the constructor llvm::DataLayout::DataLayout. */
+LLVMTargetDataRef LLVMCreateTargetData(const char *StringRep);
+
+/** Deallocates a TargetData.
+    See the destructor llvm::DataLayout::~DataLayout. */
+void LLVMDisposeTargetData(LLVMTargetDataRef TD);
+
+/** Adds target library information to a pass manager. This does not take
+    ownership of the target library info.
+    See the method llvm::PassManagerBase::add. */
+void LLVMAddTargetLibraryInfo(LLVMTargetLibraryInfoRef TLI,
+                              LLVMPassManagerRef PM);
+
+/** Converts target data to a target layout string. The string must be disposed
+    with LLVMDisposeMessage.
+    See the constructor llvm::DataLayout::DataLayout. */
+char *LLVMCopyStringRepOfTargetData(LLVMTargetDataRef TD);
+
+/** Returns the byte order of a target, either LLVMBigEndian or
+    LLVMLittleEndian.
+    See the method llvm::DataLayout::isLittleEndian. */
+enum LLVMByteOrdering LLVMByteOrder(LLVMTargetDataRef TD);
+
+/** Returns the pointer size in bytes for a target.
+    See the method llvm::DataLayout::getPointerSize. */
+unsigned LLVMPointerSize(LLVMTargetDataRef TD);
+
+/** Returns the pointer size in bytes for a target for a specified
+    address space.
+    See the method llvm::DataLayout::getPointerSize. */
+unsigned LLVMPointerSizeForAS(LLVMTargetDataRef TD, unsigned AS);
+
+/** Returns the integer type that is the same size as a pointer on a target.
+    See the method llvm::DataLayout::getIntPtrType. */
+LLVMTypeRef LLVMIntPtrType(LLVMTargetDataRef TD);
+
+/** Returns the integer type that is the same size as a pointer on a target.
+    This version allows the address space to be specified.
+    See the method llvm::DataLayout::getIntPtrType. */
+LLVMTypeRef LLVMIntPtrTypeForAS(LLVMTargetDataRef TD, unsigned AS);
+
+/** Returns the integer type that is the same size as a pointer on a target.
+    See the method llvm::DataLayout::getIntPtrType. */
+LLVMTypeRef LLVMIntPtrTypeInContext(LLVMContextRef C, LLVMTargetDataRef TD);
+
+/** Returns the integer type that is the same size as a pointer on a target.
+    This version allows the address space to be specified.
+    See the method llvm::DataLayout::getIntPtrType. */
+LLVMTypeRef LLVMIntPtrTypeForASInContext(LLVMContextRef C, LLVMTargetDataRef TD,
+                                         unsigned AS);
+
+/** Computes the size of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeSizeInBits. */
+unsigned long long LLVMSizeOfTypeInBits(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the storage size of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeStoreSize. */
+unsigned long long LLVMStoreSizeOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the ABI size of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeAllocSize. */
+unsigned long long LLVMABISizeOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the ABI alignment of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeABISize. */
+unsigned LLVMABIAlignmentOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the call frame alignment of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeABISize. */
+unsigned LLVMCallFrameAlignmentOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the preferred alignment of a type in bytes for a target.
+    See the method llvm::DataLayout::getTypeABISize. */
+unsigned LLVMPreferredAlignmentOfType(LLVMTargetDataRef TD, LLVMTypeRef Ty);
+
+/** Computes the preferred alignment of a global variable in bytes for a target.
+    See the method llvm::DataLayout::getPreferredAlignment. */
+unsigned LLVMPreferredAlignmentOfGlobal(LLVMTargetDataRef TD,
+                                        LLVMValueRef GlobalVar);
+
+/** Computes the structure element that contains the byte offset for a target.
+    See the method llvm::StructLayout::getElementContainingOffset. */
+unsigned LLVMElementAtOffset(LLVMTargetDataRef TD, LLVMTypeRef StructTy,
+                             unsigned long long Offset);
+
+/** Computes the byte offset of the indexed struct element for a target.
+    See the method llvm::StructLayout::getElementContainingOffset. */
+unsigned long long LLVMOffsetOfElement(LLVMTargetDataRef TD,
+                                       LLVMTypeRef StructTy, unsigned Element);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 163 - 0
src/llvm-c/TargetMachine.h

@@ -0,0 +1,163 @@
+/*===-- llvm-c/TargetMachine.h - Target Machine Library C Interface - C++ -*-=*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to the Target and TargetMachine       *|
+|* classes, which can be used to generate assembly or object files.           *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TARGETMACHINE_H
+#define LLVM_C_TARGETMACHINE_H
+
+#include "llvm-c/Target.h"
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef;
+typedef struct LLVMTarget *LLVMTargetRef;
+
+typedef enum {
+    LLVMCodeGenLevelNone,
+    LLVMCodeGenLevelLess,
+    LLVMCodeGenLevelDefault,
+    LLVMCodeGenLevelAggressive
+} LLVMCodeGenOptLevel;
+
+typedef enum {
+    LLVMRelocDefault,
+    LLVMRelocStatic,
+    LLVMRelocPIC,
+    LLVMRelocDynamicNoPic,
+    LLVMRelocROPI,
+    LLVMRelocRWPI,
+    LLVMRelocROPI_RWPI
+} LLVMRelocMode;
+
+typedef enum {
+    LLVMCodeModelDefault,
+    LLVMCodeModelJITDefault,
+    LLVMCodeModelTiny,
+    LLVMCodeModelSmall,
+    LLVMCodeModelKernel,
+    LLVMCodeModelMedium,
+    LLVMCodeModelLarge
+} LLVMCodeModel;
+
+typedef enum {
+    LLVMAssemblyFile,
+    LLVMObjectFile
+} LLVMCodeGenFileType;
+
+/** Returns the first llvm::Target in the registered targets list. */
+LLVMTargetRef LLVMGetFirstTarget(void);
+/** Returns the next llvm::Target given a previous one (or null if there's none) */
+LLVMTargetRef LLVMGetNextTarget(LLVMTargetRef T);
+
+/*===-- Target ------------------------------------------------------------===*/
+/** Finds the target corresponding to the given name and stores it in \p T.
+  Returns 0 on success. */
+LLVMTargetRef LLVMGetTargetFromName(const char *Name);
+
+/** Finds the target corresponding to the given triple and stores it in \p T.
+  Returns 0 on success. Optionally returns any error in ErrorMessage.
+  Use LLVMDisposeMessage to dispose the message. */
+LLVMBool LLVMGetTargetFromTriple(const char* Triple, LLVMTargetRef *T,
+                                 char **ErrorMessage);
+
+/** Returns the name of a target. See llvm::Target::getName */
+const char *LLVMGetTargetName(LLVMTargetRef T);
+
+/** Returns the description  of a target. See llvm::Target::getDescription */
+const char *LLVMGetTargetDescription(LLVMTargetRef T);
+
+/** Returns if the target has a JIT */
+LLVMBool LLVMTargetHasJIT(LLVMTargetRef T);
+
+/** Returns if the target has a TargetMachine associated */
+LLVMBool LLVMTargetHasTargetMachine(LLVMTargetRef T);
+
+/** Returns if the target as an ASM backend (required for emitting output) */
+LLVMBool LLVMTargetHasAsmBackend(LLVMTargetRef T);
+
+/*===-- Target Machine ----------------------------------------------------===*/
+/** Creates a new llvm::TargetMachine. See llvm::Target::createTargetMachine */
+LLVMTargetMachineRef LLVMCreateTargetMachine(LLVMTargetRef T,
+  const char *Triple, const char *CPU, const char *Features,
+  LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, LLVMCodeModel CodeModel);
+
+/** Dispose the LLVMTargetMachineRef instance generated by
+  LLVMCreateTargetMachine. */
+void LLVMDisposeTargetMachine(LLVMTargetMachineRef T);
+
+/** Returns the Target used in a TargetMachine */
+LLVMTargetRef LLVMGetTargetMachineTarget(LLVMTargetMachineRef T);
+
+/** Returns the triple used creating this target machine. See
+  llvm::TargetMachine::getTriple. The result needs to be disposed with
+  LLVMDisposeMessage. */
+char *LLVMGetTargetMachineTriple(LLVMTargetMachineRef T);
+
+/** Returns the cpu used creating this target machine. See
+  llvm::TargetMachine::getCPU. The result needs to be disposed with
+  LLVMDisposeMessage. */
+char *LLVMGetTargetMachineCPU(LLVMTargetMachineRef T);
+
+/** Returns the feature string used creating this target machine. See
+  llvm::TargetMachine::getFeatureString. The result needs to be disposed with
+  LLVMDisposeMessage. */
+char *LLVMGetTargetMachineFeatureString(LLVMTargetMachineRef T);
+
+/** Create a DataLayout based on the targetMachine. */
+LLVMTargetDataRef LLVMCreateTargetDataLayout(LLVMTargetMachineRef T);
+
+/** Set the target machine's ASM verbosity. */
+void LLVMSetTargetMachineAsmVerbosity(LLVMTargetMachineRef T,
+                                      LLVMBool VerboseAsm);
+
+/** Emits an asm or object file for the given module to the filename. This
+  wraps several c++ only classes (among them a file stream). Returns any
+  error in ErrorMessage. Use LLVMDisposeMessage to dispose the message. */
+LLVMBool LLVMTargetMachineEmitToFile(LLVMTargetMachineRef T, LLVMModuleRef M,
+  char *Filename, LLVMCodeGenFileType codegen, char **ErrorMessage);
+
+/** Compile the LLVM IR stored in \p M and store the result in \p OutMemBuf. */
+LLVMBool LLVMTargetMachineEmitToMemoryBuffer(LLVMTargetMachineRef T, LLVMModuleRef M,
+  LLVMCodeGenFileType codegen, char** ErrorMessage, LLVMMemoryBufferRef *OutMemBuf);
+
+/*===-- Triple ------------------------------------------------------------===*/
+/** Get a triple for the host machine as a string. The result needs to be
+  disposed with LLVMDisposeMessage. */
+char* LLVMGetDefaultTargetTriple(void);
+
+/** Normalize a target triple. The result needs to be disposed with
+  LLVMDisposeMessage. */
+char* LLVMNormalizeTargetTriple(const char* triple);
+
+/** Get the host CPU as a string. The result needs to be disposed with
+  LLVMDisposeMessage. */
+char* LLVMGetHostCPUName(void);
+
+/** Get the host CPU's features as a string. The result needs to be disposed
+  with LLVMDisposeMessage. */
+char* LLVMGetHostCPUFeatures(void);
+
+/** Adds the target-specific analysis passes to the pass manager. */
+void LLVMAddAnalysisPasses(LLVMTargetMachineRef T, LLVMPassManagerRef PM);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 43 - 0
src/llvm-c/Transforms/AggressiveInstCombine.h

@@ -0,0 +1,43 @@
+/*===-- AggressiveInstCombine.h ---------------------------------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMAggressiveInstCombine.a,    *|
+|* which combines instructions to form fewer, simple IR instructions.         *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_AGGRESSIVEINSTCOMBINE_H
+#define LLVM_C_TRANSFORMS_AGGRESSIVEINSTCOMBINE_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsAggressiveInstCombine Aggressive Instruction Combining transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createAggressiveInstCombinerPass function. */
+void LLVMAddAggressiveInstCombinerPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif
+

+ 55 - 0
src/llvm-c/Transforms/Coroutines.h

@@ -0,0 +1,55 @@
+/*===-- Coroutines.h - Coroutines Library C Interface -----------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMCoroutines.a, which         *|
+|* implements various scalar transformations of the LLVM IR.                  *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_COROUTINES_H
+#define LLVM_C_TRANSFORMS_COROUTINES_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsCoroutines Coroutine transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createCoroEarlyPass function. */
+void LLVMAddCoroEarlyPass(LLVMPassManagerRef PM);
+
+/** See llvm::createCoroSplitPass function. */
+void LLVMAddCoroSplitPass(LLVMPassManagerRef PM);
+
+/** See llvm::createCoroElidePass function. */
+void LLVMAddCoroElidePass(LLVMPassManagerRef PM);
+
+/** See llvm::createCoroCleanupPass function. */
+void LLVMAddCoroCleanupPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 84 - 0
src/llvm-c/Transforms/IPO.h

@@ -0,0 +1,84 @@
+/*===-- IPO.h - Interprocedural Transformations C Interface -----*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMIPO.a, which implements     *|
+|* various interprocedural transformations of the LLVM IR.                    *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_IPO_H
+#define LLVM_C_TRANSFORMS_IPO_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsIPO Interprocedural transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createArgumentPromotionPass function. */
+void LLVMAddArgumentPromotionPass(LLVMPassManagerRef PM);
+
+/** See llvm::createConstantMergePass function. */
+void LLVMAddConstantMergePass(LLVMPassManagerRef PM);
+
+/** See llvm::createCalledValuePropagationPass function. */
+void LLVMAddCalledValuePropagationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createDeadArgEliminationPass function. */
+void LLVMAddDeadArgEliminationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createFunctionAttrsPass function. */
+void LLVMAddFunctionAttrsPass(LLVMPassManagerRef PM);
+
+/** See llvm::createFunctionInliningPass function. */
+void LLVMAddFunctionInliningPass(LLVMPassManagerRef PM);
+
+/** See llvm::createAlwaysInlinerPass function. */
+void LLVMAddAlwaysInlinerPass(LLVMPassManagerRef PM);
+
+/** See llvm::createGlobalDCEPass function. */
+void LLVMAddGlobalDCEPass(LLVMPassManagerRef PM);
+
+/** See llvm::createGlobalOptimizerPass function. */
+void LLVMAddGlobalOptimizerPass(LLVMPassManagerRef PM);
+
+/** See llvm::createIPConstantPropagationPass function. */
+void LLVMAddIPConstantPropagationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createPruneEHPass function. */
+void LLVMAddPruneEHPass(LLVMPassManagerRef PM);
+
+/** See llvm::createIPSCCPPass function. */
+void LLVMAddIPSCCPPass(LLVMPassManagerRef PM);
+
+/** See llvm::createInternalizePass function. */
+void LLVMAddInternalizePass(LLVMPassManagerRef, unsigned AllButMain);
+
+/** See llvm::createStripDeadPrototypesPass function. */
+void LLVMAddStripDeadPrototypesPass(LLVMPassManagerRef PM);
+
+/** See llvm::createStripSymbolsPass function. */
+void LLVMAddStripSymbolsPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 43 - 0
src/llvm-c/Transforms/InstCombine.h

@@ -0,0 +1,43 @@
+/*===-- Scalar.h - Scalar Transformation Library C Interface ----*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMInstCombine.a, which        *|
+|* combines instructions to form fewer, simple IR instructions.               *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_INSTCOMBINE_H
+#define LLVM_C_TRANSFORMS_INSTCOMBINE_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsInstCombine Instruction Combining transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createInstructionCombiningPass function. */
+void LLVMAddInstructionCombiningPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif
+

+ 90 - 0
src/llvm-c/Transforms/PassManagerBuilder.h

@@ -0,0 +1,90 @@
+/*===-- llvm-c/Transform/PassManagerBuilder.h - PMB C Interface ---*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to the PassManagerBuilder class.      *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_PASSMANAGERBUILDER_H
+#define LLVM_C_TRANSFORMS_PASSMANAGERBUILDER_H
+
+#include "llvm-c/Types.h"
+
+typedef struct LLVMOpaquePassManagerBuilder *LLVMPassManagerBuilderRef;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsPassManagerBuilder Pass manager builder
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::PassManagerBuilder. */
+LLVMPassManagerBuilderRef LLVMPassManagerBuilderCreate(void);
+void LLVMPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB);
+
+/** See llvm::PassManagerBuilder::OptLevel. */
+void
+LLVMPassManagerBuilderSetOptLevel(LLVMPassManagerBuilderRef PMB,
+                                  unsigned OptLevel);
+
+/** See llvm::PassManagerBuilder::SizeLevel. */
+void
+LLVMPassManagerBuilderSetSizeLevel(LLVMPassManagerBuilderRef PMB,
+                                   unsigned SizeLevel);
+
+/** See llvm::PassManagerBuilder::DisableUnitAtATime. */
+void
+LLVMPassManagerBuilderSetDisableUnitAtATime(LLVMPassManagerBuilderRef PMB,
+                                            LLVMBool Value);
+
+/** See llvm::PassManagerBuilder::DisableUnrollLoops. */
+void
+LLVMPassManagerBuilderSetDisableUnrollLoops(LLVMPassManagerBuilderRef PMB,
+                                            LLVMBool Value);
+
+/** See llvm::PassManagerBuilder::DisableSimplifyLibCalls */
+void
+LLVMPassManagerBuilderSetDisableSimplifyLibCalls(LLVMPassManagerBuilderRef PMB,
+                                                 LLVMBool Value);
+
+/** See llvm::PassManagerBuilder::Inliner. */
+void
+LLVMPassManagerBuilderUseInlinerWithThreshold(LLVMPassManagerBuilderRef PMB,
+                                              unsigned Threshold);
+
+/** See llvm::PassManagerBuilder::populateFunctionPassManager. */
+void
+LLVMPassManagerBuilderPopulateFunctionPassManager(LLVMPassManagerBuilderRef PMB,
+                                                  LLVMPassManagerRef PM);
+
+/** See llvm::PassManagerBuilder::populateModulePassManager. */
+void
+LLVMPassManagerBuilderPopulateModulePassManager(LLVMPassManagerBuilderRef PMB,
+                                                LLVMPassManagerRef PM);
+
+/** See llvm::PassManagerBuilder::populateLTOPassManager. */
+void LLVMPassManagerBuilderPopulateLTOPassManager(LLVMPassManagerBuilderRef PMB,
+                                                  LLVMPassManagerRef PM,
+                                                  LLVMBool Internalize,
+                                                  LLVMBool RunInliner);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 167 - 0
src/llvm-c/Transforms/Scalar.h

@@ -0,0 +1,167 @@
+/*===-- Scalar.h - Scalar Transformation Library C Interface ----*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMScalarOpts.a, which         *|
+|* implements various scalar transformations of the LLVM IR.                  *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_SCALAR_H
+#define LLVM_C_TRANSFORMS_SCALAR_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsScalar Scalar transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createAggressiveDCEPass function. */
+void LLVMAddAggressiveDCEPass(LLVMPassManagerRef PM);
+
+/** See llvm::createBitTrackingDCEPass function. */
+void LLVMAddBitTrackingDCEPass(LLVMPassManagerRef PM);
+
+/** See llvm::createAlignmentFromAssumptionsPass function. */
+void LLVMAddAlignmentFromAssumptionsPass(LLVMPassManagerRef PM);
+
+/** See llvm::createCFGSimplificationPass function. */
+void LLVMAddCFGSimplificationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createDeadStoreEliminationPass function. */
+void LLVMAddDeadStoreEliminationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createScalarizerPass function. */
+void LLVMAddScalarizerPass(LLVMPassManagerRef PM);
+
+/** See llvm::createMergedLoadStoreMotionPass function. */
+void LLVMAddMergedLoadStoreMotionPass(LLVMPassManagerRef PM);
+
+/** See llvm::createGVNPass function. */
+void LLVMAddGVNPass(LLVMPassManagerRef PM);
+
+/** See llvm::createGVNPass function. */
+void LLVMAddNewGVNPass(LLVMPassManagerRef PM);
+
+/** See llvm::createIndVarSimplifyPass function. */
+void LLVMAddIndVarSimplifyPass(LLVMPassManagerRef PM);
+
+/** See llvm::createInstructionCombiningPass function. */
+void LLVMAddInstructionCombiningPass(LLVMPassManagerRef PM);
+
+/** See llvm::createJumpThreadingPass function. */
+void LLVMAddJumpThreadingPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLICMPass function. */
+void LLVMAddLICMPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopDeletionPass function. */
+void LLVMAddLoopDeletionPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopIdiomPass function */
+void LLVMAddLoopIdiomPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopRotatePass function. */
+void LLVMAddLoopRotatePass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopRerollPass function. */
+void LLVMAddLoopRerollPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopUnrollPass function. */
+void LLVMAddLoopUnrollPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopUnrollAndJamPass function. */
+void LLVMAddLoopUnrollAndJamPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLoopUnswitchPass function. */
+void LLVMAddLoopUnswitchPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLowerAtomicPass function. */
+void LLVMAddLowerAtomicPass(LLVMPassManagerRef PM);
+
+/** See llvm::createMemCpyOptPass function. */
+void LLVMAddMemCpyOptPass(LLVMPassManagerRef PM);
+
+/** See llvm::createPartiallyInlineLibCallsPass function. */
+void LLVMAddPartiallyInlineLibCallsPass(LLVMPassManagerRef PM);
+
+/** See llvm::createReassociatePass function. */
+void LLVMAddReassociatePass(LLVMPassManagerRef PM);
+
+/** See llvm::createSCCPPass function. */
+void LLVMAddSCCPPass(LLVMPassManagerRef PM);
+
+/** See llvm::createSROAPass function. */
+void LLVMAddScalarReplAggregatesPass(LLVMPassManagerRef PM);
+
+/** See llvm::createSROAPass function. */
+void LLVMAddScalarReplAggregatesPassSSA(LLVMPassManagerRef PM);
+
+/** See llvm::createSROAPass function. */
+void LLVMAddScalarReplAggregatesPassWithThreshold(LLVMPassManagerRef PM,
+                                                  int Threshold);
+
+/** See llvm::createSimplifyLibCallsPass function. */
+void LLVMAddSimplifyLibCallsPass(LLVMPassManagerRef PM);
+
+/** See llvm::createTailCallEliminationPass function. */
+void LLVMAddTailCallEliminationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createConstantPropagationPass function. */
+void LLVMAddConstantPropagationPass(LLVMPassManagerRef PM);
+
+/** See llvm::demotePromoteMemoryToRegisterPass function. */
+void LLVMAddDemoteMemoryToRegisterPass(LLVMPassManagerRef PM);
+
+/** See llvm::createVerifierPass function. */
+void LLVMAddVerifierPass(LLVMPassManagerRef PM);
+
+/** See llvm::createCorrelatedValuePropagationPass function */
+void LLVMAddCorrelatedValuePropagationPass(LLVMPassManagerRef PM);
+
+/** See llvm::createEarlyCSEPass function */
+void LLVMAddEarlyCSEPass(LLVMPassManagerRef PM);
+
+/** See llvm::createEarlyCSEPass function */
+void LLVMAddEarlyCSEMemSSAPass(LLVMPassManagerRef PM);
+
+/** See llvm::createLowerExpectIntrinsicPass function */
+void LLVMAddLowerExpectIntrinsicPass(LLVMPassManagerRef PM);
+
+/** See llvm::createTypeBasedAliasAnalysisPass function */
+void LLVMAddTypeBasedAliasAnalysisPass(LLVMPassManagerRef PM);
+
+/** See llvm::createScopedNoAliasAAPass function */
+void LLVMAddScopedNoAliasAAPass(LLVMPassManagerRef PM);
+
+/** See llvm::createBasicAliasAnalysisPass function */
+void LLVMAddBasicAliasAnalysisPass(LLVMPassManagerRef PM);
+
+/** See llvm::createUnifyFunctionExitNodesPass function */
+void LLVMAddUnifyFunctionExitNodesPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 53 - 0
src/llvm-c/Transforms/Utils.h

@@ -0,0 +1,53 @@
+/*===-- Utils.h - Transformation Utils Library C Interface ------*- C++ -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMTransformUtils.a, which     *|
+|* implements various transformation utilities of the LLVM IR.                *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_UTILS_H
+#define LLVM_C_TRANSFORMS_UTILS_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsUtils Transformation Utilities
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createLowerSwitchPass function. */
+void LLVMAddLowerSwitchPass(LLVMPassManagerRef PM);
+
+/** See llvm::createPromoteMemoryToRegisterPass function. */
+void LLVMAddPromoteMemoryToRegisterPass(LLVMPassManagerRef PM);
+
+/** See llvm::createAddDiscriminatorsPass function. */
+void LLVMAddAddDiscriminatorsPass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif
+

+ 50 - 0
src/llvm-c/Transforms/Vectorize.h

@@ -0,0 +1,50 @@
+/*===---------------------------Vectorize.h --------------------- -*- C -*-===*\
+|*===----------- Vectorization Transformation Library C Interface ---------===*|
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header declares the C interface to libLLVMVectorize.a, which          *|
+|* implements various vectorization transformations of the LLVM IR.           *|
+|*                                                                            *|
+|* Many exotic languages can interoperate with C code but have a harder time  *|
+|* with C++ due to name mangling. So in addition to C, this interface enables *|
+|* tools written in such languages.                                           *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TRANSFORMS_VECTORIZE_H
+#define LLVM_C_TRANSFORMS_VECTORIZE_H
+
+#include "llvm-c/Types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCTransformsVectorize Vectorization transformations
+ * @ingroup LLVMCTransforms
+ *
+ * @{
+ */
+
+/** See llvm::createLoopVectorizePass function. */
+void LLVMAddLoopVectorizePass(LLVMPassManagerRef PM);
+
+/** See llvm::createSLPVectorizerPass function. */
+void LLVMAddSLPVectorizePass(LLVMPassManagerRef PM);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif /* defined(__cplusplus) */
+
+#endif

+ 179 - 0
src/llvm-c/Types.h

@@ -0,0 +1,179 @@
+/*===-- llvm-c/Support.h - C Interface Types declarations ---------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This file defines types used by the C interface to LLVM.                   *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_TYPES_H
+#define LLVM_C_TYPES_H
+
+#include "llvm-c/DataTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup LLVMCSupportTypes Types and Enumerations
+ *
+ * @{
+ */
+
+typedef int LLVMBool;
+
+/* Opaque types. */
+
+/**
+ * LLVM uses a polymorphic type hierarchy which C cannot represent, therefore
+ * parameters must be passed as base types. Despite the declared types, most
+ * of the functions provided operate only on branches of the type hierarchy.
+ * The declared parameter names are descriptive and specify which type is
+ * required. Additionally, each type hierarchy is documented along with the
+ * functions that operate upon it. For more detail, refer to LLVM's C++ code.
+ * If in doubt, refer to Core.cpp, which performs parameter downcasts in the
+ * form unwrap<RequiredType>(Param).
+ */
+
+/**
+ * Used to pass regions of memory through LLVM interfaces.
+ *
+ * @see llvm::MemoryBuffer
+ */
+typedef struct LLVMOpaqueMemoryBuffer *LLVMMemoryBufferRef;
+
+/**
+ * The top-level container for all LLVM global data. See the LLVMContext class.
+ */
+typedef struct LLVMOpaqueContext *LLVMContextRef;
+
+/**
+ * The top-level container for all other LLVM Intermediate Representation (IR)
+ * objects.
+ *
+ * @see llvm::Module
+ */
+typedef struct LLVMOpaqueModule *LLVMModuleRef;
+
+/**
+ * Each value in the LLVM IR has a type, an LLVMTypeRef.
+ *
+ * @see llvm::Type
+ */
+typedef struct LLVMOpaqueType *LLVMTypeRef;
+
+/**
+ * Represents an individual value in LLVM IR.
+ *
+ * This models llvm::Value.
+ */
+typedef struct LLVMOpaqueValue *LLVMValueRef;
+
+/**
+ * Represents a basic block of instructions in LLVM IR.
+ *
+ * This models llvm::BasicBlock.
+ */
+typedef struct LLVMOpaqueBasicBlock *LLVMBasicBlockRef;
+
+/**
+ * Represents an LLVM Metadata.
+ *
+ * This models llvm::Metadata.
+ */
+typedef struct LLVMOpaqueMetadata *LLVMMetadataRef;
+
+/**
+ * Represents an LLVM Named Metadata Node.
+ *
+ * This models llvm::NamedMDNode.
+ */
+typedef struct LLVMOpaqueNamedMDNode *LLVMNamedMDNodeRef;
+
+/**
+ * Represents an entry in a Global Object's metadata attachments.
+ *
+ * This models std::pair<unsigned, MDNode *>
+ */
+typedef struct LLVMOpaqueValueMetadataEntry LLVMValueMetadataEntry;
+
+/**
+ * Represents an LLVM basic block builder.
+ *
+ * This models llvm::IRBuilder.
+ */
+typedef struct LLVMOpaqueBuilder *LLVMBuilderRef;
+
+/**
+ * Represents an LLVM debug info builder.
+ *
+ * This models llvm::DIBuilder.
+ */
+typedef struct LLVMOpaqueDIBuilder *LLVMDIBuilderRef;
+
+/**
+ * Interface used to provide a module to JIT or interpreter.
+ * This is now just a synonym for llvm::Module, but we have to keep using the
+ * different type to keep binary compatibility.
+ */
+typedef struct LLVMOpaqueModuleProvider *LLVMModuleProviderRef;
+
+/** @see llvm::PassManagerBase */
+typedef struct LLVMOpaquePassManager *LLVMPassManagerRef;
+
+/** @see llvm::PassRegistry */
+typedef struct LLVMOpaquePassRegistry *LLVMPassRegistryRef;
+
+/**
+ * Used to get the users and usees of a Value.
+ *
+ * @see llvm::Use */
+typedef struct LLVMOpaqueUse *LLVMUseRef;
+
+/**
+ * Used to represent an attributes.
+ *
+ * @see llvm::Attribute
+ */
+typedef struct LLVMOpaqueAttributeRef *LLVMAttributeRef;
+
+/**
+ * @see llvm::DiagnosticInfo
+ */
+typedef struct LLVMOpaqueDiagnosticInfo *LLVMDiagnosticInfoRef;
+
+/**
+ * @see llvm::Comdat
+ */
+typedef struct LLVMComdat *LLVMComdatRef;
+
+/**
+ * @see llvm::Module::ModuleFlagEntry
+ */
+typedef struct LLVMOpaqueModuleFlagEntry LLVMModuleFlagEntry;
+
+/**
+ * @see llvm::JITEventListener
+ */
+typedef struct LLVMOpaqueJITEventListener *LLVMJITEventListenerRef;
+
+/**
+ * @see llvm::object::Binary
+ */
+typedef struct LLVMOpaqueBinary *LLVMBinaryRef;
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 899 - 0
src/llvm-c/lto.h

@@ -0,0 +1,899 @@
+/*===-- llvm-c/lto.h - LTO Public C Interface ---------------------*- C -*-===*\
+|*                                                                            *|
+|* Part of the LLVM Project, under the Apache License v2.0 with LLVM          *|
+|* Exceptions.                                                                *|
+|* See https://llvm.org/LICENSE.txt for license information.                  *|
+|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception                    *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides public interface to an abstract link time optimization*|
+|* library.  LLVM provides an implementation of this interface for use with   *|
+|* llvm bitcode files.                                                        *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_LTO_H
+#define LLVM_C_LTO_H
+
+#ifdef __cplusplus
+#include <cstddef>
+#else
+#include <stddef.h>
+#endif
+#include <sys/types.h>
+
+#ifndef __cplusplus
+#if !defined(_MSC_VER)
+#include <stdbool.h>
+typedef bool lto_bool_t;
+#else
+/* MSVC in particular does not have anything like _Bool or bool in C, but we can
+   at least make sure the type is the same size.  The implementation side will
+   use C++ bool. */
+typedef unsigned char lto_bool_t;
+#endif
+#else
+typedef bool lto_bool_t;
+#endif
+
+/**
+ * @defgroup LLVMCLTO LTO
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+#define LTO_API_VERSION 24
+
+/**
+ * \since prior to LTO_API_VERSION=3
+ */
+typedef enum {
+    LTO_SYMBOL_ALIGNMENT_MASK              = 0x0000001F, /* log2 of alignment */
+    LTO_SYMBOL_PERMISSIONS_MASK            = 0x000000E0,
+    LTO_SYMBOL_PERMISSIONS_CODE            = 0x000000A0,
+    LTO_SYMBOL_PERMISSIONS_DATA            = 0x000000C0,
+    LTO_SYMBOL_PERMISSIONS_RODATA          = 0x00000080,
+    LTO_SYMBOL_DEFINITION_MASK             = 0x00000700,
+    LTO_SYMBOL_DEFINITION_REGULAR          = 0x00000100,
+    LTO_SYMBOL_DEFINITION_TENTATIVE        = 0x00000200,
+    LTO_SYMBOL_DEFINITION_WEAK             = 0x00000300,
+    LTO_SYMBOL_DEFINITION_UNDEFINED        = 0x00000400,
+    LTO_SYMBOL_DEFINITION_WEAKUNDEF        = 0x00000500,
+    LTO_SYMBOL_SCOPE_MASK                  = 0x00003800,
+    LTO_SYMBOL_SCOPE_INTERNAL              = 0x00000800,
+    LTO_SYMBOL_SCOPE_HIDDEN                = 0x00001000,
+    LTO_SYMBOL_SCOPE_PROTECTED             = 0x00002000,
+    LTO_SYMBOL_SCOPE_DEFAULT               = 0x00001800,
+    LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN = 0x00002800,
+    LTO_SYMBOL_COMDAT                      = 0x00004000,
+    LTO_SYMBOL_ALIAS                       = 0x00008000
+} lto_symbol_attributes;
+
+/**
+ * \since prior to LTO_API_VERSION=3
+ */
+typedef enum {
+    LTO_DEBUG_MODEL_NONE         = 0,
+    LTO_DEBUG_MODEL_DWARF        = 1
+} lto_debug_model;
+
+/**
+ * \since prior to LTO_API_VERSION=3
+ */
+typedef enum {
+    LTO_CODEGEN_PIC_MODEL_STATIC         = 0,
+    LTO_CODEGEN_PIC_MODEL_DYNAMIC        = 1,
+    LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC = 2,
+    LTO_CODEGEN_PIC_MODEL_DEFAULT        = 3
+} lto_codegen_model;
+
+/** opaque reference to a loaded object module */
+typedef struct LLVMOpaqueLTOModule *lto_module_t;
+
+/** opaque reference to a code generator */
+typedef struct LLVMOpaqueLTOCodeGenerator *lto_code_gen_t;
+
+/** opaque reference to a thin code generator */
+typedef struct LLVMOpaqueThinLTOCodeGenerator *thinlto_code_gen_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Returns a printable string.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern const char*
+lto_get_version(void);
+
+/**
+ * Returns the last error string or NULL if last operation was successful.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern const char*
+lto_get_error_message(void);
+
+/**
+ * Checks if a file is a loadable object file.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_module_is_object_file(const char* path);
+
+/**
+ * Checks if a file is a loadable object compiled for requested target.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_module_is_object_file_for_target(const char* path,
+                                     const char* target_triple_prefix);
+
+/**
+ * Return true if \p Buffer contains a bitcode file with ObjC code (category
+ * or class) in it.
+ *
+ * \since LTO_API_VERSION=20
+ */
+extern lto_bool_t
+lto_module_has_objc_category(const void *mem, size_t length);
+
+/**
+ * Checks if a buffer is a loadable object file.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t lto_module_is_object_file_in_memory(const void *mem,
+                                                      size_t length);
+
+/**
+ * Checks if a buffer is a loadable object compiled for requested target.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_module_is_object_file_in_memory_for_target(const void* mem, size_t length,
+                                              const char* target_triple_prefix);
+
+/**
+ * Loads an object file from disk.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_module_t
+lto_module_create(const char* path);
+
+/**
+ * Loads an object file from memory.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_module_t
+lto_module_create_from_memory(const void* mem, size_t length);
+
+/**
+ * Loads an object file from memory with an extra path argument.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=9
+ */
+extern lto_module_t
+lto_module_create_from_memory_with_path(const void* mem, size_t length,
+                                        const char *path);
+
+/**
+ * Loads an object file in its own context.
+ *
+ * Loads an object file in its own LLVMContext.  This function call is
+ * thread-safe.  However, modules created this way should not be merged into an
+ * lto_code_gen_t using \a lto_codegen_add_module().
+ *
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=11
+ */
+extern lto_module_t
+lto_module_create_in_local_context(const void *mem, size_t length,
+                                   const char *path);
+
+/**
+ * Loads an object file in the codegen context.
+ *
+ * Loads an object file into the same context as \c cg.  The module is safe to
+ * add using \a lto_codegen_add_module().
+ *
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=11
+ */
+extern lto_module_t
+lto_module_create_in_codegen_context(const void *mem, size_t length,
+                                     const char *path, lto_code_gen_t cg);
+
+/**
+ * Loads an object file from disk. The seek point of fd is not preserved.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=5
+ */
+extern lto_module_t
+lto_module_create_from_fd(int fd, const char *path, size_t file_size);
+
+/**
+ * Loads an object file from disk. The seek point of fd is not preserved.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=5
+ */
+extern lto_module_t
+lto_module_create_from_fd_at_offset(int fd, const char *path, size_t file_size,
+                                    size_t map_size, off_t offset);
+
+/**
+ * Frees all memory internally allocated by the module.
+ * Upon return the lto_module_t is no longer valid.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern void
+lto_module_dispose(lto_module_t mod);
+
+/**
+ * Returns triple string which the object module was compiled under.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern const char*
+lto_module_get_target_triple(lto_module_t mod);
+
+/**
+ * Sets triple string with which the object will be codegened.
+ *
+ * \since LTO_API_VERSION=4
+ */
+extern void
+lto_module_set_target_triple(lto_module_t mod, const char *triple);
+
+/**
+ * Returns the number of symbols in the object module.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern unsigned int
+lto_module_get_num_symbols(lto_module_t mod);
+
+/**
+ * Returns the name of the ith symbol in the object module.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern const char*
+lto_module_get_symbol_name(lto_module_t mod, unsigned int index);
+
+/**
+ * Returns the attributes of the ith symbol in the object module.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_symbol_attributes
+lto_module_get_symbol_attribute(lto_module_t mod, unsigned int index);
+
+/**
+ * Returns the module's linker options.
+ *
+ * The linker options may consist of multiple flags. It is the linker's
+ * responsibility to split the flags using a platform-specific mechanism.
+ *
+ * \since LTO_API_VERSION=16
+ */
+extern const char*
+lto_module_get_linkeropts(lto_module_t mod);
+
+/**
+ * Diagnostic severity.
+ *
+ * \since LTO_API_VERSION=7
+ */
+typedef enum {
+  LTO_DS_ERROR = 0,
+  LTO_DS_WARNING = 1,
+  LTO_DS_REMARK = 3, // Added in LTO_API_VERSION=10.
+  LTO_DS_NOTE = 2
+} lto_codegen_diagnostic_severity_t;
+
+/**
+ * Diagnostic handler type.
+ * \p severity defines the severity.
+ * \p diag is the actual diagnostic.
+ * The diagnostic is not prefixed by any of severity keyword, e.g., 'error: '.
+ * \p ctxt is used to pass the context set with the diagnostic handler.
+ *
+ * \since LTO_API_VERSION=7
+ */
+typedef void (*lto_diagnostic_handler_t)(
+    lto_codegen_diagnostic_severity_t severity, const char *diag, void *ctxt);
+
+/**
+ * Set a diagnostic handler and the related context (void *).
+ * This is more general than lto_get_error_message, as the diagnostic handler
+ * can be called at anytime within lto.
+ *
+ * \since LTO_API_VERSION=7
+ */
+extern void lto_codegen_set_diagnostic_handler(lto_code_gen_t,
+                                               lto_diagnostic_handler_t,
+                                               void *);
+
+/**
+ * Instantiates a code generator.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ * All modules added using \a lto_codegen_add_module() must have been created
+ * in the same context as the codegen.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_code_gen_t
+lto_codegen_create(void);
+
+/**
+ * Instantiate a code generator in its own context.
+ *
+ * Instantiates a code generator in its own context.  Modules added via \a
+ * lto_codegen_add_module() must have all been created in the same context,
+ * using \a lto_module_create_in_codegen_context().
+ *
+ * \since LTO_API_VERSION=11
+ */
+extern lto_code_gen_t
+lto_codegen_create_in_local_context(void);
+
+/**
+ * Frees all code generator and all memory it internally allocated.
+ * Upon return the lto_code_gen_t is no longer valid.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern void
+lto_codegen_dispose(lto_code_gen_t);
+
+/**
+ * Add an object module to the set of modules for which code will be generated.
+ * Returns true on error (check lto_get_error_message() for details).
+ *
+ * \c cg and \c mod must both be in the same context.  See \a
+ * lto_codegen_create_in_local_context() and \a
+ * lto_module_create_in_codegen_context().
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod);
+
+/**
+ * Sets the object module for code generation. This will transfer the ownership
+ * of the module to the code generator.
+ *
+ * \c cg and \c mod must both be in the same context.
+ *
+ * \since LTO_API_VERSION=13
+ */
+extern void
+lto_codegen_set_module(lto_code_gen_t cg, lto_module_t mod);
+
+/**
+ * Sets if debug info should be generated.
+ * Returns true on error (check lto_get_error_message() for details).
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model);
+
+/**
+ * Sets which PIC code model to generated.
+ * Returns true on error (check lto_get_error_message() for details).
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern lto_bool_t
+lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model);
+
+/**
+ * Sets the cpu to generate code for.
+ *
+ * \since LTO_API_VERSION=4
+ */
+extern void
+lto_codegen_set_cpu(lto_code_gen_t cg, const char *cpu);
+
+/**
+ * Sets the location of the assembler tool to run. If not set, libLTO
+ * will use gcc to invoke the assembler.
+ *
+ * \since LTO_API_VERSION=3
+ */
+extern void
+lto_codegen_set_assembler_path(lto_code_gen_t cg, const char* path);
+
+/**
+ * Sets extra arguments that libLTO should pass to the assembler.
+ *
+ * \since LTO_API_VERSION=4
+ */
+extern void
+lto_codegen_set_assembler_args(lto_code_gen_t cg, const char **args,
+                               int nargs);
+
+/**
+ * Adds to a list of all global symbols that must exist in the final generated
+ * code. If a function is not listed there, it might be inlined into every usage
+ * and optimized away.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern void
+lto_codegen_add_must_preserve_symbol(lto_code_gen_t cg, const char* symbol);
+
+/**
+ * Writes a new object file at the specified path that contains the
+ * merged contents of all modules added so far.
+ * Returns true on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=5
+ */
+extern lto_bool_t
+lto_codegen_write_merged_modules(lto_code_gen_t cg, const char* path);
+
+/**
+ * Generates code for all added modules into one native object file.
+ * This calls lto_codegen_optimize then lto_codegen_compile_optimized.
+ *
+ * On success returns a pointer to a generated mach-o/ELF buffer and
+ * length set to the buffer size.  The buffer is owned by the
+ * lto_code_gen_t and will be freed when lto_codegen_dispose()
+ * is called, or lto_codegen_compile() is called again.
+ * On failure, returns NULL (check lto_get_error_message() for details).
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern const void*
+lto_codegen_compile(lto_code_gen_t cg, size_t* length);
+
+/**
+ * Generates code for all added modules into one native object file.
+ * This calls lto_codegen_optimize then lto_codegen_compile_optimized (instead
+ * of returning a generated mach-o/ELF buffer, it writes to a file).
+ *
+ * The name of the file is written to name. Returns true on error.
+ *
+ * \since LTO_API_VERSION=5
+ */
+extern lto_bool_t
+lto_codegen_compile_to_file(lto_code_gen_t cg, const char** name);
+
+/**
+ * Runs optimization for the merged module. Returns true on error.
+ *
+ * \since LTO_API_VERSION=12
+ */
+extern lto_bool_t
+lto_codegen_optimize(lto_code_gen_t cg);
+
+/**
+ * Generates code for the optimized merged module into one native object file.
+ * It will not run any IR optimizations on the merged module.
+ *
+ * On success returns a pointer to a generated mach-o/ELF buffer and length set
+ * to the buffer size.  The buffer is owned by the lto_code_gen_t and will be
+ * freed when lto_codegen_dispose() is called, or
+ * lto_codegen_compile_optimized() is called again. On failure, returns NULL
+ * (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=12
+ */
+extern const void*
+lto_codegen_compile_optimized(lto_code_gen_t cg, size_t* length);
+
+/**
+ * Returns the runtime API version.
+ *
+ * \since LTO_API_VERSION=12
+ */
+extern unsigned int
+lto_api_version(void);
+
+/**
+ * Sets options to help debug codegen bugs.
+ *
+ * \since prior to LTO_API_VERSION=3
+ */
+extern void
+lto_codegen_debug_options(lto_code_gen_t cg, const char *);
+
+/**
+ * Initializes LLVM disassemblers.
+ * FIXME: This doesn't really belong here.
+ *
+ * \since LTO_API_VERSION=5
+ */
+extern void
+lto_initialize_disassembler(void);
+
+/**
+ * Sets if we should run internalize pass during optimization and code
+ * generation.
+ *
+ * \since LTO_API_VERSION=14
+ */
+extern void
+lto_codegen_set_should_internalize(lto_code_gen_t cg,
+                                   lto_bool_t ShouldInternalize);
+
+/**
+ * Set whether to embed uselists in bitcode.
+ *
+ * Sets whether \a lto_codegen_write_merged_modules() should embed uselists in
+ * output bitcode.  This should be turned on for all -save-temps output.
+ *
+ * \since LTO_API_VERSION=15
+ */
+extern void
+lto_codegen_set_should_embed_uselists(lto_code_gen_t cg,
+                                      lto_bool_t ShouldEmbedUselists);
+
+/**
+ * @} // endgoup LLVMCLTO
+ * @defgroup LLVMCTLTO ThinLTO
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+/**
+ * Type to wrap a single object returned by ThinLTO.
+ *
+ * \since LTO_API_VERSION=18
+ */
+typedef struct {
+  const char *Buffer;
+  size_t Size;
+} LTOObjectBuffer;
+
+/**
+ * Instantiates a ThinLTO code generator.
+ * Returns NULL on error (check lto_get_error_message() for details).
+ *
+ *
+ * The ThinLTOCodeGenerator is not intended to be reuse for multiple
+ * compilation: the model is that the client adds modules to the generator and
+ * ask to perform the ThinLTO optimizations / codegen, and finally destroys the
+ * codegenerator.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern thinlto_code_gen_t thinlto_create_codegen(void);
+
+/**
+ * Frees the generator and all memory it internally allocated.
+ * Upon return the thinlto_code_gen_t is no longer valid.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_dispose(thinlto_code_gen_t cg);
+
+/**
+ * Add a module to a ThinLTO code generator. Identifier has to be unique among
+ * all the modules in a code generator. The data buffer stays owned by the
+ * client, and is expected to be available for the entire lifetime of the
+ * thinlto_code_gen_t it is added to.
+ *
+ * On failure, returns NULL (check lto_get_error_message() for details).
+ *
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_add_module(thinlto_code_gen_t cg,
+                                       const char *identifier, const char *data,
+                                       int length);
+
+/**
+ * Optimize and codegen all the modules added to the codegenerator using
+ * ThinLTO. Resulting objects are accessible using thinlto_module_get_object().
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_process(thinlto_code_gen_t cg);
+
+/**
+ * Returns the number of object files produced by the ThinLTO CodeGenerator.
+ *
+ * It usually matches the number of input files, but this is not a guarantee of
+ * the API and may change in future implementation, so the client should not
+ * assume it.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern unsigned int thinlto_module_get_num_objects(thinlto_code_gen_t cg);
+
+/**
+ * Returns a reference to the ith object file produced by the ThinLTO
+ * CodeGenerator.
+ *
+ * Client should use \p thinlto_module_get_num_objects() to get the number of
+ * available objects.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern LTOObjectBuffer thinlto_module_get_object(thinlto_code_gen_t cg,
+                                                 unsigned int index);
+
+/**
+ * Returns the number of object files produced by the ThinLTO CodeGenerator.
+ *
+ * It usually matches the number of input files, but this is not a guarantee of
+ * the API and may change in future implementation, so the client should not
+ * assume it.
+ *
+ * \since LTO_API_VERSION=21
+ */
+unsigned int thinlto_module_get_num_object_files(thinlto_code_gen_t cg);
+
+/**
+ * Returns the path to the ith object file produced by the ThinLTO
+ * CodeGenerator.
+ *
+ * Client should use \p thinlto_module_get_num_object_files() to get the number
+ * of available objects.
+ *
+ * \since LTO_API_VERSION=21
+ */
+const char *thinlto_module_get_object_file(thinlto_code_gen_t cg,
+                                           unsigned int index);
+
+/**
+ * Sets which PIC code model to generate.
+ * Returns true on error (check lto_get_error_message() for details).
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern lto_bool_t thinlto_codegen_set_pic_model(thinlto_code_gen_t cg,
+                                                lto_codegen_model);
+
+/**
+ * Sets the path to a directory to use as a storage for temporary bitcode files.
+ * The intention is to make the bitcode files available for debugging at various
+ * stage of the pipeline.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg,
+                                              const char *save_temps_dir);
+
+/**
+ * Set the path to a directory where to save generated object files. This
+ * path can be used by a linker to request on-disk files instead of in-memory
+ * buffers. When set, results are available through
+ * thinlto_module_get_object_file() instead of thinlto_module_get_object().
+ *
+ * \since LTO_API_VERSION=21
+ */
+void thinlto_set_generated_objects_dir(thinlto_code_gen_t cg,
+                                       const char *save_temps_dir);
+
+/**
+ * Sets the cpu to generate code for.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu);
+
+/**
+ * Disable CodeGen, only run the stages till codegen and stop. The output will
+ * be bitcode.
+ *
+ * \since LTO_API_VERSION=19
+ */
+extern void thinlto_codegen_disable_codegen(thinlto_code_gen_t cg,
+                                            lto_bool_t disable);
+
+/**
+ * Perform CodeGen only: disable all other stages.
+ *
+ * \since LTO_API_VERSION=19
+ */
+extern void thinlto_codegen_set_codegen_only(thinlto_code_gen_t cg,
+                                             lto_bool_t codegen_only);
+
+/**
+ * Parse -mllvm style debug options.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_debug_options(const char *const *options, int number);
+
+/**
+ * Test if a module has support for ThinLTO linking.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern lto_bool_t lto_module_is_thinlto(lto_module_t mod);
+
+/**
+ * Adds a symbol to the list of global symbols that must exist in the final
+ * generated code. If a function is not listed there, it might be inlined into
+ * every usage and optimized away. For every single module, the functions
+ * referenced from code outside of the ThinLTO modules need to be added here.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg,
+                                                     const char *name,
+                                                     int length);
+
+/**
+ * Adds a symbol to the list of global symbols that are cross-referenced between
+ * ThinLTO files. If the ThinLTO CodeGenerator can ensure that every
+ * references from a ThinLTO module to this symbol is optimized away, then
+ * the symbol can be discarded.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_add_cross_referenced_symbol(thinlto_code_gen_t cg,
+                                                        const char *name,
+                                                        int length);
+
+/**
+ * @} // endgoup LLVMCTLTO
+ * @defgroup LLVMCTLTO_CACHING ThinLTO Cache Control
+ * @ingroup LLVMCTLTO
+ *
+ * These entry points control the ThinLTO cache. The cache is intended to
+ * support incremental builds, and thus needs to be persistent across builds.
+ * The client enables the cache by supplying a path to an existing directory.
+ * The code generator will use this to store objects files that may be reused
+ * during a subsequent build.
+ * To avoid filling the disk space, a few knobs are provided:
+ *  - The pruning interval limits the frequency at which the garbage collector
+ *    will try to scan the cache directory to prune expired entries.
+ *    Setting to a negative number disables the pruning.
+ *  - The pruning expiration time indicates to the garbage collector how old an
+ *    entry needs to be to be removed.
+ *  - Finally, the garbage collector can be instructed to prune the cache until
+ *    the occupied space goes below a threshold.
+ * @{
+ */
+
+/**
+ * Sets the path to a directory to use as a cache storage for incremental build.
+ * Setting this activates caching.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_cache_dir(thinlto_code_gen_t cg,
+                                          const char *cache_dir);
+
+/**
+ * Sets the cache pruning interval (in seconds). A negative value disables the
+ * pruning. An unspecified default value will be applied, and a value of 0 will
+ * force prunning to occur.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_cache_pruning_interval(thinlto_code_gen_t cg,
+                                                       int interval);
+
+/**
+ * Sets the maximum cache size that can be persistent across build, in terms of
+ * percentage of the available space on the disk. Set to 100 to indicate
+ * no limit, 50 to indicate that the cache size will not be left over half the
+ * available space. A value over 100 will be reduced to 100, a value of 0 will
+ * be ignored. An unspecified default value will be applied.
+ *
+ * The formula looks like:
+ *  AvailableSpace = FreeSpace + ExistingCacheSize
+ *  NewCacheSize = AvailableSpace * P/100
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_final_cache_size_relative_to_available_space(
+    thinlto_code_gen_t cg, unsigned percentage);
+
+/**
+ * Sets the expiration (in seconds) for an entry in the cache. An unspecified
+ * default value will be applied. A value of 0 will be ignored.
+ *
+ * \since LTO_API_VERSION=18
+ */
+extern void thinlto_codegen_set_cache_entry_expiration(thinlto_code_gen_t cg,
+                                                       unsigned expiration);
+
+/**
+ * Sets the maximum size of the cache directory (in bytes). A value over the
+ * amount of available space on the disk will be reduced to the amount of
+ * available space. An unspecified default value will be applied. A value of 0
+ * will be ignored.
+ *
+ * \since LTO_API_VERSION=22
+ */
+extern void thinlto_codegen_set_cache_size_bytes(thinlto_code_gen_t cg,
+                                                 unsigned max_size_bytes);
+
+/**
+ * Same as thinlto_codegen_set_cache_size_bytes, except the maximum size is in
+ * megabytes (2^20 bytes).
+ *
+ * \since LTO_API_VERSION=23
+ */
+extern void
+thinlto_codegen_set_cache_size_megabytes(thinlto_code_gen_t cg,
+                                         unsigned max_size_megabytes);
+
+/**
+ * Sets the maximum number of files in the cache directory. An unspecified
+ * default value will be applied. A value of 0 will be ignored.
+ *
+ * \since LTO_API_VERSION=22
+ */
+extern void thinlto_codegen_set_cache_size_files(thinlto_code_gen_t cg,
+                                                 unsigned max_size_files);
+
+/** Opaque reference to an LTO input file */
+typedef struct LLVMOpaqueLTOInput *lto_input_t;
+
+/**
+  * Creates an LTO input file from a buffer. The path
+  * argument is used for diagnotics as this function
+  * otherwise does not know which file the given buffer
+  * is associated with.
+  *
+  * \since LTO_API_VERSION=24
+  */
+extern lto_input_t lto_input_create(const void *buffer,
+                                    size_t buffer_size,
+                                    const char *path);
+
+/**
+  * Frees all memory internally allocated by the LTO input file.
+  * Upon return the lto_module_t is no longer valid.
+  *
+  * \since LTO_API_VERSION=24
+  */
+extern void lto_input_dispose(lto_input_t input);
+
+/**
+  * Returns the number of dependent library specifiers
+  * for the given LTO input file.
+  *
+  * \since LTO_API_VERSION=24
+  */
+extern unsigned lto_input_get_num_dependent_libraries(lto_input_t input);
+
+/**
+  * Returns the ith dependent library specifier
+  * for the given LTO input file. The returned
+  * string is not null-terminated.
+  *
+  * \since LTO_API_VERSION=24
+  */
+extern const char * lto_input_get_dependent_library(lto_input_t input,
+                                                    size_t index,
+                                                    size_t *size);
+
+/**
+ * @} // endgroup LLVMCTLTO_CACHING
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LLVM_C_LTO_H */

+ 11024 - 0
src/llvm_backend.cpp

@@ -0,0 +1,11024 @@
+#include "llvm_backend.hpp"
+
+gb_global lbAddr lb_global_type_info_data           = {};
+gb_global lbAddr lb_global_type_info_member_types   = {};
+gb_global lbAddr lb_global_type_info_member_names   = {};
+gb_global lbAddr lb_global_type_info_member_offsets = {};
+gb_global lbAddr lb_global_type_info_member_usings  = {};
+gb_global lbAddr lb_global_type_info_member_tags    = {};
+
+gb_global isize lb_global_type_info_data_index           = 0;
+gb_global isize lb_global_type_info_member_types_index   = 0;
+gb_global isize lb_global_type_info_member_names_index   = 0;
+gb_global isize lb_global_type_info_member_offsets_index = 0;
+gb_global isize lb_global_type_info_member_usings_index  = 0;
+gb_global isize lb_global_type_info_member_tags_index    = 0;
+
+
+struct lbLoopData {
+	lbAddr idx_addr;
+	lbValue idx;
+	lbBlock *body;
+	lbBlock *done;
+	lbBlock *loop;
+};
+
+struct lbCompoundLitElemTempData {
+	Ast *   expr;
+	lbValue value;
+	i32     elem_index;
+	lbValue gep;
+};
+
+lbLoopData lb_loop_start(lbProcedure *p, isize count, Type *index_type=t_i32);
+void lb_loop_end(lbProcedure *p, lbLoopData const &data);
+
+LLVMValueRef llvm_zero32(lbModule *m) {
+	return LLVMConstInt(lb_type(m, t_i32), 0, false);
+}
+LLVMValueRef llvm_one32(lbModule *m) {
+	return LLVMConstInt(lb_type(m, t_i32), 1, false);
+}
+
+lbValue lb_zero(lbModule *m, Type *t) {
+	lbValue v = {};
+	v.value = LLVMConstInt(lb_type(m, t), 0, false);
+	v.type = t;
+	return v;
+}
+
+LLVMValueRef llvm_cstring(lbModule *m, String const &str) {
+	lbValue v = lb_find_or_add_entity_string(m, str);
+	unsigned indices[1] = {0};
+	return LLVMConstExtractValue(v.value, indices, gb_count_of(indices));
+}
+
+
+lbAddr lb_addr(lbValue addr) {
+	lbAddr v = {lbAddr_Default, addr};
+	return v;
+}
+
+
+lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+	lbAddr v = {lbAddr_Map, addr};
+	v.map.key    = map_key;
+	v.map.type   = map_type;
+	v.map.result = map_result;
+	return v;
+}
+
+
+lbAddr lb_addr_soa_variable(lbValue addr, lbValue index, Ast *index_expr) {
+	lbAddr v = {lbAddr_SoaVariable, addr};
+	v.soa.index = index;
+	v.soa.index_expr = index_expr;
+	return v;
+}
+
+lbAddr lb_addr_bit_field(lbValue value, i32 index) {
+	lbAddr addr = {};
+	addr.kind = lbAddr_BitField;
+	addr.addr = value;
+	addr.bit_field.value_index = index;
+	return addr;
+}
+
+
+Type *lb_addr_type(lbAddr const &addr) {
+	if (addr.addr.value == nullptr) {
+		return nullptr;
+	}
+	if (addr.kind == lbAddr_Map) {
+		Type *t = base_type(addr.map.type);
+		GB_ASSERT(is_type_map(t));
+		return t->Map.value;
+	}
+	return type_deref(addr.addr.type);
+}
+LLVMTypeRef lb_addr_lb_type(lbAddr const &addr) {
+	return LLVMGetElementType(LLVMTypeOf(addr.addr.value));
+}
+
+lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
+	if (addr.addr.value == nullptr) {
+		GB_PANIC("Illegal addr -> nullptr");
+		return {};
+	}
+
+	switch (addr.kind) {
+	case lbAddr_Map: {
+		Type *map_type = base_type(addr.map.type);
+		lbValue h = lb_gen_map_header(p, addr.addr, map_type);
+		lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key);
+
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = h;
+		args[1] = key;
+
+		lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
+
+		return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
+	}
+	case lbAddr_BitField: {
+		lbValue v = lb_addr_load(p, addr);
+		return lb_address_from_load_or_generate_local(p, v);
+	}
+
+	case lbAddr_Context:
+		GB_PANIC("lbAddr_Context should be handled elsewhere");
+	}
+
+	return addr.addr;
+}
+
+
+lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
+	lbAddr addr = lb_build_addr(p, expr);
+	return lb_addr_get_ptr(p, addr);
+}
+
+
+
+void lb_addr_store(lbProcedure *p, lbAddr const &addr, lbValue value) {
+	if (addr.addr.value == nullptr) {
+		return;
+	}
+	GB_ASSERT(value.type != nullptr);
+	if (is_type_untyped_undef(value.type)) {
+		Type *t = lb_addr_type(addr);
+		value.type = t;
+		value.value = LLVMGetUndef(lb_type(p->module, t));
+	} else if (is_type_untyped_nil(value.type)) {
+		Type *t = lb_addr_type(addr);
+		value.type = t;
+		value.value = LLVMConstNull(lb_type(p->module, t));
+	}
+
+
+	if (addr.kind == lbAddr_Map) {
+		lb_insert_dynamic_map_key_and_value(p, addr, addr.map.type, addr.map.key, value);
+		return;
+	} else if (addr.kind == lbAddr_BitField) {
+		Type *bft = base_type(type_deref(addr.addr.type));
+		GB_ASSERT(is_type_bit_field(bft));
+
+		unsigned value_index = cast(unsigned)addr.bit_field.value_index;
+		i32 size_in_bits = bft->BitField.fields[value_index]->type->BitFieldValue.bits;
+		if (size_in_bits == 0) {
+			return;
+		}
+		i32 size_in_bytes = next_pow2((size_in_bits+7)/8);
+
+		LLVMTypeRef dst_type = LLVMIntTypeInContext(p->module->ctx, size_in_bits);
+		LLVMValueRef src = LLVMBuildIntCast2(p->builder, value.value, dst_type, false, "");
+
+		LLVMValueRef internal_data = LLVMBuildStructGEP(p->builder, addr.addr.value, 1, "");
+		LLVMValueRef field_ptr = LLVMBuildStructGEP(p->builder, internal_data, value_index, "");
+		LLVMBuildStore(p->builder, src, field_ptr);
+		return;
+	} else if (addr.kind == lbAddr_Context) {
+		lbValue old = lb_addr_load(p, lb_find_or_generate_context_ptr(p));
+		lbAddr next_addr = lb_add_local_generated(p, t_context, true);
+		lb_addr_store(p, next_addr, old);
+		lb_push_context_onto_stack(p, next_addr);
+		lbValue next = lb_addr_get_ptr(p, next_addr);
+
+		if (addr.ctx.sel.index.count > 0) {
+			lbValue lhs = lb_emit_deep_field_gep(p, next, addr.ctx.sel);
+			lbValue rhs = lb_emit_conv(p, value, type_deref(lhs.type));
+			lb_emit_store(p, lhs, rhs);
+		} else {
+			lbValue lhs = next;
+			lbValue rhs = lb_emit_conv(p, value, lb_addr_type(addr));
+			lb_emit_store(p, lhs, rhs);
+		}
+
+		return;
+	} else if (addr.kind == lbAddr_SoaVariable) {
+		Type *t = type_deref(addr.addr.type);
+		t = base_type(t);
+		GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
+		value = lb_emit_conv(p, value, t->Struct.soa_elem);
+
+		lbValue index = addr.soa.index;
+		if (!lb_is_const(index) || t->Struct.soa_kind != StructSoa_Fixed) {
+			Type *t = base_type(type_deref(addr.addr.type));
+			GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
+			i64 count = t->Struct.soa_count;
+			lbValue len = lb_const_int(p->module, t_int, count);
+			// lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len);
+		}
+
+		for_array(i, t->Struct.fields) {
+			lbValue dst = lb_emit_struct_ep(p, addr.addr, cast(i32)i);
+			dst = lb_emit_array_ep(p, dst, index);
+			lbValue src = lb_emit_struct_ev(p, value, cast(i32)i);
+			lb_emit_store(p, dst, src);
+		}
+		return;
+	}
+
+	GB_ASSERT(value.value != nullptr);
+	value = lb_emit_conv(p, value, lb_addr_type(addr));
+
+	LLVMBuildStore(p->builder, value.value, addr.addr.value);
+}
+
+void lb_const_store(lbValue ptr, lbValue value) {
+	GB_ASSERT(lb_is_const(ptr));
+	GB_ASSERT(lb_is_const(value));
+	GB_ASSERT(is_type_pointer(ptr.type));
+	LLVMSetInitializer(ptr.value, value.value);
+}
+
+
+void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
+	GB_ASSERT(value.value != nullptr);
+	Type *a = type_deref(ptr.type);
+	if (is_type_boolean(a)) {
+		// NOTE(bill): There are multiple sized booleans, thus force a conversion (if necessarily)
+		value = lb_emit_conv(p, value, a);
+	}
+	Type *ca = core_type(a);
+	if (ca->kind == Type_Basic) {
+		GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type));
+	} else {
+		GB_ASSERT_MSG(are_types_identical(a, value.type), "%s != %s", type_to_string(a), type_to_string(value.type));
+	}
+
+	LLVMBuildStore(p->builder, value.value, ptr.value);
+}
+
+lbValue lb_emit_load(lbProcedure *p, lbValue value) {
+	lbModule *m = p->module;
+	GB_ASSERT(value.value != nullptr);
+	Type *t = type_deref(value.type);
+	LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(m, t), value.value, "");
+	return lbValue{v, t};
+}
+
+lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
+	GB_ASSERT(addr.addr.value != nullptr);
+
+	if (addr.kind == lbAddr_Map) {
+		Type *map_type = base_type(addr.map.type);
+		lbAddr v = lb_add_local_generated(p, map_type->Map.lookup_result_type, true);
+		lbValue h = lb_gen_map_header(p, addr.addr, map_type);
+		lbValue key = lb_gen_map_key(p, addr.map.key, map_type->Map.key);
+
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = h;
+		args[1] = key;
+
+		lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
+		lbValue ok = lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 1), ok);
+
+		lbBlock *then = lb_create_block(p, "map.get.then");
+		lbBlock *done = lb_create_block(p, "map.get.done");
+		lb_emit_if(p, ok, then, done);
+		lb_start_block(p, then);
+		{
+			// TODO(bill): mem copy it instead?
+			lbValue gep0 = lb_emit_struct_ep(p, v.addr, 0);
+			lbValue value = lb_emit_conv(p, ptr, gep0.type);
+			lb_emit_store(p, gep0, lb_emit_load(p, value));
+		}
+		lb_emit_jump(p, done);
+		lb_start_block(p, done);
+
+
+		if (is_type_tuple(addr.map.result)) {
+			return lb_addr_load(p, v);
+		} else {
+			lbValue single = lb_emit_struct_ep(p, v.addr, 0);
+			return lb_emit_load(p, single);
+		}
+
+	} else if (addr.kind == lbAddr_BitField) {
+		Type *bft = base_type(type_deref(addr.addr.type));
+		GB_ASSERT(is_type_bit_field(bft));
+
+		unsigned value_index = cast(unsigned)addr.bit_field.value_index;
+		i32 size_in_bits = bft->BitField.fields[value_index]->type->BitFieldValue.bits;
+
+		i32 size_in_bytes = next_pow2((size_in_bits+7)/8);
+		if (size_in_bytes == 0) {
+			GB_ASSERT(size_in_bits == 0);
+			lbValue res = {};
+			res.type = t_i32;
+			res.value = LLVMConstInt(lb_type(p->module, res.type), 0, false);
+			return res;
+		}
+
+		Type *int_type = nullptr;
+		switch (size_in_bytes) {
+		case 1:  int_type = t_u8;   break;
+		case 2:  int_type = t_u16;  break;
+		case 4:  int_type = t_u32;  break;
+		case 8:  int_type = t_u64;  break;
+		case 16: int_type = t_u128; break;
+		}
+		GB_ASSERT(int_type != nullptr);
+
+		LLVMValueRef internal_data = LLVMBuildStructGEP(p->builder, addr.addr.value, 1, "");
+		LLVMValueRef field_ptr = LLVMBuildStructGEP(p->builder, internal_data, value_index, "");
+		LLVMValueRef field = LLVMBuildLoad(p->builder, field_ptr, "");
+
+		lbValue res = {};
+		res.type = int_type;
+		res.value = LLVMBuildZExtOrBitCast(p->builder, field, lb_type(p->module, int_type), "");
+		return res;
+	} else if (addr.kind == lbAddr_Context) {
+		if (addr.ctx.sel.index.count > 0) {
+			lbValue a = addr.addr;
+			lbValue b = lb_emit_deep_field_gep(p, a, addr.ctx.sel);
+			return lb_emit_load(p, b);
+		} else {
+			return lb_emit_load(p, addr.addr);
+		}
+	} else if (addr.kind == lbAddr_SoaVariable) {
+		Type *t = type_deref(addr.addr.type);
+		t = base_type(t);
+		GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
+		Type *elem = t->Struct.soa_elem;
+
+		lbValue len = {};
+		if (t->Struct.soa_kind == StructSoa_Fixed) {
+			len = lb_const_int(p->module, t_int, t->Struct.soa_count);
+		} else {
+			lbValue v = lb_emit_load(p, addr.addr);
+			len = lb_soa_struct_len(p, v);
+		}
+
+		lbAddr res = lb_add_local_generated(p, elem, true);
+
+		if (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed) {
+			// lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
+		}
+
+		if (t->Struct.soa_kind == StructSoa_Fixed) {
+			for_array(i, t->Struct.fields) {
+				Entity *field = t->Struct.fields[i];
+				Type *base_type = field->type;
+				GB_ASSERT(base_type->kind == Type_Array);
+
+				lbValue dst = lb_emit_struct_ep(p, res.addr, cast(i32)i);
+				lbValue src_ptr = lb_emit_struct_ep(p, addr.addr, cast(i32)i);
+				src_ptr = lb_emit_array_ep(p, src_ptr, addr.soa.index);
+				lbValue src = lb_emit_load(p, src_ptr);
+				lb_emit_store(p, dst, src);
+			}
+		} else {
+			isize field_count = t->Struct.fields.count;
+			if (t->Struct.soa_kind == StructSoa_Slice) {
+				field_count -= 1;
+			} else if (t->Struct.soa_kind == StructSoa_Dynamic) {
+				field_count -= 3;
+			}
+			for (isize i = 0; i < field_count; i++) {
+				Entity *field = t->Struct.fields[i];
+				Type *base_type = field->type;
+				GB_ASSERT(base_type->kind == Type_Pointer);
+				Type *elem = base_type->Pointer.elem;
+
+				lbValue dst = lb_emit_struct_ep(p, res.addr, cast(i32)i);
+				lbValue src_ptr = lb_emit_struct_ep(p, addr.addr, cast(i32)i);
+				src_ptr = lb_emit_ptr_offset(p, src_ptr, addr.soa.index);
+				lbValue src = lb_emit_load(p, src_ptr);
+				src = lb_emit_load(p, src);
+				lb_emit_store(p, dst, src);
+			}
+		}
+
+		return lb_addr_load(p, res);
+	}
+
+	if (is_type_proc(addr.addr.type)) {
+		return addr.addr;
+	}
+	return lb_emit_load(p, addr.addr);
+}
+
+lbValue lb_const_union_tag(lbModule *m, Type *u, Type *v) {
+	return lb_const_value(m, union_tag_type(u), exact_value_i64(union_variant_index(u, v)));
+}
+
+lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
+	Type *t = u.type;
+	GB_ASSERT_MSG(is_type_pointer(t) &&
+	              is_type_union(type_deref(t)), "%s", type_to_string(t));
+	Type *ut = type_deref(t);
+
+	GB_ASSERT(!is_type_union_maybe_pointer_original_alignment(ut));
+	GB_ASSERT(!is_type_union_maybe_pointer(ut));
+	GB_ASSERT(type_size_of(ut) > 0);
+
+	Type *tag_type = union_tag_type(ut);
+
+	LLVMTypeRef uvt = LLVMGetElementType(LLVMTypeOf(u.value));
+	unsigned element_count = LLVMCountStructElementTypes(uvt);
+	GB_ASSERT_MSG(element_count == 3, "(%s) != (%s)", type_to_string(ut), LLVMPrintTypeToString(uvt));
+
+	lbValue tag_ptr = {};
+	tag_ptr.value = LLVMBuildStructGEP(p->builder, u.value, 2, "");
+	tag_ptr.type = alloc_type_pointer(tag_type);
+	return tag_ptr;
+}
+
+lbValue lb_emit_union_tag_value(lbProcedure *p, lbValue u) {
+	lbValue ptr = lb_address_from_load_or_generate_local(p, u);
+	lbValue tag_ptr = lb_emit_union_tag_ptr(p, ptr);
+	return lb_emit_load(p, tag_ptr);
+}
+
+
+void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *variant_type) {
+	Type *t = type_deref(parent.type);
+
+	if (is_type_union_maybe_pointer(t) || type_size_of(t) == 0) {
+		// No tag needed!
+	} else {
+		lbValue tag_ptr = lb_emit_union_tag_ptr(p, parent);
+		lb_emit_store(p, tag_ptr, lb_const_union_tag(p->module, t, variant_type));
+	}
+}
+
+void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
+	gbAllocator a = heap_allocator();
+	lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+
+	lb_emit_store(p, underlying, variant);
+	lb_emit_store_union_variant_tag(p, parent, variant_type);
+}
+
+
+void lb_clone_struct_type(LLVMTypeRef dst, LLVMTypeRef src) {
+	unsigned field_count = LLVMCountStructElementTypes(src);
+	LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+	LLVMGetStructElementTypes(src, fields);
+	LLVMStructSetBody(dst, fields, field_count, LLVMIsPackedStruct(src));
+	gb_free(heap_allocator(), fields);
+}
+
+LLVMTypeRef lb_alignment_prefix_type_hack(lbModule *m, i64 alignment) {
+	switch (alignment) {
+	case 1:
+		return LLVMArrayType(lb_type(m, t_u8), 0);
+	case 2:
+		return LLVMArrayType(lb_type(m, t_u16), 0);
+	case 4:
+		return LLVMArrayType(lb_type(m, t_u32), 0);
+	case 8:
+		return LLVMArrayType(lb_type(m, t_u64), 0);
+	case 16:
+		return LLVMArrayType(LLVMVectorType(lb_type(m, t_u32), 4), 0);
+	default:
+		GB_PANIC("Invalid alignment %d", cast(i32)alignment);
+		break;
+	}
+	return nullptr;
+}
+
+bool lb_is_elem_const(Ast *elem, Type *elem_type) {
+	if (!elem_type_can_be_constant(elem_type)) {
+		return false;
+	}
+	if (elem->kind == Ast_FieldValue) {
+		elem = elem->FieldValue.value;
+	}
+	TypeAndValue tav = type_and_value_of_expr(elem);
+	GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(elem), type_to_string(tav.type));
+	return tav.value.kind != ExactValue_Invalid;
+}
+
+String lb_mangle_name(lbModule *m, Entity *e) {
+	gbAllocator a = heap_allocator();
+
+	String name = e->token.string;
+
+	AstPackage *pkg = e->pkg;
+	GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
+	String pkgn = pkg->name;
+	GB_ASSERT(!rune_is_digit(pkgn[0]));
+
+
+	isize max_len = pkgn.len + 1 + name.len + 1;
+	bool require_suffix_id = is_type_polymorphic(e->type, true);
+
+	if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
+		require_suffix_id = true;
+	} else if (is_blank_ident(e->token)) {
+		require_suffix_id = true;
+	}
+
+	if (require_suffix_id) {
+		max_len += 21;
+	}
+
+	char *new_name = gb_alloc_array(a, char, max_len);
+	isize new_name_len = gb_snprintf(
+		new_name, max_len,
+		"%.*s.%.*s", LIT(pkgn), LIT(name)
+	);
+	if (require_suffix_id) {
+		char *str = new_name + new_name_len-1;
+		isize len = max_len-new_name_len;
+		isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
+		new_name_len += extra-1;
+	}
+
+	String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
+	return mangled_name;
+}
+
+String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedure *p) {
+	// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
+	// and as a result, the declaration does not have time to determine what it should be
+
+	GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
+	if (e->TypeName.ir_mangled_name.len != 0)  {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
+
+	if (p == nullptr) {
+		Entity *proc = nullptr;
+		if (e->parent_proc_decl != nullptr) {
+			proc = e->parent_proc_decl->entity;
+		} else {
+			Scope *scope = e->scope;
+			while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
+				scope = scope->parent;
+			}
+			GB_ASSERT(scope != nullptr);
+			GB_ASSERT(scope->flags & ScopeFlag_Proc);
+			proc = scope->procedure_entity;
+		}
+		GB_ASSERT(proc->kind == Entity_Procedure);
+		GB_ASSERT(proc->code_gen_procedure != nullptr);
+		p = proc->code_gen_procedure;
+	}
+
+	// NOTE(bill): Generate a new name
+	// parent_proc.name-guid
+	String ts_name = e->token.string;
+
+	lbModule *m = p->module;
+	isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
+	char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+	u32 guid = ++p->module->nested_type_name_guid;
+	name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
+
+	String name = make_string(cast(u8 *)name_text, name_len-1);
+	e->TypeName.ir_mangled_name = name;
+	return name;
+}
+
+
+String lb_get_entity_name(lbModule *m, Entity *e, String default_name) {
+	if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
+		return e->TypeName.ir_mangled_name;
+	}
+	GB_ASSERT(e != nullptr);
+
+	if (e->pkg == nullptr) {
+		return e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
+		return lb_set_nested_type_name_ir_mangled_name(e, nullptr);
+	}
+
+	String name = {};
+
+	bool no_name_mangle = false;
+
+	if (e->kind == Entity_Variable) {
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+		no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
+		if (e->Variable.link_name.len > 0) {
+			return e->Variable.link_name;
+		}
+	} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
+		return e->Procedure.link_name;
+	} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
+		no_name_mangle = true;
+	}
+
+	if (!no_name_mangle) {
+		name = lb_mangle_name(m, e);
+	}
+	if (name.len == 0) {
+		name = e->token.string;
+	}
+
+	if (e->kind == Entity_TypeName) {
+		if ((e->scope->flags & ScopeFlag_File) == 0) {
+			gb_printf_err("<<< %.*s %.*s %p\n", LIT(e->token.string), LIT(name), e);
+		}
+
+		e->TypeName.ir_mangled_name = name;
+	} else if (e->kind == Entity_Procedure) {
+		e->Procedure.link_name = name;
+	}
+
+	return name;
+}
+
+LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
+	Type *original_type = type;
+
+	LLVMContextRef ctx = m->ctx;
+	i64 size = type_size_of(type); // Check size
+
+	GB_ASSERT(type != t_invalid);
+
+	switch (type->kind) {
+	case Type_Basic:
+		switch (type->Basic.kind) {
+		case Basic_llvm_bool: return LLVMInt1TypeInContext(ctx);
+		case Basic_bool:      return LLVMInt8TypeInContext(ctx);
+		case Basic_b8:        return LLVMInt8TypeInContext(ctx);
+		case Basic_b16:       return LLVMInt16TypeInContext(ctx);
+		case Basic_b32:       return LLVMInt32TypeInContext(ctx);
+		case Basic_b64:       return LLVMInt64TypeInContext(ctx);
+
+		case Basic_i8:   return LLVMInt8TypeInContext(ctx);
+		case Basic_u8:   return LLVMInt8TypeInContext(ctx);
+		case Basic_i16:  return LLVMInt16TypeInContext(ctx);
+		case Basic_u16:  return LLVMInt16TypeInContext(ctx);
+		case Basic_i32:  return LLVMInt32TypeInContext(ctx);
+		case Basic_u32:  return LLVMInt32TypeInContext(ctx);
+		case Basic_i64:  return LLVMInt64TypeInContext(ctx);
+		case Basic_u64:  return LLVMInt64TypeInContext(ctx);
+		case Basic_i128: return LLVMInt128TypeInContext(ctx);
+		case Basic_u128: return LLVMInt128TypeInContext(ctx);
+
+		case Basic_rune: return LLVMInt32TypeInContext(ctx);
+
+		// Basic_f16,
+		case Basic_f32: return LLVMFloatTypeInContext(ctx);
+		case Basic_f64: return LLVMDoubleTypeInContext(ctx);
+
+		// Basic_complex32,
+		case Basic_complex64:
+			{
+				char const *name = "..complex64";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[2] = {
+					lb_type(m, t_f32),
+					lb_type(m, t_f32),
+				};
+				LLVMStructSetBody(type, fields, 2, false);
+				return type;
+			}
+		case Basic_complex128:
+			{
+				char const *name = "..complex128";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[2] = {
+					lb_type(m, t_f64),
+					lb_type(m, t_f64),
+				};
+				LLVMStructSetBody(type, fields, 2, false);
+				return type;
+			}
+
+		case Basic_quaternion128:
+			{
+				char const *name = "..quaternion128";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[4] = {
+					lb_type(m, t_f32),
+					lb_type(m, t_f32),
+					lb_type(m, t_f32),
+					lb_type(m, t_f32),
+				};
+				LLVMStructSetBody(type, fields, 4, false);
+				return type;
+			}
+		case Basic_quaternion256:
+			{
+				char const *name = "..quaternion256";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[4] = {
+					lb_type(m, t_f64),
+					lb_type(m, t_f64),
+					lb_type(m, t_f64),
+					lb_type(m, t_f64),
+				};
+				LLVMStructSetBody(type, fields, 4, false);
+				return type;
+			}
+
+		case Basic_int:  return LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size);
+		case Basic_uint: return LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size);
+
+		case Basic_uintptr: return LLVMIntTypeInContext(ctx, 8*cast(unsigned)build_context.word_size);
+
+		case Basic_rawptr: return LLVMPointerType(LLVMInt8Type(), 0);
+		case Basic_string:
+			{
+				char const *name = "..string";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[2] = {
+					LLVMPointerType(lb_type(m, t_u8), 0),
+					lb_type(m, t_int),
+				};
+				LLVMStructSetBody(type, fields, 2, false);
+				return type;
+			}
+		case Basic_cstring: return LLVMPointerType(LLVMInt8Type(), 0);
+		case Basic_any:
+			{
+				char const *name = "..any";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[2] = {
+					lb_type(m, t_rawptr),
+					lb_type(m, t_typeid),
+				};
+				LLVMStructSetBody(type, fields, 2, false);
+				return type;
+			}
+
+		case Basic_typeid: return LLVMIntType(8*cast(unsigned)build_context.word_size);
+
+		// Endian Specific Types
+		case Basic_i16le:  return LLVMInt16TypeInContext(ctx);
+		case Basic_u16le:  return LLVMInt16TypeInContext(ctx);
+		case Basic_i32le:  return LLVMInt32TypeInContext(ctx);
+		case Basic_u32le:  return LLVMInt32TypeInContext(ctx);
+		case Basic_i64le:  return LLVMInt64TypeInContext(ctx);
+		case Basic_u64le:  return LLVMInt64TypeInContext(ctx);
+		case Basic_i128le: return LLVMInt128TypeInContext(ctx);
+		case Basic_u128le: return LLVMInt128TypeInContext(ctx);
+
+		case Basic_i16be:  return LLVMInt16TypeInContext(ctx);
+		case Basic_u16be:  return LLVMInt16TypeInContext(ctx);
+		case Basic_i32be:  return LLVMInt32TypeInContext(ctx);
+		case Basic_u32be:  return LLVMInt32TypeInContext(ctx);
+		case Basic_i64be:  return LLVMInt64TypeInContext(ctx);
+		case Basic_u64be:  return LLVMInt64TypeInContext(ctx);
+		case Basic_i128be: return LLVMInt128TypeInContext(ctx);
+		case Basic_u128be: return LLVMInt128TypeInContext(ctx);
+
+		// Untyped types
+		case Basic_UntypedBool:       GB_PANIC("Basic_UntypedBool"); break;
+		case Basic_UntypedInteger:    GB_PANIC("Basic_UntypedInteger"); break;
+		case Basic_UntypedFloat:      GB_PANIC("Basic_UntypedFloat"); break;
+		case Basic_UntypedComplex:    GB_PANIC("Basic_UntypedComplex"); break;
+		case Basic_UntypedQuaternion: GB_PANIC("Basic_UntypedQuaternion"); break;
+		case Basic_UntypedString:     GB_PANIC("Basic_UntypedString"); break;
+		case Basic_UntypedRune:       GB_PANIC("Basic_UntypedRune"); break;
+		case Basic_UntypedNil:        GB_PANIC("Basic_UntypedNil"); break;
+		case Basic_UntypedUndef:      GB_PANIC("Basic_UntypedUndef"); break;
+		}
+		break;
+	case Type_Named:
+		{
+			Type *base = base_type(type->Named.base);
+
+			switch (base->kind) {
+			case Type_Basic:
+				return lb_type(m, base);
+
+			case Type_Named:
+			case Type_Generic:
+			case Type_BitFieldValue:
+				GB_PANIC("INVALID TYPE");
+				break;
+
+			case Type_Opaque:
+				return lb_type(m, base->Opaque.elem);
+
+			case Type_Pointer:
+			case Type_Array:
+			case Type_EnumeratedArray:
+			case Type_Slice:
+			case Type_DynamicArray:
+			case Type_Map:
+			case Type_Enum:
+			case Type_BitSet:
+			case Type_SimdVector:
+				return lb_type(m, base);
+
+			// TODO(bill): Deal with this correctly. Can this be named?
+			case Type_Proc:
+				return lb_type(m, base);
+
+			case Type_Tuple:
+				return lb_type(m, base);
+			}
+
+			LLVMTypeRef *found = map_get(&m->types, hash_type(base));
+			if (found) {
+				LLVMTypeKind kind = LLVMGetTypeKind(*found);
+				if (kind == LLVMStructTypeKind) {
+					char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name));
+					LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name);
+					if (llvm_type != nullptr) {
+						return llvm_type;
+					}
+					llvm_type = LLVMStructCreateNamed(ctx, name);
+					map_set(&m->types, hash_type(type), llvm_type);
+					lb_clone_struct_type(llvm_type, *found);
+					return llvm_type;
+				}
+			}
+
+			switch (base->kind) {
+			case Type_Struct:
+			case Type_Union:
+			case Type_BitField:
+				{
+					char const *name = alloc_cstring(heap_allocator(), lb_get_entity_name(m, type->Named.type_name));
+					LLVMTypeRef llvm_type = LLVMGetTypeByName(m->mod, name);
+					if (llvm_type != nullptr) {
+						return llvm_type;
+					}
+					llvm_type = LLVMStructCreateNamed(ctx, name);
+					map_set(&m->types, hash_type(type), llvm_type);
+					lb_clone_struct_type(llvm_type, lb_type(m, base));
+					return llvm_type;
+				}
+			}
+
+
+			return lb_type(m, base);
+		}
+
+	case Type_Pointer:
+		return LLVMPointerType(lb_type(m, type_deref(type)), 0);
+
+	case Type_Opaque:
+		return lb_type(m, base_type(type));
+
+	case Type_Array:
+		return LLVMArrayType(lb_type(m, type->Array.elem), cast(unsigned)type->Array.count);
+
+	case Type_EnumeratedArray:
+		return LLVMArrayType(lb_type(m, type->EnumeratedArray.elem), cast(unsigned)type->EnumeratedArray.count);
+
+	case Type_Slice:
+		{
+			LLVMTypeRef fields[2] = {
+				LLVMPointerType(lb_type(m, type->Slice.elem), 0), // data
+				lb_type(m, t_int), // len
+			};
+			return LLVMStructTypeInContext(ctx, fields, 2, false);
+		}
+		break;
+
+	case Type_DynamicArray:
+		{
+			LLVMTypeRef fields[4] = {
+				LLVMPointerType(lb_type(m, type->DynamicArray.elem), 0), // data
+				lb_type(m, t_int), // len
+				lb_type(m, t_int), // cap
+				lb_type(m, t_allocator), // allocator
+			};
+			return LLVMStructTypeInContext(ctx, fields, 4, false);
+		}
+		break;
+
+	case Type_Map:
+		return lb_type(m, type->Map.internal_type);
+
+	case Type_Struct:
+		{
+			if (type->Struct.is_raw_union) {
+				unsigned field_count = 2;
+				LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+				i64 alignment = type_align_of(type);
+				unsigned size_of_union = cast(unsigned)type_size_of(type);
+				fields[0] = lb_alignment_prefix_type_hack(m, alignment);
+				fields[1] = LLVMArrayType(lb_type(m, t_u8), size_of_union);
+				return LLVMStructTypeInContext(ctx, fields, field_count, false);
+			}
+
+			isize offset = 0;
+			if (type->Struct.custom_align > 0) {
+				offset = 1;
+			}
+
+			unsigned field_count = cast(unsigned)(type->Struct.fields.count + offset);
+			LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+			GB_ASSERT(fields != nullptr);
+			defer (gb_free(heap_allocator(), fields));
+
+			for_array(i, type->Struct.fields) {
+				Entity *field = type->Struct.fields[i];
+				fields[i+offset] = lb_type(m, field->type);
+			}
+
+			if (type->Struct.custom_align > 0) {
+				fields[0] = lb_alignment_prefix_type_hack(m, type->Struct.custom_align);
+			}
+
+			return LLVMStructTypeInContext(ctx, fields, field_count, type->Struct.is_packed);
+		}
+		break;
+
+	case Type_Union:
+		if (type->Union.variants.count == 0) {
+			return LLVMStructTypeInContext(ctx, nullptr, 0, false);
+		} else {
+			// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
+			// LLVM takes the first element's alignment as the entire alignment (like C)
+			i64 align = type_align_of(type);
+			i64 size = type_size_of(type);
+
+			if (is_type_union_maybe_pointer_original_alignment(type)) {
+				LLVMTypeRef fields[1] = {lb_type(m, type->Union.variants[0])};
+				return LLVMStructTypeInContext(ctx, fields, 1, false);
+			}
+
+			unsigned block_size = cast(unsigned)type->Union.variant_block_size;
+
+			LLVMTypeRef fields[3] = {};
+			unsigned field_count = 1;
+			fields[0] = lb_alignment_prefix_type_hack(m, align);
+			if (is_type_union_maybe_pointer(type)) {
+				field_count += 1;
+				fields[1] = lb_type(m, type->Union.variants[0]);
+			} else {
+				field_count += 2;
+				if (block_size == align) {
+					fields[1] = LLVMIntTypeInContext(m->ctx, 8*block_size);
+				} else {
+					fields[1] = LLVMArrayType(lb_type(m, t_u8), block_size);
+				}
+				fields[2] = lb_type(m, union_tag_type(type));
+			}
+
+			return LLVMStructTypeInContext(ctx, fields, field_count, false);
+		}
+		break;
+
+	case Type_Enum:
+		return lb_type(m, base_enum_type(type));
+
+	case Type_Tuple:
+		if (type->Tuple.variables.count == 1) {
+			return lb_type(m, type->Tuple.variables[0]->type);
+		} else {
+			unsigned field_count = cast(unsigned)(type->Tuple.variables.count);
+			LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+			defer (gb_free(heap_allocator(), fields));
+
+			for_array(i, type->Tuple.variables) {
+				Entity *field = type->Tuple.variables[i];
+				fields[i] = lb_type(m, field->type);
+			}
+
+			return LLVMStructTypeInContext(ctx, fields, field_count, type->Tuple.is_packed);
+		}
+
+	case Type_Proc:
+		{
+			set_procedure_abi_types(heap_allocator(), type);
+
+			LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
+			isize offset = 0;
+			if (type->Proc.return_by_pointer) {
+				offset = 1;
+			} else if (type->Proc.abi_compat_result_type != nullptr) {
+				return_type = lb_type(m, type->Proc.abi_compat_result_type);
+			}
+
+			isize extra_param_count = offset;
+			if (type->Proc.calling_convention == ProcCC_Odin) {
+				extra_param_count += 1;
+			}
+
+			isize param_count = type->Proc.abi_compat_params.count + extra_param_count;
+			LLVMTypeRef *param_types = gb_alloc_array(heap_allocator(), LLVMTypeRef, param_count);
+			defer (gb_free(heap_allocator(), param_types));
+
+			isize param_index = offset;
+			for_array(i, type->Proc.abi_compat_params) {
+				Type *param = type->Proc.abi_compat_params[i];
+				if (param == nullptr) {
+					continue;
+				}
+				param_types[param_index++] = lb_type(m, param);
+			}
+			if (type->Proc.return_by_pointer) {
+				param_types[0] = LLVMPointerType(lb_type(m, type->Proc.abi_compat_result_type), 0);
+			}
+			if (type->Proc.calling_convention == ProcCC_Odin) {
+				param_types[param_index++] = lb_type(m, t_context_ptr);
+			}
+
+			LLVMTypeRef t = LLVMFunctionType(return_type, param_types, cast(unsigned)param_index, type->Proc.c_vararg);
+			return LLVMPointerType(t, 0);
+		}
+		break;
+	case Type_BitFieldValue:
+		return LLVMIntType(type->BitFieldValue.bits);
+
+	case Type_BitField:
+		{
+			LLVMTypeRef internal_type = nullptr;
+			{
+				GB_ASSERT(type->BitField.fields.count == type->BitField.sizes.count);
+				unsigned field_count = cast(unsigned)type->BitField.fields.count;
+				LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+				defer (gb_free(heap_allocator(), fields));
+
+				for_array(i, type->BitField.sizes) {
+					u32 size = type->BitField.sizes[i];
+					fields[i] = LLVMIntType(size);
+				}
+
+				internal_type = LLVMStructTypeInContext(ctx, fields, field_count, true);
+			}
+			unsigned field_count = 2;
+			LLVMTypeRef *fields = gb_alloc_array(heap_allocator(), LLVMTypeRef, field_count);
+
+			i64 alignment = 1;
+			if (type->BitField.custom_align > 0) {
+				alignment = type->BitField.custom_align;
+			}
+			fields[0] = lb_alignment_prefix_type_hack(m, alignment);
+			fields[1] = internal_type;
+
+			return LLVMStructTypeInContext(ctx, fields, field_count, true);
+		}
+		break;
+	case Type_BitSet:
+		return LLVMIntType(8*cast(unsigned)type_size_of(type));
+	case Type_SimdVector:
+		if (type->SimdVector.is_x86_mmx) {
+			return LLVMX86MMXTypeInContext(ctx);
+		}
+		return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
+	}
+
+	GB_PANIC("Invalid type %s", type_to_string(type));
+	return LLVMInt32TypeInContext(ctx);
+}
+
+LLVMTypeRef lb_type(lbModule *m, Type *type) {
+	type = default_type(type);
+
+	LLVMTypeRef *found = map_get(&m->types, hash_type(type));
+	if (found) {
+		return *found;
+	}
+
+	LLVMTypeRef llvm_type = lb_type_internal(m, type);
+
+	map_set(&m->types, hash_type(type), llvm_type);
+
+	return llvm_type;
+}
+
+void lb_add_entity(lbModule *m, Entity *e, lbValue val) {
+	if (e != nullptr) {
+		map_set(&m->values, hash_entity(e), val);
+	}
+}
+void lb_add_member(lbModule *m, String const &name, lbValue val) {
+	if (name.len > 0) {
+		map_set(&m->members, hash_string(name), val);
+	}
+}
+void lb_add_member(lbModule *m, HashKey const &key, lbValue val) {
+	map_set(&m->members, key, val);
+}
+void lb_add_procedure_value(lbModule *m, lbProcedure *p) {
+	if (p->entity != nullptr) {
+		map_set(&m->procedure_values, hash_pointer(p->value), p->entity);
+	}
+	map_set(&m->procedures, hash_string(p->name), p);
+}
+
+
+
+lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) {
+	if (false && lb_is_const(str_elem) && lb_is_const(str_len)) {
+		LLVMValueRef values[2] = {
+			str_elem.value,
+			str_len.value,
+		};
+		lbValue res = {};
+		res.type = t_string;
+		res.value = LLVMConstNamedStruct(lb_type(p->module, t_string), values, gb_count_of(values));
+		return res;
+	} else {
+		lbAddr res = lb_add_local_generated(p, t_string, false);
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), str_elem);
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), str_len);
+		return lb_addr_load(p, res);
+	}
+}
+
+LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) {
+	unsigned kind = LLVMGetEnumAttributeKindForName(name, gb_strlen(name));
+	GB_ASSERT(kind != 0);
+	return LLVMCreateEnumAttribute(ctx, kind, value);
+}
+
+void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value) {
+	LLVMAttributeRef attr = lb_create_enum_attribute(p->module->ctx, name, value);
+	GB_ASSERT(attr != nullptr);
+	LLVMAddAttributeAtIndex(p->value, cast(unsigned)index, attr);
+}
+
+void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name) {
+	lb_add_proc_attribute_at_index(p, index, name, cast(u64)true);
+}
+
+
+
+
+lbProcedure *lb_create_procedure(lbModule *m, Entity *entity) {
+	GB_ASSERT(entity != nullptr);
+
+	String link_name = lb_get_entity_name(m, entity);
+
+	{
+		HashKey key = hash_string(link_name);
+		lbValue *found = map_get(&m->members, key);
+		if (found) {
+			lb_add_entity(m, entity, *found);
+			lbProcedure **p_found = map_get(&m->procedures, key);
+			GB_ASSERT(p_found != nullptr);
+			return *p_found;
+		}
+	}
+
+
+	lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure);
+
+	p->module = m;
+	entity->code_gen_module = m;
+	entity->code_gen_procedure = p;
+	p->entity = entity;
+	p->name = link_name;
+
+	DeclInfo *decl = entity->decl_info;
+
+	ast_node(pl, ProcLit, decl->proc_lit);
+	Type *pt = base_type(entity->type);
+	GB_ASSERT(pt->kind == Type_Proc);
+
+	set_procedure_abi_types(heap_allocator(), entity->type);
+
+	p->type           = entity->type;
+	p->type_expr      = decl->type_expr;
+	p->body           = pl->body;
+	p->tags           = pt->Proc.tags;
+	p->inlining       = ProcInlining_none;
+	p->is_foreign     = entity->Procedure.is_foreign;
+	p->is_export      = entity->Procedure.is_export;
+	p->is_entry_point = false;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+	p->params.allocator        = a;
+	p->defer_stmts.allocator   = a;
+	p->blocks.allocator        = a;
+	p->branch_blocks.allocator = a;
+	p->context_stack.allocator = a;
+
+
+	char *c_link_name = alloc_cstring(heap_allocator(), p->name);
+	LLVMTypeRef func_ptr_type = lb_type(m, p->type);
+	LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type);
+
+	p->value = LLVMAddFunction(m->mod, c_link_name, func_type);
+
+	LLVMSetFunctionCallConv(p->value, lb_calling_convention_map[pt->Proc.calling_convention]);
+	lbValue proc_value = {p->value, p->type};
+	lb_add_entity(m, entity,  proc_value);
+	lb_add_member(m, p->name, proc_value);
+	lb_add_procedure_value(m, p);
+
+
+	// NOTE(bill): offset==0 is the return value
+	isize offset = 1;
+	if (pt->Proc.return_by_pointer) {
+		lb_add_proc_attribute_at_index(p, 1, "sret");
+		lb_add_proc_attribute_at_index(p, 1, "noalias");
+		offset = 2;
+	}
+
+	isize parameter_index = 0;
+	if (pt->Proc.param_count) {
+		TypeTuple *params = &pt->Proc.params->Tuple;
+		for (isize i = 0; i < pt->Proc.param_count; i++) {
+			Entity *e = params->variables[i];
+			Type *original_type = e->type;
+			Type *abi_type = pt->Proc.abi_compat_params[i];
+			if (e->kind != Entity_Variable) continue;
+
+			if (i+1 == params->variables.count && pt->Proc.c_vararg) {
+				continue;
+			}
+			if (is_type_tuple(abi_type)) {
+				for_array(j, abi_type->Tuple.variables) {
+					Type *tft = abi_type->Tuple.variables[j]->type;
+					if (e->flags&EntityFlag_NoAlias) {
+						lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias");
+					}
+				}
+				parameter_index += abi_type->Tuple.variables.count;
+			} else {
+				if (e->flags&EntityFlag_NoAlias) {
+					lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
+				}
+				parameter_index += 1;
+			}
+		}
+	}
+
+	if (pt->Proc.calling_convention == ProcCC_Odin) {
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull");
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
+	}
+
+
+	if (entity->Procedure.is_foreign) {
+		lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
+	}
+
+
+	{ // Debug Information
+		unsigned line = cast(unsigned)entity->token.pos.line;
+
+		LLVMMetadataRef file = nullptr;
+		if (entity->file != nullptr) {
+			cast(LLVMMetadataRef)entity->file->llvm_metadata;
+		}
+		LLVMMetadataRef scope = nullptr;
+		LLVMMetadataRef type = nullptr;
+
+		// type = LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, nullptr, 0, LLVMDIFlagZero);
+
+
+		LLVMMetadataRef res = LLVMDIBuilderCreateFunction(m->debug_builder, scope,
+			cast(char const *)entity->token.string.text, entity->token.string.len,
+			cast(char const *)p->name.text, p->name.len,
+			file, line, type,
+			true, p->body == nullptr,
+			line, LLVMDIFlagZero, false
+		);
+		GB_ASSERT(res != nullptr);
+		map_set(&m->debug_values, hash_pointer(p), res);
+	}
+
+	return p;
+}
+
+lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type) {
+	{
+		HashKey key = hash_string(link_name);
+		lbValue *found = map_get(&m->members, key);
+		GB_ASSERT(found == nullptr);
+	}
+
+	lbProcedure *p = gb_alloc_item(heap_allocator(), lbProcedure);
+
+	p->module = m;
+	p->name = link_name;
+
+	p->type           = type;
+	p->type_expr      = nullptr;
+	p->body           = nullptr;
+	p->tags           = 0;
+	p->inlining       = ProcInlining_none;
+	p->is_foreign     = false;
+	p->is_export      = false;
+	p->is_entry_point = false;
+
+	gbAllocator a = heap_allocator();
+	p->children.allocator      = a;
+	p->params.allocator        = a;
+	p->defer_stmts.allocator   = a;
+	p->blocks.allocator        = a;
+	p->branch_blocks.allocator = a;
+	p->context_stack.allocator = a;
+
+
+	char *c_link_name = alloc_cstring(heap_allocator(), p->name);
+	LLVMTypeRef func_ptr_type = lb_type(m, p->type);
+	LLVMTypeRef func_type = LLVMGetElementType(func_ptr_type);
+
+	p->value = LLVMAddFunction(m->mod, c_link_name, func_type);
+
+	Type *pt = p->type;
+
+	LLVMSetFunctionCallConv(p->value, lb_calling_convention_map[pt->Proc.calling_convention]);
+	lbValue proc_value = {p->value, p->type};
+	lb_add_member(m, p->name, proc_value);
+	lb_add_procedure_value(m, p);
+
+
+	// NOTE(bill): offset==0 is the return value
+	isize offset = 1;
+	if (pt->Proc.return_by_pointer) {
+		lb_add_proc_attribute_at_index(p, 1, "sret");
+		lb_add_proc_attribute_at_index(p, 1, "noalias");
+		offset = 2;
+	}
+
+	isize parameter_index = 0;
+	if (pt->Proc.param_count) {
+		TypeTuple *params = &pt->Proc.params->Tuple;
+		for (isize i = 0; i < pt->Proc.param_count; i++) {
+			Entity *e = params->variables[i];
+			Type *original_type = e->type;
+			Type *abi_type = pt->Proc.abi_compat_params[i];
+			if (e->kind != Entity_Variable) continue;
+
+			if (i+1 == params->variables.count && pt->Proc.c_vararg) {
+				continue;
+			}
+			if (is_type_tuple(abi_type)) {
+				for_array(j, abi_type->Tuple.variables) {
+					Type *tft = abi_type->Tuple.variables[j]->type;
+					if (e->flags&EntityFlag_NoAlias) {
+						lb_add_proc_attribute_at_index(p, offset+parameter_index+j, "noalias");
+					}
+				}
+				parameter_index += abi_type->Tuple.variables.count;
+			} else {
+				if (e->flags&EntityFlag_NoAlias) {
+					lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
+				}
+				parameter_index += 1;
+			}
+		}
+	}
+
+	if (pt->Proc.calling_convention == ProcCC_Odin) {
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "nonnull");
+		lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
+	}
+
+	return p;
+}
+
+
+lbValue lb_value_param(lbProcedure *p, Entity *e, Type *abi_type, i32 index, lbParamPasskind *kind_) {
+	lbParamPasskind kind = lbParamPass_Value;
+
+	if (e != nullptr && abi_type != e->type) {
+		if (is_type_pointer(abi_type)) {
+			GB_ASSERT(e->kind == Entity_Variable);
+			kind = lbParamPass_Pointer;
+			if (e->flags&EntityFlag_Value) {
+				kind = lbParamPass_ConstRef;
+			}
+		} else if (is_type_integer(abi_type)) {
+			kind = lbParamPass_Integer;
+		} else if (abi_type == t_llvm_bool) {
+			kind = lbParamPass_Value;
+		} else if (is_type_simd_vector(abi_type)) {
+			kind = lbParamPass_BitCast;
+		} else if (is_type_float(abi_type)) {
+			kind = lbParamPass_BitCast;
+		} else if (is_type_tuple(abi_type)) {
+			kind = lbParamPass_Tuple;
+		} else {
+			GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type));
+		}
+	}
+
+	if (kind_) *kind_ = kind;
+	lbValue res = {};
+	res.value = LLVMGetParam(p->value, cast(unsigned)index);
+	res.type = abi_type;
+	return res;
+}
+
+lbValue lb_add_param(lbProcedure *p, Entity *e, Ast *expr, Type *abi_type, i32 index) {
+	lbParamPasskind kind = lbParamPass_Value;
+	lbValue v = lb_value_param(p, e, abi_type, index, &kind);
+	array_add(&p->params, v);
+
+	lbValue res = {};
+
+	switch (kind) {
+	case lbParamPass_Value: {
+		lbAddr l = lb_add_local(p, e->type, e, false, index);
+		lbValue x = v;
+		if (abi_type == t_llvm_bool) {
+			x = lb_emit_conv(p, x, t_bool);
+		}
+		lb_addr_store(p, l, x);
+		return x;
+	}
+	case lbParamPass_Pointer:
+		lb_add_entity(p->module, e, v);
+		return lb_emit_load(p, v);
+
+	case lbParamPass_Integer: {
+		lbAddr l = lb_add_local(p, e->type, e, false, index);
+		lbValue iptr = lb_emit_conv(p, l.addr, alloc_type_pointer(abi_type));
+		lb_emit_store(p, iptr, v);
+		return lb_addr_load(p, l);
+	}
+
+	case lbParamPass_ConstRef:
+		lb_add_entity(p->module, e, v);
+		return lb_emit_load(p, v);
+
+	case lbParamPass_BitCast: {
+		lbAddr l = lb_add_local(p, e->type, e, false, index);
+		lbValue x = lb_emit_transmute(p, v, e->type);
+		lb_addr_store(p, l, x);
+		return x;
+	}
+	case lbParamPass_Tuple: {
+		lbAddr l = lb_add_local(p, e->type, e, true, index);
+		Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
+		lbValue ptr = lb_emit_transmute(p, l.addr, alloc_type_pointer(st));
+		if (abi_type->Tuple.variables.count > 0) {
+			array_pop(&p->params);
+		}
+		for_array(i, abi_type->Tuple.variables) {
+			Type *t = abi_type->Tuple.variables[i]->type;
+
+			lbParamPasskind elem_kind = lbParamPass_Value;
+			lbValue elem = lb_value_param(p, nullptr, t, index+cast(i32)i, &elem_kind);
+			array_add(&p->params, elem);
+
+			lbValue dst = lb_emit_struct_ep(p, ptr, cast(i32)i);
+			lb_emit_store(p, dst, elem);
+		}
+		return lb_addr_load(p, l);
+	}
+
+	}
+
+	GB_PANIC("Unreachable");
+	return {};
+}
+
+void lb_start_block(lbProcedure *p, lbBlock *b) {
+	GB_ASSERT(b != nullptr);
+	if (!b->appended) {
+		b->appended = true;
+		LLVMAppendExistingBasicBlock(p->value, b->block);
+	}
+	LLVMPositionBuilderAtEnd(p->builder, b->block);
+	p->curr_block = b;
+}
+
+
+void lb_begin_procedure_body(lbProcedure *p) {
+	DeclInfo *decl = decl_info_of_entity(p->entity);
+	if (decl != nullptr) {
+		for_array(i, decl->labels) {
+			BlockLabel bl = decl->labels[i];
+			lbBranchBlocks bb = {bl.label, nullptr, nullptr};
+			array_add(&p->branch_blocks, bb);
+		}
+	}
+
+	p->builder = LLVMCreateBuilder();
+
+	p->decl_block  = lb_create_block(p, "decls", true);
+	p->entry_block = lb_create_block(p, "entry", true);
+	lb_start_block(p, p->entry_block);
+
+	GB_ASSERT(p->type != nullptr);
+
+	i32 parameter_index = 0;
+
+	if (p->type->Proc.return_by_pointer) {
+		// NOTE(bill): this must be parameter 0
+		Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
+		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
+		e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+
+		lbValue return_ptr_value = {};
+		return_ptr_value.value = LLVMGetParam(p->value, 0);
+		return_ptr_value.type = alloc_type_pointer(p->type->Proc.abi_compat_result_type);
+		p->return_ptr = lb_addr(return_ptr_value);
+
+		lb_add_entity(p->module, e, return_ptr_value);
+
+		parameter_index += 1;
+	}
+
+	if (p->type->Proc.params != nullptr) {
+		TypeTuple *params = &p->type->Proc.params->Tuple;
+		if (p->type_expr != nullptr) {
+			ast_node(pt, ProcType, p->type_expr);
+			isize param_index = 0;
+			isize q_index = 0;
+
+			for_array(i, params->variables) {
+				ast_node(fl, FieldList, pt->params);
+				GB_ASSERT(fl->list.count > 0);
+				GB_ASSERT(fl->list[0]->kind == Ast_Field);
+				if (q_index == fl->list[param_index]->Field.names.count) {
+					q_index = 0;
+					param_index++;
+				}
+				ast_node(field, Field, fl->list[param_index]);
+				Ast *name = field->names[q_index++];
+
+				Entity *e = params->variables[i];
+				if (e->kind != Entity_Variable) {
+					continue;
+				}
+
+				Type *abi_type = p->type->Proc.abi_compat_params[i];
+				if (e->token.string != "") {
+					lb_add_param(p, e, name, abi_type, parameter_index);
+				}
+
+				if (is_type_tuple(abi_type)) {
+					parameter_index += cast(i32)abi_type->Tuple.variables.count;
+				} else {
+					parameter_index += 1;
+				}
+			}
+		} else {
+			auto abi_types = p->type->Proc.abi_compat_params;
+
+			for_array(i, params->variables) {
+				Entity *e = params->variables[i];
+				if (e->kind != Entity_Variable) {
+					continue;
+				}
+				Type *abi_type = e->type;
+				if (abi_types.count > 0) {
+					abi_type = abi_types[i];
+				}
+				if (e->token.string != "") {
+					lb_add_param(p, e, nullptr, abi_type, parameter_index);
+				}
+				if (is_type_tuple(abi_type)) {
+					parameter_index += cast(i32)abi_type->Tuple.variables.count;
+				} else {
+					parameter_index += 1;
+				}
+			}
+		}
+	}
+
+
+	if (p->type->Proc.has_named_results) {
+		GB_ASSERT(p->type->Proc.result_count > 0);
+		TypeTuple *results = &p->type->Proc.results->Tuple;
+		LLVMValueRef return_ptr = LLVMGetParam(p->value, 0);
+
+		isize result_index = 0;
+
+		for_array(i, results->variables) {
+			Entity *e = results->variables[i];
+			if (e->kind != Entity_Variable) {
+				continue;
+			}
+
+			if (e->token.string != "") {
+				GB_ASSERT(!is_blank_ident(e->token));
+
+				lbAddr res = lb_add_local(p, e->type, e);
+
+				lbValue c = {};
+				switch (e->Variable.param_value.kind) {
+				case ParameterValue_Constant:
+					c = lb_const_value(p->module, e->type, e->Variable.param_value.value);
+					break;
+				case ParameterValue_Nil:
+					c = lb_const_nil(p->module, e->type);
+					break;
+				case ParameterValue_Location:
+					GB_PANIC("ParameterValue_Location");
+					break;
+				}
+				if (c.value != nullptr) {
+					lb_addr_store(p, res, c);
+				}
+			}
+
+			result_index += 1;
+		}
+	}
+
+	if (p->type->Proc.calling_convention == ProcCC_Odin) {
+		Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
+		e->flags |= EntityFlag_NoAlias;
+		lbValue param = {};
+		param.value = LLVMGetParam(p->value, LLVMCountParams(p->value)-1);
+		param.type = e->type;
+		lb_add_entity(p->module, e, param);
+		lbAddr ctx_addr = {};
+		ctx_addr.kind = lbAddr_Context;
+		ctx_addr.addr = param;
+		lbContextData ctx = {ctx_addr, p->scope_index};
+		array_add(&p->context_stack, ctx);
+	}
+
+	lb_start_block(p, p->entry_block);
+}
+
+void lb_end_procedure_body(lbProcedure *p) {
+	LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
+	LLVMBuildBr(p->builder, p->entry_block->block);
+	LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+
+	if (p->type->Proc.result_count == 0) {
+	    LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+	    if (!LLVMIsAReturnInst(instr)) {
+	    	lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+			LLVMBuildRetVoid(p->builder);
+		}
+	} else {
+		if (p->curr_block->preds.count == 0) {
+		    LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block);
+		    if (instr == nullptr) {
+		    	// NOTE(bill): Remove dead trailing block
+		    	LLVMDeleteBasicBlock(p->curr_block->block);
+		    }
+		}
+	}
+
+	p->curr_block = nullptr;
+
+}
+void lb_end_procedure(lbProcedure *p) {
+	LLVMDisposeBuilder(p->builder);
+}
+
+void lb_add_edge(lbBlock *from, lbBlock *to) {
+	LLVMValueRef instr = LLVMGetLastInstruction(from->block);
+	if (instr == nullptr || !LLVMIsATerminatorInst(instr)) {
+		array_add(&from->succs, to);
+		array_add(&to->preds, from);
+	}
+}
+
+
+lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append) {
+	lbBlock *b = gb_alloc_item(heap_allocator(), lbBlock);
+	b->block = LLVMCreateBasicBlockInContext(p->module->ctx, name);
+	b->appended = false;
+	if (append) {
+		b->appended = true;
+		LLVMAppendExistingBasicBlock(p->value, b->block);
+	}
+
+	b->scope = p->curr_scope;
+	b->scope_index = p->scope_index;
+
+	b->preds.allocator = heap_allocator();
+	b->succs.allocator = heap_allocator();
+
+	array_add(&p->blocks, b);
+
+	return b;
+}
+
+void lb_emit_jump(lbProcedure *p, lbBlock *target_block) {
+	if (p->curr_block == nullptr) {
+		return;
+	}
+	LLVMValueRef last_instr = LLVMGetLastInstruction(p->curr_block->block);
+	if (last_instr != nullptr && LLVMIsATerminatorInst(last_instr)) {
+		return;
+	}
+
+	lb_add_edge(p->curr_block, target_block);
+	LLVMBuildBr(p->builder, target_block->block);
+	p->curr_block = nullptr;
+}
+
+void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *false_block) {
+	lbBlock *b = p->curr_block;
+	if (b == nullptr) {
+		return;
+	}
+	LLVMValueRef last_instr = LLVMGetLastInstruction(p->curr_block->block);
+	if (last_instr != nullptr && LLVMIsATerminatorInst(last_instr)) {
+		return;
+	}
+
+	lb_add_edge(b, true_block);
+	lb_add_edge(b, false_block);
+
+	LLVMValueRef cv = cond.value;
+	cv = LLVMBuildTruncOrBitCast(p->builder, cv, lb_type(p->module, t_llvm_bool), "");
+	LLVMBuildCondBr(p->builder, cv, true_block->block, false_block->block);
+}
+
+lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block) {
+	GB_ASSERT(cond != nullptr);
+	GB_ASSERT(true_block  != nullptr);
+	GB_ASSERT(false_block != nullptr);
+
+	switch (cond->kind) {
+	case_ast_node(pe, ParenExpr, cond);
+		return lb_build_cond(p, pe->expr, true_block, false_block);
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, cond);
+		if (ue->op.kind == Token_Not) {
+			return lb_build_cond(p, ue->expr, false_block, true_block);
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, cond);
+		if (be->op.kind == Token_CmpAnd) {
+			lbBlock *block = lb_create_block(p, "cmp.and");
+			lb_build_cond(p, be->left, block, false_block);
+			lb_start_block(p, block);
+			return lb_build_cond(p, be->right, true_block, false_block);
+		} else if (be->op.kind == Token_CmpOr) {
+			lbBlock *block = lb_create_block(p, "cmp.or");
+			lb_build_cond(p, be->left, true_block, block);
+			lb_start_block(p, block);
+			return lb_build_cond(p, be->right, true_block, false_block);
+		}
+	case_end;
+	}
+
+	lbValue v = lb_build_expr(p, cond);
+	// v = lb_emit_conv(p, v, t_bool);
+	v = lb_emit_conv(p, v, t_llvm_bool);
+
+	lb_emit_if(p, v, true_block, false_block);
+
+	return v;
+}
+
+
+
+lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 param_index) {
+	GB_ASSERT(p->decl_block != p->curr_block);
+	LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
+
+	char const *name = "";
+	if (e != nullptr) {
+		// name = alloc_cstring(heap_allocator(), e->token.string);
+	}
+
+	LLVMTypeRef llvm_type = lb_type(p->module, type);
+	LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name);
+	LLVMSetAlignment(ptr, 16); // TODO(bill): Make this configurable
+
+	LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+	if (zero_init) {
+		LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);
+	}
+
+	lbValue val = {};
+	val.value = ptr;
+	val.type = alloc_type_pointer(type);
+
+	if (e != nullptr) {
+		lb_add_entity(p->module, e, val);
+	}
+
+	return lb_addr(val);
+}
+
+lbAddr lb_add_local_generated(lbProcedure *p, Type *type, bool zero_init) {
+	return lb_add_local(p, type, nullptr, zero_init);
+}
+
+
+void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e) {
+	GB_ASSERT(pd->body != nullptr);
+	lbModule *m = p->module;
+	auto *min_dep_set = &m->info->minimum_dependency_set;
+
+	if (ptr_set_exists(min_dep_set, e) == false) {
+		// NOTE(bill): Nothing depends upon it so doesn't need to be built
+		return;
+	}
+
+	// NOTE(bill): Generate a new name
+	// parent.name-guid
+	String original_name = e->token.string;
+	String pd_name = original_name;
+	if (e->Procedure.link_name.len > 0) {
+		pd_name = e->Procedure.link_name;
+	}
+
+	isize name_len = p->name.len + 1 + pd_name.len + 1 + 10 + 1;
+	char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+
+	i32 guid = cast(i32)p->children.count;
+	name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid);
+	String name = make_string(cast(u8 *)name_text, name_len-1);
+
+	set_procedure_abi_types(heap_allocator(), e->type);
+
+
+	e->Procedure.link_name = name;
+
+	lbProcedure *nested_proc = lb_create_procedure(p->module, e);
+
+	lbValue value = {};
+	value.value = nested_proc->value;
+	value.type = nested_proc->type;
+
+	lb_add_entity(m, e, value);
+	array_add(&p->children, nested_proc);
+	array_add(&m->procedures_to_generate, nested_proc);
+}
+
+
+void lb_add_foreign_library_path(lbModule *m, Entity *e) {
+	if (e == nullptr) {
+		return;
+	}
+	GB_ASSERT(e->kind == Entity_LibraryName);
+	GB_ASSERT(e->flags & EntityFlag_Used);
+
+	for_array(i, e->LibraryName.paths) {
+		String library_path = e->LibraryName.paths[i];
+		if (library_path.len == 0) {
+			continue;
+		}
+
+		bool ok = true;
+		for_array(path_index, m->foreign_library_paths) {
+			String path = m->foreign_library_paths[path_index];
+	#if defined(GB_SYSTEM_WINDOWS)
+			if (str_eq_ignore_case(path, library_path)) {
+	#else
+			if (str_eq(path, library_path)) {
+	#endif
+				ok = false;
+				break;
+			}
+		}
+
+		if (ok) {
+			array_add(&m->foreign_library_paths, library_path);
+		}
+	}
+}
+
+
+
+void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) {
+	if (vd == nullptr || vd->is_mutable) {
+		return;
+	}
+
+	auto *min_dep_set = &p->module->info->minimum_dependency_set;
+
+	static i32 global_guid = 0;
+
+	for_array(i, vd->names) {
+		Ast *ident = vd->names[i];
+		GB_ASSERT(ident->kind == Ast_Ident);
+		Entity *e = entity_of_ident(ident);
+		GB_ASSERT(e != nullptr);
+		if (e->kind != Entity_TypeName) {
+			continue;
+		}
+
+		bool polymorphic_struct = false;
+		if (e->type != nullptr && e->kind == Entity_TypeName) {
+		Type *bt = base_type(e->type);
+			if (bt->kind == Type_Struct) {
+				polymorphic_struct = bt->Struct.is_polymorphic;
+			}
+		}
+
+		if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+			continue;
+		}
+
+		if (e->TypeName.ir_mangled_name.len != 0) {
+			// NOTE(bill): Already set
+			continue;
+		}
+
+		lb_set_nested_type_name_ir_mangled_name(e, p);
+	}
+
+	for_array(i, vd->names) {
+		Ast *ident = vd->names[i];
+		GB_ASSERT(ident->kind == Ast_Ident);
+		Entity *e = entity_of_ident(ident);
+		GB_ASSERT(e != nullptr);
+		if (e->kind != Entity_Procedure) {
+			continue;
+		}
+
+		CheckerInfo *info = p->module->info;
+		DeclInfo *decl = decl_info_of_entity(e);
+		ast_node(pl, ProcLit, decl->proc_lit);
+		if (pl->body != nullptr) {
+			auto *found = map_get(&info->gen_procs, hash_pointer(ident));
+			if (found) {
+				auto procs = *found;
+				for_array(i, procs) {
+					Entity *e = procs[i];
+					if (!ptr_set_exists(min_dep_set, e)) {
+						continue;
+					}
+					DeclInfo *d = decl_info_of_entity(e);
+					lb_build_nested_proc(p, &d->proc_lit->ProcLit, e);
+				}
+			} else {
+				lb_build_nested_proc(p, pl, e);
+			}
+		} else {
+
+			// FFI - Foreign function interace
+			String original_name = e->token.string;
+			String name = original_name;
+
+			if (e->Procedure.is_foreign) {
+				lb_add_foreign_library_path(p->module, e->Procedure.foreign_library);
+			}
+
+			if (e->Procedure.link_name.len > 0) {
+				name = e->Procedure.link_name;
+			}
+
+			HashKey key = hash_string(name);
+			lbValue *prev_value = map_get(&p->module->members, key);
+			if (prev_value != nullptr) {
+				// NOTE(bill): Don't do mutliple declarations in the IR
+				return;
+			}
+
+			set_procedure_abi_types(heap_allocator(), e->type);
+			e->Procedure.link_name = name;
+
+			lbProcedure *nested_proc = lb_create_procedure(p->module, e);
+
+			lbValue value = {};
+			value.value = nested_proc->value;
+			value.type = nested_proc->type;
+
+			array_add(&p->module->procedures_to_generate, nested_proc);
+			if (p != nullptr) {
+				array_add(&p->children, nested_proc);
+			} else {
+				map_set(&p->module->members, hash_string(name), value);
+			}
+		}
+	}
+}
+
+
+void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts) {
+	for_array(i, stmts) {
+		Ast *stmt = stmts[i];
+		switch (stmt->kind) {
+		case_ast_node(vd, ValueDecl, stmt);
+			lb_build_constant_value_decl(p, vd);
+		case_end;
+		case_ast_node(fb, ForeignBlockDecl, stmt);
+			ast_node(block, BlockStmt, fb->body);
+			lb_build_stmt_list(p, block->stmts);
+		case_end;
+		}
+	}
+	for_array(i, stmts) {
+		lb_build_stmt(p, stmts[i]);
+	}
+}
+
+lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
+	GB_ASSERT(ident->kind == Ast_Ident);
+	Entity *e = entity_of_ident(ident);
+	GB_ASSERT(e->kind == Entity_Label);
+	for_array(i, p->branch_blocks) {
+		lbBranchBlocks *b = &p->branch_blocks[i];
+		if (b->label == e->Label.node) {
+			return *b;
+		}
+	}
+
+	GB_PANIC("Unreachable");
+	lbBranchBlocks empty = {};
+	return empty;
+}
+
+
+lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
+	lbTargetList *tl = gb_alloc_item(heap_allocator(), lbTargetList);
+	tl->prev = p->target_list;
+	tl->break_ = break_;
+	tl->continue_ = continue_;
+	tl->fallthrough_ = fallthrough_;
+	p->target_list = tl;
+
+	if (label != nullptr) { // Set label blocks
+		GB_ASSERT(label->kind == Ast_Label);
+
+		for_array(i, p->branch_blocks) {
+			lbBranchBlocks *b = &p->branch_blocks[i];
+			GB_ASSERT(b->label != nullptr && label != nullptr);
+			GB_ASSERT(b->label->kind == Ast_Label);
+			if (b->label == label) {
+				b->break_    = break_;
+				b->continue_ = continue_;
+				return tl;
+			}
+		}
+
+		GB_PANIC("Unreachable");
+	}
+
+	return tl;
+}
+
+void lb_pop_target_list(lbProcedure *p) {
+	p->target_list = p->target_list->prev;
+}
+
+
+
+
+void lb_open_scope(lbProcedure *p) {
+	p->scope_index += 1;
+}
+
+void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, bool pop_stack=true) {
+	lb_emit_defer_stmts(p, kind, block);
+	GB_ASSERT(p->scope_index > 0);
+
+	// NOTE(bill): Remove `context`s made in that scope
+	while (p->context_stack.count > 0) {
+		lbContextData *ctx = &p->context_stack[p->context_stack.count-1];
+		if (ctx->scope_index >= p->scope_index) {
+			array_pop(&p->context_stack);
+		} else {
+			break;
+		}
+
+	}
+
+	p->scope_index -= 1;
+}
+
+void lb_build_when_stmt(lbProcedure *p, AstWhenStmt *ws) {
+	TypeAndValue tv = type_and_value_of_expr(ws->cond);
+	GB_ASSERT(is_type_boolean(tv.type));
+	GB_ASSERT(tv.value.kind == ExactValue_Bool);
+	if (tv.value.value_bool) {
+		lb_build_stmt_list(p, ws->body->BlockStmt.stmts);
+	} else if (ws->else_stmt) {
+		switch (ws->else_stmt->kind) {
+		case Ast_BlockStmt:
+			lb_build_stmt_list(p, ws->else_stmt->BlockStmt.stmts);
+			break;
+		case Ast_WhenStmt:
+			lb_build_when_stmt(p, &ws->else_stmt->WhenStmt);
+			break;
+		default:
+			GB_PANIC("Invalid 'else' statement in 'when' statement");
+			break;
+		}
+	}
+}
+
+
+
+void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValue count_ptr,
+                            lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+	lbModule *m = p->module;
+
+	lbValue count = {};
+	Type *expr_type = base_type(type_deref(expr.type));
+	switch (expr_type->kind) {
+	case Type_Array:
+		count = lb_const_int(m, t_int, expr_type->Array.count);
+		break;
+	}
+
+	lbValue val = {};
+	lbValue idx = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	lbBlock *body = nullptr;
+
+
+	lbAddr index = lb_add_local_generated(p, t_int, false);
+	lb_addr_store(p, index, lb_const_int(m, t_int, cast(u64)-1));
+
+	loop = lb_create_block(p, "for.index.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+	lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
+	lb_addr_store(p, index, incr);
+
+	body = lb_create_block(p, "for.index.body");
+	done = lb_create_block(p, "for.index.done");
+	if (count.value == nullptr) {
+		GB_ASSERT(count_ptr.value != nullptr);
+		count = lb_emit_load(p, count_ptr);
+	}
+	lbValue cond = lb_emit_comp(p, Token_Lt, incr, count);
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+	idx = lb_addr_load(p, index);
+	switch (expr_type->kind) {
+	case Type_Array: {
+		if (val_type != nullptr) {
+			val = lb_emit_load(p, lb_emit_array_ep(p, expr, idx));
+		}
+		break;
+	}
+	case Type_EnumeratedArray: {
+		if (val_type != nullptr) {
+			val = lb_emit_load(p, lb_emit_array_ep(p, expr, idx));
+			// NOTE(bill): Override the idx value for the enumeration
+			Type *index_type = expr_type->EnumeratedArray.index;
+			if (compare_exact_values(Token_NotEq, expr_type->EnumeratedArray.min_value, exact_value_u64(0))) {
+				idx = lb_emit_arith(p, Token_Add, idx, lb_const_value(m, index_type, expr_type->EnumeratedArray.min_value), index_type);
+			}
+		}
+		break;
+	}
+	case Type_Slice: {
+		if (val_type != nullptr) {
+			lbValue elem = lb_slice_elem(p, expr);
+			val = lb_emit_load(p, lb_emit_ptr_offset(p, elem, idx));
+		}
+		break;
+	}
+	case Type_DynamicArray: {
+		if (val_type != nullptr) {
+			lbValue elem = lb_emit_struct_ep(p, expr, 0);
+			elem = lb_emit_load(p, elem);
+			val = lb_emit_load(p, lb_emit_ptr_offset(p, elem, idx));
+		}
+		break;
+	}
+	case Type_Map: {
+		lbAddr key = lb_add_local_generated(p, expr_type->Map.key, true);
+
+		lbValue entries = lb_map_entries_ptr(p, expr);
+		lbValue elem = lb_emit_struct_ep(p, entries, 0);
+		elem = lb_emit_load(p, elem);
+
+		lbValue entry = lb_emit_ptr_offset(p, elem, idx);
+		val = lb_emit_load(p, lb_emit_struct_ep(p, entry, 2));
+
+		lbValue hash = lb_emit_struct_ep(p, entry, 0);
+		if (is_type_string(expr_type->Map.key)) {
+			lbValue str = lb_emit_struct_ep(p, hash, 1);
+			lb_addr_store(p, key, lb_emit_load(p, str));
+		} else {
+			lbValue hash_ptr = lb_emit_struct_ep(p, hash, 0);
+			hash_ptr = lb_emit_conv(p, hash_ptr, key.addr.type);
+			lb_addr_store(p, key, lb_emit_load(p, hash_ptr));
+		}
+
+		idx = lb_addr_load(p, key);
+
+		break;
+	}
+	default:
+		GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type));
+		break;
+	}
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+
+void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
+                            lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+	lbModule *m = p->module;
+	lbValue count = lb_const_int(m, t_int, 0);
+	Type *expr_type = base_type(expr.type);
+	switch (expr_type->kind) {
+	case Type_Basic:
+		count = lb_string_len(p, expr);
+		break;
+	default:
+		GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type));
+		break;
+	}
+
+	lbValue val = {};
+	lbValue idx = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	lbBlock *body = nullptr;
+
+
+	lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+	lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+
+	loop = lb_create_block(p, "for.string.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+
+
+	body = lb_create_block(p, "for.string.body");
+	done = lb_create_block(p, "for.string.done");
+
+	lbValue offset = lb_addr_load(p, offset_);
+	lbValue cond = lb_emit_comp(p, Token_Lt, offset, count);
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+
+	lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
+	lbValue str_len  = lb_emit_arith(p, Token_Sub, count, offset, t_int);
+	auto args = array_make<lbValue>(heap_allocator(), 1);
+	args[0] = lb_emit_string(p, str_elem, str_len);
+	lbValue rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args);
+	lbValue len  = lb_emit_struct_ev(p, rune_and_len, 1);
+	lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
+
+
+	idx = offset;
+	if (val_type != nullptr) {
+		val = lb_emit_struct_ev(p, rune_and_len, 0);
+	}
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+
+void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, Type *val_type,
+                             lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+	lbModule *m = p->module;
+
+	// TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration?
+	// If 'lower' is changed, should 'val' do so or is that not typical behaviour?
+
+	lbValue lower = lb_build_expr(p, node->left);
+	lbValue upper = {};
+
+	lbValue val = {};
+	lbValue idx = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	lbBlock *body = nullptr;
+
+	if (val_type == nullptr) {
+		val_type = lower.type;
+	}
+	lbAddr value = lb_add_local_generated(p, val_type, false);
+	lb_addr_store(p, value, lower);
+
+	lbAddr index = lb_add_local_generated(p, t_int, false);
+	lb_addr_store(p, index, lb_const_int(m, t_int, 0));
+
+	loop = lb_create_block(p, "for.interval.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+	body = lb_create_block(p, "for.interval.body");
+	done = lb_create_block(p, "for.interval.done");
+
+
+	TokenKind op = Token_Lt;
+	switch (node->op.kind) {
+	case Token_Ellipsis:  op = Token_LtEq; break;
+	case Token_RangeHalf: op = Token_Lt;  break;
+	default: GB_PANIC("Invalid interval operator"); break;
+	}
+
+	upper = lb_build_expr(p, node->right);
+
+	lbValue curr_value = lb_addr_load(p, value);
+	lbValue cond = lb_emit_comp(p, op, curr_value, upper);
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+	val = lb_addr_load(p, value);
+	idx = lb_addr_load(p, index);
+
+	lb_emit_increment(p, value.addr);
+	lb_emit_increment(p, index.addr);
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+	lbModule *m = p->module;
+
+	Type *t = enum_type;
+	GB_ASSERT(is_type_enum(t));
+	Type *enum_ptr = alloc_type_pointer(t);
+	t = base_type(t);
+	Type *core_elem = core_type(t);
+	GB_ASSERT(t->kind == Type_Enum);
+	i64 enum_count = t->Enum.fields.count;
+	lbValue max_count = lb_const_int(m, t_int, enum_count);
+
+	lbValue ti          = lb_type_info(m, t);
+	lbValue variant     = lb_emit_struct_ep(p, ti, 3);
+	lbValue eti_ptr     = lb_emit_conv(p, variant, t_type_info_enum_ptr);
+	lbValue values      = lb_emit_load(p, lb_emit_struct_ep(p, eti_ptr, 2));
+	lbValue values_data = lb_slice_elem(p, values);
+
+	lbAddr offset_ = lb_add_local_generated(p, t_int, false);
+	lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+
+	lbBlock *loop = lb_create_block(p, "for.enum.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+	lbBlock *body = lb_create_block(p, "for.enum.body");
+	lbBlock *done = lb_create_block(p, "for.enum.done");
+
+	lbValue offset = lb_addr_load(p, offset_);
+	lbValue cond = lb_emit_comp(p, Token_Lt, offset, max_count);
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+	lbValue val_ptr = lb_emit_ptr_offset(p, values_data, offset);
+	lb_emit_increment(p, offset_.addr);
+
+	lbValue val = {};
+	if (val_type != nullptr) {
+		GB_ASSERT(are_types_identical(enum_type, val_type));
+
+		if (is_type_integer(core_elem)) {
+			lbValue i = lb_emit_load(p, lb_emit_conv(p, val_ptr, t_i64_ptr));
+			val = lb_emit_conv(p, i, t);
+		} else {
+			GB_PANIC("TODO(bill): enum core type %s", type_to_string(core_elem));
+		}
+	}
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = offset;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+void lb_build_range_tuple(lbProcedure *p, Ast *expr, Type *val0_type, Type *val1_type,
+                          lbValue *val0_, lbValue *val1_, lbBlock **loop_, lbBlock **done_) {
+	lbBlock *loop = lb_create_block(p, "for.tuple.loop");
+	lb_emit_jump(p, loop);
+	lb_start_block(p, loop);
+
+	lbBlock *body = lb_create_block(p, "for.tuple.body");
+	lbBlock *done = lb_create_block(p, "for.tuple.done");
+
+	lbValue tuple_value = lb_build_expr(p, expr);
+	Type *tuple = tuple_value.type;
+	GB_ASSERT(tuple->kind == Type_Tuple);
+	i32 tuple_count = cast(i32)tuple->Tuple.variables.count;
+	i32 cond_index = tuple_count-1;
+
+	lbValue cond = lb_emit_struct_ev(p, tuple_value, cond_index);
+	lb_emit_if(p, cond, body, done);
+	lb_start_block(p, body);
+
+
+	if (val0_) *val0_ = lb_emit_struct_ev(p, tuple_value, 0);
+	if (val1_) *val1_ = lb_emit_struct_ev(p, tuple_value, 1);
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs) {
+	lb_open_scope(p);
+
+	Type *val0_type = nullptr;
+	Type *val1_type = nullptr;
+	if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
+		val0_type = type_of_expr(rs->val0);
+	}
+	if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
+		val1_type = type_of_expr(rs->val1);
+	}
+
+	if (val0_type != nullptr) {
+		Entity *e = entity_of_ident(rs->val0);
+		lb_add_local(p, e->type, e, true);
+	}
+	if (val1_type != nullptr) {
+		Entity *e = entity_of_ident(rs->val1);
+		lb_add_local(p, e->type, e, true);
+	}
+
+	lbValue val = {};
+	lbValue key = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	Ast *expr = unparen_expr(rs->expr);
+	bool is_map = false;
+
+	TypeAndValue tav = type_and_value_of_expr(expr);
+
+	if (is_ast_range(expr)) {
+		lb_build_range_interval(p, &expr->BinaryExpr, val0_type, &val, &key, &loop, &done);
+	} else if (tav.mode == Addressing_Type) {
+		lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
+	} else {
+		Type *expr_type = type_of_expr(expr);
+		Type *et = base_type(type_deref(expr_type));
+		switch (et->kind) {
+		case Type_Map: {
+			is_map = true;
+			gbAllocator a = heap_allocator();
+			lbAddr addr = lb_build_addr(p, expr);
+			lbValue map = lb_addr_get_ptr(p, addr);
+			if (is_type_pointer(type_deref(lb_addr_type(addr)))) {
+				map = lb_addr_load(p, addr);
+			}
+			lbValue entries_ptr = lb_map_entries_ptr(p, map);
+			lbValue count_ptr = lb_emit_struct_ep(p, entries_ptr, 1);
+			lb_build_range_indexed(p, map, val1_type, count_ptr, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_Array: {
+			lbValue array = lb_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = lb_emit_load(p, array);
+			}
+			lbAddr count_ptr = lb_add_local_generated(p, t_int, false);
+			lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->Array.count));
+			lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_EnumeratedArray: {
+			lbValue array = lb_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = lb_emit_load(p, array);
+			}
+			lbAddr count_ptr = lb_add_local_generated(p, t_int, false);
+			lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->EnumeratedArray.count));
+			lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_DynamicArray: {
+			lbValue count_ptr = {};
+			lbValue array = lb_build_addr_ptr(p, expr);
+			if (is_type_pointer(type_deref(array.type))) {
+				array = lb_emit_load(p, array);
+			}
+			count_ptr = lb_emit_struct_ep(p, array, 1);
+			lb_build_range_indexed(p, array, val0_type, count_ptr, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_Slice: {
+			lbValue count_ptr = {};
+			lbValue slice = lb_build_expr(p, expr);
+			if (is_type_pointer(slice.type)) {
+				count_ptr = lb_emit_struct_ep(p, slice, 1);
+				slice = lb_emit_load(p, slice);
+			} else {
+				count_ptr = lb_add_local_generated(p, t_int, false).addr;
+				lb_emit_store(p, count_ptr, lb_slice_len(p, slice));
+			}
+			lb_build_range_indexed(p, slice, val0_type, count_ptr, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_Basic: {
+			lbValue string = lb_build_expr(p, expr);
+			if (is_type_pointer(string.type)) {
+				string = lb_emit_load(p, string);
+			}
+			if (is_type_untyped(expr_type)) {
+				lbAddr s = lb_add_local_generated(p, default_type(string.type), false);
+				lb_addr_store(p, s, string);
+				string = lb_addr_load(p, s);
+			}
+			Type *t = base_type(string.type);
+			GB_ASSERT(!is_type_cstring(t));
+			lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done);
+			break;
+		}
+		case Type_Tuple:
+			lb_build_range_tuple(p, expr, val0_type, val1_type, &val, &key, &loop, &done);
+			break;
+		default:
+			GB_PANIC("Cannot range over %s", type_to_string(expr_type));
+			break;
+		}
+	}
+
+
+	if (is_map) {
+		if (val0_type) lb_store_range_stmt_val(p, rs->val0, key);
+		if (val1_type) lb_store_range_stmt_val(p, rs->val1, val);
+	} else {
+		if (val0_type) lb_store_range_stmt_val(p, rs->val0, val);
+		if (val1_type) lb_store_range_stmt_val(p, rs->val1, key);
+	}
+
+	lb_push_target_list(p, rs->label, done, loop, nullptr);
+
+	lb_build_stmt(p, rs->body);
+
+	lb_close_scope(p, lbDeferExit_Default, nullptr);
+	lb_pop_target_list(p);
+	lb_emit_jump(p, loop);
+	lb_start_block(p, done);
+}
+
+void lb_build_inline_range_stmt(lbProcedure *p, AstInlineRangeStmt *rs) {
+	lbModule *m = p->module;
+
+	lb_open_scope(p); // Open scope here
+
+	Type *val0_type = nullptr;
+	Type *val1_type = nullptr;
+	if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
+		val0_type = type_of_expr(rs->val0);
+	}
+	if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
+		val1_type = type_of_expr(rs->val1);
+	}
+
+	if (val0_type != nullptr) {
+		Entity *e = entity_of_ident(rs->val0);
+		lb_add_local(p, e->type, e, true);
+	}
+	if (val1_type != nullptr) {
+		Entity *e = entity_of_ident(rs->val1);
+		lb_add_local(p, e->type, e, true);
+	}
+
+	lbValue val = {};
+	lbValue key = {};
+	lbBlock *loop = nullptr;
+	lbBlock *done = nullptr;
+	Ast *expr = unparen_expr(rs->expr);
+
+	TypeAndValue tav = type_and_value_of_expr(expr);
+
+	if (is_ast_range(expr)) {
+
+		lbAddr val0_addr = {};
+		lbAddr val1_addr = {};
+		if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
+		if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+
+		TokenKind op = expr->BinaryExpr.op.kind;
+		Ast *start_expr = expr->BinaryExpr.left;
+		Ast *end_expr   = expr->BinaryExpr.right;
+		GB_ASSERT(start_expr->tav.mode == Addressing_Constant);
+		GB_ASSERT(end_expr->tav.mode == Addressing_Constant);
+
+		ExactValue start = start_expr->tav.value;
+		ExactValue end   = end_expr->tav.value;
+		if (op == Token_Ellipsis) { // .. [start, end]
+			ExactValue index = exact_value_i64(0);
+			for (ExactValue val = start;
+			     compare_exact_values(Token_LtEq, val, end);
+			     val = exact_value_increment_one(val), index = exact_value_increment_one(index)) {
+
+				if (val0_type) lb_addr_store(p, val0_addr, lb_const_value(m, val0_type, val));
+				if (val1_type) lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, index));
+
+				lb_build_stmt(p, rs->body);
+			}
+		} else if (op == Token_RangeHalf) { // ..< [start, end)
+			ExactValue index = exact_value_i64(0);
+			for (ExactValue val = start;
+			     compare_exact_values(Token_Lt, val, end);
+			     val = exact_value_increment_one(val), index = exact_value_increment_one(index)) {
+
+				if (val0_type) lb_addr_store(p, val0_addr, lb_const_value(m, val0_type, val));
+				if (val1_type) lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, index));
+
+				lb_build_stmt(p, rs->body);
+			}
+		}
+
+
+	} else if (tav.mode == Addressing_Type) {
+		GB_ASSERT(is_type_enum(type_deref(tav.type)));
+		Type *et = type_deref(tav.type);
+		Type *bet = base_type(et);
+
+		lbAddr val0_addr = {};
+		lbAddr val1_addr = {};
+		if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
+		if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+
+		for_array(i, bet->Enum.fields) {
+			Entity *field = bet->Enum.fields[i];
+			GB_ASSERT(field->kind == Entity_Constant);
+			if (val0_type) lb_addr_store(p, val0_addr, lb_const_value(m, val0_type, field->Constant.value));
+			if (val1_type) lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, exact_value_i64(i)));
+
+			lb_build_stmt(p, rs->body);
+		}
+	} else {
+		lbAddr val0_addr = {};
+		lbAddr val1_addr = {};
+		if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
+		if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+
+		GB_ASSERT(expr->tav.mode == Addressing_Constant);
+
+		Type *t = base_type(expr->tav.type);
+
+
+		switch (t->kind) {
+		case Type_Basic:
+			GB_ASSERT(is_type_string(t));
+			{
+				ExactValue value = expr->tav.value;
+				GB_ASSERT(value.kind == ExactValue_String);
+				String str = value.value_string;
+				Rune codepoint = 0;
+				isize offset = 0;
+				do {
+					isize width = gb_utf8_decode(str.text+offset, str.len-offset, &codepoint);
+					if (val0_type) lb_addr_store(p, val0_addr, lb_const_value(m, val0_type, exact_value_i64(codepoint)));
+					if (val1_type) lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, exact_value_i64(offset)));
+					lb_build_stmt(p, rs->body);
+
+					offset += width;
+				} while (offset < str.len);
+			}
+			break;
+		case Type_Array:
+			if (t->Array.count > 0) {
+				lbValue val = lb_build_expr(p, expr);
+				lbValue val_addr = lb_address_from_load_or_generate_local(p, val);
+
+				for (i64 i = 0; i < t->Array.count; i++) {
+					if (val0_type) {
+						// NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32
+						lbValue elem = lb_emit_array_epi(p, val_addr, cast(i32)i);
+						lb_addr_store(p, val0_addr, lb_emit_load(p, elem));
+					}
+					if (val1_type) lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, exact_value_i64(i)));
+
+					lb_build_stmt(p, rs->body);
+				}
+
+			}
+			break;
+		case Type_EnumeratedArray:
+			if (t->EnumeratedArray.count > 0) {
+				lbValue val = lb_build_expr(p, expr);
+				lbValue val_addr = lb_address_from_load_or_generate_local(p, val);
+
+				for (i64 i = 0; i < t->EnumeratedArray.count; i++) {
+					if (val0_type) {
+						// NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32
+						lbValue elem = lb_emit_array_epi(p, val_addr, cast(i32)i);
+						lb_addr_store(p, val0_addr, lb_emit_load(p, elem));
+					}
+					if (val1_type) {
+						ExactValue idx = exact_value_add(exact_value_i64(i), t->EnumeratedArray.min_value);
+						lb_addr_store(p, val1_addr, lb_const_value(m, val1_type, idx));
+					}
+
+					lb_build_stmt(p, rs->body);
+				}
+
+			}
+			break;
+		default:
+			GB_PANIC("Invalid inline for type");
+			break;
+		}
+	}
+
+
+	lb_close_scope(p, lbDeferExit_Default, nullptr);
+}
+
+
+void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss) {
+	if (ss->init != nullptr) {
+		lb_build_stmt(p, ss->init);
+	}
+	lbValue tag = lb_const_bool(p->module, t_llvm_bool, true);
+	if (ss->tag != nullptr) {
+		tag = lb_build_expr(p, ss->tag);
+	}
+	lbBlock *done = lb_create_block(p, "switch.done"); // NOTE(bill): Append later
+
+	ast_node(body, BlockStmt, ss->body);
+
+	Array<Ast *> default_stmts = {};
+	lbBlock *default_fall = nullptr;
+	lbBlock *default_block = nullptr;
+
+	lbBlock *fall = nullptr;
+
+	isize case_count = body->stmts.count;
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+
+		lbBlock *body = fall;
+
+		if (body == nullptr) {
+			body = lb_create_block(p, "switch.case.body");
+		}
+
+		fall = done;
+		if (i+1 < case_count) {
+			fall = lb_create_block(p, "switch.fall.body");
+		}
+
+		if (cc->list.count == 0) {
+			// default case
+			default_stmts = cc->stmts;
+			default_fall  = fall;
+			default_block = body;
+			continue;
+		}
+
+		lbBlock *next_cond = nullptr;
+		for_array(j, cc->list) {
+			Ast *expr = unparen_expr(cc->list[j]);
+			next_cond = lb_create_block(p, "switch.case.next");
+
+			lbValue cond = lb_const_bool(p->module, t_llvm_bool, false);
+			if (is_ast_range(expr)) {
+				ast_node(ie, BinaryExpr, expr);
+				TokenKind op = Token_Invalid;
+				switch (ie->op.kind) {
+				case Token_Ellipsis:  op = Token_LtEq; break;
+				case Token_RangeHalf: op = Token_Lt;   break;
+				default: GB_PANIC("Invalid interval operator"); break;
+				}
+				lbValue lhs = lb_build_expr(p, ie->left);
+				lbValue rhs = lb_build_expr(p, ie->right);
+				// TODO(bill): do short circuit here
+				lbValue cond_lhs = lb_emit_comp(p, Token_LtEq, lhs, tag);
+				lbValue cond_rhs = lb_emit_comp(p, op, tag, rhs);
+				cond = lb_emit_arith(p, Token_And, cond_lhs, cond_rhs, t_bool);
+			} else {
+				if (expr->tav.mode == Addressing_Type) {
+					GB_ASSERT(is_type_typeid(tag.type));
+					lbValue e = lb_typeid(p->module, expr->tav.type);
+					e = lb_emit_conv(p, e, tag.type);
+					cond = lb_emit_comp(p, Token_CmpEq, tag, e);
+				} else {
+					cond = lb_emit_comp(p, Token_CmpEq, tag, lb_build_expr(p, expr));
+				}
+			}
+			lb_emit_if(p, cond, body, next_cond);
+			lb_start_block(p, next_cond);
+		}
+		lb_start_block(p, body);
+
+		lb_push_target_list(p, ss->label, done, nullptr, fall);
+		lb_open_scope(p);
+		lb_build_stmt_list(p, cc->stmts);
+		lb_close_scope(p, lbDeferExit_Default, body);
+		lb_pop_target_list(p);
+
+		lb_emit_jump(p, done);
+		lb_start_block(p, next_cond);
+	}
+
+	if (default_block != nullptr) {
+		lb_emit_jump(p, default_block);
+		lb_start_block(p, default_block);
+
+		lb_push_target_list(p, ss->label, done, nullptr, default_fall);
+		lb_open_scope(p);
+		lb_build_stmt_list(p, default_stmts);
+		lb_close_scope(p, lbDeferExit_Default, default_block);
+		lb_pop_target_list(p);
+	}
+
+	lb_emit_jump(p, done);
+	lb_start_block(p, done);
+}
+
+void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
+	Entity *e = implicit_entity_of_node(clause);
+	GB_ASSERT(e != nullptr);
+	if (e->flags & EntityFlag_Value) {
+		// by value
+		GB_ASSERT(are_types_identical(e->type, value.type));
+		lbAddr x = lb_add_local(p, e->type, e, false);
+		lb_addr_store(p, x, value);
+	} else {
+		// by reference
+		GB_ASSERT(are_types_identical(e->type, type_deref(value.type)));
+		lb_add_entity(p->module, e, value);
+	}
+}
+
+lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value) {
+	Entity *e = entity_of_node(stmt_val);
+	if (e == nullptr) {
+		return {};
+	}
+
+	if ((e->flags & EntityFlag_Value) == 0) {
+		if (LLVMIsALoadInst(value.value)) {
+			lbValue ptr = lb_address_from_load_or_generate_local(p, value);
+			lb_add_entity(p->module, e, ptr);
+			return lb_addr(ptr);
+		}
+	}
+
+	// by value
+	lbAddr addr = lb_add_local(p, e->type, e, false);
+	lb_addr_store(p, addr, value);
+	return addr;
+}
+
+void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
+	ast_node(cc, CaseClause, clause);
+
+	lb_push_target_list(p, label, done, nullptr, nullptr);
+	lb_open_scope(p);
+	lb_build_stmt_list(p, cc->stmts);
+	lb_close_scope(p, lbDeferExit_Default, body);
+	lb_pop_target_list(p);
+
+	lb_emit_jump(p, done);
+}
+
+
+
+void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
+	lbModule *m = p->module;
+
+	ast_node(as, AssignStmt, ss->tag);
+	GB_ASSERT(as->lhs.count == 1);
+	GB_ASSERT(as->rhs.count == 1);
+
+	lbValue parent = lb_build_expr(p, as->rhs[0]);
+	bool is_parent_ptr = is_type_pointer(parent.type);
+
+	TypeSwitchKind switch_kind = check_valid_type_switch_type(parent.type);
+	GB_ASSERT(switch_kind != TypeSwitch_Invalid);
+
+	lbValue parent_value = parent;
+
+	lbValue parent_ptr = parent;
+	if (!is_parent_ptr) {
+		parent_ptr = lb_address_from_load_or_generate_local(p, parent);
+	}
+
+	lbValue tag_index = {};
+	lbValue union_data = {};
+	if (switch_kind == TypeSwitch_Union) {
+		lbValue tag_ptr = lb_emit_union_tag_ptr(p, parent_ptr);
+		tag_index = lb_emit_load(p, tag_ptr);
+		union_data = lb_emit_conv(p, parent_ptr, t_rawptr);
+	}
+
+	lbBlock *start_block = lb_create_block(p, "typeswitch.case.first");
+	lb_emit_jump(p, start_block);
+	lb_start_block(p, start_block);
+
+	// NOTE(bill): Append this later
+	lbBlock *done = lb_create_block(p, "typeswitch.done");
+	Ast *default_ = nullptr;
+
+	ast_node(body, BlockStmt, ss->body);
+
+	gb_local_persist i32 weird_count = 0;
+
+	for_array(i, body->stmts) {
+		Ast *clause = body->stmts[i];
+		ast_node(cc, CaseClause, clause);
+		if (cc->list.count == 0) {
+			default_ = clause;
+			continue;
+		}
+
+		lbBlock *body = lb_create_block(p, "typeswitch.body");
+		lbBlock *next = nullptr;
+		Type *case_type = nullptr;
+		for_array(type_index, cc->list) {
+			next = lb_create_block(p, "typeswitch.next");
+			case_type = type_of_expr(cc->list[type_index]);
+			lbValue cond = {};
+			if (switch_kind == TypeSwitch_Union) {
+				Type *ut = base_type(type_deref(parent.type));
+				lbValue variant_tag = lb_const_union_tag(m, ut, case_type);
+				cond = lb_emit_comp(p, Token_CmpEq, tag_index, variant_tag);
+			} else if (switch_kind == TypeSwitch_Any) {
+				lbValue any_typeid  = lb_emit_load(p, lb_emit_struct_ep(p, parent_ptr, 1));
+				lbValue case_typeid = lb_typeid(m, case_type);
+				cond = lb_emit_comp(p, Token_CmpEq, any_typeid, case_typeid);
+			}
+			GB_ASSERT(cond.value != nullptr);
+
+			lb_emit_if(p, cond, body, next);
+			lb_start_block(p, next);
+		}
+
+		Entity *case_entity = implicit_entity_of_node(clause);
+
+		lbValue value = parent_value;
+
+		lb_start_block(p, body);
+
+		bool by_reference = (case_entity->flags & EntityFlag_Value) == 0;
+
+		if (cc->list.count == 1) {
+			lbValue data = {};
+			if (switch_kind == TypeSwitch_Union) {
+				data = union_data;
+			} else if (switch_kind == TypeSwitch_Any) {
+				lbValue any_data = lb_emit_load(p, lb_emit_struct_ep(p, parent_ptr, 0));
+				data = any_data;
+			}
+
+			Type *ct = case_entity->type;
+			Type *ct_ptr = alloc_type_pointer(ct);
+
+			value = lb_emit_conv(p, data, ct_ptr);
+			if (!by_reference) {
+				value = lb_emit_load(p, value);
+			}
+		}
+
+		lb_store_type_case_implicit(p, clause, value);
+		lb_type_case_body(p, ss->label, clause, body, done);
+		lb_start_block(p, next);
+	}
+
+	if (default_ != nullptr) {
+		lb_store_type_case_implicit(p, default_, parent_value);
+		lb_type_case_body(p, ss->label, default_, p->curr_block, done);
+	} else {
+		lb_emit_jump(p, done);
+	}
+	lb_start_block(p, done);
+}
+
+
+lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
+	lbModule *m = p->module;
+
+	lbBlock *rhs  = lb_create_block(p, "logical.cmp.rhs");
+	lbBlock *done = lb_create_block(p, "logical.cmp.done");
+
+	type = default_type(type);
+
+	lbValue short_circuit = {};
+	if (op == Token_CmpAnd) {
+		lb_build_cond(p, left, rhs, done);
+		short_circuit = lb_const_bool(m, type, false);
+	} else if (op == Token_CmpOr) {
+		lb_build_cond(p, left, done, rhs);
+		short_circuit = lb_const_bool(m, type, true);
+	}
+
+	if (rhs->preds.count == 0) {
+		lb_start_block(p, done);
+		return short_circuit;
+	}
+
+	if (done->preds.count == 0) {
+		lb_start_block(p, rhs);
+		return lb_build_expr(p, right);
+	}
+
+	Array<LLVMValueRef> incoming_values = {};
+	Array<LLVMBasicBlockRef> incoming_blocks = {};
+	array_init(&incoming_values, heap_allocator(), done->preds.count+1);
+	array_init(&incoming_blocks, heap_allocator(), done->preds.count+1);
+
+	for_array(i, done->preds) {
+		incoming_values[i] = short_circuit.value;
+		incoming_blocks[i] = done->preds[i]->block;
+	}
+
+	lb_start_block(p, rhs);
+	lbValue edge = lb_build_expr(p, right);
+
+	incoming_values[done->preds.count] = edge.value;
+	incoming_blocks[done->preds.count] = p->curr_block->block;
+
+	lb_emit_jump(p, done);
+	lb_start_block(p, done);
+
+	lbValue res = {};
+	res.type = type;
+	res.value = LLVMBuildPhi(p->builder, lb_type(m, type), "");
+	GB_ASSERT(incoming_values.count == incoming_blocks.count);
+	LLVMAddIncoming(res.value, incoming_values.data, incoming_blocks.data, cast(unsigned)incoming_values.count);
+
+	return res;
+}
+
+
+void lb_build_stmt(lbProcedure *p, Ast *node) {
+	switch (node->kind) {
+	case_ast_node(bs, EmptyStmt, node);
+	case_end;
+
+	case_ast_node(us, UsingStmt, node);
+	case_end;
+
+	case_ast_node(ws, WhenStmt, node);
+		lb_build_when_stmt(p, ws);
+	case_end;
+
+
+	case_ast_node(bs, BlockStmt, node);
+		if (bs->label != nullptr) {
+			lbBlock *done = lb_create_block(p, "block.done");
+			lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
+			tl->is_block = true;
+
+			lb_open_scope(p);
+			lb_build_stmt_list(p, bs->stmts);
+			lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+			lb_emit_jump(p, done);
+			lb_start_block(p, done);
+		} else {
+			lb_open_scope(p);
+			lb_build_stmt_list(p, bs->stmts);
+			lb_close_scope(p, lbDeferExit_Default, nullptr);
+		}
+	case_end;
+
+	case_ast_node(vd, ValueDecl, node);
+		if (!vd->is_mutable) {
+			return;
+		}
+
+		bool is_static = false;
+		if (vd->names.count > 0) {
+			Entity *e = entity_of_ident(vd->names[0]);
+			if (e->flags & EntityFlag_Static) {
+				// NOTE(bill): If one of the entities is static, they all are
+				is_static = true;
+			}
+		}
+
+		if (is_static) {
+			for_array(i, vd->names) {
+				lbValue value = {};
+				if (vd->values.count > 0) {
+					GB_ASSERT(vd->names.count == vd->values.count);
+					Ast *ast_value = vd->values[i];
+					GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
+					          ast_value->tav.mode == Addressing_Invalid);
+
+					value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value);
+				}
+
+				Ast *ident = vd->names[i];
+				GB_ASSERT(!is_blank_ident(ident));
+				Entity *e = entity_of_ident(ident);
+				GB_ASSERT(e->flags & EntityFlag_Static);
+				String name = e->token.string;
+
+				String mangled_name = {};
+				{
+					gbString str = gb_string_make_length(heap_allocator(), p->name.text, p->name.len);
+					str = gb_string_appendc(str, "-");
+					str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id);
+					mangled_name.text = cast(u8 *)str;
+					mangled_name.len = gb_string_length(str);
+				}
+
+				char *c_name = alloc_cstring(heap_allocator(), mangled_name);
+
+				LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
+				if (value.value != nullptr) {
+					LLVMSetInitializer(global, value.value);
+				} else {
+					LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
+				}
+				if (e->Variable.thread_local_model != "") {
+					LLVMSetThreadLocal(global, true);
+
+					String m = e->Variable.thread_local_model;
+					LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
+					if (m == "default") {
+						mode = LLVMGeneralDynamicTLSModel;
+					} else if (m == "localdynamic") {
+						mode = LLVMLocalDynamicTLSModel;
+					} else if (m == "initialexec") {
+						mode = LLVMInitialExecTLSModel;
+					} else if (m == "localexec") {
+						mode = LLVMLocalExecTLSModel;
+					} else {
+						GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
+					}
+					LLVMSetThreadLocalMode(global, mode);
+				} else {
+					LLVMSetLinkage(global, LLVMInternalLinkage);
+				}
+
+
+				lbValue global_val = {global, alloc_type_pointer(e->type)};
+				lb_add_entity(p->module, e, global_val);
+				lb_add_member(p->module, mangled_name, global_val);
+			}
+			return;
+		}
+
+
+		if (vd->values.count == 0) { // declared and zero-initialized
+			for_array(i, vd->names) {
+				Ast *name = vd->names[i];
+				if (!is_blank_ident(name)) {
+					Entity *e = entity_of_ident(name);
+					lb_add_local(p, e->type, e, true);
+				}
+			}
+		} else { // Tuple(s)
+			auto lvals = array_make<lbAddr>(heap_allocator(), 0, vd->names.count);
+			auto inits = array_make<lbValue>(heap_allocator(), 0, vd->names.count);
+
+			for_array(i, vd->names) {
+				Ast *name = vd->names[i];
+				lbAddr lval = {};
+				if (!is_blank_ident(name)) {
+					Entity *e = entity_of_ident(name);
+					lval = lb_add_local(p, e->type, e, false);
+				}
+				array_add(&lvals, lval);
+			}
+
+			for_array(i, vd->values) {
+				lbValue init = lb_build_expr(p, vd->values[i]);
+				Type *t = init.type;
+				if (t->kind == Type_Tuple) {
+					for_array(i, t->Tuple.variables) {
+						Entity *e = t->Tuple.variables[i];
+						lbValue v = lb_emit_struct_ev(p, init, cast(i32)i);
+						array_add(&inits, v);
+					}
+				} else {
+					array_add(&inits, init);
+				}
+			}
+
+
+			for_array(i, inits) {
+				lbAddr lval = lvals[i];
+				lbValue init = inits[i];
+				lb_addr_store(p, lval, init);
+			}
+		}
+	case_end;
+
+	case_ast_node(as, AssignStmt, node);
+		if (as->op.kind == Token_Eq) {
+			auto lvals = array_make<lbAddr>(heap_allocator(), 0, as->lhs.count);
+
+			for_array(i, as->lhs) {
+				Ast *lhs = as->lhs[i];
+				lbAddr lval = {};
+				if (!is_blank_ident(lhs)) {
+					lval = lb_build_addr(p, lhs);
+				}
+				array_add(&lvals, lval);
+			}
+
+			if (as->lhs.count == as->rhs.count) {
+				if (as->lhs.count == 1) {
+					lbAddr lval = lvals[0];
+					Ast *rhs = as->rhs[0];
+					lbValue init = lb_build_expr(p, rhs);
+					lb_addr_store(p, lvals[0], init);
+				} else {
+					auto inits = array_make<lbValue>(heap_allocator(), 0, lvals.count);
+
+					for_array(i, as->rhs) {
+						lbValue init = lb_build_expr(p, as->rhs[i]);
+						array_add(&inits, init);
+					}
+
+					for_array(i, inits) {
+						lbAddr lval = lvals[i];
+						lbValue init = inits[i];
+						lb_addr_store(p, lval, init);
+					}
+				}
+			} else {
+				auto inits = array_make<lbValue>(heap_allocator(), 0, lvals.count);
+
+				for_array(i, as->rhs) {
+					lbValue init = lb_build_expr(p, as->rhs[i]);
+					Type *t = init.type;
+					// TODO(bill): refactor for code reuse as this is repeated a bit
+					if (t->kind == Type_Tuple) {
+						for_array(i, t->Tuple.variables) {
+							Entity *e = t->Tuple.variables[i];
+							lbValue v = lb_emit_struct_ev(p, init, cast(i32)i);
+							array_add(&inits, v);
+						}
+					} else {
+						array_add(&inits, init);
+					}
+				}
+
+				for_array(i, inits) {
+					lbAddr lval = lvals[i];
+					lbValue init = inits[i];
+					lb_addr_store(p, lval, init);
+				}
+			}
+		} else {
+			// NOTE(bill): Only 1 += 1 is allowed, no tuples
+			// +=, -=, etc
+			i32 op = cast(i32)as->op.kind;
+			op += Token_Add - Token_AddEq; // Convert += to +
+			if (op == Token_CmpAnd || op == Token_CmpOr) {
+				Type *type = as->lhs[0]->tav.type;
+				lbValue new_value = lb_emit_logical_binary_expr(p, cast(TokenKind)op, as->lhs[0], as->rhs[0], type);
+
+				lbAddr lhs = lb_build_addr(p, as->lhs[0]);
+				lb_addr_store(p, lhs, new_value);
+			} else {
+				lbAddr lhs = lb_build_addr(p, as->lhs[0]);
+				lbValue value = lb_build_expr(p, as->rhs[0]);
+
+				lbValue old_value = lb_addr_load(p, lhs);
+				Type *type = old_value.type;
+
+				lbValue change = lb_emit_conv(p, value, type);
+				lbValue new_value = lb_emit_arith(p, cast(TokenKind)op, old_value, change, type);
+				lb_addr_store(p, lhs, new_value);
+			}
+			return;
+		}
+	case_end;
+
+	case_ast_node(es, ExprStmt, node);
+		lb_build_expr(p, es->expr);
+	case_end;
+
+	case_ast_node(ds, DeferStmt, node);
+		isize scope_index = p->scope_index;
+		lb_add_defer_node(p, scope_index, ds->stmt);
+	case_end;
+
+	case_ast_node(rs, ReturnStmt, node);
+		lbValue res = {};
+
+		TypeTuple *tuple  = &p->type->Proc.results->Tuple;
+		isize return_count = p->type->Proc.result_count;
+		isize res_count = rs->results.count;
+
+		if (return_count == 0) {
+			// No return values
+			LLVMBuildRetVoid(p->builder);
+			return;
+		} else if (return_count == 1) {
+			Entity *e = tuple->variables[0];
+			if (res_count == 0) {
+				lbValue *found = map_get(&p->module->values, hash_entity(e));
+				GB_ASSERT(found);
+				res = lb_emit_load(p, *found);
+			} else {
+				res = lb_build_expr(p, rs->results[0]);
+				res = lb_emit_conv(p, res, e->type);
+			}
+		} else {
+			auto results = array_make<lbValue>(heap_allocator(), 0, return_count);
+
+			if (res_count != 0) {
+				for (isize res_index = 0; res_index < res_count; res_index++) {
+					lbValue res = lb_build_expr(p, rs->results[res_index]);
+					Type *t = res.type;
+					if (t->kind == Type_Tuple) {
+						for_array(i, t->Tuple.variables) {
+							Entity *e = t->Tuple.variables[i];
+							lbValue v = lb_emit_struct_ev(p, res, cast(i32)i);
+							array_add(&results, v);
+						}
+					} else {
+						array_add(&results, res);
+					}
+				}
+			} else {
+				for (isize res_index = 0; res_index < return_count; res_index++) {
+					Entity *e = tuple->variables[res_index];
+					lbValue *found = map_get(&p->module->values, hash_entity(e));
+					GB_ASSERT(found);
+					lbValue res = lb_emit_load(p, *found);
+					array_add(&results, res);
+				}
+			}
+
+			GB_ASSERT(results.count == return_count);
+
+			Type *ret_type = p->type->Proc.results;
+			// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+			res = lb_add_local_generated(p, ret_type, false).addr;
+			for_array(i, results) {
+				Entity *e = tuple->variables[i];
+				lbValue field = lb_emit_struct_ep(p, res, cast(i32)i);
+				lbValue val = lb_emit_conv(p, results[i], e->type);
+				lb_emit_store(p, field, val);
+			}
+
+			res = lb_emit_load(p, res);
+		}
+
+		lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+
+		if (p->type->Proc.return_by_pointer) {
+			if (res.value != nullptr) {
+				lb_addr_store(p, p->return_ptr, res);
+			} else {
+				lb_addr_store(p, p->return_ptr, lb_const_nil(p->module, p->type->Proc.abi_compat_result_type));
+			}
+			LLVMBuildRetVoid(p->builder);
+		} else {
+			GB_ASSERT_MSG(res.value != nullptr, "%.*s", LIT(p->name));
+			Type *abi_rt = p->type->Proc.abi_compat_result_type;
+			if (!are_types_identical(res.type, abi_rt)) {
+				res = lb_emit_transmute(p, res, abi_rt);
+			}
+			LLVMBuildRet(p->builder, res.value);
+		}
+	case_end;
+
+	case_ast_node(is, IfStmt, node);
+		lb_open_scope(p); // Scope #1
+
+		if (is->init != nullptr) {
+			// TODO(bill): Should this have a separate block to begin with?
+		#if 1
+			lbBlock *init = lb_create_block(p, "if.init");
+			lb_emit_jump(p, init);
+			lb_start_block(p, init);
+		#endif
+			lb_build_stmt(p, is->init);
+		}
+		lbBlock *then = lb_create_block(p, "if.then");
+		lbBlock *done = lb_create_block(p, "if.done");
+		lbBlock *else_ = done;
+		if (is->else_stmt != nullptr) {
+			else_ = lb_create_block(p, "if.else");
+		}
+
+		lb_build_cond(p, is->cond, then, else_);
+		lb_start_block(p, then);
+
+		if (is->label != nullptr) {
+			lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
+			tl->is_block = true;
+		}
+
+		lb_build_stmt(p, is->body);
+
+		lb_emit_jump(p, done);
+
+		if (is->else_stmt != nullptr) {
+			lb_start_block(p, else_);
+
+			lb_open_scope(p);
+			lb_build_stmt(p, is->else_stmt);
+			lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+			lb_emit_jump(p, done);
+		}
+
+
+		lb_start_block(p, done);
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+	case_end;
+
+	case_ast_node(fs, ForStmt, node);
+		lb_open_scope(p); // Open Scope here
+
+		if (fs->init != nullptr) {
+		#if 1
+			lbBlock *init = lb_create_block(p, "for.init");
+			lb_emit_jump(p, init);
+			lb_start_block(p, init);
+		#endif
+			lb_build_stmt(p, fs->init);
+		}
+		lbBlock *body = lb_create_block(p, "for.body");
+		lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later
+		lbBlock *loop = body;
+		if (fs->cond != nullptr) {
+			loop = lb_create_block(p, "for.loop");
+		}
+		lbBlock *post = loop;
+		if (fs->post != nullptr) {
+			post = lb_create_block(p, "for.post");
+		}
+
+
+		lb_emit_jump(p, loop);
+		lb_start_block(p, loop);
+
+		if (loop != body) {
+			lb_build_cond(p, fs->cond, body, done);
+			lb_start_block(p, body);
+		}
+
+		lb_push_target_list(p, fs->label, done, post, nullptr);
+
+		lb_build_stmt(p, fs->body);
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+		lb_pop_target_list(p);
+
+		lb_emit_jump(p, post);
+
+		if (fs->post != nullptr) {
+			lb_start_block(p, post);
+			lb_build_stmt(p, fs->post);
+			lb_emit_jump(p, loop);
+		}
+
+		lb_start_block(p, done);
+	case_end;
+
+	case_ast_node(rs, RangeStmt, node);
+		lb_build_range_stmt(p, rs);
+	case_end;
+
+	case_ast_node(rs, InlineRangeStmt, node);
+		lb_build_inline_range_stmt(p, rs);
+	case_end;
+
+	case_ast_node(ss, SwitchStmt, node);
+		lb_build_switch_stmt(p, ss);
+	case_end;
+
+	case_ast_node(ss, TypeSwitchStmt, node);
+		lb_build_type_switch_stmt(p, ss);
+	case_end;
+
+	case_ast_node(bs, BranchStmt, node);
+		lbBlock *block = nullptr;
+
+		if (bs->label != nullptr) {
+			lbBranchBlocks bb = lb_lookup_branch_blocks(p, bs->label);
+			switch (bs->token.kind) {
+			case Token_break:    block = bb.break_;    break;
+			case Token_continue: block = bb.continue_; break;
+			case Token_fallthrough:
+				GB_PANIC("fallthrough cannot have a label");
+				break;
+			}
+		} else {
+			for (lbTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
+				if (t->is_block) {
+					continue;
+				}
+
+				switch (bs->token.kind) {
+				case Token_break:       block = t->break_;       break;
+				case Token_continue:    block = t->continue_;    break;
+				case Token_fallthrough: block = t->fallthrough_; break;
+				}
+			}
+		}
+		if (block != nullptr) {
+			lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
+		}
+		lb_emit_jump(p, block);
+	case_end;
+	}
+}
+
+lbValue lb_emit_select(lbProcedure *p, lbValue cond, lbValue x, lbValue y) {
+	cond = lb_emit_conv(p, cond, t_llvm_bool);
+	lbValue res = {};
+	res.value = LLVMBuildSelect(p->builder, cond.value, x.value, y.value, "");
+	res.type = x.type;
+	return res;
+}
+
+lbValue lb_const_nil(lbModule *m, Type *type) {
+	LLVMValueRef v = LLVMConstNull(lb_type(m, type));
+	return lbValue{v, type};
+}
+
+lbValue lb_const_undef(lbModule *m, Type *type) {
+	LLVMValueRef v = LLVMGetUndef(lb_type(m, type));
+	return lbValue{v, type};
+}
+
+
+lbValue lb_const_int(lbModule *m, Type *type, u64 value) {
+	lbValue res = {};
+	res.value = LLVMConstInt(lb_type(m, type), cast(unsigned long long)value, !is_type_unsigned(type));
+	res.type = type;
+	return res;
+}
+
+lbValue lb_const_string(lbModule *m, String const &value) {
+	return lb_const_value(m, t_string, exact_value_string(value));
+}
+
+
+lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
+	lbValue res = {};
+	res.value = LLVMConstInt(lb_type(m, type), value, false);
+	res.type = type;
+	return res;
+}
+
+LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
+	u32 u = bit_cast<u32>(f);
+	LLVMValueRef i = LLVMConstInt(LLVMInt32TypeInContext(m->ctx), u, false);
+	return LLVMConstBitCast(i, lb_type(m, type));
+}
+
+lbValue lb_emit_min(lbProcedure *p, Type *t, lbValue x, lbValue y) {
+	x = lb_emit_conv(p, x, t);
+	y = lb_emit_conv(p, y, t);
+
+	if (is_type_float(t)) {
+		gbAllocator a = heap_allocator();
+		i64 sz = 8*type_size_of(t);
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = x;
+		args[1] = y;
+		switch (sz) {
+		case 32: return lb_emit_runtime_call(p, "min_f32", args);
+		case 64: return lb_emit_runtime_call(p, "min_f64", args);
+		}
+		GB_PANIC("Unknown float type");
+	}
+	return lb_emit_select(p, lb_emit_comp(p, Token_Lt, x, y), x, y);
+}
+lbValue lb_emit_max(lbProcedure *p, Type *t, lbValue x, lbValue y) {
+	x = lb_emit_conv(p, x, t);
+	y = lb_emit_conv(p, y, t);
+
+	if (is_type_float(t)) {
+		gbAllocator a = heap_allocator();
+		i64 sz = 8*type_size_of(t);
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = x;
+		args[1] = y;
+		switch (sz) {
+		case 32: return lb_emit_runtime_call(p, "max_f32", args);
+		case 64: return lb_emit_runtime_call(p, "max_f64", args);
+		}
+		GB_PANIC("Unknown float type");
+	}
+	return lb_emit_select(p, lb_emit_comp(p, Token_Gt, x, y), x, y);
+}
+
+
+lbValue lb_emit_clamp(lbProcedure *p, Type *t, lbValue x, lbValue min, lbValue max) {
+	lbValue z = {};
+	z = lb_emit_max(p, t, x, min);
+	z = lb_emit_min(p, t, z, max);
+	return z;
+}
+
+
+
+LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
+	HashKey key = hash_string(str);
+	LLVMValueRef *found = map_get(&m->const_strings, key);
+	if (found != nullptr) {
+		return *found;
+	} else {
+		LLVMValueRef indices[2] = {llvm_zero32(m), llvm_zero32(m)};
+		LLVMValueRef data = LLVMConstStringInContext(m->ctx,
+			cast(char const *)str.text,
+			cast(unsigned)str.len,
+			false);
+
+
+		isize max_len = 7+8+1;
+		char *name = gb_alloc_array(heap_allocator(), char, max_len);
+		isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index);
+		len -= 1;
+		m->global_array_index++;
+
+		LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
+		LLVMSetInitializer(global_data, data);
+
+		LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
+		map_set(&m->const_strings, key, ptr);
+		return ptr;
+	}
+}
+
+lbValue lb_find_or_add_entity_string(lbModule *m, String const &str) {
+	LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, str);
+	LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), str.len, true);
+	LLVMValueRef values[2] = {ptr, str_len};
+
+	lbValue res = {};
+	res.value = LLVMConstNamedStruct(lb_type(m, t_string), values, 2);
+	res.type = t_string;
+	return res;
+}
+
+lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) {
+	LLVMValueRef indices[2] = {llvm_zero32(m), llvm_zero32(m)};
+	LLVMValueRef data = LLVMConstStringInContext(m->ctx,
+		cast(char const *)str.text,
+		cast(unsigned)str.len,
+		false);
+
+
+	char *name = nullptr;
+	{
+		isize max_len = 7+8+1;
+		name = gb_alloc_array(heap_allocator(), char, max_len);
+		isize len = gb_snprintf(name, max_len, "csbs$%x", m->global_array_index);
+		len -= 1;
+		m->global_array_index++;
+	}
+	LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
+	LLVMSetInitializer(global_data, data);
+
+	LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
+	LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), str.len, true);
+	LLVMValueRef values[2] = {ptr, len};
+
+	lbValue res = {};
+	res.value = LLVMConstNamedStruct(lb_type(m, t_u8_slice), values, 2);
+	res.type = t_u8_slice;
+	return res;
+}
+
+isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
+	isize index = type_info_index(info, type, false);
+	if (index >= 0) {
+		auto *set = &info->minimum_dependency_type_info_set;
+		for_array(i, set->entries) {
+			if (set->entries[i].ptr == index) {
+				return i+1;
+			}
+		}
+	}
+	if (err_on_not_found) {
+		GB_PANIC("NOT FOUND lb_type_info_index %s @ index %td", type_to_string(type), index);
+	}
+	return -1;
+}
+
+lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type) {
+	type = default_type(type);
+
+	u64 id = cast(u64)lb_type_info_index(m->info, type);
+	GB_ASSERT(id >= 0);
+
+	u64 kind = Typeid_Invalid;
+	u64 named = is_type_named(type) && type->kind != Type_Basic;
+	u64 special = 0;
+	u64 reserved = 0;
+
+	Type *bt = base_type(type);
+	TypeKind tk = bt->kind;
+	switch (tk) {
+	case Type_Basic: {
+		u32 flags = bt->Basic.flags;
+		if (flags & BasicFlag_Boolean)  kind = Typeid_Boolean;
+		if (flags & BasicFlag_Integer)  kind = Typeid_Integer;
+		if (flags & BasicFlag_Unsigned) kind = Typeid_Integer;
+		if (flags & BasicFlag_Float)    kind = Typeid_Float;
+		if (flags & BasicFlag_Complex)  kind = Typeid_Complex;
+		if (flags & BasicFlag_Pointer)  kind = Typeid_Pointer;
+		if (flags & BasicFlag_String)   kind = Typeid_String;
+		if (flags & BasicFlag_Rune)     kind = Typeid_Rune;
+	} break;
+	case Type_Pointer:         kind = Typeid_Pointer;       break;
+	case Type_Array:           kind = Typeid_Array;         break;
+	case Type_EnumeratedArray: kind = Typeid_Enumerated_Array; break;
+	case Type_Slice:           kind = Typeid_Slice;         break;
+	case Type_DynamicArray:    kind = Typeid_Dynamic_Array; break;
+	case Type_Map:             kind = Typeid_Map;           break;
+	case Type_Struct:          kind = Typeid_Struct;        break;
+	case Type_Enum:            kind = Typeid_Enum;          break;
+	case Type_Union:           kind = Typeid_Union;         break;
+	case Type_Tuple:           kind = Typeid_Tuple;         break;
+	case Type_Proc:            kind = Typeid_Procedure;     break;
+	case Type_BitField:        kind = Typeid_Bit_Field;     break;
+	case Type_BitSet:          kind = Typeid_Bit_Set;       break;
+	}
+
+	if (is_type_cstring(type)) {
+		special = 1;
+	} else if (is_type_integer(type) && !is_type_unsigned(type)) {
+		special = 1;
+	}
+
+	u64 data = 0;
+	if (build_context.word_size == 4) {
+		data |= (id       &~ (1u<<24)) << 0u;  // index
+		data |= (kind     &~ (1u<<5))  << 24u; // kind
+		data |= (named    &~ (1u<<1))  << 29u; // kind
+		data |= (special  &~ (1u<<1))  << 30u; // kind
+		data |= (reserved &~ (1u<<1))  << 31u; // kind
+	} else {
+		GB_ASSERT(build_context.word_size == 8);
+		data |= (id       &~ (1ull<<56)) << 0ul;  // index
+		data |= (kind     &~ (1ull<<5))  << 56ull; // kind
+		data |= (named    &~ (1ull<<1))  << 61ull; // kind
+		data |= (special  &~ (1ull<<1))  << 62ull; // kind
+		data |= (reserved &~ (1ull<<1))  << 63ull; // kind
+	}
+
+
+	lbValue res = {};
+	res.value = LLVMConstInt(lb_type(m, typeid_type), data, false);
+	res.type = typeid_type;
+	return res;
+}
+
+lbValue lb_type_info(lbModule *m, Type *type) {
+	type = default_type(type);
+
+	isize index = lb_type_info_index(m->info, type);
+	GB_ASSERT(index >= 0);
+
+	LLVMTypeRef it = lb_type(m, t_int);
+	LLVMValueRef indices[2] = {
+		LLVMConstInt(it, 0, false),
+		LLVMConstInt(it, index, true),
+	};
+
+	lbValue value = {};
+	value.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices));
+	value.type = t_type_info_ptr;
+	return value;
+}
+
+
+lbValue lb_const_value(lbModule *m, Type *type, ExactValue value) {
+	LLVMContextRef ctx = m->ctx;
+
+	type = default_type(type);
+	Type *original_type = type;
+
+	lbValue res = {};
+	res.type = original_type;
+	type = core_type(type);
+	value = convert_exact_value_for_type(value, type);
+
+	if (value.kind == ExactValue_Typeid) {
+		return lb_typeid(m, value.value_typeid, original_type);
+	}
+
+	if (value.kind == ExactValue_Invalid) {
+		return lb_const_nil(m, type);
+	}
+
+	// GB_ASSERT_MSG(is_type_typed(type), "%s", type_to_string(type));
+
+	if (is_type_slice(type)) {
+		if (value.kind == ExactValue_String) {
+			GB_ASSERT(is_type_u8_slice(type));
+			res.value = lb_find_or_add_entity_string_byte_slice(m, value.value_string).value;
+			return res;
+		} else {
+			ast_node(cl, CompoundLit, value.value_compound);
+
+			isize count = cl->elems.count;
+			if (count == 0) {
+				return lb_const_nil(m, type);
+			}
+			count = gb_max(cl->max_count, count);
+			Type *elem = base_type(type)->Slice.elem;
+			Type *t = alloc_type_array(elem, count);
+			lbValue backing_array = lb_const_value(m, t, value);
+
+
+			isize max_len = 7+8+1;
+			char *str = gb_alloc_array(heap_allocator(), char, max_len);
+			isize len = gb_snprintf(str, max_len, "csba$%x", m->global_array_index);
+			m->global_array_index++;
+
+			String name = make_string(cast(u8 *)str, len-1);
+
+			Entity *e = alloc_entity_constant(nullptr, make_token_ident(name), t, value);
+			LLVMValueRef global_data = LLVMAddGlobal(m->mod, lb_type(m, t), str);
+			LLVMSetInitializer(global_data, backing_array.value);
+
+			lbValue g = {};
+			g.value = global_data;
+			g.type = t;
+
+			lb_add_entity(m, e, g);
+			lb_add_member(m, name, g);
+
+			{
+				LLVMValueRef indices[2] = {llvm_zero32(m), llvm_zero32(m)};
+				LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
+				LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
+				LLVMValueRef values[2] = {ptr, len};
+
+				res.value = LLVMConstNamedStruct(lb_type(m, original_type), values, 2);
+				return res;
+			}
+
+		}
+	} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
+		LLVMValueRef data = LLVMConstStringInContext(ctx,
+			cast(char const *)value.value_string.text,
+			cast(unsigned)value.value_string.len,
+			false);
+		res.value = data;
+		return res;
+	} else if (is_type_array(type) &&
+	    value.kind != ExactValue_Invalid &&
+	    value.kind != ExactValue_String &&
+	    value.kind != ExactValue_Compound) {
+
+		i64 count  = type->Array.count;
+		Type *elem = type->Array.elem;
+
+
+		lbValue single_elem = lb_const_value(m, elem, value);
+
+		LLVMValueRef *elems = gb_alloc_array(heap_allocator(), LLVMValueRef, count);
+		for (i64 i = 0; i < count; i++) {
+			elems[i] = single_elem.value;
+		}
+
+		res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)count);
+		return res;
+	}
+
+	switch (value.kind) {
+	case ExactValue_Invalid:
+		res.value = LLVMConstNull(lb_type(m, original_type));
+		return res;
+	case ExactValue_Bool:
+		res.value = LLVMConstInt(lb_type(m, original_type), value.value_bool, false);
+		return res;
+	case ExactValue_String:
+		{
+			LLVMValueRef ptr = lb_find_or_add_entity_string_ptr(m, value.value_string);
+			lbValue res = {};
+			res.type = default_type(original_type);
+			if (is_type_cstring(res.type)) {
+				res.value = ptr;
+			} else {
+				LLVMValueRef str_len = LLVMConstInt(lb_type(m, t_int), value.value_string.len, true);
+				LLVMValueRef values[2] = {ptr, str_len};
+
+				res.value = LLVMConstNamedStruct(lb_type(m, original_type), values, 2);
+			}
+
+			return res;
+		}
+
+	case ExactValue_Integer:
+		if (is_type_pointer(type)) {
+			LLVMValueRef i = LLVMConstIntOfArbitraryPrecision(lb_type(m, t_uintptr), cast(unsigned)value.value_integer.len, big_int_ptr(&value.value_integer));
+			res.value = LLVMConstIntToPtr(i, lb_type(m, original_type));
+		} else {
+			res.value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)value.value_integer.len, big_int_ptr(&value.value_integer));
+			if (value.value_integer.neg) {
+				res.value = LLVMConstNeg(res.value);
+			}
+		}
+		return res;
+	case ExactValue_Float:
+		if (type_size_of(type) == 4) {
+			f32 f = cast(f32)value.value_float;
+			res.value = lb_const_f32(m, f, type);
+			return res;
+		}
+		res.value = LLVMConstReal(lb_type(m, original_type), value.value_float);
+		return res;
+	case ExactValue_Complex:
+		{
+			LLVMValueRef values[2] = {};
+			switch (8*type_size_of(type)) {
+			case 64:
+				values[0] = lb_const_f32(m, cast(f32)value.value_complex.real);
+				values[1] = lb_const_f32(m, cast(f32)value.value_complex.imag);
+				break;
+			case 128:
+				values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.real);
+				values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_complex.imag);
+				break;
+			}
+
+			res.value = LLVMConstNamedStruct(lb_type(m, original_type), values, 2);
+			return res;
+		}
+		break;
+	case ExactValue_Quaternion:
+		{
+			LLVMValueRef values[4] = {};
+			switch (8*type_size_of(type)) {
+			case 128:
+				// @QuaternionLayout
+				values[3] = lb_const_f32(m, cast(f32)value.value_quaternion.real);
+				values[0] = lb_const_f32(m, cast(f32)value.value_quaternion.imag);
+				values[1] = lb_const_f32(m, cast(f32)value.value_quaternion.jmag);
+				values[2] = lb_const_f32(m, cast(f32)value.value_quaternion.kmag);
+				break;
+			case 256:
+				// @QuaternionLayout
+				values[3] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.real);
+				values[0] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.imag);
+				values[1] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.jmag);
+				values[2] = LLVMConstReal(lb_type(m, t_f64), value.value_quaternion.kmag);
+				break;
+			}
+
+			res.value = LLVMConstNamedStruct(lb_type(m, original_type), values, 4);
+			return res;
+		}
+		break;
+
+	case ExactValue_Pointer:
+		res.value = LLVMConstIntToPtr(LLVMConstInt(lb_type(m, t_uintptr), value.value_pointer, false), lb_type(m, original_type));
+		return res;
+
+	case ExactValue_Compound:
+		if (is_type_slice(type)) {
+			return lb_const_value(m, type, value);
+		} else if (is_type_array(type)) {
+			ast_node(cl, CompoundLit, value.value_compound);
+			Type *elem_type = type->Array.elem;
+			isize elem_count = cl->elems.count;
+			if (elem_count == 0) {
+				return lb_const_nil(m, original_type);
+			}
+			if (cl->elems[0]->kind == Ast_FieldValue) {
+				// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
+
+				LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count);
+				defer (gb_free(heap_allocator(), values));
+
+				isize value_index = 0;
+				for (i64 i = 0; i < type->Array.count; i++) {
+					bool found = false;
+
+					for (isize j = 0; j < elem_count; j++) {
+						Ast *elem = cl->elems[j];
+						ast_node(fv, FieldValue, elem);
+						if (is_ast_range(fv->field)) {
+							ast_node(ie, BinaryExpr, fv->field);
+							TypeAndValue lo_tav = ie->left->tav;
+							TypeAndValue hi_tav = ie->right->tav;
+							GB_ASSERT(lo_tav.mode == Addressing_Constant);
+							GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+							TokenKind op = ie->op.kind;
+							i64 lo = exact_value_to_i64(lo_tav.value);
+							i64 hi = exact_value_to_i64(hi_tav.value);
+							if (op == Token_Ellipsis) {
+								hi += 1;
+							}
+							if (lo == i) {
+								TypeAndValue tav = fv->value->tav;
+								if (tav.mode != Addressing_Constant) {
+									break;
+								}
+								LLVMValueRef val = lb_const_value(m, elem_type, tav.value).value;
+								for (i64 k = lo; k < hi; k++) {
+									values[value_index++] = val;
+								}
+
+								found = true;
+								i += (hi-lo-1);
+								break;
+							}
+						} else {
+							TypeAndValue index_tav = fv->field->tav;
+							GB_ASSERT(index_tav.mode == Addressing_Constant);
+							i64 index = exact_value_to_i64(index_tav.value);
+							if (index == i) {
+								TypeAndValue tav = fv->value->tav;
+								if (tav.mode != Addressing_Constant) {
+									break;
+								}
+								LLVMValueRef val = lb_const_value(m, elem_type, tav.value).value;
+								values[value_index++] = val;
+								found = true;
+								break;
+							}
+						}
+					}
+
+					if (!found) {
+						values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
+					}
+				}
+
+				res.value = LLVMConstArray(lb_type(m, elem_type), values, cast(unsigned int)type->Array.count);
+				return res;
+			} else {
+				GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
+
+				LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->Array.count);
+				defer (gb_free(heap_allocator(), values));
+
+				for (isize i = 0; i < elem_count; i++) {
+					TypeAndValue tav = cl->elems[i]->tav;
+					GB_ASSERT(tav.mode != Addressing_Invalid);
+					values[i] = lb_const_value(m, elem_type, tav.value).value;
+				}
+				for (isize i = elem_count; i < type->Array.count; i++) {
+					values[i] = LLVMConstNull(lb_type(m, elem_type));
+				}
+
+				res.value = LLVMConstArray(lb_type(m, elem_type), values, cast(unsigned int)type->Array.count);
+				return res;
+			}
+		} else if (is_type_enumerated_array(type)) {
+			ast_node(cl, CompoundLit, value.value_compound);
+			Type *elem_type = type->EnumeratedArray.elem;
+			isize elem_count = cl->elems.count;
+			if (elem_count == 0) {
+				return lb_const_nil(m, original_type);
+			}
+			if (cl->elems[0]->kind == Ast_FieldValue) {
+				// TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
+
+				LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count);
+				defer (gb_free(heap_allocator(), values));
+
+				isize value_index = 0;
+
+				i64 total_lo = exact_value_to_i64(type->EnumeratedArray.min_value);
+				i64 total_hi = exact_value_to_i64(type->EnumeratedArray.max_value);
+
+				for (i64 i = total_lo; i <= total_hi; i++) {
+					bool found = false;
+
+					for (isize j = 0; j < elem_count; j++) {
+						Ast *elem = cl->elems[j];
+						ast_node(fv, FieldValue, elem);
+						if (is_ast_range(fv->field)) {
+							ast_node(ie, BinaryExpr, fv->field);
+							TypeAndValue lo_tav = ie->left->tav;
+							TypeAndValue hi_tav = ie->right->tav;
+							GB_ASSERT(lo_tav.mode == Addressing_Constant);
+							GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+							TokenKind op = ie->op.kind;
+							i64 lo = exact_value_to_i64(lo_tav.value);
+							i64 hi = exact_value_to_i64(hi_tav.value);
+							if (op == Token_Ellipsis) {
+								hi += 1;
+							}
+							if (lo == i) {
+								TypeAndValue tav = fv->value->tav;
+								if (tav.mode != Addressing_Constant) {
+									break;
+								}
+								LLVMValueRef val = lb_const_value(m, elem_type, tav.value).value;
+								for (i64 k = lo; k < hi; k++) {
+									values[value_index++] = val;
+								}
+
+								found = true;
+								i += (hi-lo-1);
+								break;
+							}
+						} else {
+							TypeAndValue index_tav = fv->field->tav;
+							GB_ASSERT(index_tav.mode == Addressing_Constant);
+							i64 index = exact_value_to_i64(index_tav.value);
+							if (index == i) {
+								TypeAndValue tav = fv->value->tav;
+								if (tav.mode != Addressing_Constant) {
+									break;
+								}
+								LLVMValueRef val = lb_const_value(m, elem_type, tav.value).value;
+								values[value_index++] = val;
+								found = true;
+								break;
+							}
+						}
+					}
+
+					if (!found) {
+						values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
+					}
+				}
+
+				res.value = LLVMConstArray(lb_type(m, elem_type), values, cast(unsigned int)type->EnumeratedArray.count);
+				return res;
+			} else {
+				GB_ASSERT_MSG(elem_count == type->EnumeratedArray.count, "%td != %td", elem_count, type->EnumeratedArray.count);
+
+				LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, type->EnumeratedArray.count);
+				defer (gb_free(heap_allocator(), values));
+
+				for (isize i = 0; i < elem_count; i++) {
+					TypeAndValue tav = cl->elems[i]->tav;
+					GB_ASSERT(tav.mode != Addressing_Invalid);
+					values[i] = lb_const_value(m, elem_type, tav.value).value;
+				}
+				for (isize i = elem_count; i < type->EnumeratedArray.count; i++) {
+					values[i] = LLVMConstNull(lb_type(m, elem_type));
+				}
+
+				res.value = LLVMConstArray(lb_type(m, elem_type), values, cast(unsigned int)type->EnumeratedArray.count);
+				return res;
+			}
+		} else if (is_type_simd_vector(type)) {
+			ast_node(cl, CompoundLit, value.value_compound);
+
+			Type *elem_type = type->SimdVector.elem;
+			isize elem_count = cl->elems.count;
+			if (elem_count == 0) {
+				return lb_const_nil(m, original_type);
+			}
+
+			isize total_elem_count = type->SimdVector.count;
+			LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, total_elem_count);
+			defer (gb_free(heap_allocator(), values));
+
+			for (isize i = 0; i < elem_count; i++) {
+				TypeAndValue tav = cl->elems[i]->tav;
+				GB_ASSERT(tav.mode != Addressing_Invalid);
+				values[i] = lb_const_value(m, elem_type, tav.value).value;
+			}
+			for (isize i = elem_count; i < type->SimdVector.count; i++) {
+				values[i] = LLVMConstNull(lb_type(m, elem_type));
+			}
+
+			res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
+			return res;
+		} else if (is_type_struct(type)) {
+			ast_node(cl, CompoundLit, value.value_compound);
+
+			if (cl->elems.count == 0) {
+				return lb_const_nil(m, original_type);
+			}
+
+			isize offset = 0;
+			if (type->Struct.custom_align > 0) {
+				offset = 1;
+			}
+
+			isize value_count = type->Struct.fields.count + offset;
+			LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, value_count);
+			bool *visited = gb_alloc_array(heap_allocator(), bool, value_count);
+			defer (gb_free(heap_allocator(), values));
+			defer (gb_free(heap_allocator(), visited));
+
+
+
+			if (cl->elems.count > 0) {
+				if (cl->elems[0]->kind == Ast_FieldValue) {
+					isize elem_count = cl->elems.count;
+					for (isize i = 0; i < elem_count; i++) {
+						ast_node(fv, FieldValue, cl->elems[i]);
+						String name = fv->field->Ident.token.string;
+
+						TypeAndValue tav = fv->value->tav;
+						GB_ASSERT(tav.mode != Addressing_Invalid);
+
+						Selection sel = lookup_field(type, name, false);
+						Entity *f = type->Struct.fields[sel.index[0]];
+
+						values[offset+f->Variable.field_index] = lb_const_value(m, f->type, tav.value).value;
+						visited[offset+f->Variable.field_index] = true;
+					}
+				} else {
+					for_array(i, cl->elems) {
+						Entity *f = type->Struct.fields[i];
+						TypeAndValue tav = cl->elems[i]->tav;
+						ExactValue val = {};
+						if (tav.mode != Addressing_Invalid) {
+							val = tav.value;
+						}
+						values[offset+f->Variable.field_index]  = lb_const_value(m, f->type, val).value;
+						visited[offset+f->Variable.field_index] = true;
+					}
+				}
+			}
+
+			for (isize i = 0; i < type->Struct.fields.count; i++) {
+				if (!visited[offset+i]) {
+					GB_ASSERT(values[offset+i] == nullptr);
+					values[offset+i] = lb_const_nil(m, get_struct_field_type(type, i)).value;
+				}
+			}
+
+			if (type->Struct.custom_align > 0) {
+				values[0] = LLVMConstNull(lb_alignment_prefix_type_hack(m, type->Struct.custom_align));
+			}
+
+			res.value = LLVMConstNamedStruct(lb_type(m, original_type), values, cast(unsigned)value_count);
+			return res;
+		} else if (is_type_bit_set(type)) {
+			ast_node(cl, CompoundLit, value.value_compound);
+			if (cl->elems.count == 0) {
+				return lb_const_nil(m, original_type);
+			}
+
+			i64 sz = type_size_of(type);
+			if (sz == 0) {
+				return lb_const_nil(m, original_type);
+			}
+
+			u64 bits = 0;
+			for_array(i, cl->elems) {
+				Ast *e = cl->elems[i];
+				GB_ASSERT(e->kind != Ast_FieldValue);
+
+				TypeAndValue tav = e->tav;
+				if (tav.mode != Addressing_Constant) {
+					continue;
+				}
+				GB_ASSERT(tav.value.kind == ExactValue_Integer);
+				i64 v = big_int_to_i64(&tav.value.value_integer);
+				i64 lower = type->BitSet.lower;
+				bits |= 1ull<<cast(u64)(v-lower);
+			}
+			if (is_type_different_to_arch_endianness(type)) {
+				i64 size = type_size_of(type);
+				switch (size) {
+				case 2: bits = cast(u64)gb_endian_swap16(cast(u16)bits); break;
+				case 4: bits = cast(u64)gb_endian_swap32(cast(u32)bits); break;
+				case 8: bits = cast(u64)gb_endian_swap64(cast(u64)bits); break;
+				}
+			}
+
+			res.value = LLVMConstInt(lb_type(m, original_type), bits, false);
+			return res;
+		} else {
+			return lb_const_nil(m, original_type);
+		}
+		break;
+	case ExactValue_Procedure:
+		{
+			Ast *expr = value.value_procedure;
+			GB_ASSERT(expr != nullptr);
+			if (expr->kind == Ast_ProcLit) {
+				return lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
+			}
+		}
+		break;
+	case ExactValue_Typeid:
+		return lb_typeid(m, value.value_typeid, original_type);
+	}
+
+	return lb_const_nil(m, original_type);
+}
+
+u64 lb_generate_source_code_location_hash(TokenPos const &pos) {
+	u64 h = 0xcbf29ce484222325;
+	for (isize i = 0; i < pos.file.len; i++) {
+		h = (h ^ u64(pos.file[i])) * 0x100000001b3;
+	}
+	h = h ^ (u64(pos.line) * 0x100000001b3);
+	h = h ^ (u64(pos.column) * 0x100000001b3);
+	return h;
+}
+
+lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) {
+	lbModule *m = p->module;
+
+	LLVMValueRef fields[5] = {};
+	fields[0]/*file*/      = lb_find_or_add_entity_string(p->module, pos.file).value;
+	fields[1]/*line*/      = lb_const_int(m, t_int, pos.line).value;
+	fields[2]/*column*/    = lb_const_int(m, t_int, pos.column).value;
+	fields[3]/*procedure*/ = lb_find_or_add_entity_string(p->module, procedure).value;
+	fields[4]/*hash*/      = lb_const_int(m, t_u64, lb_generate_source_code_location_hash(pos)).value;
+
+	lbValue res = {};
+	res.value = LLVMConstNamedStruct(lb_type(m, t_source_code_location), fields, 5);
+	res.type = t_source_code_location;
+	return res;
+}
+
+lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
+	String proc_name = {};
+	if (p->entity) {
+		proc_name = p->entity->token.string;
+	}
+	TokenPos pos = {};
+	if (node) {
+		pos = ast_token(node).pos;
+	}
+	return lb_emit_source_code_location(p, proc_name, pos);
+}
+
+
+lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type) {
+	switch (op) {
+	case Token_Add:
+		return x;
+	case Token_Not: // Boolean not
+	case Token_Xor: // Bitwise not
+	case Token_Sub: // Number negation
+		break;
+	case Token_Pointer:
+		GB_PANIC("This should be handled elsewhere");
+		break;
+	}
+
+	if (is_type_array(x.type)) {
+		// IMPORTANT TODO(bill): This is very wasteful with regards to stack memory
+		Type *tl = base_type(x.type);
+		lbValue val = lb_address_from_load_or_generate_local(p, x);
+		GB_ASSERT(is_type_array(type));
+		Type *elem_type = base_array_type(type);
+
+		// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+		lbAddr res_addr = lb_add_local_generated(p, type, false);
+		lbValue res = lb_addr_get_ptr(p, res_addr);
+
+		bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+
+		i32 count = cast(i32)tl->Array.count;
+
+		if (inline_array_arith) {
+			// inline
+			for (i32 i = 0; i < count; i++) {
+				lbValue e = lb_emit_load(p, lb_emit_array_epi(p, val, i));
+				lbValue z = lb_emit_unary_arith(p, op, e, elem_type);
+				lb_emit_store(p, lb_emit_array_epi(p, res, i), z);
+			}
+		} else {
+			auto loop_data = lb_loop_start(p, count, t_i32);
+
+			lbValue e = lb_emit_load(p, lb_emit_array_ep(p, val, loop_data.idx));
+			lbValue z = lb_emit_unary_arith(p, op, e, elem_type);
+			lb_emit_store(p, lb_emit_array_ep(p, res, loop_data.idx), z);
+
+			lb_loop_end(p, loop_data);
+		}
+		return lb_emit_load(p, res);
+
+	}
+
+	if (op == Token_Xor) {
+		lbValue cmp = {};
+		cmp.value = LLVMBuildNot(p->builder, x.value, "");
+		cmp.type = x.type;
+		return lb_emit_conv(p, cmp, type);
+	}
+
+	if (op == Token_Not) {
+		lbValue cmp = {};
+		LLVMValueRef zero =  LLVMConstInt(lb_type(p->module, x.type), 0, false);
+		cmp.value = LLVMBuildICmp(p->builder, LLVMIntEQ, x.value, zero, "");
+		cmp.type = t_llvm_bool;
+		return lb_emit_conv(p, cmp, type);
+	}
+
+	if (op == Token_Sub && is_type_integer(type) && is_type_different_to_arch_endianness(type)) {
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		lbValue v = lb_emit_byte_swap(p, x, platform_type);
+
+		lbValue res = {};
+		res.value = LLVMBuildNeg(p->builder, v.value, "");
+		res.type = platform_type;
+
+		return lb_emit_byte_swap(p, res, type);
+	}
+
+
+	lbValue res = {};
+
+	switch (op) {
+	case Token_Not: // Boolean not
+	case Token_Xor: // Bitwise not
+		res.value = LLVMBuildNot(p->builder, x.value, "");
+		res.type = x.type;
+		return res;
+	case Token_Sub: // Number negation
+		if (is_type_integer(x.type)) {
+			res.value = LLVMBuildNeg(p->builder, x.value, "");
+		} else if (is_type_float(x.type)) {
+			res.value = LLVMBuildFNeg(p->builder, x.value, "");
+		} else {
+			GB_PANIC("Unhandled type %s", type_to_string(x.type));
+		}
+		res.type = x.type;
+		return res;
+	}
+
+	return res;
+}
+
+
+
+lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type) {
+	lbModule *m = p->module;
+
+	if (is_type_array(lhs.type) || is_type_array(rhs.type)) {
+		lhs = lb_emit_conv(p, lhs, type);
+		rhs = lb_emit_conv(p, rhs, type);
+
+		lbValue x = lb_address_from_load_or_generate_local(p, lhs);
+		lbValue y = lb_address_from_load_or_generate_local(p, rhs);
+
+		GB_ASSERT(is_type_array(type));
+		Type *elem_type = base_array_type(type);
+
+		lbAddr res = lb_add_local_generated(p, type, false);
+
+		i64 count = base_type(type)->Array.count;
+
+		bool inline_array_arith = type_size_of(type) <= build_context.max_align;
+
+		if (inline_array_arith) {
+			for (i64 i = 0; i < count; i++) {
+				lbValue a = lb_emit_load(p, lb_emit_array_epi(p, x, i));
+				lbValue b = lb_emit_load(p, lb_emit_array_epi(p, y, i));
+				lbValue c = lb_emit_arith(p, op, a, b, elem_type);
+				lb_emit_store(p, lb_emit_array_epi(p, res.addr, i), c);
+			}
+		} else {
+			auto loop_data = lb_loop_start(p, count);
+
+			lbValue a = lb_emit_load(p, lb_emit_array_ep(p, x, loop_data.idx));
+			lbValue b = lb_emit_load(p, lb_emit_array_ep(p, y, loop_data.idx));
+			lbValue c = lb_emit_arith(p, op, a, b, elem_type);
+			lb_emit_store(p, lb_emit_array_ep(p, res.addr, loop_data.idx), c);
+
+			lb_loop_end(p, loop_data);
+		}
+
+		return lb_addr_load(p, res);
+	} else if (is_type_complex(type)) {
+		lhs = lb_emit_conv(p, lhs, type);
+		rhs = lb_emit_conv(p, rhs, type);
+
+		Type *ft = base_complex_elem_type(type);
+
+		if (op == Token_Quo) {
+			auto args = array_make<lbValue>(heap_allocator(), 2);
+			args[0] = lhs;
+			args[1] = rhs;
+
+			switch (type_size_of(ft)) {
+			case 4: return lb_emit_runtime_call(p, "quo_complex64", args);
+			case 8: return lb_emit_runtime_call(p, "quo_complex128", args);
+			default: GB_PANIC("Unknown float type"); break;
+			}
+		}
+
+		lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later
+		lbValue a = lb_emit_struct_ev(p, lhs, 0);
+		lbValue b = lb_emit_struct_ev(p, lhs, 1);
+		lbValue c = lb_emit_struct_ev(p, rhs, 0);
+		lbValue d = lb_emit_struct_ev(p, rhs, 1);
+
+		lbValue real = {};
+		lbValue imag = {};
+
+		switch (op) {
+		case Token_Add:
+			real = lb_emit_arith(p, Token_Add, a, c, ft);
+			imag = lb_emit_arith(p, Token_Add, b, d, ft);
+			break;
+		case Token_Sub:
+			real = lb_emit_arith(p, Token_Sub, a, c, ft);
+			imag = lb_emit_arith(p, Token_Sub, b, d, ft);
+			break;
+		case Token_Mul: {
+			lbValue x = lb_emit_arith(p, Token_Mul, a, c, ft);
+			lbValue y = lb_emit_arith(p, Token_Mul, b, d, ft);
+			real = lb_emit_arith(p, Token_Sub, x, y, ft);
+			lbValue z = lb_emit_arith(p, Token_Mul, b, c, ft);
+			lbValue w = lb_emit_arith(p, Token_Mul, a, d, ft);
+			imag = lb_emit_arith(p, Token_Add, z, w, ft);
+			break;
+		}
+		}
+
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), real);
+		lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), imag);
+
+		return lb_addr_load(p, res);
+	} else if (is_type_quaternion(type)) {
+		lhs = lb_emit_conv(p, lhs, type);
+		rhs = lb_emit_conv(p, rhs, type);
+
+		Type *ft = base_complex_elem_type(type);
+
+		if (op == Token_Add || op == Token_Sub) {
+			lbAddr res = lb_add_local_generated(p, type, false); // NOTE: initialized in full later
+			lbValue x0 = lb_emit_struct_ev(p, lhs, 0);
+			lbValue x1 = lb_emit_struct_ev(p, lhs, 1);
+			lbValue x2 = lb_emit_struct_ev(p, lhs, 2);
+			lbValue x3 = lb_emit_struct_ev(p, lhs, 3);
+
+			lbValue y0 = lb_emit_struct_ev(p, rhs, 0);
+			lbValue y1 = lb_emit_struct_ev(p, rhs, 1);
+			lbValue y2 = lb_emit_struct_ev(p, rhs, 2);
+			lbValue y3 = lb_emit_struct_ev(p, rhs, 3);
+
+			lbValue z0 = lb_emit_arith(p, op, x0, y0, ft);
+			lbValue z1 = lb_emit_arith(p, op, x1, y1, ft);
+			lbValue z2 = lb_emit_arith(p, op, x2, y2, ft);
+			lbValue z3 = lb_emit_arith(p, op, x3, y3, ft);
+
+			lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 0), z0);
+			lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 1), z1);
+			lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 2), z2);
+			lb_emit_store(p, lb_emit_struct_ep(p, res.addr, 3), z3);
+
+			return lb_addr_load(p, res);
+		} else if (op == Token_Mul) {
+			auto args = array_make<lbValue>(heap_allocator(), 2);
+			args[0] = lhs;
+			args[1] = rhs;
+
+			switch (8*type_size_of(ft)) {
+			case 32: return lb_emit_runtime_call(p, "mul_quaternion128", args);
+			case 64: return lb_emit_runtime_call(p, "mul_quaternion256", args);
+			default: GB_PANIC("Unknown float type"); break;
+			}
+		} else if (op == Token_Quo) {
+			auto args = array_make<lbValue>(heap_allocator(), 2);
+			args[0] = lhs;
+			args[1] = rhs;
+
+			switch (8*type_size_of(ft)) {
+			case 32: return lb_emit_runtime_call(p, "quo_quaternion128", args);
+			case 64: return lb_emit_runtime_call(p, "quo_quaternion256", args);
+			default: GB_PANIC("Unknown float type"); break;
+			}
+		}
+	}
+
+	if (is_type_integer(type) && is_type_different_to_arch_endianness(type)) {
+		switch (op) {
+		case Token_AndNot:
+		case Token_And:
+		case Token_Or:
+		case Token_Xor:
+			goto handle_op;
+		}
+
+		Type *platform_type = integer_endian_type_to_platform_type(type);
+		lbValue x = lb_emit_byte_swap(p, lhs, integer_endian_type_to_platform_type(lhs.type));
+		lbValue y = lb_emit_byte_swap(p, rhs, integer_endian_type_to_platform_type(rhs.type));
+
+		lbValue res = lb_emit_arith(p, op, x, y, platform_type);
+
+		return lb_emit_byte_swap(p, res, type);
+	}
+
+
+
+handle_op:
+	lhs = lb_emit_conv(p, lhs, type);
+	rhs = lb_emit_conv(p, rhs, type);
+
+	lbValue res = {};
+	res.type = type;
+
+
+	switch (op) {
+	case Token_Add:
+		if (is_type_float(type)) {
+			res.value = LLVMBuildFAdd(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		res.value = LLVMBuildAdd(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Sub:
+		if (is_type_float(type)) {
+			res.value = LLVMBuildFSub(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		res.value = LLVMBuildSub(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Mul:
+		if (is_type_float(type)) {
+			res.value = LLVMBuildFMul(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		res.value = LLVMBuildMul(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Quo:
+		if (is_type_float(type)) {
+			res.value = LLVMBuildFDiv(p->builder, lhs.value, rhs.value, "");
+			return res;
+		} else if (is_type_unsigned(type)) {
+			res.value = LLVMBuildUDiv(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		res.value = LLVMBuildSDiv(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Mod:
+		if (is_type_float(type)) {
+			res.value = LLVMBuildFRem(p->builder, lhs.value, rhs.value, "");
+			return res;
+		} else if (is_type_unsigned(type)) {
+			res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		res.value = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_ModMod:
+		if (is_type_unsigned(type)) {
+			res.value = LLVMBuildURem(p->builder, lhs.value, rhs.value, "");
+			return res;
+		} else {
+			LLVMValueRef a = LLVMBuildSRem(p->builder, lhs.value, rhs.value, "");
+			LLVMValueRef b = LLVMBuildAdd(p->builder, a, rhs.value, "");
+			LLVMValueRef c = LLVMBuildSRem(p->builder, b, rhs.value, "");
+			res.value = c;
+			return res;
+		}
+
+	case Token_And:
+		res.value = LLVMBuildAnd(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Or:
+		res.value = LLVMBuildOr(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Xor:
+		res.value = LLVMBuildXor(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Shl:
+		rhs = lb_emit_conv(p, rhs, lhs.type);
+		res.value = LLVMBuildShl(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_Shr:
+		if (is_type_unsigned(type)) {
+			res.value = LLVMBuildLShr(p->builder, lhs.value, rhs.value, "");
+			return res;
+		}
+		rhs = lb_emit_conv(p, rhs, lhs.type);
+		res.value = LLVMBuildAShr(p->builder, lhs.value, rhs.value, "");
+		return res;
+	case Token_AndNot:
+		{
+			LLVMValueRef new_rhs = LLVMBuildNot(p->builder, rhs.value, "");
+			res.value = LLVMBuildAnd(p->builder, lhs.value, new_rhs, "");
+			return res;
+		}
+		break;
+	}
+
+	GB_PANIC("unhandled operator of lb_emit_arith");
+
+	return {};
+}
+
+lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
+	ast_node(be, BinaryExpr, expr);
+
+	TypeAndValue tv = type_and_value_of_expr(expr);
+
+	switch (be->op.kind) {
+	case Token_Add:
+	case Token_Sub:
+	case Token_Mul:
+	case Token_Quo:
+	case Token_Mod:
+	case Token_ModMod:
+	case Token_And:
+	case Token_Or:
+	case Token_Xor:
+	case Token_AndNot:
+	case Token_Shl:
+	case Token_Shr: {
+		Type *type = default_type(tv.type);
+		lbValue left = lb_build_expr(p, be->left);
+		lbValue right = lb_build_expr(p, be->right);
+		return lb_emit_arith(p, be->op.kind, left, right, type);
+	}
+
+	case Token_CmpEq:
+	case Token_NotEq:
+	case Token_Lt:
+	case Token_LtEq:
+	case Token_Gt:
+	case Token_GtEq:
+		{
+			lbValue left = lb_build_expr(p, be->left);
+			Type *type = default_type(tv.type);
+			lbValue right = lb_build_expr(p, be->right);
+			lbValue cmp = lb_emit_comp(p, be->op.kind, left, right);
+			return lb_emit_conv(p, cmp, type);
+		}
+
+	case Token_CmpAnd:
+	case Token_CmpOr:
+		return lb_emit_logical_binary_expr(p, be->op.kind, be->left, be->right, tv.type);
+
+	case Token_in:
+	case Token_not_in:
+		{
+			lbValue left = lb_build_expr(p, be->left);
+			Type *type = default_type(tv.type);
+			lbValue right = lb_build_expr(p, be->right);
+			Type *rt = base_type(right.type);
+			switch (rt->kind) {
+			case Type_Map:
+				{
+					lbValue addr = lb_address_from_load_or_generate_local(p, right);
+					lbValue h = lb_gen_map_header(p, addr, rt);
+					lbValue key = lb_gen_map_key(p, left, rt->Map.key);
+
+					auto args = array_make<lbValue>(heap_allocator(), 2);
+					args[0] = h;
+					args[1] = key;
+
+					lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
+					if (be->op.kind == Token_in) {
+						return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_NotEq, ptr), t_bool);
+					} else {
+						return lb_emit_conv(p, lb_emit_comp_against_nil(p, Token_CmpEq, ptr), t_bool);
+					}
+				}
+				break;
+			case Type_BitSet:
+				{
+					Type *key_type = rt->BitSet.elem;
+					GB_ASSERT(are_types_identical(left.type, key_type));
+
+					Type *it = bit_set_to_int(rt);
+					left = lb_emit_conv(p, left, it);
+
+					lbValue lower = lb_const_value(p->module, it, exact_value_i64(rt->BitSet.lower));
+					lbValue key = lb_emit_arith(p, Token_Sub, left, lower, it);
+					lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, it, 1), key, it);
+					bit = lb_emit_conv(p, bit, it);
+
+					lbValue old_value = lb_emit_transmute(p, right, it);
+					lbValue new_value = lb_emit_arith(p, Token_And, old_value, bit, it);
+
+					if (be->op.kind == Token_in) {
+						return lb_emit_conv(p, lb_emit_comp(p, Token_NotEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+					} else {
+						return lb_emit_conv(p, lb_emit_comp(p, Token_CmpEq, new_value, lb_const_int(p->module, new_value.type, 0)), t_bool);
+					}
+				}
+				break;
+			default:
+				GB_PANIC("Invalid 'in' type");
+			}
+			break;
+		}
+		break;
+	default:
+		GB_PANIC("Invalid binary expression");
+		break;
+	}
+	return {};
+}
+
+
+String lookup_subtype_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));
+	bool src_is_ptr = src != prev_src;
+	// bool dst_is_ptr = dst != prev_dst;
+
+	GB_ASSERT(is_type_struct(src) || is_type_union(src));
+	for_array(i, src->Struct.fields) {
+		Entity *f = src->Struct.fields[i];
+		if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
+			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;
+				}
+			}
+			if (is_type_struct(f->type)) {
+				String name = lookup_subtype_polymorphic_field(info, dst, f->type);
+				if (name.len > 0) {
+					return name;
+				}
+			}
+		}
+	}
+	return str_lit("");
+}
+
+lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
+	GB_ASSERT(is_type_pointer(value.type));
+	GB_ASSERT(is_type_pointer(t));
+	GB_ASSERT(lb_is_const(value));
+
+	lbValue res = {};
+	res.value = LLVMConstPointerCast(value.value, lb_type(m, t));
+	res.type = t;
+	return res;
+}
+
+lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
+	lbModule *m = p->module;
+	t = reduce_tuple_to_single_type(t);
+
+	Type *src_type = value.type;
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+	Type *src = core_type(src_type);
+	Type *dst = core_type(t);
+
+	if (is_type_untyped_nil(src)) {
+		return lb_const_nil(m, t);
+	}
+	if (is_type_untyped_undef(src)) {
+		return lb_const_undef(m, t);
+	}
+
+	if (LLVMIsConstant(value.value)) {
+		if (is_type_any(dst)) {
+			Type *st = default_type(src_type);
+			lbAddr default_value = lb_add_local_generated(p, st, false);
+			lb_addr_store(p, default_value, value);
+			lbValue data = lb_emit_conv(p, default_value.addr, t_rawptr);
+			lbValue id = lb_typeid(m, st);
+
+			lbAddr res = lb_add_local_generated(p, t, false);
+			lbValue a0 = lb_emit_struct_ep(p, res.addr, 0);
+			lbValue a1 = lb_emit_struct_ep(p, res.addr, 1);
+			lb_emit_store(p, a0, data);
+			lb_emit_store(p, a1, id);
+			return lb_addr_load(p, res);
+		} else if (dst->kind == Type_Basic) {
+			if (src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) {
+				String str = lb_get_const_string(m, value);
+				lbValue res = {};
+				res.type = t;
+				res.value = llvm_cstring(m, str);
+				return res;
+			}
+			// if (is_type_float(dst)) {
+			// 	return value;
+			// } else if (is_type_integer(dst)) {
+			// 	return value;
+			// }
+			// ExactValue ev = value->Constant.value;
+			// if (is_type_float(dst)) {
+			// 	ev = exact_value_to_float(ev);
+			// } else if (is_type_complex(dst)) {
+			// 	ev = exact_value_to_complex(ev);
+			// } else if (is_type_quaternion(dst)) {
+			// 	ev = exact_value_to_quaternion(ev);
+			// } else if (is_type_string(dst)) {
+			// 	// Handled elsewhere
+			// 	GB_ASSERT_MSG(ev.kind == ExactValue_String, "%d", ev.kind);
+			// } 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'
+			// 	lbValue i = lb_add_module_constant(p->module, t_uintptr, ev);
+			// 	return lb_emit(p, lb_instr_conv(p, irConv_inttoptr, i, t_uintptr, dst));
+			// }
+			// return lb_const_value(p->module, t, ev);
+		}
+	}
+
+	if (are_types_identical(src, dst)) {
+		if (!are_types_identical(src_type, t)) {
+			return lb_emit_transmute(p, value, t);
+		}
+		return value;
+	}
+
+
+
+	// bool <-> llvm bool
+	if (is_type_boolean(src) && dst == t_llvm_bool) {
+		lbValue res = {};
+		res.value = LLVMBuildTrunc(p->builder, value.value, lb_type(m, dst), "");
+		res.type = dst;
+		return res;
+	}
+	if (src == t_llvm_bool && is_type_boolean(dst)) {
+		lbValue res = {};
+		res.value = LLVMBuildZExt(p->builder, value.value, lb_type(m, dst), "");
+		res.type = dst;
+		return res;
+	}
+
+
+	// 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(default_type(src));
+		i64 dz = type_size_of(default_type(dst));
+
+		if (sz > 1 && is_type_different_to_arch_endianness(src)) {
+			Type *platform_src_type = integer_endian_type_to_platform_type(src);
+			value = lb_emit_byte_swap(p, value, platform_src_type);
+		}
+		LLVMOpcode op = LLVMTrunc;
+
+		if (dz < sz) {
+			op = LLVMTrunc;
+		} else if (dz == sz) {
+			// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
+			// NOTE(bill): Copy the value just for type correctness
+			op = LLVMBitCast;
+		} else if (dz > sz) {
+			op = is_type_unsigned(src) ? LLVMZExt : LLVMSExt; // zero extent
+		}
+
+		if (dz > 1 && is_type_different_to_arch_endianness(dst)) {
+			Type *platform_dst_type = integer_endian_type_to_platform_type(dst);
+			lbValue res = {};
+			res.value = LLVMBuildCast(p->builder, op, value.value, lb_type(m, platform_dst_type), "");
+			res.type = t;
+			return lb_emit_byte_swap(p, res, t);
+		} else {
+			lbValue res = {};
+			res.value = LLVMBuildCast(p->builder, op, value.value, lb_type(m, t), "");
+			res.type = t;
+			return res;
+		}
+	}
+
+
+	// boolean -> boolean/integer
+	if (is_type_boolean(src) && (is_type_boolean(dst) || is_type_integer(dst))) {
+		LLVMValueRef b = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, value.type)), "");
+		lbValue res = {};
+		res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), false, "");
+		res.type = t;
+		return res;
+	}
+
+	if (is_type_cstring(src) && is_type_u8_ptr(dst)) {
+		return lb_emit_transmute(p, value, dst);
+	}
+	if (is_type_u8_ptr(src) && is_type_cstring(dst)) {
+		return lb_emit_transmute(p, value, dst);
+	}
+	if (is_type_cstring(src) && is_type_rawptr(dst)) {
+		return lb_emit_transmute(p, value, dst);
+	}
+	if (is_type_rawptr(src) && is_type_cstring(dst)) {
+		return lb_emit_transmute(p, value, dst);
+	}
+
+	if (are_types_identical(src, t_cstring) && are_types_identical(dst, t_string)) {
+		lbValue c = lb_emit_conv(p, value, t_cstring);
+		auto args = array_make<lbValue>(heap_allocator(), 1);
+		args[0] = c;
+		lbValue s = lb_emit_runtime_call(p, "cstring_to_string", args);
+		return lb_emit_conv(p, s, dst);
+	}
+
+
+	// integer -> boolean
+	if (is_type_integer(src) && is_type_boolean(dst)) {
+		lbValue res = {};
+		res.value = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, value.type)), "");
+		res.type = t_llvm_bool;
+		return lb_emit_conv(p, res, t);
+	}
+
+	// float -> float
+	if (is_type_float(src) && is_type_float(dst)) {
+		gbAllocator a = heap_allocator();
+		i64 sz = type_size_of(src);
+		i64 dz = type_size_of(dst);
+
+		lbValue res = {};
+		res.type = t;
+
+		if (dz >= sz) {
+			res.value = LLVMBuildFPExt(p->builder, value.value, lb_type(m, t), "");
+		} else {
+			res.value = LLVMBuildFPTrunc(p->builder, value.value, lb_type(m, t), "");
+		}
+		return res;
+	}
+
+	if (is_type_complex(src) && is_type_complex(dst)) {
+		Type *ft = base_complex_elem_type(dst);
+		lbAddr gen = lb_add_local_generated(p, dst, false);
+		lbValue gp = lb_addr_get_ptr(p, gen);
+		lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
+		lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), imag);
+		return lb_addr_load(p, gen);
+	}
+
+	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
+		// @QuaternionLayout
+		Type *ft = base_complex_elem_type(dst);
+		lbAddr gen = lb_add_local_generated(p, dst, false);
+		lbValue gp = lb_addr_get_ptr(p, gen);
+		lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
+		lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
+		lbValue q2 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 2), ft);
+		lbValue q3 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 3), ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), q0);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 1), q1);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 2), q2);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), q3);
+		return lb_addr_load(p, gen);
+	}
+
+	if (is_type_float(src) && is_type_complex(dst)) {
+		Type *ft = base_complex_elem_type(dst);
+		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbValue gp = lb_addr_get_ptr(p, gen);
+		lbValue real = lb_emit_conv(p, value, ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
+		return lb_addr_load(p, gen);
+	}
+	if (is_type_float(src) && is_type_quaternion(dst)) {
+		Type *ft = base_complex_elem_type(dst);
+		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbValue gp = lb_addr_get_ptr(p, gen);
+		lbValue real = lb_emit_conv(p, value, ft);
+		// @QuaternionLayout
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real);
+		return lb_addr_load(p, gen);
+	}
+	if (is_type_complex(src) && is_type_quaternion(dst)) {
+		Type *ft = base_complex_elem_type(dst);
+		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbValue gp = lb_addr_get_ptr(p, gen);
+		lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
+		lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
+		// @QuaternionLayout
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 3), real);
+		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), imag);
+		return lb_addr_load(p, gen);
+	}
+
+	// float <-> integer
+	if (is_type_float(src) && is_type_integer(dst)) {
+		lbValue res = {};
+		res.type = t;
+		if (is_type_unsigned(dst)) {
+			res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
+		} else {
+			res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
+		}
+		return res;
+	}
+	if (is_type_integer(src) && is_type_float(dst)) {
+		lbValue res = {};
+		res.type = t;
+		if (is_type_unsigned(src)) {
+			res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), "");
+		} else {
+			res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), "");
+		}
+		return res;
+	}
+
+	// Pointer <-> uintptr
+	if (is_type_pointer(src) && is_type_uintptr(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+	if (is_type_uintptr(src) && is_type_pointer(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+#if 1
+	if (is_type_union(dst)) {
+		for_array(i, dst->Union.variants) {
+			Type *vt = dst->Union.variants[i];
+			if (are_types_identical(vt, src_type)) {
+				lbAddr parent = lb_add_local_generated(p, t, true);
+				lb_emit_store_union_variant(p, parent.addr, value, vt);
+				return lb_addr_load(p, parent);
+			}
+		}
+	}
+#endif
+
+	// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
+	// subtype polymorphism casting
+	if (check_is_assignable_to_using_subtype(src_type, t)) {
+		Type *st = type_deref(src_type);
+		Type *pst = st;
+		st = type_deref(st);
+
+		bool st_is_ptr = is_type_pointer(src_type);
+		st = base_type(st);
+
+		Type *dt = t;
+		bool dt_is_ptr = type_deref(dt) != dt;
+
+		GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
+		String field_name = lookup_subtype_polymorphic_field(p->module->info, t, src_type);
+		if (field_name.len > 0) {
+			// NOTE(bill): It can be casted
+			Selection sel = lookup_field(st, field_name, false, true);
+			if (sel.entity != nullptr) {
+				if (st_is_ptr) {
+					lbValue res = lb_emit_deep_field_gep(p, value, sel);
+					Type *rt = res.type;
+					if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+						res = lb_emit_load(p, res);
+					}
+					return res;
+				} else {
+					if (is_type_pointer(value.type)) {
+						Type *rt = value.type;
+						if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
+							value = lb_emit_load(p, value);
+						} else {
+							value = lb_emit_deep_field_gep(p, value, sel);
+							return lb_emit_load(p, value);
+						}
+					}
+
+					return lb_emit_deep_field_ev(p, value, sel);
+
+				}
+			} else {
+				GB_PANIC("invalid subtype cast  %s.%.*s", type_to_string(src_type), LIT(field_name));
+			}
+		}
+	}
+
+
+
+	// Pointer <-> Pointer
+	if (is_type_pointer(src) && is_type_pointer(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+
+
+	// proc <-> proc
+	if (is_type_proc(src) && is_type_proc(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+	// pointer -> proc
+	if (is_type_pointer(src) && is_type_proc(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+	// proc -> pointer
+	if (is_type_proc(src) && is_type_pointer(dst)) {
+		lbValue res = {};
+		res.type = t;
+		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+
+
+	// []byte/[]u8 <-> string
+	if (is_type_u8_slice(src) && is_type_string(dst)) {
+		return lb_emit_transmute(p, value, t);
+	}
+	if (is_type_string(src) && is_type_u8_slice(dst)) {
+		return lb_emit_transmute(p, value, t);
+	}
+
+	if (is_type_array(dst)) {
+		Type *elem = dst->Array.elem;
+		lbValue e = lb_emit_conv(p, value, elem);
+		// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+		lbAddr v = lb_add_local_generated(p, t, false);
+		isize index_count = cast(isize)dst->Array.count;
+
+		for (isize i = 0; i < index_count; i++) {
+			lbValue elem = lb_emit_array_epi(p, v.addr, i);
+			lb_emit_store(p, elem, e);
+		}
+		return lb_addr_load(p, v);
+	}
+
+	if (is_type_any(dst)) {
+		if (is_type_untyped_nil(src)) {
+			return lb_const_nil(p->module, t);
+		}
+		if (is_type_untyped_undef(src)) {
+			return lb_const_undef(p->module, t);
+		}
+
+		lbAddr result = lb_add_local_generated(p, t, true);
+
+		Type *st = default_type(src_type);
+
+		lbValue data = lb_address_from_load_or_generate_local(p, value);
+		GB_ASSERT_MSG(is_type_pointer(data.type), "%s", type_to_string(data.type));
+		GB_ASSERT_MSG(is_type_typed(st), "%s", type_to_string(st));
+		data = lb_emit_conv(p, data, t_rawptr);
+
+		lbValue id = lb_typeid(p->module, st);
+		lbValue any_data = lb_emit_struct_ep(p, result.addr, 0);
+		lbValue any_id   = lb_emit_struct_ep(p, result.addr, 1);
+
+		lb_emit_store(p, any_data, data);
+		lb_emit_store(p, any_id,   id);
+
+		return lb_addr_load(p, result);
+	}
+
+	if (is_type_untyped(src)) {
+		if (is_type_string(src) && is_type_string(dst)) {
+			lbAddr result = lb_add_local_generated(p, t, false);
+			lb_addr_store(p, result, value);
+			return lb_addr_load(p, result);
+		}
+	}
+
+	gb_printf_err("%.*s\n", LIT(p->name));
+	gb_printf_err("lb_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_printf_err("Not Identical %p != %p\n", src_type, t);
+	gb_printf_err("Not Identical %p != %p\n", src, dst);
+
+
+	GB_PANIC("Invalid type conversion: '%s' to '%s' for procedure '%.*s'",
+	         type_to_string(src_type), type_to_string(t),
+	         LIT(p->name));
+
+	return {};
+}
+
+bool lb_is_type_aggregate(Type *t) {
+	t = base_type(t);
+	switch (t->kind) {
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_string:
+		case Basic_any:
+			return true;
+
+		// case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
+			return true;
+		}
+		break;
+
+	case Type_Pointer:
+		return false;
+
+	case Type_Array:
+	case Type_Slice:
+	case Type_Struct:
+	case Type_Union:
+	case Type_Tuple:
+	case Type_DynamicArray:
+	case Type_Map:
+	case Type_BitField:
+	case Type_SimdVector:
+		return true;
+
+	case Type_Named:
+		return lb_is_type_aggregate(t->Named.base);
+	}
+
+	return false;
+}
+
+lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
+	Type *src_type = value.type;
+	if (are_types_identical(t, src_type)) {
+		return value;
+	}
+
+	lbValue res = {};
+	res.type = t;
+
+
+	Type *src = base_type(src_type);
+	Type *dst = base_type(t);
+
+	lbModule *m = p->module;
+
+	i64 sz = type_size_of(src);
+	i64 dz = type_size_of(dst);
+
+	GB_ASSERT_MSG(sz == dz, "Invalid transmute conversion: '%s' to '%s'", type_to_string(src_type), type_to_string(t));
+
+	// NOTE(bill): Casting between an integer and a pointer cannot be done through a bitcast
+	if (is_type_uintptr(src) && is_type_pointer(dst)) {
+		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+	if (is_type_pointer(src) && is_type_uintptr(dst)) {
+		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+	if (is_type_uintptr(src) && is_type_proc(dst)) {
+		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+	if (is_type_proc(src) && is_type_uintptr(dst)) {
+		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+	if (is_type_integer(src) && (is_type_pointer(dst) || is_type_cstring(dst))) {
+		res.value = LLVMBuildIntToPtr(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	} else if ((is_type_pointer(src) || is_type_cstring(src)) && is_type_integer(dst)) {
+		res.value = LLVMBuildPtrToInt(p->builder, value.value, lb_type(m, t), "");
+		return res;
+	}
+
+	if (is_type_pointer(src) && is_type_pointer(dst)) {
+		res.value = LLVMBuildPointerCast(p->builder, value.value, lb_type(p->module, t), "");
+		return res;
+	}
+
+	if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
+		lbValue s = lb_address_from_load_or_generate_local(p, value);
+		lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
+		return lb_emit_load(p, d);
+	}
+
+
+	res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
+	// GB_PANIC("lb_emit_transmute");
+	return res;
+}
+
+
+void lb_emit_init_context(lbProcedure *p, lbAddr addr) {
+	GB_ASSERT(addr.kind == lbAddr_Context);
+	GB_ASSERT(addr.ctx.sel.index.count == 0);
+
+	lbModule *m = p->module;
+	gbAllocator a = heap_allocator();
+	auto args = array_make<lbValue>(a, 1);
+	args[0] = addr.addr;
+	lb_emit_runtime_call(p, "__init_context", args);
+}
+
+void lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx) {
+	ctx.kind = lbAddr_Context;
+	lbContextData cd = {ctx, p->scope_index};
+	array_add(&p->context_stack, cd);
+}
+
+
+lbAddr lb_find_or_generate_context_ptr(lbProcedure *p) {
+	if (p->context_stack.count > 0) {
+		return p->context_stack[p->context_stack.count-1].ctx;
+	}
+
+	Type *pt = base_type(p->type);
+	GB_ASSERT(pt->kind == Type_Proc);
+	if (pt->Proc.calling_convention != ProcCC_Odin) {
+		return p->module->global_default_context;
+	} else {
+		lbAddr c = lb_add_local_generated(p, t_context, false);
+		c.kind = lbAddr_Context;
+		lb_push_context_onto_stack(p, c);
+		lb_addr_store(p, c, lb_addr_load(p, p->module->global_default_context));
+		lb_emit_init_context(p, c);
+		return c;
+	}
+}
+
+lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value) {
+	if (LLVMIsALoadInst(value.value)) {
+		lbValue res = {};
+		res.value = LLVMGetOperand(value.value, 0);
+		res.type = alloc_type_pointer(value.type);
+		return res;
+	}
+
+	GB_ASSERT(is_type_typed(value.type));
+
+	lbAddr res = lb_add_local_generated(p, value.type, false);
+	lb_addr_store(p, res, value);
+	return res.addr;
+}
+
+lbValue lb_copy_value_to_ptr(lbProcedure *p, lbValue val, Type *new_type, i64 alignment) {
+	i64 type_alignment = type_align_of(new_type);
+	if (alignment < type_alignment) {
+		alignment = type_alignment;
+	}
+	GB_ASSERT_MSG(are_types_identical(new_type, val.type), "%s %s", type_to_string(new_type), type_to_string(val.type));
+
+	lbAddr ptr = lb_add_local_generated(p, new_type, false);
+	LLVMSetAlignment(ptr.addr.value, cast(unsigned)alignment);
+	lb_addr_store(p, ptr, val);
+	ptr.kind = lbAddr_Context;
+	return ptr.addr;
+}
+
+lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
+	gbAllocator a = heap_allocator();
+	GB_ASSERT(is_type_pointer(s.type));
+	Type *t = base_type(type_deref(s.type));
+	Type *result_type = nullptr;
+
+	if (t->kind == Type_Opaque) {
+		t = t->Opaque.elem;
+	}
+
+	if (is_type_struct(t)) {
+		result_type = get_struct_field_type(t, index);
+	} else if (is_type_union(t)) {
+		GB_ASSERT(index == -1);
+		return lb_emit_union_tag_ptr(p, s);
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->Tuple.variables.count > 0);
+		result_type = t->Tuple.variables[index]->type;
+	} else if (is_type_complex(t)) {
+		Type *ft = base_complex_elem_type(t);
+		switch (index) {
+		case 0: result_type = ft; break;
+		case 1: result_type = ft; break;
+		}
+	} else if (is_type_quaternion(t)) {
+		Type *ft = base_complex_elem_type(t);
+		switch (index) {
+		case 0: result_type = ft; break;
+		case 1: result_type = ft; break;
+		case 2: result_type = ft; break;
+		case 3: result_type = ft; break;
+		}
+	} else if (is_type_slice(t)) {
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->Slice.elem); break;
+		case 1: 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_rawptr; break;
+		case 1: result_type = t_typeid; break;
+		}
+	} else if (is_type_dynamic_array(t)) {
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->DynamicArray.elem); break;
+		case 1: result_type = t_int;       break;
+		case 2: result_type = t_int;       break;
+		case 3: result_type = t_allocator; break;
+		}
+	} else if (is_type_map(t)) {
+		init_map_internal_types(t);
+		Type *itp = alloc_type_pointer(t->Map.internal_type);
+		s = lb_emit_transmute(p, s, itp);
+
+		Type *gst = t->Map.internal_type;
+		GB_ASSERT(gst->kind == Type_Struct);
+		switch (index) {
+		case 0: result_type = get_struct_field_type(gst, 0); break;
+		case 1: result_type = get_struct_field_type(gst, 1); break;
+		}
+	} else if (is_type_array(t)) {
+		return lb_emit_array_epi(p, s, index);
+	} else {
+		GB_PANIC("TODO(bill): struct_gep type: %s, %d", type_to_string(s.type), index);
+	}
+
+	GB_ASSERT_MSG(result_type != nullptr, "%s %d", type_to_string(t), index);
+
+	if (lb_is_const(s)) {
+		lbModule *m = p->module;
+		lbValue res = {};
+		LLVMValueRef indices[2] = {llvm_zero32(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
+		res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
+		res.type = alloc_type_pointer(result_type);
+		return res;
+	} else {
+		lbValue res = {};
+		res.value = LLVMBuildStructGEP(p->builder, s.value, cast(unsigned)index, "");
+		res.type = alloc_type_pointer(result_type);
+		return res;
+	}
+}
+
+lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
+	if (LLVMIsALoadInst(s.value)) {
+		lbValue res = {};
+		res.value = LLVMGetOperand(s.value, 0);
+		res.type = alloc_type_pointer(s.type);
+		lbValue ptr = lb_emit_struct_ep(p, res, index);
+		return lb_emit_load(p, ptr);
+	}
+
+	gbAllocator a = heap_allocator();
+	Type *t = base_type(s.type);
+	Type *result_type = nullptr;
+
+	switch (t->kind) {
+	case Type_Basic:
+		switch (t->Basic.kind) {
+		case Basic_string:
+			switch (index) {
+			case 0: result_type = t_u8_ptr; break;
+			case 1: result_type = t_int;    break;
+			}
+			break;
+		case Basic_any:
+			switch (index) {
+			case 0: result_type = t_rawptr; break;
+			case 1: result_type = t_typeid; break;
+			}
+			break;
+		case Basic_complex64: case Basic_complex128:
+		{
+			Type *ft = base_complex_elem_type(t);
+			switch (index) {
+			case 0: result_type = ft; break;
+			case 1: result_type = ft; break;
+			}
+			break;
+		}
+		case Basic_quaternion128: case Basic_quaternion256:
+		{
+			Type *ft = base_complex_elem_type(t);
+			switch (index) {
+			case 0: result_type = ft; break;
+			case 1: result_type = ft; break;
+			case 2: result_type = ft; break;
+			case 3: result_type = ft; break;
+			}
+			break;
+		}
+		}
+		break;
+	case Type_Struct:
+		result_type = get_struct_field_type(t, index);
+		break;
+	case Type_Union:
+		GB_ASSERT(index == -1);
+		// return lb_emit_union_tag_value(p, s);
+		GB_PANIC("lb_emit_union_tag_value");
+
+	case Type_Tuple:
+		GB_ASSERT(t->Tuple.variables.count > 0);
+		result_type = t->Tuple.variables[index]->type;
+		if (t->Tuple.variables.count == 1) {
+			return s;
+		}
+		break;
+	case Type_Slice:
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->Slice.elem); break;
+		case 1: result_type = t_int; break;
+		}
+		break;
+	case Type_DynamicArray:
+		switch (index) {
+		case 0: result_type = alloc_type_pointer(t->DynamicArray.elem); break;
+		case 1: result_type = t_int;                                    break;
+		case 2: result_type = t_int;                                    break;
+		case 3: result_type = t_allocator;                              break;
+		}
+		break;
+
+	case Type_Map:
+		{
+			init_map_internal_types(t);
+			Type *gst = t->Map.generated_struct_type;
+			switch (index) {
+			case 0: result_type = get_struct_field_type(gst, 0); break;
+			case 1: result_type = get_struct_field_type(gst, 1); break;
+			}
+		}
+		break;
+
+	case Type_Array:
+		result_type = t->Array.elem;
+		break;
+
+	default:
+		GB_PANIC("TODO(bill): struct_ev type: %s, %d", type_to_string(s.type), index);
+		break;
+	}
+
+	GB_ASSERT_MSG(result_type != nullptr, "%s, %d", type_to_string(s.type), index);
+
+
+	lbValue res = {};
+	res.value = LLVMBuildExtractValue(p->builder, s.value, cast(unsigned)index, "");
+	res.type = result_type;
+	return res;
+}
+
+lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel) {
+	GB_ASSERT(sel.index.count > 0);
+	Type *type = type_deref(e.type);
+	gbAllocator a = heap_allocator();
+
+	for_array(i, sel.index) {
+		i32 index = cast(i32)sel.index[i];
+		if (is_type_pointer(type)) {
+			type = type_deref(type);
+			e = lb_emit_load(p, e);
+		}
+		type = core_type(type);
+		if (type->kind == Type_Opaque) {
+			type = type->Opaque.elem;
+		}
+
+		if (is_type_quaternion(type)) {
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (is_type_raw_union(type)) {
+			type = get_struct_field_type(type, index);
+			GB_ASSERT(is_type_pointer(e.type));
+			e = lb_emit_transmute(p, e, alloc_type_pointer(type));
+		} else if (is_type_struct(type)) {
+			type = get_struct_field_type(type, index);
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (type->kind == Type_Union) {
+			GB_ASSERT(index == -1);
+			type = t_type_info_ptr;
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (type->kind == Type_Tuple) {
+			type = type->Tuple.variables[index]->type;
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (type->kind == Type_Basic) {
+			switch (type->Basic.kind) {
+			case Basic_any: {
+				if (index == 0) {
+					type = t_rawptr;
+				} else if (index == 1) {
+					type = t_type_info_ptr;
+				}
+				e = lb_emit_struct_ep(p, e, index);
+				break;
+			}
+
+			case Basic_string:
+				e = lb_emit_struct_ep(p, e, index);
+				break;
+
+			default:
+				GB_PANIC("un-gep-able type");
+				break;
+			}
+		} else if (type->kind == Type_Slice) {
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (type->kind == Type_DynamicArray) {
+			e = lb_emit_struct_ep(p, e, index);
+		} else if (type->kind == Type_Array) {
+			e = lb_emit_array_epi(p, e, index);
+		} else if (type->kind == Type_Map) {
+			e = lb_emit_struct_ep(p, e, index);
+		} else {
+			GB_PANIC("un-gep-able type %s", type_to_string(type));
+		}
+	}
+
+	return e;
+}
+
+
+lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel) {
+	lbValue ptr = lb_address_from_load_or_generate_local(p, e);
+	lbValue res = lb_emit_deep_field_gep(p, ptr, sel);
+	return lb_emit_load(p, res);
+}
+
+
+
+void lb_build_defer_stmt(lbProcedure *p, lbDefer d) {
+	// NOTE(bill): The prev block may defer injection before it's terminator
+	LLVMValueRef last_instr = LLVMGetLastInstruction(p->curr_block->block);
+	if (last_instr != nullptr && LLVMIsAReturnInst(last_instr)) {
+		// NOTE(bill): ReturnStmt defer stuff will be handled previously
+		return;
+	}
+
+	lbBlock *b = lb_create_block(p, "defer");
+	if (last_instr == nullptr || !LLVMIsATerminatorInst(last_instr)) {
+		lb_emit_jump(p, b);
+	}
+
+	if (last_instr == nullptr || !LLVMIsATerminatorInst(last_instr)) {
+		lb_emit_jump(p, b);
+	}
+	lb_start_block(p, b);
+	if (d.kind == lbDefer_Node) {
+		lb_build_stmt(p, d.stmt);
+	} else if (d.kind == lbDefer_Instr) {
+		// NOTE(bill): Need to make a new copy
+		LLVMValueRef instr = LLVMInstructionClone(d.instr.value);
+		LLVMInsertIntoBuilder(p->builder, instr);
+	} else if (d.kind == lbDefer_Proc) {
+		lb_emit_call(p, d.proc.deferred, d.proc.result_as_args);
+	}
+}
+
+void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block) {
+	isize count = p->defer_stmts.count;
+	isize i = count;
+	while (i --> 0) {
+		lbDefer d = p->defer_stmts[i];
+
+		if (kind == lbDeferExit_Default) {
+			if (p->scope_index == d.scope_index &&
+			    d.scope_index > 0) { // TODO(bill): Which is correct: > 0 or > 1?
+				lb_build_defer_stmt(p, d);
+				array_pop(&p->defer_stmts);
+				continue;
+			} else {
+				break;
+			}
+		} else if (kind == lbDeferExit_Return) {
+			lb_build_defer_stmt(p, d);
+		} else if (kind == lbDeferExit_Branch) {
+			GB_ASSERT(block != nullptr);
+			isize lower_limit = block->scope_index;
+			if (lower_limit < d.scope_index) {
+				lb_build_defer_stmt(p, d);
+			}
+		}
+	}
+}
+
+lbDefer lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt) {
+	lbDefer d = {lbDefer_Node};
+	d.scope_index = scope_index;
+	d.context_stack_count = p->context_stack.count;
+	d.block = p->curr_block;
+	d.stmt = stmt;
+	array_add(&p->defer_stmts, d);
+	return d;
+}
+
+lbDefer lb_add_defer_proc(lbProcedure *p, isize scope_index, lbValue deferred, Array<lbValue> const &result_as_args) {
+	lbDefer d = {lbDefer_Proc};
+	d.scope_index = p->scope_index;
+	d.block = p->curr_block;
+	d.proc.deferred = deferred;
+	d.proc.result_as_args = result_as_args;
+	array_add(&p->defer_stmts, d);
+	return d;
+}
+
+
+
+Array<lbValue> lb_value_to_array(lbProcedure *p, lbValue value) {
+	Array<lbValue> array = {};
+	Type *t = base_type(value.type);
+	if (t == nullptr) {
+		// Do nothing
+	} else if (is_type_tuple(t)) {
+		GB_ASSERT(t->kind == Type_Tuple);
+		auto *rt = &t->Tuple;
+		if (rt->variables.count > 0) {
+			array = array_make<lbValue>(heap_allocator(), rt->variables.count);
+			for_array(i, rt->variables) {
+				lbValue elem = lb_emit_struct_ev(p, value, cast(i32)i);
+				array[i] = elem;
+			}
+		}
+	} else {
+		array = array_make<lbValue>(heap_allocator(), 1);
+		array[0] = value;
+	}
+	return array;
+}
+
+
+
+lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) {
+	unsigned arg_count = cast(unsigned)processed_args.count;
+	if (return_ptr.value != nullptr) {
+		arg_count += 1;
+	}
+	if (context_ptr.addr.value != nullptr) {
+		arg_count += 1;
+	}
+
+	LLVMValueRef *args = gb_alloc_array(heap_allocator(), LLVMValueRef, arg_count);
+	isize arg_index = 0;
+	if (return_ptr.value != nullptr) {
+		args[arg_index++] = return_ptr.value;
+	}
+	for_array(i, processed_args) {
+		lbValue arg = processed_args[i];
+		args[arg_index++] = arg.value;
+	}
+	if (context_ptr.addr.value != nullptr) {
+		args[arg_index++] = context_ptr.addr.value;
+	}
+
+	LLVMBasicBlockRef curr_block = LLVMGetInsertBlock(p->builder);
+	GB_ASSERT(curr_block != p->decl_block->block);
+
+	LLVMValueRef ret = LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(p->module, value.type)), value.value, args, arg_count, "");;
+	lbValue res = {};
+	res.value = ret;
+	res.type = abi_rt;
+	return res;
+}
+
+lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args) {
+	String name = make_string_c(c_name);
+
+
+	AstPackage *pkg = p->module->info->runtime_package;
+	Entity *e = scope_lookup_current(pkg->scope, name);
+
+	lbValue *found = nullptr;
+	if (p->module != e->code_gen_module) {
+		gb_mutex_lock(&p->module->mutex);
+	}
+	found = map_get(&e->code_gen_module->values, hash_entity(e));
+	if (p->module != e->code_gen_module) {
+		gb_mutex_unlock(&p->module->mutex);
+	}
+
+	GB_ASSERT_MSG(found != nullptr, "%s", c_name);
+	return lb_emit_call(p, *found, args);
+}
+
+lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, bool use_return_ptr_hint) {
+	lbModule *m = p->module;
+
+	Type *pt = base_type(value.type);
+	GB_ASSERT(pt->kind == Type_Proc);
+	Type *results = pt->Proc.results;
+
+	if (p->entity != nullptr) {
+		if (p->entity->flags & EntityFlag_Disabled) {
+			return {};
+		}
+	}
+
+	lbAddr context_ptr = {};
+	if (pt->Proc.calling_convention == ProcCC_Odin) {
+		context_ptr = lb_find_or_generate_context_ptr(p);
+	}
+
+	set_procedure_abi_types(heap_allocator(), pt);
+
+	bool is_c_vararg = pt->Proc.c_vararg;
+	isize param_count = pt->Proc.param_count;
+	if (is_c_vararg) {
+		GB_ASSERT(param_count-1 <= args.count);
+		param_count -= 1;
+	} else {
+		GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count);
+	}
+
+	auto processed_args = array_make<lbValue>(heap_allocator(), 0, args.count);
+
+	for (isize i = 0; i < param_count; i++) {
+		Entity *e = pt->Proc.params->Tuple.variables[i];
+		if (e->kind != Entity_Variable) {
+			// array_add(&processed_args, args[i]);
+			continue;
+		}
+		GB_ASSERT(e->flags & EntityFlag_Param);
+
+		Type *original_type = e->type;
+		Type *new_type = pt->Proc.abi_compat_params[i];
+		Type *arg_type = args[i].type;
+
+		if (are_types_identical(arg_type, new_type)) {
+			// NOTE(bill): Done
+			array_add(&processed_args, args[i]);
+		} else if (!are_types_identical(original_type, new_type)) {
+			if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
+				if (e->flags&EntityFlag_ImplicitReference) {
+					array_add(&processed_args, lb_address_from_load_or_generate_local(p, args[i]));
+				} else if (!is_type_pointer(arg_type)) {
+					array_add(&processed_args, lb_copy_value_to_ptr(p, args[i], original_type, 16));
+				}
+			} else if (is_type_integer(new_type) || is_type_float(new_type)) {
+				array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+			} else if (new_type == t_llvm_bool) {
+				array_add(&processed_args, lb_emit_conv(p, args[i], new_type));
+			} else if (is_type_simd_vector(new_type)) {
+				array_add(&processed_args, lb_emit_transmute(p, args[i], new_type));
+			} else if (is_type_tuple(new_type)) {
+				Type *abi_type = pt->Proc.abi_compat_params[i];
+				Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type);
+				lbValue x = lb_emit_transmute(p, args[i], st);
+				for (isize j = 0; j < new_type->Tuple.variables.count; j++) {
+					lbValue xx = lb_emit_struct_ev(p, x, cast(i32)j);
+					array_add(&processed_args, xx);
+				}
+			}
+		} else {
+			lbValue x = lb_emit_conv(p, args[i], new_type);
+			array_add(&processed_args, x);
+		}
+	}
+
+	if (inlining == ProcInlining_none) {
+		inlining = p->inlining;
+	}
+
+	lbValue result = {};
+
+	Type *abi_rt = reduce_tuple_to_single_type(pt->Proc.abi_compat_result_type);
+	Type *rt = reduce_tuple_to_single_type(results);
+	if (pt->Proc.return_by_pointer) {
+		lbValue return_ptr = {};
+		if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) {
+			if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) {
+				return_ptr = p->return_ptr_hint_value;
+				p->return_ptr_hint_used = true;
+			}
+		}
+		if (return_ptr.value == nullptr) {
+			lbAddr r = lb_add_local_generated(p, rt, true);
+			return_ptr = r.addr;
+		}
+		GB_ASSERT(is_type_pointer(return_ptr.type));
+		lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
+		result = lb_emit_load(p, return_ptr);
+	} else {
+		result = lb_emit_call_internal(p, value, {}, processed_args, abi_rt, context_ptr, inlining);
+		if (abi_rt != rt) {
+			result = lb_emit_transmute(p, result, rt);
+		}
+	}
+
+	Entity **found = map_get(&p->module->procedure_values, hash_pointer(value.value));
+	if (found != nullptr) {
+		Entity *e = *found;
+		if (e != nullptr && entity_has_deferred_procedure(e)) {
+			DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind;
+			Entity *deferred_entity = e->Procedure.deferred_procedure.entity;
+			lbValue *deferred_found = map_get(&p->module->values, hash_entity(deferred_entity));
+			GB_ASSERT(deferred_found != nullptr);
+			lbValue deferred = *deferred_found;
+
+
+			auto in_args = args;
+			Array<lbValue> result_as_args = {};
+			switch (kind) {
+			case DeferredProcedure_none:
+				break;
+			case DeferredProcedure_in:
+				result_as_args = in_args;
+				break;
+			case DeferredProcedure_out:
+				result_as_args = lb_value_to_array(p, result);
+				break;
+			}
+
+			lb_add_defer_proc(p, p->scope_index, deferred, result_as_args);
+		}
+	}
+
+	return result;
+}
+
+lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
+	Type *t = s.type;
+	GB_ASSERT(is_type_pointer(t));
+	Type *st = base_type(type_deref(t));
+	GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st), "%s", type_to_string(st));
+	GB_ASSERT_MSG(is_type_integer(index.type), "%s", type_to_string(index.type));
+
+	LLVMValueRef indices[2] = {};
+	indices[0] = llvm_zero32(p->module);
+	indices[1] = lb_emit_conv(p, index, t_int).value;
+
+	Type *ptr = base_array_type(st);
+	lbValue res = {};
+	res.value = LLVMBuildGEP(p->builder, s.value, indices, 2, "");
+	res.type = alloc_type_pointer(ptr);
+	return res;
+}
+
+lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
+	Type *t = s.type;
+	GB_ASSERT(is_type_pointer(t));
+	Type *st = base_type(type_deref(t));
+	GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st), "%s", type_to_string(st));
+
+	GB_ASSERT(0 <= index);
+	Type *ptr = base_array_type(st);
+
+
+	LLVMValueRef indices[2] = {
+		LLVMConstInt(lb_type(p->module, t_int), 0, false),
+		LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)index, false),
+	};
+
+	lbValue res = {};
+	if (lb_is_const(s)) {
+		res.value = LLVMConstGEP(s.value, indices, gb_count_of(indices));
+	} else {
+		res.value = LLVMBuildGEP(p->builder, s.value, indices, gb_count_of(indices), "");
+	}
+	res.type = alloc_type_pointer(ptr);
+	return res;
+}
+
+lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
+	LLVMValueRef indices[1] = {index.value};
+	lbValue res = {};
+	res.type = ptr.type;
+
+	if (lb_is_const(ptr) && lb_is_const(index)) {
+		res.value = LLVMConstGEP(ptr.value, indices, 1);
+	} else {
+		res.value = LLVMBuildGEP(p->builder, ptr.value, indices, 1, "");
+	}
+	return res;
+}
+
+LLVMValueRef llvm_const_slice(lbValue data, lbValue len) {
+	GB_ASSERT(is_type_pointer(data.type));
+	GB_ASSERT(are_types_identical(len.type, t_int));
+	LLVMValueRef vals[2] = {
+		data.value,
+		len.value,
+	};
+	return LLVMConstStruct(vals, gb_count_of(vals), false);
+}
+
+
+void lb_fill_slice(lbProcedure *p, lbAddr const &slice, lbValue base_elem, lbValue len) {
+	Type *t = lb_addr_type(slice);
+	GB_ASSERT(is_type_slice(t));
+	lbValue ptr = lb_addr_get_ptr(p, slice);
+	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 0), base_elem);
+	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
+}
+void lb_fill_string(lbProcedure *p, lbAddr const &string, lbValue base_elem, lbValue len) {
+	Type *t = lb_addr_type(string);
+	GB_ASSERT(is_type_string(t));
+	lbValue ptr = lb_addr_get_ptr(p, string);
+	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 0), base_elem);
+	lb_emit_store(p, lb_emit_struct_ep(p, ptr, 1), len);
+}
+
+lbValue lb_string_elem(lbProcedure *p, lbValue string) {
+	Type *t = base_type(string.type);
+	GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
+	return lb_emit_struct_ev(p, string, 0);
+}
+lbValue lb_string_len(lbProcedure *p, lbValue string) {
+	Type *t = base_type(string.type);
+	GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
+	return lb_emit_struct_ev(p, string, 1);
+}
+
+lbValue lb_cstring_len(lbProcedure *p, lbValue value) {
+	GB_ASSERT(is_type_cstring(value.type));
+	auto args = array_make<lbValue>(heap_allocator(), 1);
+	args[0] = lb_emit_conv(p, value, t_cstring);
+	return lb_emit_runtime_call(p, "cstring_len", args);
+}
+
+
+lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr) {
+	Type *t = type_deref(array_ptr.type);
+	GB_ASSERT(is_type_array(t));
+	return lb_emit_struct_ep(p, array_ptr, 0);
+}
+
+lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
+	GB_ASSERT(is_type_slice(slice.type));
+	return lb_emit_struct_ev(p, slice, 0);
+}
+lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
+	GB_ASSERT(is_type_slice(slice.type));
+	return lb_emit_struct_ev(p, slice, 1);
+}
+lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
+	GB_ASSERT(is_type_dynamic_array(da.type));
+	return lb_emit_struct_ev(p, da, 0);
+}
+lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da) {
+	GB_ASSERT(is_type_dynamic_array(da.type));
+	return lb_emit_struct_ev(p, da, 1);
+}
+lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da) {
+	GB_ASSERT(is_type_dynamic_array(da.type));
+	return lb_emit_struct_ev(p, da, 2);
+}
+lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da) {
+	GB_ASSERT(is_type_dynamic_array(da.type));
+	return lb_emit_struct_ev(p, da, 3);
+}
+
+lbValue lb_map_entries(lbProcedure *p, lbValue value) {
+	gbAllocator a = heap_allocator();
+	Type *t = base_type(value.type);
+	GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
+	init_map_internal_types(t);
+	Type *gst = t->Map.generated_struct_type;
+	i32 index = 1;
+	lbValue entries = lb_emit_struct_ev(p, value, index);
+	return entries;
+}
+
+lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value) {
+	gbAllocator a = heap_allocator();
+	Type *t = base_type(type_deref(value.type));
+	GB_ASSERT_MSG(t->kind == Type_Map, "%s", type_to_string(t));
+	init_map_internal_types(t);
+	Type *gst = t->Map.generated_struct_type;
+	i32 index = 1;
+	lbValue entries = lb_emit_struct_ep(p, value, index);
+	return entries;
+}
+
+lbValue lb_map_len(lbProcedure *p, lbValue value) {
+	lbValue entries = lb_map_entries(p, value);
+	return lb_dynamic_array_len(p, entries);
+}
+
+lbValue lb_map_cap(lbProcedure *p, lbValue value) {
+	lbValue entries = lb_map_entries(p, value);
+	return lb_dynamic_array_cap(p, entries);
+}
+
+lbValue lb_soa_struct_len(lbProcedure *p, lbValue value) {
+	Type *t = base_type(value.type);
+	bool is_ptr = false;
+	if (is_type_pointer(t)) {
+		is_ptr = true;
+		t = base_type(type_deref(t));
+	}
+
+
+	if (t->Struct.soa_kind == StructSoa_Fixed) {
+		return lb_const_int(p->module, t_int, t->Struct.soa_count);
+	}
+
+	GB_ASSERT(t->Struct.soa_kind == StructSoa_Slice ||
+	          t->Struct.soa_kind == StructSoa_Dynamic);
+
+	isize n = 0;
+	Type *elem = base_type(t->Struct.soa_elem);
+	if (elem->kind == Type_Struct) {
+		n = elem->Struct.fields.count;
+	} else if (elem->kind == Type_Array) {
+		n = elem->Array.count;
+	} else {
+		GB_PANIC("Unreachable");
+	}
+
+	if (is_ptr) {
+		lbValue v = lb_emit_struct_ep(p, value, cast(i32)n);
+		return lb_emit_load(p, v);
+	}
+	return lb_emit_struct_ev(p, value, cast(i32)n);
+}
+
+lbValue lb_soa_struct_cap(lbProcedure *p, lbValue value) {
+	Type *t = base_type(value.type);
+
+	bool is_ptr = false;
+	if (is_type_pointer(t)) {
+		is_ptr = true;
+		t = base_type(type_deref(t));
+	}
+
+	if (t->Struct.soa_kind == StructSoa_Fixed) {
+		return lb_const_int(p->module, t_int, t->Struct.soa_count);
+	}
+
+	GB_ASSERT(t->Struct.soa_kind == StructSoa_Dynamic);
+
+	isize n = 0;
+	Type *elem = base_type(t->Struct.soa_elem);
+	if (elem->kind == Type_Struct) {
+		n = elem->Struct.fields.count+1;
+	} else if (elem->kind == Type_Array) {
+		n = elem->Array.count+1;
+	} else {
+		GB_PANIC("Unreachable");
+	}
+
+	if (is_ptr) {
+		lbValue v = lb_emit_struct_ep(p, value, cast(i32)n);
+		return lb_emit_load(p, v);
+	}
+	return lb_emit_struct_ev(p, value, cast(i32)n);
+}
+
+
+
+
+lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
+	ast_node(ce, CallExpr, expr);
+
+	switch (id) {
+	case BuiltinProc_DIRECTIVE: {
+		ast_node(bd, BasicDirective, ce->proc);
+		String name = bd->name;
+		GB_ASSERT(name == "location");
+		String procedure = p->entity->token.string;
+		TokenPos pos = ast_token(ce->proc).pos;
+		if (ce->args.count > 0) {
+			Ast *ident = unselector_expr(ce->args[0]);
+			GB_ASSERT(ident->kind == Ast_Ident);
+			Entity *e = entity_of_ident(ident);
+			GB_ASSERT(e != nullptr);
+
+			if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
+				procedure = e->parent_proc_decl->entity->token.string;
+			} else {
+				procedure = str_lit("");
+			}
+			pos = e->token.pos;
+
+		}
+		return lb_emit_source_code_location(p, procedure, pos);
+	}
+
+	case BuiltinProc_type_info_of: {
+		Ast *arg = ce->args[0];
+		TypeAndValue tav = type_and_value_of_expr(arg);
+		if (tav.mode == Addressing_Type) {
+			Type *t = default_type(type_of_expr(arg));
+			return lb_type_info(p->module, t);
+		}
+		GB_ASSERT(is_type_typeid(tav.type));
+
+		auto args = array_make<lbValue>(heap_allocator(), 1);
+		args[0] = lb_build_expr(p, arg);
+		return lb_emit_runtime_call(p, "__type_info_of", args);
+	}
+
+	case BuiltinProc_typeid_of: {
+		Ast *arg = ce->args[0];
+		TypeAndValue tav = type_and_value_of_expr(arg);
+		if (tav.mode == Addressing_Type) {
+			Type *t = default_type(type_of_expr(arg));
+			return lb_typeid(p->module, t);
+		}
+		Type *t = base_type(tav.type);
+		GB_ASSERT(are_types_identical(t, t_type_info_ptr));
+
+		auto args = array_make<lbValue>(heap_allocator(), 1);
+		args[0] = lb_emit_conv(p, lb_build_expr(p, arg), t_type_info_ptr);
+		return lb_emit_runtime_call(p, "__typeid_of", args);
+	}
+
+	case BuiltinProc_len: {
+		lbValue v = lb_build_expr(p, ce->args[0]);
+		Type *t = base_type(v.type);
+		if (is_type_pointer(t)) {
+			// IMPORTANT TODO(bill): Should there be a nil pointer check?
+			v = lb_emit_load(p, v);
+			t = type_deref(t);
+		}
+		if (is_type_cstring(t)) {
+			return lb_cstring_len(p, v);
+		} else if (is_type_string(t)) {
+			return lb_string_len(p, v);
+		} else if (is_type_array(t)) {
+			GB_PANIC("Array lengths are constant");
+		} else if (is_type_slice(t)) {
+			return lb_slice_len(p, v);
+		} else if (is_type_dynamic_array(t)) {
+			return lb_dynamic_array_len(p, v);
+		} else if (is_type_map(t)) {
+			return lb_map_len(p, v);
+		} else if (is_type_soa_struct(t)) {
+			return lb_soa_struct_len(p, v);
+		}
+
+		GB_PANIC("Unreachable");
+		break;
+	}
+
+	case BuiltinProc_cap: {
+		lbValue v = lb_build_expr(p, ce->args[0]);
+		Type *t = base_type(v.type);
+		if (is_type_pointer(t)) {
+			// IMPORTANT TODO(bill): Should there be a nil pointer check?
+			v = lb_emit_load(p, v);
+			t = type_deref(t);
+		}
+		if (is_type_string(t)) {
+			GB_PANIC("Unreachable");
+		} else if (is_type_array(t)) {
+			GB_PANIC("Array lengths are constant");
+		} else if (is_type_slice(t)) {
+			return lb_slice_len(p, v);
+		} else if (is_type_dynamic_array(t)) {
+			return lb_dynamic_array_cap(p, v);
+		} else if (is_type_map(t)) {
+			return lb_map_cap(p, v);
+		} else if (is_type_soa_struct(t)) {
+			return lb_soa_struct_cap(p, v);
+		}
+
+		GB_PANIC("Unreachable");
+
+		break;
+	}
+
+	case BuiltinProc_swizzle: {
+		lbAddr addr = lb_build_addr(p, ce->args[0]);
+		isize index_count = ce->args.count-1;
+		if (index_count == 0) {
+			return lb_addr_load(p, addr);
+		}
+		lbValue src = lb_addr_get_ptr(p, addr);
+		// TODO(bill): Should this be zeroed or not?
+		lbAddr dst = lb_add_local_generated(p, tv.type, true);
+		lbValue dst_ptr = lb_addr_get_ptr(p, dst);
+
+		for (i32 i = 1; i < ce->args.count; i++) {
+			TypeAndValue tv = type_and_value_of_expr(ce->args[i]);
+			GB_ASSERT(is_type_integer(tv.type));
+			GB_ASSERT(tv.value.kind == ExactValue_Integer);
+
+			i32 src_index = cast(i32)big_int_to_i64(&tv.value.value_integer);
+			i32 dst_index = i-1;
+
+			lbValue src_elem = lb_emit_array_epi(p, src, src_index);
+			lbValue dst_elem = lb_emit_array_epi(p, dst_ptr, dst_index);
+
+			lb_emit_store(p, dst_elem, lb_emit_load(p, src_elem));
+		}
+		return lb_addr_load(p, dst);
+	}
+
+	case BuiltinProc_complex: {
+		lbValue real = lb_build_expr(p, ce->args[0]);
+		lbValue imag = lb_build_expr(p, ce->args[1]);
+		lbAddr dst_addr = lb_add_local_generated(p, tv.type, false);
+		lbValue dst = lb_addr_get_ptr(p, dst_addr);
+
+		Type *ft = base_complex_elem_type(tv.type);
+		real = lb_emit_conv(p, real, ft);
+		imag = lb_emit_conv(p, imag, ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), real);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), imag);
+
+		return lb_emit_load(p, dst);
+	}
+
+	case BuiltinProc_quaternion: {
+		lbValue real = lb_build_expr(p, ce->args[0]);
+		lbValue imag = lb_build_expr(p, ce->args[1]);
+		lbValue jmag = lb_build_expr(p, ce->args[2]);
+		lbValue kmag = lb_build_expr(p, ce->args[3]);
+
+		// @QuaternionLayout
+		lbAddr dst_addr = lb_add_local_generated(p, tv.type, false);
+		lbValue dst = lb_addr_get_ptr(p, dst_addr);
+
+		Type *ft = base_complex_elem_type(tv.type);
+		real = lb_emit_conv(p, real, ft);
+		imag = lb_emit_conv(p, imag, ft);
+		jmag = lb_emit_conv(p, jmag, ft);
+		kmag = lb_emit_conv(p, kmag, ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), real);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), imag);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), jmag);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), kmag);
+
+		return lb_emit_load(p, dst);
+	}
+
+	case BuiltinProc_real: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		if (is_type_complex(val.type)) {
+			lbValue real = lb_emit_struct_ev(p, val, 0);
+			return lb_emit_conv(p, real, tv.type);
+		} else if (is_type_quaternion(val.type)) {
+			// @QuaternionLayout
+			lbValue real = lb_emit_struct_ev(p, val, 3);
+			return lb_emit_conv(p, real, tv.type);
+		}
+		GB_PANIC("invalid type for real");
+		return {};
+	}
+	case BuiltinProc_imag: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		if (is_type_complex(val.type)) {
+			lbValue imag = lb_emit_struct_ev(p, val, 1);
+			return lb_emit_conv(p, imag, tv.type);
+		} else if (is_type_quaternion(val.type)) {
+			// @QuaternionLayout
+			lbValue imag = lb_emit_struct_ev(p, val, 0);
+			return lb_emit_conv(p, imag, tv.type);
+		}
+		GB_PANIC("invalid type for imag");
+		return {};
+	}
+	case BuiltinProc_jmag: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		if (is_type_quaternion(val.type)) {
+			// @QuaternionLayout
+			lbValue imag = lb_emit_struct_ev(p, val, 1);
+			return lb_emit_conv(p, imag, tv.type);
+		}
+		GB_PANIC("invalid type for jmag");
+		return {};
+	}
+	case BuiltinProc_kmag: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		if (is_type_quaternion(val.type)) {
+			// @QuaternionLayout
+			lbValue imag = lb_emit_struct_ev(p, val, 2);
+			return lb_emit_conv(p, imag, tv.type);
+		}
+		GB_PANIC("invalid type for kmag");
+		return {};
+	}
+
+	case BuiltinProc_conj: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		lbValue res = {};
+		Type *t = val.type;
+		if (is_type_complex(t)) {
+			res = lb_addr_get_ptr(p, lb_add_local_generated(p, tv.type, false));
+			lbValue real = lb_emit_struct_ev(p, val, 0);
+			lbValue imag = lb_emit_struct_ev(p, val, 1);
+			imag = lb_emit_unary_arith(p, Token_Sub, imag, imag.type);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 0), real);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 1), imag);
+		} else if (is_type_quaternion(t)) {
+			// @QuaternionLayout
+			res = lb_addr_get_ptr(p, lb_add_local_generated(p, tv.type, false));
+			lbValue real = lb_emit_struct_ev(p, val, 3);
+			lbValue imag = lb_emit_struct_ev(p, val, 0);
+			lbValue jmag = lb_emit_struct_ev(p, val, 1);
+			lbValue kmag = lb_emit_struct_ev(p, val, 2);
+			imag = lb_emit_unary_arith(p, Token_Sub, imag, imag.type);
+			jmag = lb_emit_unary_arith(p, Token_Sub, jmag, jmag.type);
+			kmag = lb_emit_unary_arith(p, Token_Sub, kmag, kmag.type);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 3), real);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 0), imag);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 1), jmag);
+			lb_emit_store(p, lb_emit_struct_ep(p, res, 2), kmag);
+		}
+		return lb_emit_load(p, res);
+	}
+
+	case BuiltinProc_expand_to_tuple: {
+		lbValue val = lb_build_expr(p, ce->args[0]);
+		Type *t = base_type(val.type);
+
+		if (!is_type_tuple(tv.type)) {
+			if (t->kind == Type_Struct) {
+				GB_ASSERT(t->Struct.fields.count == 1);
+				return lb_emit_struct_ev(p, val, 0);
+			} else if (t->kind == Type_Array) {
+				GB_ASSERT(t->Array.count == 1);
+				return lb_emit_array_epi(p, val, 0);
+			} else {
+				GB_PANIC("Unknown type of expand_to_tuple");
+			}
+
+		}
+
+		GB_ASSERT(is_type_tuple(tv.type));
+		// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
+		lbValue tuple = lb_addr_get_ptr(p, lb_add_local_generated(p, tv.type, false));
+		if (t->kind == Type_Struct) {
+			for_array(src_index, t->Struct.fields) {
+				Entity *field = t->Struct.fields[src_index];
+				i32 field_index = field->Variable.field_index;
+				lbValue f = lb_emit_struct_ev(p, val, field_index);
+				lbValue ep = lb_emit_struct_ep(p, tuple, cast(i32)src_index);
+				lb_emit_store(p, ep, f);
+			}
+		} else if (t->kind == Type_Array) {
+			// TODO(bill): Clean-up this code
+			lbValue ap = lb_address_from_load_or_generate_local(p, val);
+			for (i32 i = 0; i < cast(i32)t->Array.count; i++) {
+				lbValue f = lb_emit_load(p, lb_emit_array_epi(p, ap, i));
+				lbValue ep = lb_emit_struct_ep(p, tuple, i);
+				lb_emit_store(p, ep, f);
+			}
+		} else {
+			GB_PANIC("Unknown type of expand_to_tuple");
+		}
+		return lb_emit_load(p, tuple);
+	}
+
+	case BuiltinProc_min: {
+		Type *t = type_of_expr(expr);
+		if (ce->args.count == 2) {
+			return lb_emit_min(p, t, lb_build_expr(p, ce->args[0]), lb_build_expr(p, ce->args[1]));
+		} else {
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				x = lb_emit_min(p, t, x, lb_build_expr(p, ce->args[i]));
+			}
+			return x;
+		}
+	}
+
+	case BuiltinProc_max: {
+		Type *t = type_of_expr(expr);
+		if (ce->args.count == 2) {
+			return lb_emit_max(p, t, lb_build_expr(p, ce->args[0]), lb_build_expr(p, ce->args[1]));
+		} else {
+			lbValue x = lb_build_expr(p, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				x = lb_emit_max(p, t, x, lb_build_expr(p, ce->args[i]));
+			}
+			return x;
+		}
+	}
+
+	case BuiltinProc_abs: {
+		gbAllocator a = heap_allocator();
+		lbValue x = lb_build_expr(p, ce->args[0]);
+		Type *t = x.type;
+		if (is_type_unsigned(t)) {
+			return x;
+		}
+		if (is_type_quaternion(t)) {
+			i64 sz = 8*type_size_of(t);
+			auto args = array_make<lbValue>(heap_allocator(), 1);
+			args[0] = x;
+			switch (sz) {
+			case 128: return lb_emit_runtime_call(p, "abs_quaternion128", args);
+			case 256: return lb_emit_runtime_call(p, "abs_quaternion256", args);
+			}
+			GB_PANIC("Unknown complex type");
+		} else if (is_type_complex(t)) {
+			i64 sz = 8*type_size_of(t);
+			auto args = array_make<lbValue>(heap_allocator(), 1);
+			args[0] = x;
+			switch (sz) {
+			case 64:  return lb_emit_runtime_call(p, "abs_complex64",  args);
+			case 128: return lb_emit_runtime_call(p, "abs_complex128", args);
+			}
+			GB_PANIC("Unknown complex type");
+		} else if (is_type_float(t)) {
+			i64 sz = 8*type_size_of(t);
+			auto args = array_make<lbValue>(heap_allocator(), 1);
+			args[0] = x;
+			switch (sz) {
+			case 32: return lb_emit_runtime_call(p, "abs_f32", args);
+			case 64: return lb_emit_runtime_call(p, "abs_f64", args);
+			}
+			GB_PANIC("Unknown float type");
+		}
+		lbValue zero = lb_const_nil(p->module, t);
+		lbValue cond = lb_emit_comp(p, Token_Lt, x, zero);
+		lbValue neg = lb_emit_unary_arith(p, Token_Sub, x, t);
+		return lb_emit_select(p, cond, neg, x);
+	}
+
+	case BuiltinProc_clamp:
+		return lb_emit_clamp(p, type_of_expr(expr),
+		                     lb_build_expr(p, ce->args[0]),
+		                     lb_build_expr(p, ce->args[1]),
+		                     lb_build_expr(p, ce->args[2]));
+
+
+
+	// "Intrinsics"
+	case BuiltinProc_cpu_relax:
+		// TODO(bill): BuiltinProc_cpu_relax
+		// ir_write_str_lit(f, "call void asm sideeffect \"pause\", \"\"()");
+		return {};
+
+	case BuiltinProc_atomic_fence:
+		LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
+		return {};
+	case BuiltinProc_atomic_fence_acq:
+		LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
+		return {};
+	case BuiltinProc_atomic_fence_rel:
+		LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
+		return {};
+	case BuiltinProc_atomic_fence_acqrel:
+		LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
+		return {};
+
+	case BuiltinProc_atomic_store:
+	case BuiltinProc_atomic_store_rel:
+	case BuiltinProc_atomic_store_relaxed:
+	case BuiltinProc_atomic_store_unordered: {
+		lbValue dst = lb_build_expr(p, ce->args[0]);
+		lbValue val = lb_build_expr(p, ce->args[1]);
+		val = lb_emit_conv(p, val, type_deref(dst.type));
+
+		LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
+		switch (id) {
+		case BuiltinProc_atomic_store:           LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+		case BuiltinProc_atomic_store_rel:       LLVMSetOrdering(instr, LLVMAtomicOrderingRelease);                break;
+		case BuiltinProc_atomic_store_relaxed:   LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic);              break;
+		case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered);              break;
+		}
+
+		LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
+
+		return {};
+	}
+
+	case BuiltinProc_atomic_load:
+	case BuiltinProc_atomic_load_acq:
+	case BuiltinProc_atomic_load_relaxed:
+	case BuiltinProc_atomic_load_unordered: {
+		lbValue dst = lb_build_expr(p, ce->args[0]);
+
+		LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
+		switch (id) {
+		case BuiltinProc_atomic_load:           LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+		case BuiltinProc_atomic_load_acq:       LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire);                break;
+		case BuiltinProc_atomic_load_relaxed:   LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic);              break;
+		case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered);              break;
+		}
+		LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
+
+		lbValue res = {};
+		res.value = instr;
+		res.type = type_deref(dst.type);
+		return res;
+	}
+
+	case BuiltinProc_atomic_add:
+	case BuiltinProc_atomic_add_acq:
+	case BuiltinProc_atomic_add_rel:
+	case BuiltinProc_atomic_add_acqrel:
+	case BuiltinProc_atomic_add_relaxed:
+	case BuiltinProc_atomic_sub:
+	case BuiltinProc_atomic_sub_acq:
+	case BuiltinProc_atomic_sub_rel:
+	case BuiltinProc_atomic_sub_acqrel:
+	case BuiltinProc_atomic_sub_relaxed:
+	case BuiltinProc_atomic_and:
+	case BuiltinProc_atomic_and_acq:
+	case BuiltinProc_atomic_and_rel:
+	case BuiltinProc_atomic_and_acqrel:
+	case BuiltinProc_atomic_and_relaxed:
+	case BuiltinProc_atomic_nand:
+	case BuiltinProc_atomic_nand_acq:
+	case BuiltinProc_atomic_nand_rel:
+	case BuiltinProc_atomic_nand_acqrel:
+	case BuiltinProc_atomic_nand_relaxed:
+	case BuiltinProc_atomic_or:
+	case BuiltinProc_atomic_or_acq:
+	case BuiltinProc_atomic_or_rel:
+	case BuiltinProc_atomic_or_acqrel:
+	case BuiltinProc_atomic_or_relaxed:
+	case BuiltinProc_atomic_xor:
+	case BuiltinProc_atomic_xor_acq:
+	case BuiltinProc_atomic_xor_rel:
+	case BuiltinProc_atomic_xor_acqrel:
+	case BuiltinProc_atomic_xor_relaxed:
+	case BuiltinProc_atomic_xchg:
+	case BuiltinProc_atomic_xchg_acq:
+	case BuiltinProc_atomic_xchg_rel:
+	case BuiltinProc_atomic_xchg_acqrel:
+	case BuiltinProc_atomic_xchg_relaxed: {
+		lbValue dst = lb_build_expr(p, ce->args[0]);
+		lbValue val = lb_build_expr(p, ce->args[1]);
+		val = lb_emit_conv(p, val, type_deref(dst.type));
+
+		LLVMAtomicRMWBinOp op = {};
+		LLVMAtomicOrdering ordering = {};
+
+		switch (id) {
+		case BuiltinProc_atomic_add:          op = LLVMAtomicRMWBinOpAdd;  ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_add_acq:      op = LLVMAtomicRMWBinOpAdd;  ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_add_rel:      op = LLVMAtomicRMWBinOpAdd;  ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_add_acqrel:   op = LLVMAtomicRMWBinOpAdd;  ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_add_relaxed:  op = LLVMAtomicRMWBinOpAdd;  ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_sub:          op = LLVMAtomicRMWBinOpSub;  ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_sub_acq:      op = LLVMAtomicRMWBinOpSub;  ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_sub_rel:      op = LLVMAtomicRMWBinOpSub;  ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_sub_acqrel:   op = LLVMAtomicRMWBinOpSub;  ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_sub_relaxed:  op = LLVMAtomicRMWBinOpSub;  ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_and:          op = LLVMAtomicRMWBinOpAnd;  ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_and_acq:      op = LLVMAtomicRMWBinOpAnd;  ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_and_rel:      op = LLVMAtomicRMWBinOpAnd;  ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_and_acqrel:   op = LLVMAtomicRMWBinOpAnd;  ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_and_relaxed:  op = LLVMAtomicRMWBinOpAnd;  ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_nand:         op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_nand_acq:     op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_nand_rel:     op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_nand_acqrel:  op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_or:           op = LLVMAtomicRMWBinOpOr;   ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_or_acq:       op = LLVMAtomicRMWBinOpOr;   ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_or_rel:       op = LLVMAtomicRMWBinOpOr;   ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_or_acqrel:    op = LLVMAtomicRMWBinOpOr;   ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_or_relaxed:   op = LLVMAtomicRMWBinOpOr;   ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_xor:          op = LLVMAtomicRMWBinOpXor;  ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_xor_acq:      op = LLVMAtomicRMWBinOpXor;  ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_xor_rel:      op = LLVMAtomicRMWBinOpXor;  ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_xor_acqrel:   op = LLVMAtomicRMWBinOpXor;  ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_xor_relaxed:  op = LLVMAtomicRMWBinOpXor;  ordering = LLVMAtomicOrderingMonotonic; break;
+		case BuiltinProc_atomic_xchg:         op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+		case BuiltinProc_atomic_xchg_acq:     op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
+		case BuiltinProc_atomic_xchg_rel:     op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
+		case BuiltinProc_atomic_xchg_acqrel:  op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
+		case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
+		}
+
+		LLVMValueRef instr = LLVMBuildAtomicRMW(p->builder, op, dst.value, val.value, ordering, false);
+		return {};
+	}
+
+	case BuiltinProc_atomic_cxchg:
+	case BuiltinProc_atomic_cxchg_acq:
+	case BuiltinProc_atomic_cxchg_rel:
+	case BuiltinProc_atomic_cxchg_acqrel:
+	case BuiltinProc_atomic_cxchg_relaxed:
+	case BuiltinProc_atomic_cxchg_failrelaxed:
+	case BuiltinProc_atomic_cxchg_failacq:
+	case BuiltinProc_atomic_cxchg_acq_failrelaxed:
+	case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
+	case BuiltinProc_atomic_cxchgweak:
+	case BuiltinProc_atomic_cxchgweak_acq:
+	case BuiltinProc_atomic_cxchgweak_rel:
+	case BuiltinProc_atomic_cxchgweak_acqrel:
+	case BuiltinProc_atomic_cxchgweak_relaxed:
+	case BuiltinProc_atomic_cxchgweak_failrelaxed:
+	case BuiltinProc_atomic_cxchgweak_failacq:
+	case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
+	case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: {
+		Type *type = expr->tav.type;
+
+		lbValue address = lb_build_expr(p, ce->args[0]);
+		Type *elem = type_deref(address.type);
+		lbValue old_value = lb_build_expr(p, ce->args[1]);
+		lbValue new_value = lb_build_expr(p, ce->args[2]);
+		old_value = lb_emit_conv(p, old_value, elem);
+		new_value = lb_emit_conv(p, new_value, elem);
+
+		LLVMAtomicOrdering success_ordering = {};
+		LLVMAtomicOrdering failure_ordering = {};
+		LLVMBool weak = false;
+
+		switch (id) {
+		case BuiltinProc_atomic_cxchg:                        success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+		case BuiltinProc_atomic_cxchg_acq:                    success_ordering = LLVMAtomicOrderingAcquire;                failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+		case BuiltinProc_atomic_cxchg_rel:                    success_ordering = LLVMAtomicOrderingRelease;                failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+		case BuiltinProc_atomic_cxchg_acqrel:                 success_ordering = LLVMAtomicOrderingAcquireRelease;         failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+		case BuiltinProc_atomic_cxchg_relaxed:                success_ordering = LLVMAtomicOrderingMonotonic;              failure_ordering = LLVMAtomicOrderingMonotonic;              weak = false; break;
+		case BuiltinProc_atomic_cxchg_failrelaxed:            success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic;              weak = false; break;
+		case BuiltinProc_atomic_cxchg_failacq:                success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire;                weak = false; break;
+		case BuiltinProc_atomic_cxchg_acq_failrelaxed:        success_ordering = LLVMAtomicOrderingAcquire;                failure_ordering = LLVMAtomicOrderingMonotonic;              weak = false; break;
+		case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:     success_ordering = LLVMAtomicOrderingAcquireRelease;         failure_ordering = LLVMAtomicOrderingMonotonic;              weak = false; break;
+		case BuiltinProc_atomic_cxchgweak:                    success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+		case BuiltinProc_atomic_cxchgweak_acq:                success_ordering = LLVMAtomicOrderingAcquire;                failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_rel:                success_ordering = LLVMAtomicOrderingRelease;                failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_acqrel:             success_ordering = LLVMAtomicOrderingAcquireRelease;         failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_relaxed:            success_ordering = LLVMAtomicOrderingMonotonic;              failure_ordering = LLVMAtomicOrderingMonotonic;              weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_failrelaxed:        success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic;              weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_failacq:            success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire;                weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:    success_ordering = LLVMAtomicOrderingAcquire;                failure_ordering = LLVMAtomicOrderingMonotonic;              weak = true;  break;
+		case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease;         failure_ordering = LLVMAtomicOrderingMonotonic;              weak = true;  break;
+		}
+
+		// TODO(bill): Figure out how to make it weak
+		LLVMBool single_threaded = !weak;
+
+		LLVMValueRef instr = LLVMBuildAtomicCmpXchg(p->builder, address.value,
+		                                            old_value.value, new_value.value,
+		                                            success_ordering,
+		                                            failure_ordering,
+		                                            single_threaded);
+
+		return {};
+	}
+	}
+
+	GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
+	return {};
+}
+
+
+lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
+	lbModule *m = p->module;
+
+	TypeAndValue tv = type_and_value_of_expr(expr);
+
+	ast_node(ce, CallExpr, expr);
+
+	TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
+	AddressingMode proc_mode = proc_tv.mode;
+	if (proc_mode == Addressing_Type) {
+		GB_ASSERT(ce->args.count == 1);
+		lbValue x = lb_build_expr(p, ce->args[0]);
+		lbValue y = lb_emit_conv(p, x, tv.type);
+		return y;
+	}
+
+	Ast *pexpr = unparen_expr(ce->proc);
+	if (proc_mode == Addressing_Builtin) {
+		Entity *e = entity_of_node(pexpr);
+		BuiltinProcId id = BuiltinProc_Invalid;
+		if (e != nullptr) {
+			id = cast(BuiltinProcId)e->Builtin.id;
+		} else {
+			id = BuiltinProc_DIRECTIVE;
+		}
+		return lb_build_builtin_proc(p, expr, tv, id);
+	}
+
+	// NOTE(bill): Regular call
+	lbValue value = {};
+	Ast *proc_expr = unparen_expr(ce->proc);
+	if (proc_expr->tav.mode == Addressing_Constant) {
+		ExactValue v = proc_expr->tav.value;
+		switch (v.kind) {
+		case ExactValue_Integer:
+			{
+				u64 u = big_int_to_u64(&v.value_integer);
+				lbValue x = {};
+				x.value = LLVMConstInt(lb_type(m, t_uintptr), u, false);
+				x.type = t_uintptr;
+				x = lb_emit_conv(p, x, t_rawptr);
+				value = lb_emit_conv(p, x, proc_expr->tav.type);
+				break;
+			}
+		case ExactValue_Pointer:
+			{
+				u64 u = cast(u64)v.value_pointer;
+				lbValue x = {};
+				x.value = LLVMConstInt(lb_type(m, t_uintptr), u, false);
+				x.type = t_uintptr;
+				x = lb_emit_conv(p, x, t_rawptr);
+				value = lb_emit_conv(p, x, proc_expr->tav.type);
+				break;
+			}
+		}
+	}
+
+	if (value.value == nullptr) {
+		value = lb_build_expr(p, proc_expr);
+	}
+
+	GB_ASSERT(value.value != nullptr);
+	Type *proc_type_ = base_type(value.type);
+	GB_ASSERT(proc_type_->kind == Type_Proc);
+	TypeProc *pt = &proc_type_->Proc;
+	set_procedure_abi_types(heap_allocator(), proc_type_);
+
+	if (is_call_expr_field_value(ce)) {
+		auto args = array_make<lbValue>(heap_allocator(), pt->param_count);
+
+		for_array(arg_index, ce->args) {
+			Ast *arg = ce->args[arg_index];
+			ast_node(fv, FieldValue, arg);
+			GB_ASSERT(fv->field->kind == Ast_Ident);
+			String name = fv->field->Ident.token.string;
+			isize index = lookup_procedure_parameter(pt, name);
+			GB_ASSERT(index >= 0);
+			TypeAndValue tav = type_and_value_of_expr(fv->value);
+			if (tav.mode == Addressing_Type) {
+				args[index] = lb_const_nil(m, tav.type);
+			} else {
+				args[index] = lb_build_expr(p, fv->value);
+			}
+		}
+		TypeTuple *params = &pt->params->Tuple;
+		for (isize i = 0; i < args.count; i++) {
+			Entity *e = params->variables[i];
+			if (e->kind == Entity_TypeName) {
+				args[i] = lb_const_nil(m, e->type);
+			} else if (e->kind == Entity_Constant) {
+				continue;
+			} else {
+				GB_ASSERT(e->kind == Entity_Variable);
+				if (args[i].value == nullptr) {
+					switch (e->Variable.param_value.kind) {
+					case ParameterValue_Constant:
+						args[i] = lb_const_value(p->module, e->type, e->Variable.param_value.value);
+						break;
+					case ParameterValue_Nil:
+						args[i] = lb_const_nil(m, e->type);
+						break;
+					case ParameterValue_Location:
+						args[i] = lb_emit_source_code_location(p, p->entity->token.string, ast_token(expr).pos);
+						break;
+					case ParameterValue_Value:
+						args[i] = lb_build_expr(p, e->Variable.param_value.ast_value);
+						break;
+					}
+				} else {
+					args[i] = lb_emit_conv(p, args[i], e->type);
+				}
+			}
+		}
+
+		for (isize i = 0; i < args.count; i++) {
+			Entity *e = params->variables[i];
+			if (args[i].type == nullptr) {
+				continue;
+			} else if (is_type_untyped_nil(args[i].type)) {
+				args[i] = lb_const_nil(m, e->type);
+			} else if (is_type_untyped_undef(args[i].type)) {
+				args[i] = lb_const_undef(m, e->type);
+			}
+		}
+
+		return lb_emit_call(p, value, args, ce->inlining, p->return_ptr_hint_ast == expr);
+	}
+
+	isize arg_index = 0;
+
+	isize arg_count = 0;
+	for_array(i, ce->args) {
+		Ast *arg = ce->args[i];
+		TypeAndValue tav = type_and_value_of_expr(arg);
+		GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
+		GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
+		Type *at = tav.type;
+		if (at->kind == Type_Tuple) {
+			arg_count += at->Tuple.variables.count;
+		} else {
+			arg_count++;
+		}
+	}
+
+	isize param_count = 0;
+	if (pt->params) {
+		GB_ASSERT(pt->params->kind == Type_Tuple);
+		param_count = pt->params->Tuple.variables.count;
+	}
+
+	auto args = array_make<lbValue>(heap_allocator(), cast(isize)gb_max(param_count, arg_count));
+	isize variadic_index = pt->variadic_index;
+	bool variadic = pt->variadic && variadic_index >= 0;
+	bool vari_expand = ce->ellipsis.pos.line != 0;
+	bool is_c_vararg = pt->c_vararg;
+
+	String proc_name = {};
+	if (p->entity != nullptr) {
+		proc_name = p->entity->token.string;
+	}
+	TokenPos pos = ast_token(ce->proc).pos;
+
+	TypeTuple *param_tuple = nullptr;
+	if (pt->params) {
+		GB_ASSERT(pt->params->kind == Type_Tuple);
+		param_tuple = &pt->params->Tuple;
+	}
+
+	for_array(i, ce->args) {
+		Ast *arg = ce->args[i];
+		TypeAndValue arg_tv = type_and_value_of_expr(arg);
+		if (arg_tv.mode == Addressing_Type) {
+			args[arg_index++] = lb_const_nil(m, arg_tv.type);
+		} else {
+			lbValue a = lb_build_expr(p, arg);
+			Type *at = a.type;
+			if (at->kind == Type_Tuple) {
+				for_array(i, at->Tuple.variables) {
+					Entity *e = at->Tuple.variables[i];
+					lbValue v = lb_emit_struct_ev(p, a, cast(i32)i);
+					args[arg_index++] = v;
+				}
+			} else {
+				args[arg_index++] = a;
+			}
+		}
+	}
+
+
+	if (param_count > 0) {
+		GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
+		GB_ASSERT(param_count < 1000000);
+
+		if (arg_count < param_count) {
+			isize end = cast(isize)param_count;
+			if (variadic) {
+				end = variadic_index;
+			}
+			while (arg_index < end) {
+				Entity *e = param_tuple->variables[arg_index];
+				GB_ASSERT(e->kind == Entity_Variable);
+
+				switch (e->Variable.param_value.kind) {
+				case ParameterValue_Constant:
+					args[arg_index++] = lb_const_value(p->module, e->type, e->Variable.param_value.value);
+					break;
+				case ParameterValue_Nil:
+					args[arg_index++] = lb_const_nil(m, e->type);
+					break;
+				case ParameterValue_Location:
+					args[arg_index++] = lb_emit_source_code_location(p, proc_name, pos);
+					break;
+				case ParameterValue_Value:
+					args[arg_index++] = lb_build_expr(p, e->Variable.param_value.ast_value);
+					break;
+				}
+			}
+		}
+
+		if (is_c_vararg) {
+			GB_ASSERT(variadic);
+			GB_ASSERT(!vari_expand);
+			isize i = 0;
+			for (; i < variadic_index; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					args[i] = lb_emit_conv(p, args[i], e->type);
+				}
+			}
+			Type *variadic_type = param_tuple->variables[i]->type;
+			GB_ASSERT(is_type_slice(variadic_type));
+			variadic_type = base_type(variadic_type)->Slice.elem;
+			if (!is_type_any(variadic_type)) {
+				for (; i < arg_count; i++) {
+					args[i] = lb_emit_conv(p, args[i], variadic_type);
+				}
+			} else {
+				for (; i < arg_count; i++) {
+					args[i] = lb_emit_conv(p, args[i], default_type(args[i].type));
+				}
+			}
+		} else if (variadic) {
+			isize i = 0;
+			for (; i < variadic_index; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					args[i] = lb_emit_conv(p, args[i], e->type);
+				}
+			}
+			if (!vari_expand) {
+				Type *variadic_type = param_tuple->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] = lb_emit_conv(p, args[i], variadic_type);
+				}
+			}
+		} else {
+			for (isize i = 0; i < param_count; i++) {
+				Entity *e = param_tuple->variables[i];
+				if (e->kind == Entity_Variable) {
+					if (args[i].value == nullptr) {
+						continue;
+					}
+					GB_ASSERT_MSG(args[i].value != nullptr, "%.*s", LIT(e->token.string));
+					args[i] = lb_emit_conv(p, args[i], e->type);
+				}
+			}
+		}
+
+		if (variadic && !vari_expand && !is_c_vararg) {
+			// variadic call argument generation
+			gbAllocator allocator = heap_allocator();
+			Type *slice_type = param_tuple->variables[variadic_index]->type;
+			Type *elem_type  = base_type(slice_type)->Slice.elem;
+			lbAddr slice = lb_add_local_generated(p, slice_type, true);
+			isize slice_len = arg_count+1 - (variadic_index+1);
+
+			if (slice_len > 0) {
+				lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+
+				for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
+					lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)j);
+					lb_emit_store(p, addr, args[i]);
+				}
+
+				lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
+				lbValue len = lb_const_int(m, t_int, slice_len);
+				lb_fill_slice(p, slice, base_elem, len);
+			}
+
+			arg_count = param_count;
+			args[variadic_index] = lb_addr_load(p, slice);
+		}
+	}
+
+	if (variadic && variadic_index+1 < param_count) {
+		for (isize i = variadic_index+1; i < param_count; i++) {
+			Entity *e = param_tuple->variables[i];
+			switch (e->Variable.param_value.kind) {
+			case ParameterValue_Constant:
+				args[i] = lb_const_value(p->module, e->type, e->Variable.param_value.value);
+				break;
+			case ParameterValue_Nil:
+				args[i] = lb_const_nil(m, e->type);
+				break;
+			case ParameterValue_Location:
+				args[i] = lb_emit_source_code_location(p, proc_name, pos);
+				break;
+			case ParameterValue_Value:
+				args[i] = lb_build_expr(p, e->Variable.param_value.ast_value);
+				break;
+			}
+		}
+	}
+
+	isize final_count = param_count;
+	if (is_c_vararg) {
+		final_count = arg_count;
+	}
+
+	if (param_tuple != nullptr) {
+		for (isize i = 0; i < gb_min(args.count, param_tuple->variables.count); i++) {
+			Entity *e = param_tuple->variables[i];
+			if (args[i].type == nullptr) {
+				continue;
+			} else if (is_type_untyped_nil(args[i].type)) {
+				args[i] = lb_const_nil(m, e->type);
+			} else if (is_type_untyped_undef(args[i].type)) {
+				args[i] = lb_const_undef(m, e->type);
+			}
+		}
+	}
+
+	auto call_args = array_slice(args, 0, final_count);
+	return lb_emit_call(p, value, call_args, ce->inlining, p->return_ptr_hint_ast == expr);
+}
+
+bool lb_is_const(lbValue value) {
+	LLVMValueRef v = value.value;
+	if (is_type_untyped_nil(value.type) || is_type_untyped_undef(value.type)) {
+		// TODO(bill): Is this correct behaviour?
+		return true;
+	}
+	if (LLVMIsConstant(v)) {
+		return true;
+	}
+	return false;
+}
+bool lb_is_const_nil(lbValue value) {
+	LLVMValueRef v = value.value;
+	if (LLVMIsConstant(v)) {
+		if (LLVMIsAConstantAggregateZero(v)) {
+			return true;
+		} else if (LLVMIsAConstantPointerNull(v)) {
+			return true;
+		}
+	}
+	return false;
+}
+
+String lb_get_const_string(lbModule *m, lbValue value) {
+	GB_ASSERT(lb_is_const(value));
+
+	Type *t = base_type(value.type);
+	GB_ASSERT(are_types_identical(t, t_string));
+
+
+
+	unsigned     ptr_indices[1] = {0};
+	unsigned     len_indices[1] = {1};
+	LLVMValueRef underlying_ptr = LLVMConstExtractValue(value.value, ptr_indices, gb_count_of(ptr_indices));
+	LLVMValueRef underlying_len = LLVMConstExtractValue(value.value, len_indices, gb_count_of(len_indices));
+
+	GB_ASSERT(LLVMGetConstOpcode(underlying_ptr) == LLVMGetElementPtr);
+	underlying_ptr = LLVMGetOperand(underlying_ptr, 0);
+	GB_ASSERT(LLVMIsAGlobalVariable(underlying_ptr));
+	underlying_ptr = LLVMGetInitializer(underlying_ptr);
+
+	size_t length = 0;
+	char const *text = LLVMGetAsString(underlying_ptr, &length);
+
+	isize real_length = cast(isize)LLVMConstIntGetSExtValue(underlying_len);
+
+	return make_string(cast(u8 const *)text, real_length);
+}
+
+
+void lb_emit_increment(lbProcedure *p, lbValue addr) {
+	GB_ASSERT(is_type_pointer(addr.type));
+	Type *type = type_deref(addr.type);
+	lbValue v_one = lb_const_value(p->module, type, exact_value_i64(1));
+	lb_emit_store(p, addr, lb_emit_arith(p, Token_Add, lb_emit_load(p, addr), v_one, type));
+
+}
+
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
+	Type *vt = core_type(value.type);
+	GB_ASSERT(type_size_of(vt) == type_size_of(platform_type));
+	GB_ASSERT(is_type_integer(vt));
+
+	// TODO(bill): lb_emit_byte_swap
+	lbValue res = {};
+	res.type = platform_type;
+	res.value = value.value;
+
+	// int sz = cast(int)type_size_of(vt);
+	// if (sz > 1) {
+	// 	char buf[32] = {};
+	// 	gb_snprintf(buf, gb_count_of(buf), "llvm.bswap.i%d", sz*8);
+	// 	unsigned id = LLVMLookupIntrinsicID(buf, gb_strlen(buf));
+	// 	gb_printf(">>> %s %u\n", buf, id);
+
+	// 	LLVMTypeRef types[2] = {};
+	// 	types[0] = lb_type(p->module, value.type);
+	// 	types[1] = lb_type(p->module, value.type);
+
+	// 	LLVMValueRef fn = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+	// 	res.value = LLVMBuildCall(p->builder, fn, &value.value, 1, "");
+	// }
+
+	return res;
+}
+
+
+lbLoopData lb_loop_start(lbProcedure *p, isize count, Type *index_type) {
+	lbLoopData data = {};
+
+	lbValue max = lb_const_int(p->module, t_int, count);
+
+	data.idx_addr = lb_add_local_generated(p, index_type, true);
+
+	data.body = lb_create_block(p, "loop.body");
+	data.done = lb_create_block(p, "loop.done");
+	data.loop = lb_create_block(p, "loop.loop");
+
+	lb_emit_jump(p, data.loop);
+	lb_start_block(p, data.loop);
+
+	data.idx = lb_addr_load(p, data.idx_addr);
+
+	lbValue cond = lb_emit_comp(p, Token_Lt, data.idx, max);
+	lb_emit_if(p, cond, data.body, data.done);
+	lb_start_block(p, data.body);
+
+	return data;
+}
+
+void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
+	if (data.idx_addr.addr.value != nullptr) {
+		lb_emit_increment(p, data.idx_addr.addr);
+		lb_emit_jump(p, data.loop);
+		lb_start_block(p, data.done);
+	}
+}
+
+lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
+	lbValue res = {};
+	res.type = t_llvm_bool;
+	Type *t = x.type;
+	if (is_type_pointer(t)) {
+		if (op_kind == Token_CmpEq) {
+			res.value = LLVMBuildIsNull(p->builder, x.value, "");
+		} else if (op_kind == Token_NotEq) {
+			res.value = LLVMBuildIsNotNull(p->builder, x.value, "");
+		}
+		return res;
+	} else if (is_type_cstring(t)) {
+		lbValue ptr = lb_emit_conv(p, x, t_u8_ptr);
+		if (op_kind == Token_CmpEq) {
+			res.value = LLVMBuildIsNull(p->builder, ptr.value, "");
+		} else if (op_kind == Token_NotEq) {
+			res.value = LLVMBuildIsNotNull(p->builder, ptr.value, "");
+		}
+		return res;
+	} else if (is_type_any(t)) {
+		lbValue data = lb_emit_struct_ev(p, x, 0);
+		lbValue ti   = lb_emit_struct_ev(p, x, 1);
+		if (op_kind == Token_CmpEq) {
+			LLVMValueRef a =  LLVMBuildIsNull(p->builder, data.value, "");
+			LLVMValueRef b =  LLVMBuildIsNull(p->builder, ti.value, "");
+			res.value = LLVMBuildOr(p->builder, a, b, "");
+			return res;
+		} else if (op_kind == Token_NotEq) {
+			LLVMValueRef a =  LLVMBuildIsNotNull(p->builder, data.value, "");
+			LLVMValueRef b =  LLVMBuildIsNotNull(p->builder, ti.value, "");
+			res.value = LLVMBuildAnd(p->builder, a, b, "");
+			return res;
+		}
+	} else if (is_type_slice(t)) {
+		lbValue data = lb_emit_struct_ev(p, x, 0);
+		lbValue cap  = lb_emit_struct_ev(p, x, 1);
+		if (op_kind == Token_CmpEq) {
+			LLVMValueRef a = LLVMBuildIsNull(p->builder, data.value, "");
+			LLVMValueRef b = LLVMBuildIsNull(p->builder, cap.value, "");
+			res.value = LLVMBuildOr(p->builder, a, b, "");
+			return res;
+		} else if (op_kind == Token_NotEq) {
+			LLVMValueRef a = LLVMBuildIsNotNull(p->builder, data.value, "");
+			LLVMValueRef b = LLVMBuildIsNotNull(p->builder, cap.value, "");
+			res.value = LLVMBuildAnd(p->builder, a, b, "");
+			return res;
+		}
+	} else if (is_type_dynamic_array(t)) {
+		lbValue data = lb_emit_struct_ev(p, x, 0);
+		lbValue cap  = lb_emit_struct_ev(p, x, 2);
+		if (op_kind == Token_CmpEq) {
+			LLVMValueRef a = LLVMBuildIsNull(p->builder, data.value, "");
+			LLVMValueRef b = LLVMBuildIsNull(p->builder, cap.value, "");
+			res.value = LLVMBuildOr(p->builder, a, b, "");
+			return res;
+		} else if (op_kind == Token_NotEq) {
+			LLVMValueRef a = LLVMBuildIsNotNull(p->builder, data.value, "");
+			LLVMValueRef b = LLVMBuildIsNotNull(p->builder, cap.value, "");
+			res.value = LLVMBuildAnd(p->builder, a, b, "");
+			return res;
+		}
+	} else if (is_type_map(t)) {
+		lbValue cap = lb_map_cap(p, x);
+		return lb_emit_comp(p, op_kind, cap, lb_zero(p->module, cap.type));
+	} else if (is_type_union(t)) {
+		if (type_size_of(t) == 0) {
+			if (op_kind == Token_CmpEq) {
+				return lb_const_bool(p->module, t_llvm_bool, true);
+			} else if (op_kind == Token_NotEq) {
+				return lb_const_bool(p->module, t_llvm_bool, false);
+			}
+		} else {
+			lbValue tag = lb_emit_union_tag_value(p, x);
+			return lb_emit_comp(p, op_kind, tag, lb_zero(p->module, tag.type));
+		}
+	} else if (is_type_typeid(t)) {
+		lbValue invalid_typeid = lb_const_value(p->module, t_typeid, exact_value_i64(0));
+		return lb_emit_comp(p, op_kind, x, invalid_typeid);
+	} else if (is_type_bit_field(t)) {
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		lbValue lhs = lb_address_from_load_or_generate_local(p, x);
+		args[0] = lb_emit_conv(p, lhs, t_rawptr);
+		args[1] = lb_const_int(p->module, t_int, type_size_of(t));
+		lbValue val = lb_emit_runtime_call(p, "memory_compare_zero", args);
+		lbValue res = lb_emit_comp(p, op_kind, val, lb_const_int(p->module, t_int, 0));
+		return res;
+	} else if (is_type_soa_struct(t)) {
+		GB_PANIC("#soa struct nil comparison");
+		// Type *bt = base_type(t);
+		// if (bt->Struct.soa_kind == StructSoa_Slice) {
+		// 	lbValue len  = lb_soa_struct_len(p, x);
+		// 	if (bt->Struct.fields.count > 1) {
+		// 		lbValue data = lb_emit_struct_ev(p, x, 0);
+		// 		if (op_kind == Token_CmpEq) {
+		// 			lbValue a = lb_emit_comp(p, Token_CmpEq, data, v_raw_nil);
+		// 			lbValue b = lb_emit_comp(p, Token_CmpEq, len, v_zero);
+		// 			return lb_emit_arith(p, Token_Or, a, b, t_bool);
+		// 		} else if (op_kind == Token_NotEq) {
+		// 			lbValue a = lb_emit_comp(p, Token_NotEq, data, v_raw_nil);
+		// 			lbValue b = lb_emit_comp(p, Token_NotEq, len, v_zero);
+		// 			return lb_emit_arith(p, Token_And, a, b, t_bool);
+		// 		}
+		// 	} else {
+		// 		return lb_emit_comp(p, op_kind, len, v_zero);
+		// 	}
+		// } else if (bt->Struct.soa_kind == StructSoa_Dynamic) {
+		// 	lbValue cap  = lb_soa_struct_len(p, x);
+		// 	if (bt->Struct.fields.count > 1) {
+		// 		lbValue data = lb_emit_struct_ev(p, x, 0);
+		// 		if (op_kind == Token_CmpEq) {
+		// 			lbValue a = lb_emit_comp(p, Token_CmpEq, data, v_raw_nil);
+		// 			lbValue b = lb_emit_comp(p, Token_CmpEq, cap, v_zero);
+		// 			return lb_emit_arith(p, Token_Or, a, b, t_bool);
+		// 		} else if (op_kind == Token_NotEq) {
+		// 			lbValue a = lb_emit_comp(p, Token_NotEq, data, v_raw_nil);
+		// 			lbValue b = lb_emit_comp(p, Token_NotEq, cap, v_zero);
+		// 			return lb_emit_arith(p, Token_And, a, b, t_bool);
+		// 		}
+		// 	} else {
+		// 		return lb_emit_comp(p, op_kind, cap, v_zero);
+		// 	}
+		// }
+	}
+	return {};
+}
+
+
+lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
+	Type *a = base_type(left.type);
+	Type *b = base_type(right.type);
+
+	GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
+
+	lbValue nil_check = {};
+	if (is_type_untyped_nil(left.type)) {
+		nil_check = lb_emit_comp_against_nil(p, op_kind, right);
+	} else if (is_type_untyped_nil(right.type)) {
+		nil_check = lb_emit_comp_against_nil(p, op_kind, left);
+	}
+	if (nil_check.value != nullptr) {
+		return nil_check;
+	}
+
+	if (are_types_identical(a, b)) {
+		// NOTE(bill): No need for a conversion
+	} else if (lb_is_const(left) || lb_is_const_nil(left)) {
+		left = lb_emit_conv(p, left, right.type);
+	} else if (lb_is_const(right) || lb_is_const_nil(right)) {
+		right = lb_emit_conv(p, right, left.type);
+	} else {
+		gbAllocator a = heap_allocator();
+
+		Type *lt = left.type;
+		Type *rt = right.type;
+
+		if (is_type_bit_set(lt) && is_type_bit_set(rt)) {
+			Type *blt = base_type(lt);
+			Type *brt = base_type(rt);
+			GB_ASSERT(is_type_bit_field_value(blt));
+			GB_ASSERT(is_type_bit_field_value(brt));
+			i64 bits = gb_max(blt->BitFieldValue.bits, brt->BitFieldValue.bits);
+			i64 bytes = bits / 8;
+			switch (bytes) {
+			case 1:
+				left = lb_emit_conv(p, left, t_u8);
+				right = lb_emit_conv(p, right, t_u8);
+				break;
+			case 2:
+				left = lb_emit_conv(p, left, t_u16);
+				right = lb_emit_conv(p, right, t_u16);
+				break;
+			case 4:
+				left = lb_emit_conv(p, left, t_u32);
+				right = lb_emit_conv(p, right, t_u32);
+				break;
+			case 8:
+				left = lb_emit_conv(p, left, t_u64);
+				right = lb_emit_conv(p, right, t_u64);
+				break;
+			default: GB_PANIC("Unknown integer size"); break;
+			}
+		}
+
+		lt = left.type;
+		rt = right.type;
+		i64 ls = type_size_of(lt);
+		i64 rs = type_size_of(rt);
+		if (ls < rs) {
+			left = lb_emit_conv(p, left, rt);
+		} else if (ls > rs) {
+			right = lb_emit_conv(p, right, lt);
+		} else {
+			right = lb_emit_conv(p, right, lt);
+		}
+	}
+
+	if (is_type_array(a)) {
+		Type *tl = base_type(a);
+		lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+		lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+		TokenKind cmp_op = Token_And;
+		lbValue res = lb_const_bool(p->module, t_llvm_bool, true);
+		if (op_kind == Token_NotEq) {
+			res = lb_const_bool(p->module, t_llvm_bool, false);
+			cmp_op = Token_Or;
+		} else if (op_kind == Token_CmpEq) {
+			res = lb_const_bool(p->module, t_llvm_bool, true);
+			cmp_op = Token_And;
+		}
+
+		bool inline_array_arith = type_size_of(tl) <= build_context.max_align;
+		i32 count = cast(i32)tl->Array.count;
+
+		if (inline_array_arith) {
+			// inline
+			lbAddr val = lb_add_local_generated(p, t_bool, false);
+			lb_addr_store(p, val, res);
+			for (i32 i = 0; i < count; i++) {
+				lbValue x = lb_emit_load(p, lb_emit_array_epi(p, lhs, i));
+				lbValue y = lb_emit_load(p, lb_emit_array_epi(p, rhs, i));
+				lbValue cmp = lb_emit_comp(p, op_kind, x, y);
+				lbValue new_res = lb_emit_arith(p, cmp_op, lb_addr_load(p, val), cmp, t_bool);
+				lb_addr_store(p, val, lb_emit_conv(p, new_res, t_bool));
+			}
+
+			return lb_addr_load(p, val);
+		} else {
+			if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+				// TODO(bill): Test to see if this is actually faster!!!!
+				auto args = array_make<lbValue>(heap_allocator(), 3);
+				args[0] = lb_emit_conv(p, lhs, t_rawptr);
+				args[1] = lb_emit_conv(p, rhs, t_rawptr);
+				args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+				lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+				lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+				return lb_emit_conv(p, res, t_bool);
+			} else {
+				lbAddr val = lb_add_local_generated(p, t_bool, false);
+				lb_addr_store(p, val, res);
+				auto loop_data = lb_loop_start(p, count, t_i32);
+				{
+					lbValue i = loop_data.idx;
+					lbValue x = lb_emit_load(p, lb_emit_array_ep(p, lhs, i));
+					lbValue y = lb_emit_load(p, lb_emit_array_ep(p, rhs, i));
+					lbValue cmp = lb_emit_comp(p, op_kind, x, y);
+					lbValue new_res = lb_emit_arith(p, cmp_op, lb_addr_load(p, val), cmp, t_bool);
+					lb_addr_store(p, val, lb_emit_conv(p, new_res, t_bool));
+				}
+				lb_loop_end(p, loop_data);
+
+				return lb_addr_load(p, val);
+			}
+		}
+	}
+
+	if (is_type_string(a)) {
+		if (is_type_cstring(a)) {
+			left  = lb_emit_conv(p, left, t_string);
+			right = lb_emit_conv(p, right, t_string);
+		}
+
+		char const *runtime_procedure = nullptr;
+		switch (op_kind) {
+		case Token_CmpEq: runtime_procedure = "string_eq"; break;
+		case Token_NotEq: runtime_procedure = "string_ne"; break;
+		case Token_Lt:    runtime_procedure = "string_lt"; break;
+		case Token_Gt:    runtime_procedure = "string_gt"; break;
+		case Token_LtEq:  runtime_procedure = "string_le"; break;
+		case Token_GtEq:  runtime_procedure = "string_gt"; break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return lb_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_complex(a)) {
+		char const *runtime_procedure = "";
+		i64 sz = 8*type_size_of(a);
+		switch (sz) {
+		case 64:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "complex64_eq"; break;
+			case Token_NotEq: runtime_procedure = "complex64_ne"; break;
+			}
+			break;
+		case 128:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "complex128_eq"; break;
+			case Token_NotEq: runtime_procedure = "complex128_ne"; break;
+			}
+			break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return lb_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_quaternion(a)) {
+		char const *runtime_procedure = "";
+		i64 sz = 8*type_size_of(a);
+		switch (sz) {
+		case 128:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "quaternion128_eq"; break;
+			case Token_NotEq: runtime_procedure = "quaternion128_ne"; break;
+			}
+			break;
+		case 256:
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "quaternion256_eq"; break;
+			case Token_NotEq: runtime_procedure = "quaternion256_ne"; break;
+			}
+			break;
+		}
+		GB_ASSERT(runtime_procedure != nullptr);
+
+		auto args = array_make<lbValue>(heap_allocator(), 2);
+		args[0] = left;
+		args[1] = right;
+		return lb_emit_runtime_call(p, runtime_procedure, args);
+	}
+
+	if (is_type_bit_set(a)) {
+		switch (op_kind) {
+		case Token_Lt:
+		case Token_LtEq:
+		case Token_Gt:
+		case Token_GtEq:
+			{
+				Type *it = bit_set_to_int(a);
+				lbValue lhs = lb_emit_transmute(p, left, it);
+				lbValue rhs = lb_emit_transmute(p, right, it);
+				lbValue res = lb_emit_arith(p, Token_And, lhs, rhs, it);
+
+				if (op_kind == Token_Lt || op_kind == Token_LtEq) {
+					// (lhs & rhs) == lhs
+					res.value = LLVMBuildICmp(p->builder, LLVMIntEQ, res.value, lhs.value, "");
+					res.type = t_llvm_bool;
+				} else if (op_kind == Token_Gt || op_kind == Token_GtEq) {
+					// (lhs & rhs) == rhs
+					res.value = LLVMBuildICmp(p->builder, LLVMIntEQ, res.value, rhs.value, "");
+					res.type = t_llvm_bool;
+				}
+
+				// NOTE(bill): Strict subsets
+				if (op_kind == Token_Lt || op_kind == Token_Gt) {
+					// res &~ (lhs == rhs)
+					lbValue eq = {};
+					eq.value = LLVMBuildICmp(p->builder, LLVMIntEQ, lhs.value, rhs.value, "");
+					eq.type = t_llvm_bool;
+					res = lb_emit_arith(p, Token_AndNot, res, eq, t_llvm_bool);
+				}
+
+				return res;
+			}
+
+		case Token_CmpEq:
+		case Token_NotEq:
+			{
+				LLVMIntPredicate pred = {};
+				switch (op_kind) {
+				case Token_CmpEq: pred = LLVMIntEQ;  break;
+				case Token_NotEq: pred = LLVMIntNE;  break;
+				}
+				lbValue res = {};
+				res.type = t_llvm_bool;
+				res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+				return res;
+			}
+		}
+	}
+
+	if (op_kind != Token_CmpEq && op_kind != Token_NotEq) {
+		Type *t = left.type;
+		if (is_type_integer(t) && is_type_different_to_arch_endianness(t)) {
+			Type *platform_type = integer_endian_type_to_platform_type(t);
+			lbValue x = lb_emit_byte_swap(p, left, platform_type);
+			lbValue y = lb_emit_byte_swap(p, right, platform_type);
+			left = x;
+			right = y;
+		}
+	}
+
+
+	lbValue res = {};
+	res.type = t_llvm_bool;
+	if (is_type_integer(left.type) ||
+	    is_type_boolean(left.type) ||
+	    is_type_pointer(left.type) ||
+	    is_type_proc(left.type) ||
+	    is_type_enum(left.type)) {
+		LLVMIntPredicate pred = {};
+		if (is_type_unsigned(left.type)) {
+			switch (op_kind) {
+			case Token_Gt:   pred = LLVMIntUGT; break;
+			case Token_GtEq: pred = LLVMIntUGE; break;
+			case Token_Lt:   pred = LLVMIntULT; break;
+			case Token_LtEq: pred = LLVMIntULE; break;
+			}
+		} else {
+			switch (op_kind) {
+			case Token_Gt:   pred = LLVMIntSGT; break;
+			case Token_GtEq: pred = LLVMIntSGE; break;
+			case Token_Lt:   pred = LLVMIntSLT; break;
+			case Token_LtEq: pred = LLVMIntSLE; break;
+			}
+		}
+		switch (op_kind) {
+		case Token_CmpEq: pred = LLVMIntEQ;  break;
+		case Token_NotEq: pred = LLVMIntNE;  break;
+		}
+		res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+	} else if (is_type_float(left.type)) {
+		LLVMRealPredicate pred = {};
+		switch (op_kind) {
+		case Token_CmpEq: pred = LLVMRealOEQ; break;
+		case Token_Gt:    pred = LLVMRealOGT; break;
+		case Token_GtEq:  pred = LLVMRealOGE; break;
+		case Token_Lt:    pred = LLVMRealOLT; break;
+		case Token_LtEq:  pred = LLVMRealOLE; break;
+		case Token_NotEq: pred = LLVMRealONE; break;
+		}
+		res.value = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
+	} else if (is_type_typeid(left.type)) {
+		LLVMIntPredicate pred = {};
+		switch (op_kind) {
+		case Token_Gt:   pred = LLVMIntUGT; break;
+		case Token_GtEq: pred = LLVMIntUGE; break;
+		case Token_Lt:   pred = LLVMIntULT; break;
+		case Token_LtEq: pred = LLVMIntULE; break;
+		case Token_CmpEq: pred = LLVMIntEQ;  break;
+		case Token_NotEq: pred = LLVMIntNE;  break;
+		}
+		res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+	} else {
+		GB_PANIC("Unhandled comparison kind %s %.*s %s", type_to_string(left.type), LIT(token_strings[op_kind]), type_to_string(right.type));
+	}
+
+	return res;
+}
+
+
+lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
+	ast_node(pl, ProcLit, expr);
+
+	// NOTE(bill): Generate a new name
+	// parent$count
+	isize name_len = prefix_name.len + 1 + 8 + 1;
+	char *name_text = gb_alloc_array(heap_allocator(), char, name_len);
+	i32 name_id = cast(i32)m->anonymous_proc_lits.entries.count;
+
+	name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id);
+	String name = make_string((u8 *)name_text, name_len-1);
+
+	Type *type = type_of_expr(expr);
+	set_procedure_abi_types(heap_allocator(), type);
+
+
+	Token token = {};
+	token.pos = ast_token(expr).pos;
+	token.kind = Token_Ident;
+	token.string = name;
+	Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
+	e->decl_info = pl->decl;
+	lbProcedure *p = lb_create_procedure(m, e);
+
+	lbValue value = {};
+	value.value = p->value;
+	value.type = p->type;
+
+	array_add(&m->procedures_to_generate, p);
+	if (parent != nullptr) {
+		array_add(&parent->children, p);
+	} else {
+		map_set(&m->members, hash_string(name), value);
+	}
+
+	map_set(&m->anonymous_proc_lits, hash_pointer(expr), p);
+
+	return value;
+}
+
+lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos, bool do_conversion_check=true) {
+	lbModule *m = p->module;
+
+	Type *src_type = value.type;
+	bool is_ptr = is_type_pointer(src_type);
+
+	bool is_tuple = true;
+	Type *tuple = type;
+	if (type->kind != Type_Tuple) {
+		is_tuple = false;
+		tuple = make_optional_ok_type(type);
+	}
+
+	lbAddr v = lb_add_local_generated(p, tuple, true);
+
+	if (is_ptr) {
+		value = lb_emit_load(p, value);
+	}
+	Type *src = base_type(type_deref(src_type));
+	GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type));
+	Type *dst = tuple->Tuple.variables[0]->type;
+
+	lbValue value_  = lb_address_from_load_or_generate_local(p, value);
+
+	lbValue tag = {};
+	lbValue dst_tag = {};
+	lbValue cond = {};
+	lbValue data = {};
+
+	lbValue gep0 = lb_emit_struct_ep(p, v.addr, 0);
+	lbValue gep1 = lb_emit_struct_ep(p, v.addr, 1);
+
+	if (is_type_union_maybe_pointer(src)) {
+		data = lb_emit_load(p, lb_emit_conv(p, value_, gep0.type));
+	} else {
+		tag     = lb_emit_load(p, lb_emit_union_tag_ptr(p, value_));
+		dst_tag = lb_const_union_tag(m, src, dst);
+	}
+
+	lbBlock *ok_block = lb_create_block(p, "union_cast.ok");
+	lbBlock *end_block = lb_create_block(p, "union_cast.end");
+
+	if (data.value != nullptr) {
+		GB_ASSERT(is_type_union_maybe_pointer(src));
+		cond = lb_emit_comp_against_nil(p, Token_NotEq, data);
+	} else {
+		cond = lb_emit_comp(p, Token_CmpEq, tag, dst_tag);
+	}
+
+	lb_emit_if(p, cond, ok_block, end_block);
+	lb_start_block(p, ok_block);
+
+
+
+	if (data.value == nullptr) {
+		data = lb_emit_load(p, lb_emit_conv(p, value_, gep0.type));
+	}
+	lb_emit_store(p, gep0, data);
+	lb_emit_store(p, gep1, lb_const_bool(m, t_bool, true));
+
+	lb_emit_jump(p, end_block);
+	lb_start_block(p, end_block);
+
+	if (!is_tuple) {
+		if (do_conversion_check) {
+			// NOTE(bill): Panic on invalid conversion
+			Type *dst_type = tuple->Tuple.variables[0]->type;
+
+			lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+			auto args = array_make<lbValue>(heap_allocator(), 6);
+			args[0] = ok;
+
+			args[1] = lb_const_string(m, pos.file);
+			args[2] = lb_const_int(m, t_int, pos.line);
+			args[3] = lb_const_int(m, t_int, pos.column);
+
+			args[4] = lb_typeid(m, src_type);
+			args[5] = lb_typeid(m, dst_type);
+			lb_emit_runtime_call(p, "type_assertion_check", args);
+		}
+
+		return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
+	}
+	return lb_addr_load(p, v);
+}
+
+lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+	lbModule *m = p->module;
+
+	Type *src_type = value.type;
+
+	if (is_type_pointer(src_type)) {
+		value = lb_emit_load(p, value);
+	}
+
+	bool is_tuple = true;
+	Type *tuple = type;
+	if (type->kind != Type_Tuple) {
+		is_tuple = false;
+		tuple = make_optional_ok_type(type);
+	}
+	Type *dst_type = tuple->Tuple.variables[0]->type;
+
+	lbAddr v = lb_add_local_generated(p, tuple, true);
+
+	lbValue dst_typeid = lb_typeid(m, dst_type);
+	lbValue any_typeid = lb_emit_struct_ev(p, value, 1);
+
+
+	lbBlock *ok_block = lb_create_block(p, "any_cast.ok");
+	lbBlock *end_block = lb_create_block(p, "any_cast.end");
+	lbValue cond = lb_emit_comp(p, Token_CmpEq, any_typeid, dst_typeid);
+	lb_emit_if(p, cond, ok_block, end_block);
+	lb_start_block(p, ok_block);
+
+	lbValue gep0 = lb_emit_struct_ep(p, v.addr, 0);
+	lbValue gep1 = lb_emit_struct_ep(p, v.addr, 1);
+
+	lbValue any_data = lb_emit_struct_ev(p, value, 0);
+	lbValue ptr = lb_emit_conv(p, any_data, alloc_type_pointer(dst_type));
+	lb_emit_store(p, gep0, lb_emit_load(p, ptr));
+	lb_emit_store(p, gep1, lb_const_bool(m, t_bool, true));
+
+	lb_emit_jump(p, end_block);
+	lb_start_block(p, end_block);
+
+	if (!is_tuple) {
+		// NOTE(bill): Panic on invalid conversion
+
+		lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+		auto args = array_make<lbValue>(heap_allocator(), 6);
+		args[0] = ok;
+
+		args[1] = lb_const_string(m, pos.file);
+		args[2] = lb_const_int(m, t_int, pos.line);
+		args[3] = lb_const_int(m, t_int, pos.column);
+
+		args[4] = any_typeid;
+		args[5] = dst_typeid;
+		lb_emit_runtime_call(p, "type_assertion_check", args);
+
+		return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
+	}
+	return v;
+}
+lbValue lb_emit_any_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) {
+	return lb_addr_load(p, lb_emit_any_cast_addr(p, value, type, pos));
+}
+
+
+lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
+	lbModule *m = p->module;
+
+	expr = unparen_expr(expr);
+
+	TypeAndValue tv = type_and_value_of_expr(expr);
+	GB_ASSERT(tv.mode != Addressing_Invalid);
+	GB_ASSERT(tv.mode != Addressing_Type);
+
+	if (tv.value.kind != ExactValue_Invalid) {
+		// NOTE(bill): Short on constant values
+		return lb_const_value(p->module, tv.type, tv.value);
+	}
+
+
+
+	switch (expr->kind) {
+	case_ast_node(bl, BasicLit, expr);
+		TokenPos pos = bl->token.pos;
+		GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(token_strings[bl->token.kind]));
+	case_end;
+
+	case_ast_node(bd, BasicDirective, expr);
+		TokenPos pos = bd->token.pos;
+		GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(bd->name));
+	case_end;
+
+	case_ast_node(i, Implicit, expr);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(u, Undef, expr)
+		lbValue res = {};
+		if (is_type_untyped(tv.type)) {
+			res.value = nullptr;
+			res.type  = t_untyped_undef;
+		} else {
+			res.value = LLVMGetUndef(lb_type(m, tv.type));
+			res.type  = tv.type;
+		}
+		return res;
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		Entity *e = entity_of_ident(expr);
+		GB_ASSERT_MSG(e != nullptr, "%s", expr_to_string(expr));
+		if (e->kind == Entity_Builtin) {
+			Token token = ast_token(expr);
+			GB_PANIC("TODO(bill): lb_build_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 {};
+		} else if (e->kind == Entity_Nil) {
+			lbValue res = {};
+			res.value = nullptr;
+			res.type = e->type;
+			return res;
+		}
+		GB_ASSERT(e->kind != Entity_ProcGroup);
+
+		auto *found = map_get(&p->module->values, hash_entity(e));
+		if (found) {
+			auto v = *found;
+			// NOTE(bill): This is because pointers are already pointers in LLVM
+			if (is_type_proc(v.type)) {
+				return v;
+			}
+			return lb_emit_load(p, v);
+		} else if (e != nullptr && e->kind == Entity_Variable) {
+			return lb_addr_load(p, lb_build_addr(p, expr));
+		}
+		gb_printf_err("Error in: %.*s(%td:%td)\n", LIT(p->name), i->token.pos.line, i->token.pos.column);
+		GB_PANIC("nullptr value for expression from identifier: %.*s.%.*s (%p) : %s @ %p", LIT(e->pkg->name), LIT(e->token.string), e, type_to_string(e->type), expr);
+		return {};
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(expr);
+		GB_ASSERT(tav.mode != Addressing_Invalid);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ise, ImplicitSelectorExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(expr);
+		GB_ASSERT(tav.mode == Addressing_Constant);
+
+		return lb_const_value(p->module, tv.type, tv.value);
+	case_end;
+
+	case_ast_node(te, TernaryExpr, expr);
+		LLVMValueRef incoming_values[2] = {};
+		LLVMBasicBlockRef incoming_blocks[2] = {};
+
+		GB_ASSERT(te->y != nullptr);
+		lbBlock *then  = lb_create_block(p, "if.then");
+		lbBlock *done  = lb_create_block(p, "if.done"); // NOTE(bill): Append later
+		lbBlock *else_ = lb_create_block(p, "if.else");
+
+		lbValue cond = lb_build_cond(p, te->cond, then, else_);
+		lb_start_block(p, then);
+
+		Type *type = default_type(type_of_expr(expr));
+
+		lb_open_scope(p);
+		incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value;
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+		lb_emit_jump(p, done);
+		lb_start_block(p, else_);
+
+		lb_open_scope(p);
+		incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value;
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+		lb_emit_jump(p, done);
+		lb_start_block(p, done);
+
+		lbValue res = {};
+		res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
+		res.type = type;
+
+		GB_ASSERT(p->curr_block->preds.count >= 2);
+		incoming_blocks[0] = p->curr_block->preds[0]->block;
+		incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+		LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2);
+
+		return res;
+	case_end;
+
+	case_ast_node(te, TernaryIfExpr, expr);
+		LLVMValueRef incoming_values[2] = {};
+		LLVMBasicBlockRef incoming_blocks[2] = {};
+
+		GB_ASSERT(te->y != nullptr);
+		lbBlock *then  = lb_create_block(p, "if.then");
+		lbBlock *done  = lb_create_block(p, "if.done"); // NOTE(bill): Append later
+		lbBlock *else_ = lb_create_block(p, "if.else");
+
+		lbValue cond = lb_build_cond(p, te->cond, then, else_);
+		lb_start_block(p, then);
+
+		Type *type = default_type(type_of_expr(expr));
+
+		lb_open_scope(p);
+		incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value;
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+		lb_emit_jump(p, done);
+		lb_start_block(p, else_);
+
+		lb_open_scope(p);
+		incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value;
+		lb_close_scope(p, lbDeferExit_Default, nullptr);
+
+		lb_emit_jump(p, done);
+		lb_start_block(p, done);
+
+		lbValue res = {};
+		res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
+		res.type = type;
+
+		GB_ASSERT(p->curr_block->preds.count >= 2);
+		incoming_blocks[0] = p->curr_block->preds[0]->block;
+		incoming_blocks[1] = p->curr_block->preds[1]->block;
+
+		LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2);
+
+		return res;
+	case_end;
+
+	case_ast_node(te, TernaryWhenExpr, expr);
+		TypeAndValue tav = type_and_value_of_expr(te->cond);
+		GB_ASSERT(tav.mode == Addressing_Constant);
+		GB_ASSERT(tav.value.kind == ExactValue_Bool);
+		if (tav.value.value_bool) {
+			return lb_build_expr(p, te->x);
+		} else {
+			return lb_build_expr(p, te->y);
+		}
+	case_end;
+
+	case_ast_node(ta, TypeAssertion, expr);
+		TokenPos pos = ast_token(expr).pos;
+		Type *type = tv.type;
+		lbValue e = lb_build_expr(p, ta->expr);
+		Type *t = type_deref(e.type);
+		if (is_type_union(t)) {
+			return lb_emit_union_cast(p, e, type, pos);
+		} else if (is_type_any(t)) {
+			return lb_emit_any_cast(p, e, type, pos);
+		} else {
+			GB_PANIC("TODO(bill): type assertion %s", type_to_string(e.type));
+		}
+	case_end;
+
+	case_ast_node(tc, TypeCast, expr);
+		lbValue e = lb_build_expr(p, tc->expr);
+		switch (tc->token.kind) {
+		case Token_cast:
+			return lb_emit_conv(p, e, tv.type);
+		case Token_transmute:
+			return lb_emit_transmute(p, e, tv.type);
+		}
+		GB_PANIC("Invalid AST TypeCast");
+	case_end;
+
+	case_ast_node(ac, AutoCast, expr);
+		return lb_build_expr(p, ac->expr);
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		switch (ue->op.kind) {
+		case Token_And: {
+			Ast *ue_expr = unparen_expr(ue->expr);
+			if (ue_expr->kind == Ast_CompoundLit) {
+				lbValue v = lb_build_expr(p, ue->expr);
+
+				Type *type = v.type;
+				lbAddr addr = {};
+				if (p->is_startup) {
+					addr = lb_add_global_generated(p->module, type, v);
+				} else {
+					addr = lb_add_local_generated(p, type, false);
+				}
+				lb_addr_store(p, addr, v);
+				return addr.addr;
+
+			} else if (ue_expr->kind == Ast_TypeAssertion) {
+				gbAllocator a = heap_allocator();
+				GB_ASSERT(is_type_pointer(tv.type));
+
+				ast_node(ta, TypeAssertion, ue_expr);
+				TokenPos pos = ast_token(expr).pos;
+				Type *type = type_of_expr(ue_expr);
+				GB_ASSERT(!is_type_tuple(type));
+
+				lbValue e = lb_build_expr(p, ta->expr);
+				Type *t = type_deref(e.type);
+				if (is_type_union(t)) {
+					lbValue v = e;
+					if (!is_type_pointer(v.type)) {
+						v = lb_address_from_load_or_generate_local(p, v);
+					}
+					Type *src_type = type_deref(v.type);
+					Type *dst_type = type;
+
+					lbValue src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+					lbValue dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+
+					lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+					auto args = array_make<lbValue>(heap_allocator(), 6);
+					args[0] = ok;
+
+					args[1] = lb_find_or_add_entity_string(p->module, pos.file);
+					args[2] = lb_const_int(p->module, t_int, pos.line);
+					args[3] = lb_const_int(p->module, t_int, pos.column);
+
+					args[4] = lb_typeid(p->module, src_type);
+					args[5] = lb_typeid(p->module, dst_type);
+					lb_emit_runtime_call(p, "type_assertion_check", args);
+
+					lbValue data_ptr = v;
+					return lb_emit_conv(p, data_ptr, tv.type);
+				} else if (is_type_any(t)) {
+					lbValue v = e;
+					if (is_type_pointer(v.type)) {
+						v = lb_emit_load(p, v);
+					}
+
+					lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
+					lbValue any_id = lb_emit_struct_ev(p, v, 1);
+					lbValue id = lb_typeid(p->module, type);
+
+
+					lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+					auto args = array_make<lbValue>(heap_allocator(), 6);
+					args[0] = ok;
+
+					args[1] = lb_find_or_add_entity_string(p->module, pos.file);
+					args[2] = lb_const_int(p->module, t_int, pos.line);
+					args[3] = lb_const_int(p->module, t_int, pos.column);
+
+					args[4] = any_id;
+					args[5] = id;
+					lb_emit_runtime_call(p, "type_assertion_check", args);
+
+					return lb_emit_conv(p, data_ptr, tv.type);
+				} else {
+					GB_PANIC("TODO(bill): type assertion %s", type_to_string(type));
+				}
+			}
+
+			return lb_build_addr_ptr(p, ue->expr);
+		}
+		default:
+			{
+				lbValue v = lb_build_expr(p, ue->expr);
+				return lb_emit_unary_arith(p, ue->op.kind, v, tv.type);
+			}
+		}
+	case_end;
+
+	case_ast_node(be, BinaryExpr, expr);
+		return lb_build_binary_expr(p, expr);
+	case_end;
+
+	case_ast_node(pl, ProcLit, expr);
+		return lb_generate_anonymous_proc_lit(p->module, p->name, expr, p);
+	case_end;
+
+	case_ast_node(cl, CompoundLit, expr);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ce, CallExpr, expr);
+		return lb_build_call_expr(p, expr);
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		return lb_addr_load(p, lb_build_addr(p, expr));
+	case_end;
+	}
+
+	GB_PANIC("lb_build_expr: %.*s", LIT(ast_strings[expr->kind]));
+
+	return {};
+}
+
+lbValue lb_get_using_variable(lbProcedure *p, Entity *e) {
+	GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
+	String name = e->token.string;
+	Entity *parent = e->using_parent;
+	Selection sel = lookup_field(parent->type, name, false);
+	GB_ASSERT(sel.entity != nullptr);
+	lbValue *pv = map_get(&p->module->values, hash_entity(parent));
+	lbValue v = {};
+	if (pv != nullptr) {
+		v = *pv;
+	} else {
+		GB_ASSERT_MSG(e->using_expr != nullptr, "%.*s", LIT(name));
+		v = lb_build_addr_ptr(p, e->using_expr);
+	}
+	GB_ASSERT(v.value != nullptr);
+	GB_ASSERT(parent->type == type_deref(v.type));
+	return lb_emit_deep_field_gep(p, v, sel);
+}
+
+
+lbAddr lb_build_addr_from_entity(lbProcedure *p, Entity *e, Ast *expr) {
+	GB_ASSERT(e != nullptr);
+	if (e->kind == Entity_Constant) {
+		Type *t = default_type(type_of_expr(expr));
+		lbValue v = lb_const_value(p->module, t, e->Constant.value);
+		lbAddr g = lb_add_global_generated(p->module, t, v);
+		return g;
+	}
+
+
+	lbValue v = {};
+	lbValue *found = map_get(&p->module->values, hash_entity(e));
+	if (found) {
+		v = *found;
+	} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {
+		// NOTE(bill): Calculate the using variable every time
+		v = lb_get_using_variable(p, e);
+	}
+
+	if (v.value == nullptr) {
+		error(expr, "%.*s Unknown value: %.*s, entity: %p %.*s",
+		      LIT(p->name),
+		      LIT(e->token.string), e, LIT(entity_strings[e->kind]));
+		GB_PANIC("Unknown value");
+	}
+
+	return lb_addr(v);
+}
+
+lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type) {
+	GB_ASSERT_MSG(is_type_pointer(map_val_ptr.type), "%s", type_to_string(map_val_ptr.type));
+	gbAllocator a = heap_allocator();
+	lbAddr h = lb_add_local_generated(p, t_map_header, false); // all the values will be initialzed later
+	map_type = base_type(map_type);
+	GB_ASSERT(map_type->kind == Type_Map);
+
+	Type *key_type = map_type->Map.key;
+	Type *val_type = map_type->Map.value;
+
+	// NOTE(bill): Removes unnecessary allocation if split gep
+	lbValue gep0 = lb_emit_struct_ep(p, h.addr, 0);
+	lbValue m = lb_emit_conv(p, map_val_ptr, type_deref(gep0.type));
+	lb_emit_store(p, gep0, m);
+
+	lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 1), lb_const_bool(p->module, t_bool, is_type_string(key_type)));
+
+	i64 entry_size   = type_size_of  (map_type->Map.entry_type);
+	i64 entry_align  = type_align_of (map_type->Map.entry_type);
+	i64 value_offset = type_offset_of(map_type->Map.entry_type, 2);
+	i64 value_size   = type_size_of  (map_type->Map.value);
+
+	lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 2), lb_const_int(p->module, t_int, entry_size));
+	lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 3), lb_const_int(p->module, t_int, entry_align));
+	lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 4), lb_const_int(p->module, t_uintptr, value_offset));
+	lb_emit_store(p, lb_emit_struct_ep(p, h.addr, 5), lb_const_int(p->module, t_int, value_size));
+
+	return lb_addr_load(p, h);
+}
+
+lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type) {
+	Type *hash_type = t_u64;
+	lbAddr v = lb_add_local_generated(p, t_map_key, true);
+	Type *t = base_type(key.type);
+	key = lb_emit_conv(p, key, key_type);
+	if (is_type_integer(t)) {
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, key, hash_type));
+	} else if (is_type_enum(t)) {
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, key, hash_type));
+	} else if (is_type_typeid(t)) {
+		lbValue i = lb_emit_transmute(p, key, t_uint);
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, i, hash_type));
+	} else if (is_type_pointer(t)) {
+		lbValue ptr = lb_emit_conv(p, key, t_uintptr);
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, ptr, hash_type));
+	} else if (is_type_float(t)) {
+		lbValue bits = {};
+		i64 size = type_size_of(t);
+		switch (8*size) {
+		case 32:  bits = lb_emit_transmute(p, key, t_u32); break;
+		case 64:  bits = lb_emit_transmute(p, key, t_u64);  break;
+		default: GB_PANIC("Unhandled float size: %lld bits", size); break;
+		}
+
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), lb_emit_conv(p, bits, hash_type));
+	} else if (is_type_string(t)) {
+		lbValue str = lb_emit_conv(p, key, t_string);
+		lbValue hashed_str = {};
+
+		if (false && lb_is_const(str)) {
+			String value = lb_get_const_string(p->module, str);
+			u64 hs = fnv64a(value.text, value.len);
+			hashed_str = lb_const_value(p->module, t_u64, exact_value_u64(hs));
+		} else {
+			auto args = array_make<lbValue>(heap_allocator(), 1);
+			args[0] = str;
+			hashed_str = lb_emit_runtime_call(p, "default_hash_string", args);
+		}
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 0), hashed_str);
+		lb_emit_store(p, lb_emit_struct_ep(p, v.addr, 1), str);
+	} else {
+		GB_PANIC("Unhandled map key type");
+	}
+
+	return lb_addr_load(p, v);
+}
+
+void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type,
+                                         lbValue map_key, lbValue map_value) {
+	map_type = base_type(map_type);
+	GB_ASSERT(map_type->kind == Type_Map);
+
+	lbValue h = lb_gen_map_header(p, addr.addr, map_type);
+	lbValue key = lb_gen_map_key(p, map_key, map_type->Map.key);
+	lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
+
+	lbAddr value_addr = lb_add_local_generated(p, v.type, false);
+	lb_addr_store(p, value_addr, v);
+
+	auto args = array_make<lbValue>(heap_allocator(), 4);
+	args[0] = h;
+	args[1] = key;
+	args[2] = lb_emit_conv(p, value_addr.addr, t_rawptr);
+	args[3] = lb_emit_source_code_location(p, nullptr);
+	lb_emit_runtime_call(p, "__dynamic_map_set", args);
+}
+
+
+lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
+	expr = unparen_expr(expr);
+
+	switch (expr->kind) {
+	case_ast_node(i, Implicit, expr);
+		lbAddr v = {};
+		switch (i->kind) {
+		case Token_context:
+			v = lb_find_or_generate_context_ptr(p);
+			break;
+		}
+
+		GB_ASSERT(v.addr.value != nullptr);
+		return v;
+	case_end;
+
+	case_ast_node(i, Ident, expr);
+		if (is_blank_ident(expr)) {
+			lbAddr val = {};
+			return val;
+		}
+		String name = i->token.string;
+		Entity *e = entity_of_ident(expr);
+		return lb_build_addr_from_entity(p, e, expr);
+	case_end;
+
+	case_ast_node(se, SelectorExpr, expr);
+		Ast *sel = unparen_expr(se->selector);
+		if (sel->kind == Ast_Ident) {
+			String selector = sel->Ident.token.string;
+			TypeAndValue tav = type_and_value_of_expr(se->expr);
+
+			if (tav.mode == Addressing_Invalid) {
+				// NOTE(bill): Imports
+				Entity *imp = entity_of_ident(se->expr);
+				if (imp != nullptr) {
+					GB_ASSERT(imp->kind == Entity_ImportName);
+				}
+				return lb_build_addr(p, unparen_expr(se->selector));
+			}
+
+
+			Type *type = base_type(tav.type);
+			if (tav.mode == Addressing_Type) { // Addressing_Type
+				Selection sel = lookup_field(type, selector, true);
+				Entity *e = sel.entity;
+				GB_ASSERT(e->kind == Entity_Variable);
+				GB_ASSERT(e->flags & EntityFlag_TypeField);
+				String name = e->token.string;
+				/*if (name == "names") {
+					lbValue ti_ptr = lb_type_info(m, type);
+					lbValue variant = lb_emit_struct_ep(p, ti_ptr, 2);
+
+					lbValue names_ptr = nullptr;
+
+					if (is_type_enum(type)) {
+						lbValue enum_info = lb_emit_conv(p, variant, t_type_info_enum_ptr);
+						names_ptr = lb_emit_struct_ep(p, enum_info, 1);
+					} else if (type->kind == Type_Struct) {
+						lbValue struct_info = lb_emit_conv(p, variant, t_type_info_struct_ptr);
+						names_ptr = lb_emit_struct_ep(p, struct_info, 1);
+					}
+					return ir_addr(names_ptr);
+				} else */{
+					GB_PANIC("Unhandled TypeField %.*s", LIT(name));
+				}
+				GB_PANIC("Unreachable");
+			}
+
+			Selection sel = lookup_field(type, selector, false);
+			GB_ASSERT(sel.entity != nullptr);
+
+
+			if (sel.entity->type->kind == Type_BitFieldValue) {
+				lbAddr addr = lb_build_addr(p, se->expr);
+				Type *bft = type_deref(lb_addr_type(addr));
+				if (sel.index.count == 1) {
+					GB_ASSERT(is_type_bit_field(bft));
+					i32 index = sel.index[0];
+					return lb_addr_bit_field(lb_addr_get_ptr(p, addr), index);
+				} else {
+					Selection s = sel;
+					s.index.count--;
+					i32 index = s.index[s.index.count-1];
+					lbValue a = lb_addr_get_ptr(p, addr);
+					a = lb_emit_deep_field_gep(p, a, s);
+					return lb_addr_bit_field(a, index);
+				}
+			} else {
+				lbAddr addr = lb_build_addr(p, se->expr);
+				if (addr.kind == lbAddr_Context) {
+					GB_ASSERT(sel.index.count > 0);
+					if (addr.ctx.sel.index.count >= 0) {
+						sel = selection_combine(addr.ctx.sel, sel);
+					}
+					addr.ctx.sel = sel;
+					addr.kind = lbAddr_Context;
+					return addr;
+				} else if (addr.kind == lbAddr_SoaVariable) {
+					lbValue index = addr.soa.index;
+					i32 first_index = sel.index[0];
+					Selection sub_sel = sel;
+					sub_sel.index.data += 1;
+					sub_sel.index.count -= 1;
+
+					lbValue arr = lb_emit_struct_ep(p, addr.addr, first_index);
+
+					Type *t = base_type(type_deref(addr.addr.type));
+					GB_ASSERT(is_type_soa_struct(t));
+
+					// TODO(bill): Bounds check
+					if (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed) {
+						lbValue len = lb_soa_struct_len(p, addr.addr);
+						// lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
+					}
+
+					lbValue item = {};
+
+					if (t->Struct.soa_kind == StructSoa_Fixed) {
+						item = lb_emit_array_ep(p, arr, index);
+					} else {
+						item = lb_emit_load(p, lb_emit_ptr_offset(p, arr, index));
+					}
+					if (sub_sel.index.count > 0) {
+						item = lb_emit_deep_field_gep(p, item, sub_sel);
+					}
+					return lb_addr(item);
+				}
+				lbValue a = lb_addr_get_ptr(p, addr);
+				a = lb_emit_deep_field_gep(p, a, sel);
+				return lb_addr(a);
+			}
+		} else {
+			GB_PANIC("Unsupported selector expression");
+		}
+	case_end;
+
+	case_ast_node(ta, TypeAssertion, expr);
+		gbAllocator a = heap_allocator();
+		TokenPos pos = ast_token(expr).pos;
+		lbValue e = lb_build_expr(p, ta->expr);
+		Type *t = type_deref(e.type);
+		if (is_type_union(t)) {
+			Type *type = type_of_expr(expr);
+			lbAddr v = lb_add_local_generated(p, type, false);
+			lb_addr_store(p, v, lb_emit_union_cast(p, lb_build_expr(p, ta->expr), type, pos));
+			return v;
+		} else if (is_type_any(t)) {
+			Type *type = type_of_expr(expr);
+			return lb_emit_any_cast_addr(p, lb_build_expr(p, ta->expr), type, pos);
+		} else {
+			GB_PANIC("TODO(bill): type assertion %s", type_to_string(e.type));
+		}
+	case_end;
+
+	case_ast_node(ue, UnaryExpr, expr);
+		switch (ue->op.kind) {
+		case Token_And: {
+			return lb_build_addr(p, ue->expr);
+		}
+		default:
+			GB_PANIC("Invalid unary expression for lb_build_addr");
+		}
+	case_end;
+	case_ast_node(be, BinaryExpr, expr);
+		lbValue v = lb_build_expr(p, expr);
+		Type *t = v.type;
+		if (is_type_pointer(t)) {
+			return lb_addr(v);
+		}
+		return lb_addr(lb_address_from_load_or_generate_local(p, v));
+	case_end;
+
+	case_ast_node(ie, IndexExpr, expr);
+		Type *t = base_type(type_of_expr(ie->expr));
+		gbAllocator a = heap_allocator();
+
+		bool deref = is_type_pointer(t);
+		t = base_type(type_deref(t));
+		if (is_type_soa_struct(t)) {
+			// SOA STRUCTURES!!!!
+			lbValue val = lb_build_addr_ptr(p, ie->expr);
+			if (deref) {
+				val = lb_emit_load(p, val);
+			}
+
+			lbValue index = lb_build_expr(p, ie->index);
+			return lb_addr_soa_variable(val, index, ie->index);
+		}
+
+		if (ie->expr->tav.mode == Addressing_SoaVariable) {
+			// SOA Structures for slices/dynamic arrays
+			GB_ASSERT(is_type_pointer(type_of_expr(ie->expr)));
+
+			lbValue field = lb_build_expr(p, ie->expr);
+			lbValue index = lb_build_expr(p, ie->index);
+
+
+			if (!build_context.no_bounds_check) {
+				// // TODO HACK(bill): Clean up this hack to get the length for bounds checking
+				// GB_ASSERT(LLVMIsALoadInst(field.value));
+
+				// lbValue a = {};
+				// a.value = LLVMGetOperand(field.value, 0);
+				// a.type = alloc_type_pointer(field.type);
+
+				// irInstr *b = &a->Instr;
+				// GB_ASSERT(b->kind == irInstr_StructElementPtr);
+				// lbValue base_struct = b->StructElementPtr.address;
+
+				// GB_ASSERT(is_type_soa_struct(type_deref(ir_type(base_struct))));
+				// lbValue len = ir_soa_struct_len(p, base_struct);
+				// ir_emit_bounds_check(p, ast_token(ie->index), index, len);
+			}
+
+			lbValue val = lb_emit_ptr_offset(p, field, index);
+			return lb_addr(val);
+		}
+
+		GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
+
+		if (is_type_map(t)) {
+			lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+			if (deref) {
+				map_val = lb_emit_load(p, map_val);
+			}
+
+			lbValue key = lb_build_expr(p, ie->index);
+			key = lb_emit_conv(p, key, t->Map.key);
+
+			Type *result_type = type_of_expr(expr);
+			return lb_addr_map(map_val, key, t, result_type);
+		}
+
+		lbValue using_addr = {};
+
+		switch (t->kind) {
+		case Type_Array: {
+			lbValue array = {};
+			if (using_addr.value != nullptr) {
+				array = using_addr;
+			} else {
+				array = lb_build_addr_ptr(p, ie->expr);
+				if (deref) {
+					array = lb_emit_load(p, array);
+				}
+			}
+			lbValue index = lb_build_expr(p, ie->index);
+			index = lb_emit_conv(p, index, t_int);
+			lbValue elem = lb_emit_array_ep(p, array, index);
+
+			auto index_tv = type_and_value_of_expr(ie->index);
+			if (index_tv.mode != Addressing_Constant) {
+				// lbValue len = lb_const_int(p->module, t_int, t->Array.count);
+				// ir_emit_bounds_check(p, ast_token(ie->index), index, len);
+			}
+			return lb_addr(elem);
+		}
+
+		case Type_EnumeratedArray: {
+			lbValue array = {};
+			if (using_addr.value != nullptr) {
+				array = using_addr;
+			} else {
+				array = lb_build_addr_ptr(p, ie->expr);
+				if (deref) {
+					array = lb_emit_load(p, array);
+				}
+			}
+
+			Type *index_type = t->EnumeratedArray.index;
+
+			auto index_tv = type_and_value_of_expr(ie->index);
+
+			lbValue index = {};
+			if (compare_exact_values(Token_NotEq, t->EnumeratedArray.min_value, exact_value_i64(0))) {
+				if (index_tv.mode == Addressing_Constant) {
+					ExactValue idx = exact_value_sub(index_tv.value, t->EnumeratedArray.min_value);
+					index = lb_const_value(p->module, index_type, idx);
+				} else {
+					index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+					index = lb_emit_arith(p, Token_Sub, index, lb_const_value(p->module, index_type, t->EnumeratedArray.min_value), index_type);
+				}
+			} else {
+				index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+			}
+
+			lbValue elem = lb_emit_array_ep(p, array, index);
+
+			if (index_tv.mode != Addressing_Constant) {
+				// lbValue len = ir_const_int(t->EnumeratedArray.count);
+				// ir_emit_bounds_check(p, ast_token(ie->index), index, len);
+			}
+			return lb_addr(elem);
+		}
+
+		case Type_Slice: {
+			lbValue slice = {};
+			if (using_addr.value != nullptr) {
+				slice = lb_emit_load(p, using_addr);
+			} else {
+				slice = lb_build_expr(p, ie->expr);
+				if (deref) {
+					slice = lb_emit_load(p, slice);
+				}
+			}
+			lbValue elem = lb_slice_elem(p, slice);
+			lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+			lbValue len = lb_slice_len(p, slice);
+			// ir_emit_bounds_check(p, ast_token(ie->index), index, len);
+			lbValue v = lb_emit_ptr_offset(p, elem, index);
+			return lb_addr(v);
+		}
+
+		case Type_DynamicArray: {
+			lbValue dynamic_array = {};
+			if (using_addr.value != nullptr) {
+				dynamic_array = lb_emit_load(p, using_addr);
+			} else {
+				dynamic_array = lb_build_expr(p, ie->expr);
+				if (deref) {
+					dynamic_array = lb_emit_load(p, dynamic_array);
+				}
+			}
+			lbValue elem = lb_dynamic_array_elem(p, dynamic_array);
+			lbValue len = lb_dynamic_array_len(p, dynamic_array);
+			lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+			// lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+			lbValue v = lb_emit_ptr_offset(p, elem, index);
+			return lb_addr(v);
+		}
+
+
+		case Type_Basic: { // Basic_string
+			lbValue str;
+			lbValue elem;
+			lbValue len;
+			lbValue index;
+
+			if (using_addr.value != nullptr) {
+				str = lb_emit_load(p, using_addr);
+			} else {
+				str = lb_build_expr(p, ie->expr);
+				if (deref) {
+					str = lb_emit_load(p, str);
+				}
+			}
+			elem = lb_string_elem(p, str);
+			len = lb_string_len(p, str);
+
+			index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int);
+			// lb_emit_bounds_check(p, ast_token(ie->index), index, len);
+
+			return lb_addr(lb_emit_ptr_offset(p, elem, index));
+		}
+		}
+	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		gbAllocator a = heap_allocator();
+		lbValue low  = lb_const_int(p->module, t_int, 0);
+		lbValue high = {};
+
+		if (se->low  != nullptr) low  = lb_build_expr(p, se->low);
+		if (se->high != nullptr) high = lb_build_expr(p, se->high);
+
+		bool no_indices = se->low == nullptr && se->high == nullptr;
+
+		lbValue addr = lb_build_addr_ptr(p, se->expr);
+		lbValue base = lb_emit_load(p, addr);
+		Type *type = base_type(base.type);
+
+		if (is_type_pointer(type)) {
+			type = base_type(type_deref(type));
+			addr = base;
+			base = lb_emit_load(p, base);
+		}
+		// TODO(bill): Cleanup like mad!
+
+		switch (type->kind) {
+		case Type_Slice: {
+			Type *slice_type = type;
+			lbValue len = lb_slice_len(p, base);
+			if (high.value == nullptr) high = len;
+
+			if (!no_indices) {
+				// ir_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+			}
+
+			lbValue elem    = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low);
+			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+			lbAddr slice = lb_add_local_generated(p, slice_type, false);
+			lb_fill_slice(p, slice, elem, new_len);
+			return slice;
+		}
+
+		case Type_DynamicArray: {
+			Type *elem_type = type->DynamicArray.elem;
+			Type *slice_type = alloc_type_slice(elem_type);
+
+			lbValue len = lb_dynamic_array_len(p, base);
+			if (high.value == nullptr) high = len;
+
+			if (!no_indices) {
+				// lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+			}
+
+			lbValue elem    = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
+			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+			lbAddr slice = lb_add_local_generated(p, slice_type, false);
+			lb_fill_slice(p, slice, elem, new_len);
+			return slice;
+		}
+
+
+		case Type_Array: {
+			Type *slice_type = alloc_type_slice(type->Array.elem);
+			lbValue len = lb_const_int(p->module, t_int, type->Array.count);
+
+			if (high.value == nullptr) high = len;
+
+			bool low_const  = type_and_value_of_expr(se->low).mode  == Addressing_Constant;
+			bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
+
+			if (!low_const || !high_const) {
+				if (!no_indices) {
+					// lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+				}
+			}
+			lbValue elem    = lb_emit_ptr_offset(p, lb_array_elem(p, addr), low);
+			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+			lbAddr slice = lb_add_local_generated(p, slice_type, false);
+			lb_fill_slice(p, slice, elem, new_len);
+			return slice;
+		}
+
+		case Type_Basic: {
+			GB_ASSERT(type == t_string);
+			lbValue len = lb_string_len(p, base);
+			if (high.value == nullptr) high = len;
+
+			if (!no_indices) {
+				// lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+			}
+
+			lbValue elem    = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
+			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+			lbAddr str = lb_add_local_generated(p, t_string, false);
+			lb_fill_string(p, str, elem, new_len);
+			return str;
+		}
+
+
+		case Type_Struct:
+			if (is_type_soa_struct(type)) {
+				lbValue len = lb_soa_struct_len(p, addr);
+				if (high.value == nullptr) high = len;
+
+				if (!no_indices) {
+					// lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+				}
+				#if 1
+
+				lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true);
+				if (type->Struct.soa_kind == StructSoa_Fixed) {
+					i32 field_count = cast(i32)type->Struct.fields.count;
+					for (i32 i = 0; i < field_count; i++) {
+						lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+						lbValue field_src = lb_emit_struct_ep(p, addr, i);
+						field_src = lb_emit_array_ep(p, field_src, low);
+						lb_emit_store(p, field_dst, field_src);
+					}
+
+					lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+					lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+					lb_emit_store(p, len_dst, new_len);
+				} else if (type->Struct.soa_kind == StructSoa_Slice) {
+					if (no_indices) {
+						lb_addr_store(p, dst, base);
+					} else {
+						i32 field_count = cast(i32)type->Struct.fields.count - 1;
+						for (i32 i = 0; i < field_count; i++) {
+							lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+							lbValue field_src = lb_emit_struct_ev(p, base, i);
+							field_src = lb_emit_ptr_offset(p, field_src, low);
+							lb_emit_store(p, field_dst, field_src);
+						}
+
+
+						lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+						lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+						lb_emit_store(p, len_dst, new_len);
+					}
+				} else if (type->Struct.soa_kind == StructSoa_Dynamic) {
+					i32 field_count = cast(i32)type->Struct.fields.count - 3;
+					for (i32 i = 0; i < field_count; i++) {
+						lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+						lbValue field_src = lb_emit_struct_ev(p, base, i);
+						field_src = lb_emit_ptr_offset(p, field_src, low);
+						lb_emit_store(p, field_dst, field_src);
+					}
+
+
+					lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+					lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+					lb_emit_store(p, len_dst, new_len);
+				}
+
+				return dst;
+				#endif
+			}
+			break;
+
+		}
+
+		GB_PANIC("Unknown slicable type");
+	case_end;
+
+	case_ast_node(de, DerefExpr, expr);
+		lbValue addr = lb_build_expr(p, de->expr);
+		return lb_addr(addr);
+	case_end;
+
+	case_ast_node(ce, CallExpr, expr);
+		// NOTE(bill): This is make sure you never need to have an 'array_ev'
+		lbValue e = lb_build_expr(p, expr);
+		lbAddr v = lb_add_local_generated(p, e.type, false);
+		lb_addr_store(p, v, e);
+		return v;
+	case_end;
+
+	case_ast_node(cl, CompoundLit, expr);
+		Type *type = type_of_expr(expr);
+		Type *bt = base_type(type);
+
+		lbAddr v = lb_add_local_generated(p, type, true);
+
+		Type *et = nullptr;
+		switch (bt->kind) {
+		case Type_Array:           et = bt->Array.elem;           break;
+		case Type_EnumeratedArray: et = bt->EnumeratedArray.elem; break;
+		case Type_Slice:           et = bt->Slice.elem;           break;
+		case Type_BitSet:          et = bt->BitSet.elem;          break;
+		case Type_SimdVector:      et = bt->SimdVector.elem;      break;
+		}
+
+		String proc_name = {};
+		if (p->entity) {
+			proc_name = p->entity->token.string;
+		}
+		TokenPos pos = ast_token(expr).pos;
+
+		switch (bt->kind) {
+		default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break;
+
+		case Type_Struct: {
+
+			// TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment.
+			// NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR
+			bool is_raw_union = is_type_raw_union(bt);
+			GB_ASSERT(is_type_struct(bt) || is_raw_union);
+			TypeStruct *st = &bt->Struct;
+			if (cl->elems.count > 0) {
+				lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+				for_array(field_index, cl->elems) {
+					Ast *elem = cl->elems[field_index];
+
+					lbValue field_expr = {};
+					Entity *field = nullptr;
+					isize index = field_index;
+
+					if (elem->kind == Ast_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+						String name = fv->field->Ident.token.string;
+						Selection sel = lookup_field(bt, name, false);
+						index = sel.index[0];
+						elem = fv->value;
+						TypeAndValue tav = type_and_value_of_expr(elem);
+					} else {
+						TypeAndValue tav = type_and_value_of_expr(elem);
+						Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_src_index);
+						index = sel.index[0];
+					}
+
+					field = st->fields[index];
+					Type *ft = field->type;
+					if (!is_raw_union && !is_type_typeid(ft) && lb_is_elem_const(elem, ft)) {
+						continue;
+					}
+
+					field_expr = lb_build_expr(p, elem);
+
+
+					Type *fet = field_expr.type;
+					GB_ASSERT(fet->kind != Type_Tuple);
+
+					// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
+					if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
+						GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
+
+						lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index);
+						lb_emit_store_union_variant(p, gep, field_expr, fet);
+					} else {
+						lbValue fv = lb_emit_conv(p, field_expr, ft);
+						lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index);
+						lb_emit_store(p, gep, fv);
+					}
+				}
+			}
+			break;
+		}
+
+		case Type_Map: {
+			if (cl->elems.count == 0) {
+				break;
+			}
+			gbAllocator a = heap_allocator();
+			{
+				auto args = array_make<lbValue>(a, 3);
+				args[0] = lb_gen_map_header(p, v.addr, type);
+				args[1] = lb_const_int(p->module, t_int, 2*cl->elems.count);
+				args[2] = lb_emit_source_code_location(p, proc_name, pos);
+				lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
+			}
+			for_array(field_index, cl->elems) {
+				Ast *elem = cl->elems[field_index];
+				ast_node(fv, FieldValue, elem);
+
+				lbValue key   = lb_build_expr(p, fv->field);
+				lbValue value = lb_build_expr(p, fv->value);
+				lb_insert_dynamic_map_key_and_value(p, v, type, key, value);
+			}
+			break;
+		}
+
+		case Type_Array: {
+			if (cl->elems.count > 0) {
+				lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+
+				auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
+				defer (array_free(&temp_data));
+
+				// NOTE(bill): Separate value, gep, store into their own chunks
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					if (elem->kind == Ast_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+						if (lb_is_elem_const(fv->value, et)) {
+							continue;
+						}
+						if (is_ast_range(fv->field)) {
+							ast_node(ie, BinaryExpr, fv->field);
+							TypeAndValue lo_tav = ie->left->tav;
+							TypeAndValue hi_tav = ie->right->tav;
+							GB_ASSERT(lo_tav.mode == Addressing_Constant);
+							GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+							TokenKind op = ie->op.kind;
+							i64 lo = exact_value_to_i64(lo_tav.value);
+							i64 hi = exact_value_to_i64(hi_tav.value);
+							if (op == Token_Ellipsis) {
+								hi += 1;
+							}
+
+							lbValue value = lb_build_expr(p, fv->value);
+
+							for (i64 k = lo; k < hi; k++) {
+								lbCompoundLitElemTempData data = {};
+								data.value = value;
+								data.elem_index = cast(i32)k;
+								array_add(&temp_data, data);
+							}
+
+						} else {
+							auto tav = fv->field->tav;
+							GB_ASSERT(tav.mode == Addressing_Constant);
+							i64 index = exact_value_to_i64(tav.value);
+
+							lbValue value = lb_build_expr(p, fv->value);
+							lbCompoundLitElemTempData data = {};
+							data.value = lb_emit_conv(p, value, et);
+							data.expr = fv->value;
+							data.elem_index = cast(i32)index;
+							array_add(&temp_data, data);
+						}
+
+					} else {
+						if (lb_is_elem_const(elem, et)) {
+							continue;
+						}
+						lbCompoundLitElemTempData data = {};
+						data.expr = elem;
+						data.elem_index = cast(i32)i;
+						array_add(&temp_data, data);
+					}
+				}
+
+				for_array(i, temp_data) {
+					temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), temp_data[i].elem_index);
+				}
+
+				for_array(i, temp_data) {
+					auto return_ptr_hint_ast   = p->return_ptr_hint_ast;
+					auto return_ptr_hint_value = p->return_ptr_hint_value;
+					auto return_ptr_hint_used  = p->return_ptr_hint_used;
+					defer (p->return_ptr_hint_ast   = return_ptr_hint_ast);
+					defer (p->return_ptr_hint_value = return_ptr_hint_value);
+					defer (p->return_ptr_hint_used  = return_ptr_hint_used);
+
+					lbValue field_expr = temp_data[i].value;
+					Ast *expr = temp_data[i].expr;
+
+					p->return_ptr_hint_value = temp_data[i].gep;
+					p->return_ptr_hint_ast = unparen_expr(expr);
+
+					if (field_expr.value == nullptr) {
+						field_expr = lb_build_expr(p, expr);
+					}
+					Type *t = field_expr.type;
+					GB_ASSERT(t->kind != Type_Tuple);
+					lbValue ev = lb_emit_conv(p, field_expr, et);
+
+					if (!p->return_ptr_hint_used) {
+						temp_data[i].value = ev;
+					}
+				}
+
+				for_array(i, temp_data) {
+					if (temp_data[i].value.value != nullptr) {
+						lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
+					}
+				}
+			}
+			break;
+		}
+		case Type_EnumeratedArray: {
+			if (cl->elems.count > 0) {
+				lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+
+				auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
+				defer (array_free(&temp_data));
+
+				// NOTE(bill): Separate value, gep, store into their own chunks
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					if (elem->kind == Ast_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+						if (lb_is_elem_const(fv->value, et)) {
+							continue;
+						}
+						if (is_ast_range(fv->field)) {
+							ast_node(ie, BinaryExpr, fv->field);
+							TypeAndValue lo_tav = ie->left->tav;
+							TypeAndValue hi_tav = ie->right->tav;
+							GB_ASSERT(lo_tav.mode == Addressing_Constant);
+							GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+							TokenKind op = ie->op.kind;
+							i64 lo = exact_value_to_i64(lo_tav.value);
+							i64 hi = exact_value_to_i64(hi_tav.value);
+							if (op == Token_Ellipsis) {
+								hi += 1;
+							}
+
+							lbValue value = lb_build_expr(p, fv->value);
+
+							for (i64 k = lo; k < hi; k++) {
+								lbCompoundLitElemTempData data = {};
+								data.value = value;
+								data.elem_index = cast(i32)k;
+								array_add(&temp_data, data);
+							}
+
+						} else {
+							auto tav = fv->field->tav;
+							GB_ASSERT(tav.mode == Addressing_Constant);
+							i64 index = exact_value_to_i64(tav.value);
+
+							lbValue value = lb_build_expr(p, fv->value);
+							lbCompoundLitElemTempData data = {};
+							data.value = lb_emit_conv(p, value, et);
+							data.expr = fv->value;
+							data.elem_index = cast(i32)index;
+							array_add(&temp_data, data);
+						}
+
+					} else {
+						if (lb_is_elem_const(elem, et)) {
+							continue;
+						}
+						lbCompoundLitElemTempData data = {};
+						data.expr = elem;
+						data.elem_index = cast(i32)i;
+						array_add(&temp_data, data);
+					}
+				}
+
+
+				i32 index_offset = cast(i32)exact_value_to_i64(bt->EnumeratedArray.min_value);
+
+				for_array(i, temp_data) {
+					i32 index = temp_data[i].elem_index - index_offset;
+					temp_data[i].gep = lb_emit_array_epi(p, lb_addr_get_ptr(p, v), index);
+				}
+
+				for_array(i, temp_data) {
+					auto return_ptr_hint_ast   = p->return_ptr_hint_ast;
+					auto return_ptr_hint_value = p->return_ptr_hint_value;
+					auto return_ptr_hint_used  = p->return_ptr_hint_used;
+					defer (p->return_ptr_hint_ast   = return_ptr_hint_ast);
+					defer (p->return_ptr_hint_value = return_ptr_hint_value);
+					defer (p->return_ptr_hint_used  = return_ptr_hint_used);
+
+					lbValue field_expr = temp_data[i].value;
+					Ast *expr = temp_data[i].expr;
+
+					p->return_ptr_hint_value = temp_data[i].gep;
+					p->return_ptr_hint_ast = unparen_expr(expr);
+
+					if (field_expr.value == nullptr) {
+						field_expr = lb_build_expr(p, expr);
+					}
+					Type *t = field_expr.type;
+					GB_ASSERT(t->kind != Type_Tuple);
+					lbValue ev = lb_emit_conv(p, field_expr, et);
+
+					if (!p->return_ptr_hint_used) {
+						temp_data[i].value = ev;
+					}
+				}
+
+				for_array(i, temp_data) {
+					if (temp_data[i].value.value != nullptr) {
+						lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
+					}
+				}
+			}
+			break;
+		}
+		case Type_Slice: {
+			if (cl->elems.count > 0) {
+				Type *elem_type = bt->Slice.elem;
+				Type *elem_ptr_type = alloc_type_pointer(elem_type);
+				Type *elem_ptr_ptr_type = alloc_type_pointer(elem_ptr_type);
+				lbValue slice = lb_const_value(p->module, type, exact_value_compound(expr));
+
+				lbValue data = lb_slice_elem(p, slice);
+
+				auto temp_data = array_make<lbCompoundLitElemTempData>(heap_allocator(), 0, cl->elems.count);
+				defer (array_free(&temp_data));
+
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					if (elem->kind == Ast_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+
+						if (lb_is_elem_const(fv->value, et)) {
+							continue;
+						}
+
+						if (is_ast_range(fv->field)) {
+							ast_node(ie, BinaryExpr, fv->field);
+							TypeAndValue lo_tav = ie->left->tav;
+							TypeAndValue hi_tav = ie->right->tav;
+							GB_ASSERT(lo_tav.mode == Addressing_Constant);
+							GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+							TokenKind op = ie->op.kind;
+							i64 lo = exact_value_to_i64(lo_tav.value);
+							i64 hi = exact_value_to_i64(hi_tav.value);
+							if (op == Token_Ellipsis) {
+								hi += 1;
+							}
+
+							lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
+
+							for (i64 k = lo; k < hi; k++) {
+								lbCompoundLitElemTempData data = {};
+								data.value = value;
+								data.elem_index = cast(i32)k;
+								array_add(&temp_data, data);
+							}
+
+						} else {
+							GB_ASSERT(fv->field->tav.mode == Addressing_Constant);
+							i64 index = exact_value_to_i64(fv->field->tav.value);
+
+							lbValue field_expr = lb_build_expr(p, fv->value);
+							GB_ASSERT(!is_type_tuple(field_expr.type));
+
+							lbValue ev = lb_emit_conv(p, field_expr, et);
+
+							lbCompoundLitElemTempData data = {};
+							data.value = ev;
+							data.elem_index = cast(i32)index;
+							array_add(&temp_data, data);
+						}
+					} else {
+						if (lb_is_elem_const(elem, et)) {
+							continue;
+						}
+						lbValue field_expr = lb_build_expr(p, elem);
+						GB_ASSERT(!is_type_tuple(field_expr.type));
+
+						lbValue ev = lb_emit_conv(p, field_expr, et);
+
+						lbCompoundLitElemTempData data = {};
+						data.value = ev;
+						data.elem_index = cast(i32)i;
+						array_add(&temp_data, data);
+					}
+				}
+
+				for_array(i, temp_data) {
+					temp_data[i].gep = lb_emit_ptr_offset(p, data, lb_const_int(p->module, t_int, temp_data[i].elem_index));
+				}
+
+				for_array(i, temp_data) {
+					lb_emit_store(p, temp_data[i].gep, temp_data[i].value);
+				}
+
+				{
+					GB_ASSERT(lb_is_const(slice));
+					unsigned indices[1] = {1};
+
+					lbValue count = {};
+					count.type = t_int;
+					count.value = LLVMConstExtractValue(slice.value, indices, gb_count_of(indices));
+					lb_fill_slice(p, v, data, count);
+				}
+			}
+			break;
+		}
+
+		case Type_DynamicArray: {
+			if (cl->elems.count == 0) {
+				break;
+			}
+			Type *et = bt->DynamicArray.elem;
+			gbAllocator a = heap_allocator();
+			lbValue size  = lb_const_int(p->module, t_int, type_size_of(et));
+			lbValue align = lb_const_int(p->module, t_int, type_align_of(et));
+
+			i64 item_count = gb_max(cl->max_count, cl->elems.count);
+			{
+
+				auto args = array_make<lbValue>(a, 5);
+				args[0] = lb_emit_conv(p, lb_addr_get_ptr(p, v), t_rawptr);
+				args[1] = size;
+				args[2] = align;
+				args[3] = lb_const_int(p->module, t_int, 2*item_count); // TODO(bill): Is this too much waste?
+				args[4] = lb_emit_source_code_location(p, proc_name, pos);
+				lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
+			}
+
+			lbValue items = lb_generate_array(p->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr);
+
+			for_array(i, cl->elems) {
+				Ast *elem = cl->elems[i];
+				if (elem->kind == Ast_FieldValue) {
+					ast_node(fv, FieldValue, elem);
+					if (is_ast_range(fv->field)) {
+						ast_node(ie, BinaryExpr, fv->field);
+						TypeAndValue lo_tav = ie->left->tav;
+						TypeAndValue hi_tav = ie->right->tav;
+						GB_ASSERT(lo_tav.mode == Addressing_Constant);
+						GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+						TokenKind op = ie->op.kind;
+						i64 lo = exact_value_to_i64(lo_tav.value);
+						i64 hi = exact_value_to_i64(hi_tav.value);
+						if (op == Token_Ellipsis) {
+							hi += 1;
+						}
+
+						lbValue value = lb_emit_conv(p, lb_build_expr(p, fv->value), et);
+
+						for (i64 k = lo; k < hi; k++) {
+							lbValue ep = lb_emit_array_epi(p, items, cast(i32)k);
+							lb_emit_store(p, ep, value);
+						}
+					} else {
+						GB_ASSERT(fv->field->tav.mode == Addressing_Constant);
+
+						i64 field_index = exact_value_to_i64(fv->field->tav.value);
+
+						lbValue ev = lb_build_expr(p, fv->value);
+						lbValue value = lb_emit_conv(p, ev, et);
+						lbValue ep = lb_emit_array_epi(p, items, cast(i32)field_index);
+						lb_emit_store(p, ep, value);
+					}
+				} else {
+					lbValue value = lb_emit_conv(p, lb_build_expr(p, elem), et);
+					lbValue ep = lb_emit_array_epi(p, items, cast(i32)i);
+					lb_emit_store(p, ep, value);
+				}
+			}
+
+			{
+				auto args = array_make<lbValue>(a, 6);
+				args[0] = lb_emit_conv(p, v.addr, t_rawptr);
+				args[1] = size;
+				args[2] = align;
+				args[3] = lb_emit_conv(p, items, t_rawptr);
+				args[4] = lb_const_int(p->module, t_int, item_count);
+				args[5] = lb_emit_source_code_location(p, proc_name, pos);
+				lb_emit_runtime_call(p, "__dynamic_array_append", args);
+			}
+			break;
+		}
+
+		case Type_Basic: {
+			GB_ASSERT(is_type_any(bt));
+			if (cl->elems.count > 0) {
+				lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+				String field_names[2] = {
+					str_lit("data"),
+					str_lit("id"),
+				};
+				Type *field_types[2] = {
+					t_rawptr,
+					t_typeid,
+				};
+
+				for_array(field_index, cl->elems) {
+					Ast *elem = cl->elems[field_index];
+
+					lbValue field_expr = {};
+					isize index = field_index;
+
+					if (elem->kind == Ast_FieldValue) {
+						ast_node(fv, FieldValue, elem);
+						Selection sel = lookup_field(bt, fv->field->Ident.token.string, false);
+						index = sel.index[0];
+						elem = fv->value;
+					} else {
+						TypeAndValue tav = type_and_value_of_expr(elem);
+						Selection sel = lookup_field(bt, field_names[field_index], false);
+						index = sel.index[0];
+					}
+
+					field_expr = lb_build_expr(p, elem);
+
+					GB_ASSERT(field_expr.type->kind != Type_Tuple);
+
+					Type *ft = field_types[index];
+					lbValue fv = lb_emit_conv(p, field_expr, ft);
+					lbValue gep = lb_emit_struct_ep(p, lb_addr_get_ptr(p, v), cast(i32)index);
+					lb_emit_store(p, gep, fv);
+				}
+			}
+
+			break;
+		}
+
+		case Type_BitSet: {
+			i64 sz = type_size_of(type);
+			if (cl->elems.count > 0 && sz > 0) {
+				lb_addr_store(p, v, lb_const_value(p->module, type, exact_value_compound(expr)));
+
+				lbValue lower = lb_const_value(p->module, t_int, exact_value_i64(bt->BitSet.lower));
+				for_array(i, cl->elems) {
+					Ast *elem = cl->elems[i];
+					GB_ASSERT(elem->kind != Ast_FieldValue);
+
+					if (lb_is_elem_const(elem, et)) {
+						continue;
+					}
+
+					lbValue expr = lb_build_expr(p, elem);
+					GB_ASSERT(expr.type->kind != Type_Tuple);
+
+					Type *it = bit_set_to_int(bt);
+					lbValue one = lb_const_value(p->module, it, exact_value_i64(1));
+					lbValue e = lb_emit_conv(p, expr, it);
+					e = lb_emit_arith(p, Token_Sub, e, lower, it);
+					e = lb_emit_arith(p, Token_Shl, one, e, it);
+
+					lbValue old_value = lb_emit_transmute(p, lb_addr_load(p, v), it);
+					lbValue new_value = lb_emit_arith(p, Token_Or, old_value, e, it);
+					new_value = lb_emit_transmute(p, new_value, type);
+					lb_addr_store(p, v, new_value);
+				}
+			}
+			break;
+		}
+
+		}
+
+		return v;
+	case_end;
+
+	case_ast_node(tc, TypeCast, expr);
+		Type *type = type_of_expr(expr);
+		lbValue x = lb_build_expr(p, tc->expr);
+		lbValue e = {};
+		switch (tc->token.kind) {
+		case Token_cast:
+			e = lb_emit_conv(p, x, type);
+			break;
+		case Token_transmute:
+			e = lb_emit_transmute(p, x, type);
+			break;
+		default:
+			GB_PANIC("Invalid AST TypeCast");
+		}
+		lbAddr v = lb_add_local_generated(p, type, false);
+		lb_addr_store(p, v, e);
+		return v;
+	case_end;
+
+	case_ast_node(ac, AutoCast, expr);
+		return lb_build_addr(p, ac->expr);
+	case_end;
+	}
+
+	TokenPos token_pos = ast_token(expr).pos;
+	GB_PANIC("Unexpected address expression\n"
+	         "\tAst: %.*s @ "
+	         "%.*s(%td:%td)\n",
+	         LIT(ast_strings[expr->kind]),
+	         LIT(token_pos.file), token_pos.line, token_pos.column);
+
+
+	return {};
+}
+
+void lb_init_module(lbModule *m, Checker *c) {
+	m->info = &c->info;
+
+	m->ctx = LLVMGetGlobalContext();
+	m->mod = LLVMModuleCreateWithNameInContext("odin_module", m->ctx);
+	m->debug_builder = LLVMCreateDIBuilder(m->mod);
+
+	gb_mutex_init(&m->mutex);
+	gbAllocator a = heap_allocator();
+	map_init(&m->types, a);
+	map_init(&m->values, a);
+	map_init(&m->members, a);
+	map_init(&m->procedure_values, a);
+	map_init(&m->procedures, a);
+	map_init(&m->const_strings, a);
+	map_init(&m->anonymous_proc_lits, a);
+	array_init(&m->procedures_to_generate, a);
+	array_init(&m->foreign_library_paths, a);
+
+	map_init(&m->debug_values, a);
+
+}
+
+
+bool lb_init_generator(lbGenerator *gen, Checker *c) {
+	if (global_error_collector.count != 0) {
+		return false;
+	}
+
+	isize tc = c->parser->total_token_count;
+	if (tc < 2) {
+		return false;
+	}
+
+
+	String init_fullpath = c->parser->init_fullpath;
+
+	if (build_context.out_filepath.len == 0) {
+		gen->output_name = remove_directory_from_path(init_fullpath);
+		gen->output_name = remove_extension_from_path(gen->output_name);
+		gen->output_base = gen->output_name;
+	} else {
+		gen->output_name = build_context.out_filepath;
+		isize pos = string_extension_position(gen->output_name);
+		if (pos < 0) {
+			gen->output_base = gen->output_name;
+		} else {
+			gen->output_base = substring(gen->output_name, 0, pos);
+		}
+	}
+	gbAllocator ha = heap_allocator();
+	array_init(&gen->output_object_paths, ha);
+
+	gen->output_base = path_to_full_path(ha, gen->output_base);
+
+	gbString output_file_path = gb_string_make_length(ha, gen->output_base.text, gen->output_base.len);
+	output_file_path = gb_string_appendc(output_file_path, ".obj");
+	defer (gb_string_free(output_file_path));
+
+	gen->info = &c->info;
+
+	lb_init_module(&gen->module, c);
+
+
+	return true;
+}
+
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
+	GB_ASSERT(type != nullptr);
+	type = default_type(type);
+
+	isize max_len = 7+8+1;
+	u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
+	isize len = gb_snprintf(cast(char *)str, max_len, "ggv$%x", m->global_generated_index);
+	m->global_generated_index++;
+	String name = make_string(str, len-1);
+
+	Scope *scope = nullptr;
+	Entity *e = alloc_entity_variable(scope, make_token_ident(name), type);
+	lbValue g = {};
+	g.type = alloc_type_pointer(type);
+	g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str);
+	if (value.value != nullptr) {
+		GB_ASSERT(LLVMIsConstant(value.value));
+		LLVMSetInitializer(g.value, value.value);
+	} else {
+		LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type)));
+	}
+
+	lb_add_entity(m, e, g);
+	lb_add_member(m, name, g);
+	return lb_addr(g);
+}
+
+lbValue lb_find_runtime_value(lbModule *m, String const &name) {
+	AstPackage *p = m->info->runtime_package;
+	Entity *e = scope_lookup_current(p->scope, name);
+	lbValue *found = map_get(&m->values, hash_entity(e));
+	GB_ASSERT_MSG(found != nullptr, "Unable to find runtime value '%.*s'", LIT(name));
+	lbValue value = *found;
+	return value;
+}
+
+lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
+	i32 index = cast(i32)lb_type_info_index(m->info, type);
+	GB_ASSERT(index >= 0);
+	// gb_printf_err("%d %s\n", index, type_to_string(type));
+
+	LLVMValueRef indices[2] = {
+		LLVMConstInt(lb_type(m, t_int), 0, false),
+		LLVMConstInt(lb_type(m, t_int), index, false),
+	};
+
+	lbValue res = {};
+	res.type = t_type_info_ptr;
+	res.value = LLVMConstGEP(lb_global_type_info_data.addr.value, indices, cast(unsigned)gb_count_of(indices));
+	return res;
+}
+
+
+lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) {
+	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index);
+	lb_global_type_info_member_types_index += cast(i32)count;
+	return offset;
+}
+lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) {
+	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index);
+	lb_global_type_info_member_names_index += cast(i32)count;
+	return offset;
+}
+lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) {
+	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index);
+	lb_global_type_info_member_offsets_index += cast(i32)count;
+	return offset;
+}
+lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) {
+	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index);
+	lb_global_type_info_member_usings_index += cast(i32)count;
+	return offset;
+}
+lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
+	lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index);
+	lb_global_type_info_member_tags_index += cast(i32)count;
+	return offset;
+}
+
+
+lbValue lb_generate_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id) {
+	gbAllocator a = heap_allocator();
+	Token token = {Token_Ident};
+	isize name_len = prefix.len + 1 + 20;
+
+	auto suffix_id = cast(unsigned long long)id;
+	char *text = gb_alloc_array(a, char, name_len+1);
+	gb_snprintf(text, name_len,
+	            "%.*s-%llu", LIT(prefix), suffix_id);
+	text[name_len] = 0;
+
+	String s = make_string_c(text);
+
+	Type *t = alloc_type_array(elem_type, count);
+	lbValue g = {};
+	g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
+	g.type = alloc_type_pointer(t);
+	LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
+	LLVMSetLinkage(g.value, LLVMInternalLinkage);
+	map_set(&m->members, hash_string(s), g);
+	return g;
+}
+
+
+
+void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+	lbModule *m = p->module;
+	LLVMContextRef ctx = m->ctx;
+	gbAllocator a = heap_allocator();
+	CheckerInfo *info = m->info;
+
+	{
+		// NOTE(bill): Set the type_table slice with the global backing array
+		lbValue global_type_table = lb_find_runtime_value(m, str_lit("type_table"));
+		Type *type = base_type(lb_addr_type(lb_global_type_info_data));
+		GB_ASSERT(is_type_array(type));
+
+		LLVMValueRef indices[2] = {llvm_zero32(m), llvm_zero32(m)};
+		LLVMValueRef values[2] = {
+			LLVMConstInBoundsGEP(lb_global_type_info_data.addr.value, indices, gb_count_of(indices)),
+			LLVMConstInt(lb_type(m, t_int), type->Array.count, true),
+		};
+		LLVMValueRef slice = LLVMConstStructInContext(ctx, values, gb_count_of(values), false);
+
+		LLVMSetInitializer(global_type_table.value, slice);
+	}
+
+
+	// Useful types
+	Type *t_i64_slice_ptr    = alloc_type_pointer(alloc_type_slice(t_i64));
+	Type *t_string_slice_ptr = alloc_type_pointer(alloc_type_slice(t_string));
+
+	i32 type_info_member_types_index = 0;
+	i32 type_info_member_names_index = 0;
+	i32 type_info_member_offsets_index = 0;
+
+	for_array(type_info_type_index, info->type_info_types) {
+		Type *t = info->type_info_types[type_info_type_index];
+		t = default_type(t);
+		if (t == t_invalid) {
+			continue;
+		}
+
+		isize entry_index = lb_type_info_index(info, t, false);
+		if (entry_index <= 0) {
+			continue;
+		}
+
+		lbValue tag = {};
+		lbValue ti_ptr = lb_emit_array_epi(p, lb_global_type_info_data.addr, cast(i32)entry_index);
+		lbValue variant_ptr = lb_emit_struct_ep(p, ti_ptr, 3);
+
+		lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 0), lb_const_int(m, t_int, type_size_of(t)));
+		lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 1), lb_const_int(m, t_int, type_align_of(t)));
+		lb_emit_store(p, lb_emit_struct_ep(p, ti_ptr, 2), lb_typeid(m, t));
+
+
+		switch (t->kind) {
+		case Type_Named: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_named_ptr);
+			LLVMValueRef vals[2] = {
+				lb_const_string(p->module, t->Named.type_name->token.string).value,
+				lb_get_type_info_ptr(m, t->Named.base).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+
+		case Type_Basic:
+			switch (t->Basic.kind) {
+			case Basic_bool:
+			case Basic_b8:
+			case Basic_b16:
+			case Basic_b32:
+			case Basic_b64:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_boolean_ptr);
+				break;
+
+			case Basic_i8:
+			case Basic_u8:
+			case Basic_i16:
+			case Basic_u16:
+			case Basic_i32:
+			case Basic_u32:
+			case Basic_i64:
+			case Basic_u64:
+			case Basic_i128:
+			case Basic_u128:
+
+			case Basic_i16le:
+			case Basic_u16le:
+			case Basic_i32le:
+			case Basic_u32le:
+			case Basic_i64le:
+			case Basic_u64le:
+			case Basic_i128le:
+			case Basic_u128le:
+			case Basic_i16be:
+			case Basic_u16be:
+			case Basic_i32be:
+			case Basic_u32be:
+			case Basic_i64be:
+			case Basic_u64be:
+			case Basic_i128be:
+			case Basic_u128be:
+
+			case Basic_int:
+			case Basic_uint:
+			case Basic_uintptr: {
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_integer_ptr);
+
+				lbValue is_signed = lb_const_bool(m, t_bool, (t->Basic.flags & BasicFlag_Unsigned) == 0);
+				// NOTE(bill): This is matches the runtime layout
+				u8 endianness_value = 0;
+				if (t->Basic.flags & BasicFlag_EndianLittle) {
+					endianness_value = 1;
+				} else if (t->Basic.flags & BasicFlag_EndianBig) {
+					endianness_value = 2;
+				}
+				lbValue endianness = lb_const_int(m, t_u8, endianness_value);
+
+				LLVMValueRef vals[2] = {
+					is_signed.value,
+					endianness.value,
+				};
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+				break;
+			}
+
+			case Basic_rune:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
+				break;
+
+			// case Basic_f16:
+			case Basic_f32:
+			case Basic_f64:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_float_ptr);
+				break;
+
+			// case Basic_complex32:
+			case Basic_complex64:
+			case Basic_complex128:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
+				break;
+
+			case Basic_quaternion128:
+			case Basic_quaternion256:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);
+				break;
+
+			case Basic_rawptr:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
+				break;
+
+			case Basic_string:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
+				break;
+
+			case Basic_cstring:
+				{
+					tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_string_ptr);
+					LLVMValueRef vals[1] = {
+						lb_const_bool(m, t_bool, true).value,
+					};
+
+					lbValue res = {};
+					res.type = type_deref(tag.type);
+					res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+					lb_emit_store(p, tag, res);
+				}
+				break;
+
+			case Basic_any:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_any_ptr);
+				break;
+
+			case Basic_typeid:
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_typeid_ptr);
+				break;
+			}
+			break;
+
+		case Type_Pointer: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
+			lbValue gep = lb_get_type_info_ptr(m, t->Pointer.elem);
+
+			LLVMValueRef vals[1] = {
+				gep.value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+		case Type_Array: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_array_ptr);
+			i64 ez = type_size_of(t->Array.elem);
+
+			LLVMValueRef vals[3] = {
+				lb_get_type_info_ptr(m, t->Array.elem).value,
+				lb_const_int(m, t_int, ez).value,
+				lb_const_int(m, t_int, t->Array.count).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+		case Type_EnumeratedArray: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
+
+			LLVMValueRef vals[6] = {
+				lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
+				lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
+				lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
+				lb_const_int(m, t_int, t->EnumeratedArray.count).value,
+
+				// Unions
+				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+				LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+
+			// NOTE(bill): Union assignment
+			lbValue min_value = lb_emit_struct_ep(p, tag, 4);
+			lbValue max_value = lb_emit_struct_ep(p, tag, 5);
+
+			lbValue min_v = lb_const_value(m, core_type(t->EnumeratedArray.index), t->EnumeratedArray.min_value);
+			lbValue max_v = lb_const_value(m, core_type(t->EnumeratedArray.index), t->EnumeratedArray.max_value);
+
+			lb_emit_store_union_variant(p, min_value, min_v, min_v.type);
+			lb_emit_store_union_variant(p, max_value, max_v, max_v.type);
+			break;
+		}
+		case Type_DynamicArray: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
+
+			LLVMValueRef vals[2] = {
+				lb_get_type_info_ptr(m, t->DynamicArray.elem).value,
+				lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+		case Type_Slice: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
+
+			LLVMValueRef vals[2] = {
+				lb_get_type_info_ptr(m, t->Slice.elem).value,
+				lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+		case Type_Proc: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_procedure_ptr);
+
+			LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
+			LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
+			if (t->Proc.params != nullptr) {
+				params = lb_get_type_info_ptr(m, t->Proc.params).value;
+			}
+			if (t->Proc.results != nullptr) {
+				results = lb_get_type_info_ptr(m, t->Proc.results).value;
+			}
+
+			LLVMValueRef vals[4] = {
+				params,
+				results,
+				lb_const_bool(m, t_bool, t->Proc.variadic).value,
+				lb_const_int(m, t_u8, t->Proc.calling_convention).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+		case Type_Tuple: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_tuple_ptr);
+
+
+			lbValue memory_types = lb_type_info_member_types_offset(p, t->Tuple.variables.count);
+			lbValue memory_names = lb_type_info_member_names_offset(p, t->Tuple.variables.count);
+
+
+			for_array(i, t->Tuple.variables) {
+				// NOTE(bill): offset is not used for tuples
+				Entity *f = t->Tuple.variables[i];
+
+				lbValue index     = lb_const_int(m, t_int, i);
+				lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
+
+				// TODO(bill): Make this constant if possible, 'lb_const_store' does not work
+				lb_emit_store(p, type_info, lb_type_info(m, f->type));
+				if (f->token.string.len > 0) {
+					lbValue name = lb_emit_ptr_offset(p, memory_names, index);
+					lb_emit_store(p, name, lb_const_string(m, f->token.string));
+				}
+			}
+
+			lbValue count = lb_const_int(m, t_int, t->Tuple.variables.count);
+
+			LLVMValueRef types_slice = llvm_const_slice(memory_types, count);
+			LLVMValueRef names_slice = llvm_const_slice(memory_names, count);
+
+			LLVMValueRef vals[2] = {
+				types_slice,
+				names_slice,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+
+			break;
+		}
+
+		case Type_Enum:
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enum_ptr);
+
+			{
+				GB_ASSERT(t->Enum.base_type != nullptr);
+				GB_ASSERT(type_size_of(t_type_info_enum_value) == 16);
+
+
+				LLVMValueRef vals[3] = {};
+				vals[0] = lb_type_info(m, t->Enum.base_type).value;
+				if (t->Enum.fields.count > 0) {
+					auto fields = t->Enum.fields;
+					lbValue name_array  = lb_generate_array(m, t_string, fields.count,
+					                                        str_lit("$enum_names"), cast(i64)entry_index);
+					lbValue value_array = lb_generate_array(m, t_type_info_enum_value, fields.count,
+					                                        str_lit("$enum_values"), cast(i64)entry_index);
+
+
+					LLVMValueRef *name_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count);
+					LLVMValueRef *value_values = gb_alloc_array(heap_allocator(), LLVMValueRef, fields.count);
+					defer (gb_free(heap_allocator(), name_values));
+					defer (gb_free(heap_allocator(), value_values));
+
+					GB_ASSERT(is_type_integer(t->Enum.base_type));
+
+					LLVMTypeRef align_type = lb_alignment_prefix_type_hack(m, type_align_of(t));
+					LLVMTypeRef array_type = LLVMArrayType(lb_type(m, t_u8), 8);
+					LLVMTypeRef u64_type = lb_type(m, t_u64);
+
+					for_array(i, fields) {
+						ExactValue value = fields[i]->Constant.value;
+						lbValue v = lb_const_value(m, t->Enum.base_type, value);
+						LLVMValueRef zv = LLVMConstZExt(v.value, u64_type);
+						lbValue tag = lb_const_union_tag(m, t_type_info_enum_value, v.type);
+
+						LLVMValueRef vals[3] = {
+							LLVMConstNull(align_type),
+							zv,
+							tag.value,
+						};
+
+						name_values[i] = lb_const_string(m, fields[i]->token.string).value;
+						value_values[i] = LLVMConstStruct(vals, gb_count_of(vals), false);
+					}
+
+					LLVMValueRef name_init  = LLVMConstArray(lb_type(m, t_string),               name_values,  cast(unsigned)fields.count);
+					LLVMValueRef value_init = LLVMConstArray(lb_type(m, t_type_info_enum_value), value_values, cast(unsigned)fields.count);
+					LLVMSetInitializer(name_array.value,  name_init);
+					LLVMSetInitializer(value_array.value, value_init);
+
+					lbValue v_count = lb_const_int(m, t_int, fields.count);
+
+					vals[1] = llvm_const_slice(lb_array_elem(p, name_array), v_count);
+					vals[2] = llvm_const_slice(lb_array_elem(p, value_array), v_count);
+				} else {
+					vals[1] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[1]->type));
+					vals[2] = LLVMConstNull(lb_type(m, base_type(t_type_info_enum)->Struct.fields[2]->type));
+				}
+
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+			break;
+
+		case Type_Union: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
+
+			{
+				LLVMValueRef vals[6] = {};
+
+				isize variant_count = gb_max(0, t->Union.variants.count);
+				lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
+
+				// NOTE(bill): Zeroth is nil so ignore it
+				for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
+					Type *vt = t->Union.variants[variant_index];
+					lbValue tip = lb_get_type_info_ptr(m, vt);
+
+					lbValue index     = lb_const_int(m, t_int, variant_index);
+					lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
+					lb_emit_store(p, type_info, lb_type_info(m, vt));
+				}
+
+				lbValue count = lb_const_int(m, t_int, variant_count);
+				vals[0] = llvm_const_slice(memory_types, count);
+
+				i64 tag_size   = union_tag_size(t);
+				i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
+
+				if (tag_size > 0) {
+					vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
+					vals[2] = lb_type_info(m, union_tag_type(t)).value;
+				} else {
+					vals[1] = lb_const_int(m, t_uintptr, 0).value;
+					vals[2] = LLVMConstNull(lb_type(m, t_type_info_ptr));
+				}
+
+				vals[3] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
+				vals[4] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
+				vals[5] = lb_const_bool(m, t_bool, t->Union.maybe).value;
+
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+
+			break;
+		}
+
+		case Type_Struct: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_struct_ptr);
+
+			LLVMValueRef vals[11] = {};
+
+
+			{
+				lbValue is_packed       = lb_const_bool(m, t_bool, t->Struct.is_packed);
+				lbValue is_raw_union    = lb_const_bool(m, t_bool, t->Struct.is_raw_union);
+				lbValue is_custom_align = lb_const_bool(m, t_bool, t->Struct.custom_align != 0);
+				vals[5] = is_packed.value;
+				vals[6] = is_raw_union.value;
+				vals[7] = is_custom_align.value;
+
+				if (t->Struct.soa_kind != StructSoa_None) {
+					lbValue kind = lb_emit_struct_ep(p, tag, 8);
+					Type *kind_type = type_deref(kind.type);
+
+					lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
+					lbValue soa_type = lb_type_info(m, t->Struct.soa_elem);
+					lbValue soa_len = lb_const_int(m, t_int, t->Struct.soa_count);
+
+					vals[8]  = soa_kind.value;
+					vals[9]  = soa_type.value;
+					vals[10] = soa_len.value;
+				}
+			}
+
+			isize count = t->Struct.fields.count;
+			if (count > 0) {
+				lbValue memory_types   = lb_type_info_member_types_offset  (p, count);
+				lbValue memory_names   = lb_type_info_member_names_offset  (p, count);
+				lbValue memory_offsets = lb_type_info_member_offsets_offset(p, count);
+				lbValue memory_usings  = lb_type_info_member_usings_offset (p, count);
+				lbValue memory_tags    = lb_type_info_member_tags_offset   (p, count);
+
+				type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
+				for (isize source_index = 0; source_index < count; source_index++) {
+					// TODO(bill): Order fields in source order not layout order
+					Entity *f = t->Struct.fields[source_index];
+					lbValue tip = lb_get_type_info_ptr(m, f->type);
+					i64 foffset = 0;
+					if (!t->Struct.is_raw_union) {
+						foffset = t->Struct.offsets[f->Variable.field_index];
+					}
+					GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
+
+					lbValue index     = lb_const_int(m, t_int, source_index);
+					lbValue type_info = lb_emit_ptr_offset(p, memory_types,   index);
+					lbValue offset    = lb_emit_ptr_offset(p, memory_offsets, index);
+					lbValue is_using  = lb_emit_ptr_offset(p, memory_usings,  index);
+
+					lb_emit_store(p, type_info, lb_type_info(m, f->type));
+					if (f->token.string.len > 0) {
+						lbValue name = lb_emit_ptr_offset(p, memory_names,   index);
+						lb_emit_store(p, name, lb_const_string(m, f->token.string));
+					}
+					lb_emit_store(p, offset, lb_const_int(m, t_uintptr, foffset));
+					lb_emit_store(p, is_using, lb_const_bool(m, t_bool, (f->flags&EntityFlag_Using) != 0));
+
+					if (t->Struct.tags.count > 0) {
+						String tag_string = t->Struct.tags[source_index];
+						if (tag_string.len > 0) {
+							lbValue tag_ptr = lb_emit_ptr_offset(p, memory_tags, index);
+							lb_emit_store(p, tag_ptr, lb_const_string(m, tag_string));
+						}
+					}
+
+				}
+
+				lbValue cv = lb_const_int(m, t_int, count);
+				vals[0] = llvm_const_slice(memory_types,   cv);
+				vals[1] = llvm_const_slice(memory_names,   cv);
+				vals[2] = llvm_const_slice(memory_offsets, cv);
+				vals[3] = llvm_const_slice(memory_usings,  cv);
+				vals[4] = llvm_const_slice(memory_tags,    cv);
+			}
+			for (isize i = 0; i < gb_count_of(vals); i++) {
+				if (vals[i] == nullptr) {
+					vals[i]  = LLVMConstNull(lb_type(m, get_struct_field_type(tag.type, i)));
+				}
+			}
+
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+
+			break;
+		}
+
+		case Type_Map: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
+			init_map_internal_types(t);
+
+			LLVMValueRef vals[3] = {
+				lb_get_type_info_ptr(m, t->Map.key).value,
+				lb_get_type_info_ptr(m, t->Map.value).value,
+				lb_get_type_info_ptr(m, t->Map.generated_struct_type).value,
+			};
+
+			lbValue res = {};
+			res.type = type_deref(tag.type);
+			res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+			lb_emit_store(p, tag, res);
+			break;
+		}
+
+		case Type_BitField: {
+			tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_field_ptr);
+			// names:   []string;
+			// bits:    []u32;
+			// offsets: []u32;
+			isize count = t->BitField.fields.count;
+			if (count > 0) {
+				auto fields = t->BitField.fields;
+				lbValue name_array   = lb_generate_array(m, t_string, count, str_lit("$bit_field_names"),   cast(i64)entry_index);
+				lbValue bit_array    = lb_generate_array(m, t_i32,    count, str_lit("$bit_field_bits"),    cast(i64)entry_index);
+				lbValue offset_array = lb_generate_array(m, t_i32,    count, str_lit("$bit_field_offsets"), cast(i64)entry_index);
+
+				for (isize i = 0; i < count; i++) {
+					Entity *f = fields[i];
+					GB_ASSERT(f->type != nullptr);
+					GB_ASSERT(f->type->kind == Type_BitFieldValue);
+					lbValue name_ep   = lb_emit_array_epi(p, name_array,   cast(i32)i);
+					lbValue bit_ep    = lb_emit_array_epi(p, bit_array,    cast(i32)i);
+					lbValue offset_ep = lb_emit_array_epi(p, offset_array, cast(i32)i);
+
+					lb_emit_store(p, name_ep,   lb_const_string(m, f->token.string));
+					lb_emit_store(p, bit_ep,    lb_const_int(m, t_i32, f->type->BitFieldValue.bits));
+					lb_emit_store(p, offset_ep, lb_const_int(m, t_i32, t->BitField.offsets[i]));
+
+				}
+
+				lbValue v_count = lb_const_int(m, t_int, count);
+				lbValue name_array_elem = lb_array_elem(p, name_array);
+				lbValue bit_array_elem = lb_array_elem(p, bit_array);
+				lbValue offset_array_elem = lb_array_elem(p, offset_array);
+
+
+				LLVMValueRef vals[3] = {
+					llvm_const_slice(name_array_elem, v_count),
+					llvm_const_slice(bit_array_elem, v_count),
+					llvm_const_slice(offset_array_elem, v_count),
+				};
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+			break;
+		}
+
+		case Type_BitSet:
+			{
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_bit_set_ptr);
+
+				GB_ASSERT(is_type_typed(t->BitSet.elem));
+
+
+				LLVMValueRef vals[4] = {
+					lb_get_type_info_ptr(m, t->BitSet.elem).value,
+					LLVMConstNull(lb_type(m, t_type_info_ptr)),
+					lb_const_int(m, t_i64, t->BitSet.lower).value,
+					lb_const_int(m, t_i64, t->BitSet.upper).value,
+				};
+				if (t->BitSet.underlying != nullptr) {
+					vals[1] =lb_get_type_info_ptr(m, t->BitSet.underlying).value;
+				}
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+			break;
+
+		case Type_Opaque:
+			{
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_opaque_ptr);
+				LLVMValueRef vals[1] = {
+					lb_get_type_info_ptr(m, t->Opaque.elem).value,
+				};
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+			break;
+		case Type_SimdVector:
+			{
+				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_simd_vector_ptr);
+
+				LLVMValueRef vals[4] = {};
+
+				if (t->SimdVector.is_x86_mmx) {
+					vals[3] = lb_const_bool(m, t_bool, true).value;
+				} else {
+					vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
+					vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
+					vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
+				}
+
+				lbValue res = {};
+				res.type = type_deref(tag.type);
+				res.value = LLVMConstNamedStruct(lb_type(m, res.type), vals, gb_count_of(vals));
+				lb_emit_store(p, tag, res);
+			}
+			break;
+		}
+
+
+		if (tag.value != nullptr) {
+			Type *tag_type = type_deref(tag.type);
+			GB_ASSERT(is_type_named(tag_type));
+			// lb_emit_store_union_variant(p, variant_ptr, lb_emit_load(p, tag), tag_type);
+			lb_emit_store_union_variant_tag(p, variant_ptr, tag_type);
+		} else {
+			if (t != t_llvm_bool) {
+				GB_PANIC("Unhandled Type_Info variant: %s", type_to_string(t));
+			}
+		}
+	}
+}
+
+
+void lb_generate_code(lbGenerator *gen) {
+	#define TIME_SECTION(str) do { if (build_context.show_more_timings) timings_start_section(&global_timings, str_lit(str)); } while (0)
+
+	TIME_SECTION("LLVM Initializtion");
+
+	lbModule *m = &gen->module;
+	LLVMModuleRef mod = gen->module.mod;
+	CheckerInfo *info = gen->info;
+
+	Arena temp_arena = {};
+	arena_init(&temp_arena, heap_allocator());
+	gbAllocator temp_allocator = arena_allocator(&temp_arena);
+
+	gen->module.global_default_context = lb_add_global_generated(m, t_context, {});
+	gen->module.global_default_context.kind = lbAddr_Context;
+
+	auto *min_dep_set = &info->minimum_dependency_set;
+
+
+	LLVMInitializeAllTargetInfos();
+	LLVMInitializeAllTargets();
+	LLVMInitializeAllTargetMCs();
+	LLVMInitializeAllAsmPrinters();
+	LLVMInitializeAllAsmParsers();
+	LLVMInitializeAllDisassemblers();
+	LLVMInitializeNativeTarget();
+
+
+	char const *target_triple = "x86_64-pc-windows-msvc";
+	char const *target_data_layout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128";
+	LLVMSetTarget(mod, target_triple);
+
+	LLVMTargetRef target = {};
+	char *llvm_error = nullptr;
+	LLVMGetTargetFromTriple(target_triple, &target, &llvm_error);
+	GB_ASSERT(target != nullptr);
+
+	TIME_SECTION("LLVM Create Target Machine");
+
+	LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine(target, target_triple, "generic", "", LLVMCodeGenLevelNone, LLVMRelocDefault, LLVMCodeModelDefault);
+	defer (LLVMDisposeTargetMachine(target_machine));
+
+	LLVMSetModuleDataLayout(mod, LLVMCreateTargetDataLayout(target_machine));
+
+	{ // Debug Info
+		for_array(i, info->files.entries) {
+			AstFile *f = info->files.entries[i].value;
+			String fullpath = f->fullpath;
+			String filename = filename_from_path(fullpath);
+			String directory = directory_from_path(fullpath);
+			LLVMMetadataRef res = LLVMDIBuilderCreateFile(m->debug_builder,
+				cast(char const *)filename.text, filename.len,
+				cast(char const *)directory.text, directory.len);
+			map_set(&m->debug_values, hash_pointer(f), res);
+			f->llvm_metadata = res;
+		}
+
+		m->debug_compile_unit = LLVMDIBuilderCreateCompileUnit(m->debug_builder, LLVMDWARFSourceLanguageC,
+			cast(LLVMMetadataRef)m->info->files.entries[0].value->llvm_metadata,
+			"odin", 4,
+			false, "", 0,
+			1, "", 0,
+			LLVMDWARFEmissionFull, 0, true,
+			true
+		);
+	}
+
+	TIME_SECTION("LLVM Global Variables");
+
+	{
+		{ // Add type info data
+			isize max_type_info_count = info->minimum_dependency_type_info_set.entries.count+1;
+			// gb_printf_err("max_type_info_count: %td\n", max_type_info_count);
+			Type *t = alloc_type_array(t_type_info, max_type_info_count);
+			LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME);
+			LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+			LLVMSetLinkage(g, LLVMInternalLinkage);
+
+			lbValue value = {};
+			value.value = g;
+			value.type = alloc_type_pointer(t);
+			lb_global_type_info_data = lb_addr(value);
+		}
+		{ // 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_types) {
+				Type *t = m->info->type_info_types[entry_index];
+
+				isize index = lb_type_info_index(m->info, t, false);
+				if (index < 0) {
+					continue;
+				}
+
+				switch (t->kind) {
+				case Type_Union:
+					count += t->Union.variants.count;
+					break;
+				case Type_Struct:
+					count += t->Struct.fields.count;
+					break;
+				case Type_Tuple:
+					count += t->Tuple.variables.count;
+					break;
+				}
+			}
+
+			if (count > 0) {
+				{
+					char const *name = LB_TYPE_INFO_TYPES_NAME;
+					Type *t = alloc_type_array(t_type_info_ptr, count);
+					LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name);
+					LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+					LLVMSetLinkage(g, LLVMInternalLinkage);
+					lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)});
+
+				}
+				{
+					char const *name = LB_TYPE_INFO_NAMES_NAME;
+					Type *t = alloc_type_array(t_string, count);
+					LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name);
+					LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+					LLVMSetLinkage(g, LLVMInternalLinkage);
+					lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)});
+				}
+				{
+					char const *name = LB_TYPE_INFO_OFFSETS_NAME;
+					Type *t = alloc_type_array(t_uintptr, count);
+					LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name);
+					LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+					LLVMSetLinkage(g, LLVMInternalLinkage);
+					lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)});
+				}
+
+				{
+					char const *name = LB_TYPE_INFO_USINGS_NAME;
+					Type *t = alloc_type_array(t_bool, count);
+					LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name);
+					LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+					LLVMSetLinkage(g, LLVMInternalLinkage);
+					lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)});
+				}
+
+				{
+					char const *name = LB_TYPE_INFO_TAGS_NAME;
+					Type *t = alloc_type_array(t_string, count);
+					LLVMValueRef g = LLVMAddGlobal(mod, lb_type(m, t), name);
+					LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
+					LLVMSetLinkage(g, LLVMInternalLinkage);
+					lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)});
+				}
+			}
+		}
+	}
+
+
+	isize global_variable_max_count = 0;
+	Entity *entry_point = info->entry_point;
+	bool has_dll_main = false;
+	bool has_win_main = false;
+
+	for_array(i, info->entities) {
+		Entity *e = info->entities[i];
+		String name = e->token.string;
+
+		bool is_global = e->pkg != nullptr;
+
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		} else if (e->kind == Entity_Procedure && !is_global) {
+			if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
+				GB_ASSERT(e == entry_point);
+				// entry_point = e;
+			}
+			if (e->Procedure.is_export ||
+			    (e->Procedure.link_name.len > 0) ||
+			    ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) {
+				if (!has_dll_main && name == "DllMain") {
+					has_dll_main = true;
+				} else if (!has_win_main && name == "WinMain") {
+					has_win_main = true;
+				}
+			}
+		}
+	}
+
+	struct GlobalVariable {
+		lbValue var;
+		lbValue init;
+		DeclInfo *decl;
+	};
+	auto global_variables = array_make<GlobalVariable>(heap_allocator(), 0, global_variable_max_count);
+
+	for_array(i, info->variable_init_order) {
+		DeclInfo *d = info->variable_init_order[i];
+
+		Entity *e = d->entity;
+
+		if ((e->scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			continue;
+		}
+		DeclInfo *decl = decl_info_of_entity(e);
+		if (decl == nullptr) {
+			continue;
+		}
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+
+		String name = lb_get_entity_name(m, e);
+
+
+		lbValue g = {};
+		g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(heap_allocator(), name));
+		g.type = alloc_type_pointer(e->type);
+		if (e->Variable.thread_local_model != "") {
+			LLVMSetThreadLocal(g.value, true);
+
+			String m = e->Variable.thread_local_model;
+			LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
+			if (m == "default") {
+				mode = LLVMGeneralDynamicTLSModel;
+			} else if (m == "localdynamic") {
+				mode = LLVMLocalDynamicTLSModel;
+			} else if (m == "initialexec") {
+				mode = LLVMInitialExecTLSModel;
+			} else if (m == "localexec") {
+				mode = LLVMLocalExecTLSModel;
+			} else {
+				GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
+			}
+			LLVMSetThreadLocalMode(g.value, mode);
+		}
+		if (is_foreign) {
+			LLVMSetExternallyInitialized(g.value, true);
+		} else {
+			LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, e->type)));
+		}
+		if (is_export) {
+			LLVMSetLinkage(g.value, LLVMDLLExportLinkage);
+		}
+
+		GlobalVariable var = {};
+		var.var = g;
+		var.decl = decl;
+
+		if (decl->init_expr != nullptr && !is_type_any(e->type)) {
+			TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
+			if (tav.mode != Addressing_Invalid) {
+				if (tav.value.kind != ExactValue_Invalid) {
+					ExactValue v = tav.value;
+					lbValue init = lb_const_value(m, tav.type, v);
+					LLVMSetInitializer(g.value, init.value);
+				}
+			}
+		}
+
+		array_add(&global_variables, var);
+
+		lb_add_entity(m, e, g);
+		lb_add_member(m, name, g);
+	}
+
+
+	TIME_SECTION("LLVM Global Procedures and Types");
+	for_array(i, info->entities) {
+		// arena_free_all(&temp_arena);
+		// gbAllocator a = temp_allocator;
+
+		Entity *e = info->entities[i];
+		String    name  = e->token.string;
+		DeclInfo *decl  = e->decl_info;
+		Scope *   scope = e->scope;
+
+		if ((scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		Scope *package_scope = scope->parent;
+		GB_ASSERT(package_scope->flags & ScopeFlag_Pkg);
+
+		switch (e->kind) {
+		case Entity_Variable:
+			// NOTE(bill): Handled above as it requires a specific load order
+			continue;
+		case Entity_ProcGroup:
+			continue;
+
+		case Entity_TypeName:
+		case Entity_Procedure:
+			break;
+		}
+
+		bool polymorphic_struct = false;
+		if (e->type != nullptr && e->kind == Entity_TypeName) {
+			Type *bt = base_type(e->type);
+			if (bt->kind == Type_Struct) {
+				polymorphic_struct = is_type_polymorphic(bt);
+			}
+		}
+
+		if (!polymorphic_struct && !ptr_set_exists(min_dep_set, e)) {
+			// NOTE(bill): Nothing depends upon it so doesn't need to be built
+			continue;
+		}
+
+
+		String mangled_name = lb_get_entity_name(m, e);
+
+		switch (e->kind) {
+		case Entity_TypeName:
+			lb_type(m, e->type);
+			break;
+		case Entity_Procedure:
+			{
+				lbProcedure *p = lb_create_procedure(m, e);
+				array_add(&m->procedures_to_generate, p);
+			}
+			break;
+		}
+	}
+
+	TIME_SECTION("LLVM Procedure Generation");
+	for_array(i, m->procedures_to_generate) {
+		lbProcedure *p = m->procedures_to_generate[i];
+		if (p->is_done) {
+			continue;
+		}
+		if (p->body != nullptr) { // Build Procedure
+			lb_begin_procedure_body(p);
+			lb_build_stmt(p, p->body);
+			lb_end_procedure_body(p);
+			p->is_done = true;
+		}
+		lb_end_procedure(p);
+
+		// Add Flags
+		if (p->body != nullptr) {
+			if (p->name == "memcpy" || p->name == "memmove" ||
+			    p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
+			    string_starts_with(p->name, str_lit("llvm.memcpy")) ||
+			    string_starts_with(p->name, str_lit("llvm.memmove"))) {
+				p->flags |= lbProcedureFlag_WithoutMemcpyPass;
+			}
+		}
+
+		if (LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+		}
+	}
+
+
+	TIME_SECTION("LLVM Function Pass");
+
+	LLVMPassRegistryRef pass_registry = LLVMGetGlobalPassRegistry();
+
+	LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(mod);
+	defer (LLVMDisposePassManager(default_function_pass_manager));
+	{
+		LLVMAddMemCpyOptPass(default_function_pass_manager);
+		LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager);
+		LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager);
+		LLVMAddAggressiveInstCombinerPass(default_function_pass_manager);
+		LLVMAddConstantPropagationPass(default_function_pass_manager);
+		LLVMAddAggressiveDCEPass(default_function_pass_manager);
+		LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager);
+		LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager);
+		// LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager);
+	}
+
+	LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(mod);
+	defer (LLVMDisposePassManager(default_function_pass_manager_without_memcpy));
+	{
+		LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy);
+		LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy);
+		LLVMAddAggressiveInstCombinerPass(default_function_pass_manager_without_memcpy);
+		LLVMAddConstantPropagationPass(default_function_pass_manager_without_memcpy);
+		LLVMAddAggressiveDCEPass(default_function_pass_manager_without_memcpy);
+		LLVMAddMergedLoadStoreMotionPass(default_function_pass_manager_without_memcpy);
+		LLVMAddPromoteMemoryToRegisterPass(default_function_pass_manager_without_memcpy);
+		// LLVMAddUnifyFunctionExitNodesPass(default_function_pass_manager_without_memcpy);
+	}
+
+	for_array(i, m->procedures_to_generate) {
+		lbProcedure *p = m->procedures_to_generate[i];
+		if (p->body != nullptr) { // Build Procedure
+			if (p->flags & lbProcedureFlag_WithoutMemcpyPass) {
+				LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value);
+			} else {
+				LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+			}
+		}
+	}
+
+	TIME_SECTION("LLVM Runtime Creation");
+
+	lbProcedure *startup_type_info = nullptr;
+	lbProcedure *startup_context = nullptr;
+	lbProcedure *startup_runtime = nullptr;
+	{ // Startup Type Info
+		Type *params  = alloc_type_tuple();
+		Type *results = alloc_type_tuple();
+
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+
+		lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_TYPE_INFO_PROC_NAME), proc_type);
+		p->is_startup = true;
+		startup_type_info = p;
+
+		lb_begin_procedure_body(p);
+
+		lb_setup_type_info_data(p);
+
+		lb_end_procedure_body(p);
+
+		if (LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+		}
+
+		LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+	}
+	{ // Startup Context
+		Type *params  = alloc_type_tuple();
+		Type *results = alloc_type_tuple();
+
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+
+		lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_CONTEXT_PROC_NAME), proc_type);
+		p->is_startup = true;
+		startup_context = p;
+
+		lb_begin_procedure_body(p);
+
+		lb_emit_init_context(p, p->module->global_default_context);
+
+		lb_end_procedure_body(p);
+
+		if (LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+		}
+
+		LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+	}
+	{ // Startup Runtime
+		Type *params  = alloc_type_tuple();
+		Type *results = alloc_type_tuple();
+
+		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+
+		lbProcedure *p = lb_create_dummy_procedure(m, str_lit(LB_STARTUP_RUNTIME_PROC_NAME), proc_type);
+		p->is_startup = true;
+		startup_runtime = p;
+
+		lb_begin_procedure_body(p);
+
+		for_array(i, global_variables) {
+			auto *var = &global_variables[i];
+			if (var->decl->init_expr != nullptr)  {
+				var->init = lb_build_expr(p, var->decl->init_expr);
+			}
+
+			Entity *e = var->decl->entity;
+			GB_ASSERT(e->kind == Entity_Variable);
+
+			if (e->Variable.is_foreign) {
+				Entity *fl = e->Procedure.foreign_library;
+				lb_add_foreign_library_path(m, fl);
+			}
+
+			if (e->flags & EntityFlag_Static) {
+				LLVMSetLinkage(var->var.value, LLVMInternalLinkage);
+			}
+
+			if (var->init.value != nullptr) {
+				Type *t = type_deref(var->var.type);
+
+				if (is_type_any(t)) {
+					// NOTE(bill): Edge case for 'any' type
+					Type *var_type = default_type(var->init.type);
+					lbAddr g = lb_add_global_generated(m, var_type, var->init);
+					lb_addr_store(p, g, var->init);
+					lbValue gp = lb_addr_get_ptr(p, g);
+
+					lbValue data = lb_emit_struct_ep(p, var->var, 0);
+					lbValue ti   = lb_emit_struct_ep(p, var->var, 1);
+					lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
+					lb_emit_store(p, ti,   lb_type_info(m, var_type));
+				} else {
+					lb_emit_store(p, var->var, lb_emit_conv(p, var->init, t));
+				}
+			}
+		}
+
+
+		lb_end_procedure_body(p);
+
+		if (LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+		}
+
+		LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+
+		/*{
+			LLVMValueRef last_instr = LLVMGetLastInstruction(p->decl_block->block);
+			for (LLVMValueRef instr = LLVMGetFirstInstruction(p->decl_block->block);
+			     instr != last_instr;
+			     instr = LLVMGetNextInstruction(instr)) {
+				if (LLVMIsAAllocaInst(instr)) {
+					LLVMTypeRef type = LLVMGetAllocatedType(instr);
+					LLVMValueRef sz_val = LLVMSizeOf(type);
+					GB_ASSERT(LLVMIsConstant(sz_val));
+					gb_printf_err(">> 0x%p\n", sz_val);
+					LLVMTypeRef sz_type = LLVMTypeOf(sz_val);
+					gb_printf_err(">> %s\n", LLVMPrintTypeToString(sz_type));
+					unsigned long long sz = LLVMConstIntGetZExtValue(sz_val);
+					// long long sz = LLVMConstIntGetSExtValue(sz_val);
+					gb_printf_err(">> %ll\n", sz);
+				}
+			}
+		}*/
+	}
+
+	if (!(build_context.is_dll && !has_dll_main)) {
+		Type *params  = alloc_type_tuple();
+		Type *results = alloc_type_tuple();
+
+		array_init(&params->Tuple.variables, heap_allocator(), 2);
+		params->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("argc"), t_i32, false, true);
+		params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("argv"), alloc_type_pointer(t_cstring), false, true);
+
+		array_init(&results->Tuple.variables, heap_allocator(), 1);
+		results->Tuple.variables[0] = alloc_entity_param(nullptr, make_token_ident("_"),   t_i32, false, true);
+
+		Type *proc_type = alloc_type_proc(nullptr, params, 2, results, 1, false, ProcCC_CDecl);
+
+		lbProcedure *p = lb_create_dummy_procedure(m, str_lit("main"), proc_type);
+		p->is_startup = true;
+
+		lb_begin_procedure_body(p);
+
+		lbValue *found = map_get(&m->values, hash_entity(entry_point));
+		GB_ASSERT(found != nullptr);
+
+		LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+		LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_context->type)), startup_context->value, nullptr, 0, "");
+		LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, startup_runtime->type)), startup_runtime->value, nullptr, 0, "");
+		LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(m, found->type)), found->value, nullptr, 0, "");
+		LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
+
+		lb_end_procedure_body(p);
+
+		if (LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
+			gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
+			LLVMDumpValue(p->value);
+			gb_printf_err("\n\n\n\n");
+			LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
+		}
+
+		LLVMRunFunctionPassManager(default_function_pass_manager, p->value);
+	}
+
+
+	TIME_SECTION("LLVM Module Pass");
+
+	LLVMPassManagerRef module_pass_manager = LLVMCreatePassManager();
+	defer (LLVMDisposePassManager(module_pass_manager));
+	LLVMAddAlwaysInlinerPass(module_pass_manager);
+	LLVMAddStripDeadPrototypesPass(module_pass_manager);
+	// LLVMAddConstantMergePass(module_pass_manager);
+
+	LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
+	defer (LLVMPassManagerBuilderDispose(pass_manager_builder));
+	LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_context.optimization_level);
+	LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, build_context.optimization_level);
+
+	LLVMPassManagerBuilderPopulateLTOPassManager(pass_manager_builder, module_pass_manager, false, false);
+	LLVMRunPassManager(module_pass_manager, mod);
+
+	llvm_error = nullptr;
+	defer (LLVMDisposeMessage(llvm_error));
+
+	String filepath_ll  = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".ll"));
+	defer (gb_free(heap_allocator(), filepath_ll.text));
+
+	String filepath_obj = concatenate_strings(heap_allocator(), gen->output_base, STR_LIT(".obj"));
+
+
+	if (build_context.keep_temp_files) {
+		TIME_SECTION("LLVM Print Module to File");
+		LLVMPrintModuleToFile(mod, cast(char const *)filepath_ll.text, &llvm_error);
+		// exit(1);
+	}
+	LLVMDIBuilderFinalize(m->debug_builder);
+	LLVMVerifyModule(mod, LLVMAbortProcessAction, &llvm_error);
+	llvm_error = nullptr;
+
+	TIME_SECTION("LLVM Object Generation");
+
+	LLVMBool was_an_error = LLVMTargetMachineEmitToFile(target_machine, mod, cast(char *)filepath_obj.text, LLVMObjectFile, &llvm_error);
+	if (was_an_error) {
+		gb_printf_err("LLVM Error: %s\n", llvm_error);
+		gb_exit(1);
+		return;
+	}
+
+	array_add(&gen->output_object_paths, filepath_obj);
+
+#undef TIME_SECTION
+}

+ 407 - 0
src/llvm_backend.hpp

@@ -0,0 +1,407 @@
+#include "llvm-c/Core.h"
+#include "llvm-c/ExecutionEngine.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/Analysis.h"
+#include "llvm-c/Object.h"
+#include "llvm-c/BitWriter.h"
+#include "llvm-c/DebugInfo.h"
+#include "llvm-c/Transforms/AggressiveInstCombine.h"
+#include "llvm-c/Transforms/InstCombine.h"
+#include "llvm-c/Transforms/IPO.h"
+#include "llvm-c/Transforms/PassManagerBuilder.h"
+#include "llvm-c/Transforms/Scalar.h"
+#include "llvm-c/Transforms/Utils.h"
+#include "llvm-c/Transforms/Vectorize.h"
+
+struct lbProcedure;
+
+struct lbValue {
+	LLVMValueRef value;
+	Type *type;
+};
+
+
+enum lbAddrKind {
+	lbAddr_Default,
+	lbAddr_Map,
+	lbAddr_BitField,
+	lbAddr_Context,
+	lbAddr_SoaVariable,
+};
+
+struct lbAddr {
+	lbAddrKind kind;
+	lbValue addr;
+	union {
+		struct {
+			lbValue key;
+			Type *type;
+			Type *result;
+		} map;
+		struct {
+			i32 value_index;
+		} bit_field;
+		struct {
+			Selection sel;
+		} ctx;
+		struct {
+			lbValue index;
+			Ast *index_expr;
+		} soa;
+	};
+};
+
+struct lbModule {
+	LLVMModuleRef mod;
+	LLVMContextRef ctx;
+
+	CheckerInfo *info;
+
+	gbMutex mutex;
+
+	Map<LLVMTypeRef> types; // Key: Type *
+
+	Map<lbValue>  values;           // Key: Entity *
+	Map<lbValue>  members;          // Key: String
+	Map<lbProcedure *> procedures;  // Key: String
+	Map<Entity *> procedure_values; // Key: LLVMValueRef
+
+	Map<LLVMValueRef> const_strings; // Key: String
+
+	Map<lbProcedure *> anonymous_proc_lits; // Key: Ast *
+
+	lbAddr global_default_context;
+
+	u32 global_array_index;
+	u32 global_generated_index;
+	u32 nested_type_name_guid;
+
+	Array<lbProcedure *> procedures_to_generate;
+	Array<String> foreign_library_paths;
+
+
+
+	LLVMDIBuilderRef debug_builder;
+	LLVMMetadataRef debug_compile_unit;
+	Map<LLVMMetadataRef> debug_values; // Key: Pointer
+};
+
+struct lbGenerator {
+	lbModule module;
+	CheckerInfo *info;
+
+	Array<String> output_object_paths;
+	String   output_base;
+	String   output_name;
+};
+
+
+struct lbBlock {
+	LLVMBasicBlockRef block;
+	Scope *scope;
+	isize scope_index;
+	bool appended;
+
+	Array<lbBlock *> preds;
+	Array<lbBlock *> succs;
+};
+
+struct lbBranchBlocks {
+	Ast *label;
+	lbBlock *break_;
+	lbBlock *continue_;
+};
+
+
+struct lbContextData {
+	lbAddr ctx;
+	isize scope_index;
+};
+
+enum lbParamPasskind {
+	lbParamPass_Value,    // Pass by value
+	lbParamPass_Pointer,  // Pass as a pointer rather than by value
+	lbParamPass_Integer,  // Pass as an integer of the same size
+	lbParamPass_ConstRef, // Pass as a pointer but the value is immutable
+	lbParamPass_BitCast,  // Pass by value and bit cast to the correct type
+	lbParamPass_Tuple,    // Pass across multiple parameters (System V AMD64, up to 2)
+};
+
+enum lbDeferExitKind {
+	lbDeferExit_Default,
+	lbDeferExit_Return,
+	lbDeferExit_Branch,
+};
+
+enum lbDeferKind {
+	lbDefer_Node,
+	lbDefer_Instr,
+	lbDefer_Proc,
+};
+
+struct lbDefer {
+	lbDeferKind kind;
+	isize       scope_index;
+	isize       context_stack_count;
+	lbBlock *   block;
+	union {
+		Ast *stmt;
+		// NOTE(bill): 'instr' will be copied every time to create a new one
+		lbValue instr;
+		struct {
+			lbValue deferred;
+			Array<lbValue> result_as_args;
+		} proc;
+	};
+};
+
+struct lbTargetList {
+	lbTargetList *prev;
+	bool          is_block;
+	lbBlock *     break_;
+	lbBlock *     continue_;
+	lbBlock *     fallthrough_;
+};
+
+
+enum lbProcedureFlag : u32 {
+	lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+};
+
+struct lbProcedure {
+	u32 flags;
+
+	lbProcedure *parent;
+	Array<lbProcedure *> children;
+
+	Entity *     entity;
+	lbModule *   module;
+	String       name;
+	Type *       type;
+	Ast *        type_expr;
+	Ast *        body;
+	u64          tags;
+	ProcInlining inlining;
+	bool         is_foreign;
+	bool         is_export;
+	bool         is_entry_point;
+	bool         is_startup;
+
+
+	LLVMValueRef    value;
+	LLVMBuilderRef  builder;
+	bool            is_done;
+
+	lbAddr           return_ptr;
+	Array<lbValue>   params;
+	Array<lbDefer>   defer_stmts;
+	Array<lbBlock *> blocks;
+	Array<lbBranchBlocks> branch_blocks;
+	Scope *          curr_scope;
+	i32              scope_index;
+	lbBlock *        decl_block;
+	lbBlock *        entry_block;
+	lbBlock *        curr_block;
+	lbTargetList *   target_list;
+
+	Array<lbContextData> context_stack;
+
+	lbValue  return_ptr_hint_value;
+	Ast *    return_ptr_hint_ast;
+	bool     return_ptr_hint_used;
+};
+
+
+
+
+
+bool lb_init_generator(lbGenerator *gen, Checker *c);
+void lb_generate_module(lbGenerator *gen);
+
+String lb_mangle_name(lbModule *m, Entity *e);
+String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
+
+LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value);
+void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
+void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
+lbProcedure *lb_create_procedure(lbModule *module, Entity *entity);
+void lb_end_procedure(lbProcedure *p);
+
+
+LLVMTypeRef  lb_type(lbModule *m, Type *type);
+
+lbBlock *lb_create_block(lbProcedure *p, char const *name, bool append=false);
+
+lbValue lb_const_nil(lbModule *m, Type *type);
+lbValue lb_const_undef(lbModule *m, Type *type);
+lbValue lb_const_value(lbModule *m, Type *type, ExactValue value);
+lbValue lb_const_bool(lbModule *m, Type *type, bool value);
+lbValue lb_const_int(lbModule *m, Type *type, u64 value);
+
+
+lbAddr lb_addr(lbValue addr);
+Type *lb_addr_type(lbAddr const &addr);
+LLVMTypeRef lb_addr_lb_type(lbAddr const &addr);
+void lb_addr_store(lbProcedure *p, lbAddr const &addr, lbValue value);
+lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr);
+lbValue lb_emit_load(lbProcedure *p, lbValue v);
+void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value);
+
+
+void    lb_build_stmt(lbProcedure *p, Ast *stmt);
+lbValue lb_build_expr(lbProcedure *p, Ast *expr);
+lbAddr  lb_build_addr(lbProcedure *p, Ast *expr);
+void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts);
+
+lbValue lb_build_gep(lbProcedure *p, lbValue const &value, i32 index) ;
+
+lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index);
+lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index);
+lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index);
+lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index);
+lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection sel);
+lbValue lb_emit_deep_field_ev(lbProcedure *p, lbValue e, Selection sel);
+
+lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type);
+lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type);
+void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block);
+lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
+lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
+lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false);
+lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
+lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x);
+
+void lb_emit_jump(lbProcedure *p, lbBlock *target_block);
+void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *false_block);
+void lb_start_block(lbProcedure *p, lbBlock *b);
+
+lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
+
+
+lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
+void lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
+
+
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
+lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0);
+
+void lb_add_foreign_library_path(lbModule *m, Entity *e);
+
+lbValue lb_typeid(lbModule *m, Type *type, Type *typeid_type=t_typeid);
+
+lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value);
+lbDefer lb_add_defer_node(lbProcedure *p, isize scope_index, Ast *stmt);
+lbAddr lb_add_local_generated(lbProcedure *p, Type *type, bool zero_init);
+
+lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array<lbValue> const &args);
+
+
+lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index);
+lbValue lb_string_elem(lbProcedure *p, lbValue string);
+lbValue lb_string_len(lbProcedure *p, lbValue string);
+lbValue lb_cstring_len(lbProcedure *p, lbValue value);
+lbValue lb_array_elem(lbProcedure *p, lbValue array_ptr);
+lbValue lb_slice_elem(lbProcedure *p, lbValue slice);
+lbValue lb_slice_len(lbProcedure *p, lbValue slice);
+lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
+lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
+lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
+lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
+lbValue lb_map_entries(lbProcedure *p, lbValue value);
+lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value);
+lbValue lb_map_len(lbProcedure *p, lbValue value);
+lbValue lb_map_cap(lbProcedure *p, lbValue value);
+lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
+
+void lb_emit_increment(lbProcedure *p, lbValue addr);
+
+
+lbValue lb_type_info(lbModule *m, Type *type);
+
+lbValue lb_find_or_add_entity_string(lbModule *m, String const &str);
+lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent = nullptr);
+
+bool lb_is_const(lbValue value);
+bool lb_is_const_nil(lbValue value);
+String lb_get_const_string(lbModule *m, lbValue value);
+
+lbValue lb_generate_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
+lbValue lb_gen_map_header(lbProcedure *p, lbValue map_val_ptr, Type *map_type);
+lbValue lb_gen_map_key(lbProcedure *p, lbValue key, Type *key_type);
+void    lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbAddr addr, Type *map_type, lbValue map_key, lbValue map_value);
+
+
+void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
+lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
+
+
+#define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
+#define LB_STARTUP_CONTEXT_PROC_NAME   "__$startup_context"
+#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
+#define LB_TYPE_INFO_DATA_NAME       "__$type_info_data"
+#define LB_TYPE_INFO_TYPES_NAME      "__$type_info_types_data"
+#define LB_TYPE_INFO_NAMES_NAME      "__$type_info_names_data"
+#define LB_TYPE_INFO_OFFSETS_NAME    "__$type_info_offsets_data"
+#define LB_TYPE_INFO_USINGS_NAME     "__$type_info_usings_data"
+#define LB_TYPE_INFO_TAGS_NAME       "__$type_info_tags_data"
+
+
+
+enum lbCallingConventionKind {
+    lbCallingConvention_C = 0,
+    lbCallingConvention_Fast = 8,
+    lbCallingConvention_Cold = 9,
+    lbCallingConvention_GHC = 10,
+    lbCallingConvention_HiPE = 11,
+    lbCallingConvention_WebKit_JS = 12,
+    lbCallingConvention_AnyReg = 13,
+    lbCallingConvention_PreserveMost = 14,
+    lbCallingConvention_PreserveAll = 15,
+    lbCallingConvention_Swift = 16,
+    lbCallingConvention_CXX_FAST_TLS = 17,
+    lbCallingConvention_FirstTargetCC = 64,
+    lbCallingConvention_X86_StdCall = 64,
+    lbCallingConvention_X86_FastCall = 65,
+    lbCallingConvention_ARM_APCS = 66,
+    lbCallingConvention_ARM_AAPCS = 67,
+    lbCallingConvention_ARM_AAPCS_VFP = 68,
+    lbCallingConvention_MSP430_INTR = 69,
+    lbCallingConvention_X86_ThisCall = 70,
+    lbCallingConvention_PTX_Kernel = 71,
+    lbCallingConvention_PTX_Device = 72,
+    lbCallingConvention_SPIR_FUNC = 75,
+    lbCallingConvention_SPIR_KERNEL = 76,
+    lbCallingConvention_Intel_OCL_BI = 77,
+    lbCallingConvention_X86_64_SysV = 78,
+    lbCallingConvention_Win64 = 79,
+    lbCallingConvention_X86_VectorCall = 80,
+    lbCallingConvention_HHVM = 81,
+    lbCallingConvention_HHVM_C = 82,
+    lbCallingConvention_X86_INTR = 83,
+    lbCallingConvention_AVR_INTR = 84,
+    lbCallingConvention_AVR_SIGNAL = 85,
+    lbCallingConvention_AVR_BUILTIN = 86,
+    lbCallingConvention_AMDGPU_VS = 87,
+    lbCallingConvention_AMDGPU_GS = 88,
+    lbCallingConvention_AMDGPU_PS = 89,
+    lbCallingConvention_AMDGPU_CS = 90,
+    lbCallingConvention_AMDGPU_KERNEL = 91,
+    lbCallingConvention_X86_RegCall = 92,
+    lbCallingConvention_AMDGPU_HS = 93,
+    lbCallingConvention_MSP430_BUILTIN = 94,
+    lbCallingConvention_AMDGPU_LS = 95,
+    lbCallingConvention_AMDGPU_ES = 96,
+    lbCallingConvention_AArch64_VectorCall = 97,
+    lbCallingConvention_MaxID = 1023,
+};
+
+lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
+	lbCallingConvention_C,            // ProcCC_Invalid,
+	lbCallingConvention_C,            // ProcCC_Odin,
+	lbCallingConvention_C,            // ProcCC_Contextless,
+	lbCallingConvention_C,            // ProcCC_CDecl,
+	lbCallingConvention_X86_StdCall,  // ProcCC_StdCall,
+	lbCallingConvention_X86_FastCall, // ProcCC_FastCall,
+
+	lbCallingConvention_C,            // ProcCC_None,
+};

+ 754 - 399
src/main.cpp

@@ -10,6 +10,10 @@
 
 gb_global Timings global_timings = {0};
 
+#if defined(LLVM_BACKEND_SUPPORT)
+#include "llvm-c/Types.h"
+#endif
+
 
 #include "parser.hpp"
 #include "checker.hpp"
@@ -17,11 +21,18 @@ gb_global Timings global_timings = {0};
 #include "parser.cpp"
 #include "docs.cpp"
 #include "checker.cpp"
+
+
+#if defined(LLVM_BACKEND_SUPPORT)
+#include "llvm_backend.cpp"
+#endif
+
 #include "ir.cpp"
 #include "ir_opt.cpp"
 #include "ir_print.cpp"
 #include "query_data.cpp"
 
+
 #if defined(GB_SYSTEM_WINDOWS)
 // NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
 #include "microsoft_craziness.h"
@@ -121,165 +132,464 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
 
 
 
-Array<String> setup_args(int argc, char const **argv) {
-	gbAllocator a = heap_allocator();
 
-#if defined(GB_SYSTEM_WINDOWS)
-	int wargc = 0;
-	wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
-	auto args = array_make<String>(a, 0, wargc);
-	for (isize i = 0; i < wargc; i++) {
-		wchar_t *warg = wargv[i];
-		isize wlen = string16_len(warg);
-		String16 wstr = make_string16(warg, wlen);
-		String arg = string16_to_string(a, wstr);
-		if (arg.len > 0) {
-			array_add(&args, arg);
-		}
-	}
-	return args;
-#else
-	auto args = array_make<String>(a, 0, argc);
-	for (isize i = 0; i < argc; i++) {
-		String arg = make_string_c(argv[i]);
-		if (arg.len > 0) {
-			array_add(&args, arg);
-		}
-	}
-	return args;
-#endif
-}
+#if defined(LLVM_BACKEND_SUPPORT)
+i32 linker_stage(lbGenerator *gen) {
+	i32 exit_code = 0;
 
+	Timings *timings = &global_timings;
 
+	String output_base = gen->output_base;
 
+	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+#ifdef GB_SYSTEM_UNIX
+		system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s",
+				LIT(output_base), LIT(output_base), LIT(build_context.link_flags));
+#else
+		gb_printf_err("Don't know how to cross compile to selected target.\n");
+#endif
+	} else {
+	#if defined(GB_SYSTEM_WINDOWS)
+		timings_start_section(timings, str_lit("msvc-link"));
 
+		gbString lib_str = gb_string_make(heap_allocator(), "");
+		defer (gb_string_free(lib_str));
+		char lib_str_buf[1024] = {0};
 
-void print_usage_line(i32 indent, char const *fmt, ...) {
-	while (indent --> 0) {
-		gb_printf_err("\t");
-	}
-	va_list va;
-	va_start(va, fmt);
-	gb_printf_err_va(fmt, va);
-	va_end(va);
-	gb_printf_err("\n");
-}
+		char const *output_ext = "exe";
+		gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
+		defer (gb_string_free(link_settings));
 
-void usage(String argv0) {
-	print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
-	print_usage_line(0, "Usage:");
-	print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
-	print_usage_line(0, "Commands:");
-	print_usage_line(1, "build     compile .odin file, or directory of .odin files, as an executable.");
-	print_usage_line(1, "          one must contain the program's entry point, all must be in the same package.");
-	print_usage_line(1, "run       same as 'build', but also then runs the newly compiled executable.");
-	print_usage_line(1, "check     parse and type check .odin file");
-	print_usage_line(1, "query     parse, type check, and output a .json file containing information about the program");
-	print_usage_line(1, "docs      generate documentation for a .odin file");
-	print_usage_line(1, "version   print version");
-	print_usage_line(0, "");
-	print_usage_line(0, "For more information of flags, apply the flag to see what is possible");
-	print_usage_line(1, "-help");
-}
 
+		// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+		Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
 
-bool string_is_valid_identifier(String str) {
-	if (str.len <= 0) return false;
+		if (find_result.windows_sdk_version == 0) {
+			gb_printf_err("Windows SDK not found.\n");
+			return 1;
+		}
 
-	isize rune_count = 0;
+		// Add library search paths.
+		if (find_result.vs_library_path.len > 0) {
+			GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
+			GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
 
-	isize w = 0;
-	isize offset = 0;
-	while (offset < str.len) {
-		Rune r = 0;
-		w = gb_utf8_decode(str.text, str.len, &r);
-		if (r == GB_RUNE_INVALID) {
-			return false;
+			String path = {};
+			auto add_path = [&](String path) {
+				if (path[path.len-1] == '\\') {
+					path.len -= 1;
+				}
+				link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
+			};
+			add_path(find_result.windows_sdk_um_library_path);
+			add_path(find_result.windows_sdk_ucrt_library_path);
+			add_path(find_result.vs_library_path);
 		}
 
-		if (rune_count == 0) {
-			if (!rune_is_letter(r)) {
-				return false;
-			}
-		} else {
-			if (!rune_is_letter(r) && !rune_is_digit(r)) {
-				return false;
-			}
+		for_array(i, gen->module.foreign_library_paths) {
+			String lib = gen->module.foreign_library_paths[i];
+			GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
+			isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+			                        " \"%.*s\"", LIT(lib));
+			lib_str = gb_string_appendc(lib_str, lib_str_buf);
 		}
-		rune_count += 1;
-		offset += w;
-	}
 
-	return true;
-}
 
-enum BuildFlagKind {
-	BuildFlag_Invalid,
 
-	BuildFlag_Help,
+		if (build_context.is_dll) {
+			output_ext = "dll";
+			link_settings = gb_string_append_fmt(link_settings, "/DLL");
+		} else {
+			link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
+		}
 
-	BuildFlag_OutFile,
-	BuildFlag_OptimizationLevel,
-	BuildFlag_ShowTimings,
-	BuildFlag_ShowMoreTimings,
-	BuildFlag_ThreadCount,
-	BuildFlag_KeepTempFiles,
-	BuildFlag_Collection,
-	BuildFlag_Define,
-	BuildFlag_BuildMode,
-	BuildFlag_Target,
-	BuildFlag_Debug,
-	BuildFlag_DisableAssert,
-	BuildFlag_NoBoundsCheck,
-	BuildFlag_NoCRT,
-	BuildFlag_UseLLD,
-	BuildFlag_Vet,
-	BuildFlag_IgnoreUnknownAttributes,
+		if (build_context.pdb_filepath != "") {
+			link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+		}
 
-	BuildFlag_Compact,
-	BuildFlag_GlobalDefinitions,
-	BuildFlag_GoToDefinitions,
+		if (build_context.no_crt) {
+			link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
+		} else {
+			link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+		}
 
-#if defined(GB_SYSTEM_WINDOWS)
-	BuildFlag_ResourceFile,
-	BuildFlag_WindowsPdbName,
-	BuildFlag_Subsystem,
-#endif
+		if (build_context.ODIN_DEBUG) {
+			link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+		}
 
-	BuildFlag_COUNT,
-};
+		gbString object_files = gb_string_make(heap_allocator(), "");
+		defer (gb_string_free(object_files));
+		for_array(i, gen->output_object_paths) {
+			String object_path = gen->output_object_paths[i];
+			object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+		}
 
-enum BuildFlagParamKind {
-	BuildFlagParam_None,
+		char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
+		if (!build_context.use_lld) { // msvc
+			if (build_context.has_resource) {
+				exit_code = system_exec_command_line_app("msvc-link",
+					"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
+					LIT(find_result.vs_exe_path),
+					LIT(output_base),
+					LIT(build_context.resource_filepath)
+				);
 
-	BuildFlagParam_Boolean,
-	BuildFlagParam_Integer,
-	BuildFlagParam_Float,
-	BuildFlagParam_String,
+	            if (exit_code != 0) {
+					return exit_code;
+				}
 
-	BuildFlagParam_COUNT,
-};
+				exit_code = system_exec_command_line_app("msvc-link",
+					"\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+					"/nologo /incremental:no /opt:ref /subsystem:%s "
+					" %.*s "
+					" %s "
+					"",
+					LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
+					link_settings,
+					subsystem_str,
+					LIT(build_context.link_flags),
+					lib_str
+				);
+			} else {
+				exit_code = system_exec_command_line_app("msvc-link",
+					"\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
+					"/nologo /incremental:no /opt:ref /subsystem:%s "
+					" %.*s "
+					" %s "
+					"",
+					LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
+					link_settings,
+					subsystem_str,
+					LIT(build_context.link_flags),
+					lib_str
+				);
+			}
+		} else { // lld
+			exit_code = system_exec_command_line_app("msvc-link",
+				"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
+				"/nologo /incremental:no /opt:ref /subsystem:%s "
+				" %.*s "
+				" %s "
+				"",
+				LIT(build_context.ODIN_ROOT),
+				LIT(output_base), object_files, output_ext,
+				link_settings,
+				subsystem_str,
+				LIT(build_context.link_flags),
+				lib_str
+			);
+		}
+	#else
+		timings_start_section(timings, str_lit("ld-link"));
 
-struct BuildFlag {
-	BuildFlagKind      kind;
-	String             name;
-	BuildFlagParamKind param_kind;
-};
+		// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
+		char cwd[256];
+		getcwd(&cwd[0], 256);
+		//printf("%s\n", cwd);
 
-void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
-	BuildFlag flag = {kind, name, param_kind};
-	array_add(build_flags, flag);
-}
+		// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
+		//                files can be passed with -l:
+		gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+		defer (gb_string_free(lib_str));
 
-ExactValue build_param_to_exact_value(String name, String param) {
-	ExactValue value = {};
-	if (str_eq_ignore_case(param, str_lit("t")) ||
-	    str_eq_ignore_case(param, str_lit("true"))) {
-		value = exact_value_bool(true);
-	} else if (str_eq_ignore_case(param, str_lit("f")) ||
-	           str_eq_ignore_case(param, str_lit("false"))) {
-		value = exact_value_bool(false);
-	} else if (param.len > 0) {
+		for_array(i, ir_gen.module.foreign_library_paths) {
+			String lib = ir_gen.module.foreign_library_paths[i];
+
+			// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
+			//   This allows you to specify '-f' in a #foreign_system_library,
+			//   without having to implement any new syntax specifically for MacOS.
+			#if defined(GB_SYSTEM_OSX)
+				if (string_ends_with(lib, str_lit(".framework"))) {
+					// framework thingie
+					String lib_name = lib;
+					lib_name = remove_extension_from_path(lib_name);
+					lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
+				} else if (string_ends_with(lib, str_lit(".a"))) {
+					// static libs, absolute full path relative to the file in which the lib was imported from
+					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+				} else if (string_ends_with(lib, str_lit(".dylib"))) {
+					// dynamic lib
+					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+				} else {
+					// dynamic or static system lib, just link regularly searching system library paths
+					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+				}
+			#else
+				// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
+				//                since those are statically linked to at link time. shared libraries (.so) has to be
+				//                available at runtime wherever the executable is run, so we make require those to be
+				//                local to the executable (unless the system collection is used, in which case we search
+				//                the system library paths for the library file).
+				if (string_ends_with(lib, str_lit(".a"))) {
+					// static libs, absolute full path relative to the file in which the lib was imported from
+					lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
+				} else if (string_ends_with(lib, str_lit(".so"))) {
+					// dynamic lib, relative path to executable
+					// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
+					//                at runtimeto the executable
+					lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
+				} else {
+					// dynamic or static system lib, just link regularly searching system library paths
+					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+				}
+			#endif
+		}
+
+		gbString object_files = gb_string_make(heap_allocator(), "");
+		defer (gb_string_free(object_files));
+		for_array(i, gen->output_object_paths) {
+			String object_path = gen->output_object_paths[i];
+			object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+		}
+
+		// Unlike the Win32 linker code, the output_ext includes the dot, because
+		// typically executable files on *NIX systems don't have extensions.
+		String output_ext = {};
+		char const *link_settings = "";
+		char const *linker;
+		if (build_context.is_dll) {
+			// Shared libraries are .dylib on MacOS and .so on Linux.
+			#if defined(GB_SYSTEM_OSX)
+				output_ext = STR_LIT(".dylib");
+				link_settings = "-dylib -dynamic";
+			#else
+				output_ext = STR_LIT(".so");
+				link_settings = "-shared";
+			#endif
+		} else {
+			// TODO: Do I need anything here?
+			link_settings = "";
+		}
+
+		if (build_context.out_filepath.len > 0) {
+			//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
+			isize pos = string_extension_position(build_context.out_filepath);
+			if (pos > 0) {
+				output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
+			}
+		}
+
+		#if defined(GB_SYSTEM_OSX)
+			linker = "ld";
+		#else
+			// TODO(zangent): Figure out how to make ld work on Linux.
+			//   It probably has to do with including the entire CRT, but
+			//   that's quite a complicated issue to solve while remaining distro-agnostic.
+			//   Clang can figure out linker flags for us, and that's good enough _for now_.
+			linker = "clang -Wno-unused-command-line-argument";
+		#endif
+
+		exit_code = system_exec_command_line_app("ld-link",
+			"%s %s -o \"%.*s%.*s\" %s "
+			" %s "
+			" %.*s "
+			" %s "
+			#if defined(GB_SYSTEM_OSX)
+				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+				//       make sure to also change the 'mtriple' param passed to 'opt'
+				" -macosx_version_min 10.8.0 "
+				// This points the linker to where the entry point is
+				" -e _main "
+			#endif
+			, linker, object_files, LIT(output_base), LIT(output_ext),
+			lib_str,
+			"-lc -lm",
+			LIT(build_context.link_flags),
+			link_settings);
+		if (exit_code != 0) {
+			return exit_code;
+		}
+
+	#if defined(GB_SYSTEM_OSX)
+		if (build_context.ODIN_DEBUG) {
+			// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
+			// to the symbols in the object file
+			exit_code = system_exec_command_line_app("dsymutil",
+				"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
+			);
+
+			if (exit_code != 0) {
+				return exit_code;
+			}
+		}
+	#endif
+
+
+		if (build_context.show_timings) {
+			show_timings(&checker, timings);
+		}
+
+		remove_temp_files(output_base);
+
+
+	#endif
+	}
+
+	return exit_code;
+}
+#endif
+
+Array<String> setup_args(int argc, char const **argv) {
+	gbAllocator a = heap_allocator();
+
+#if defined(GB_SYSTEM_WINDOWS)
+	int wargc = 0;
+	wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
+	auto args = array_make<String>(a, 0, wargc);
+	for (isize i = 0; i < wargc; i++) {
+		wchar_t *warg = wargv[i];
+		isize wlen = string16_len(warg);
+		String16 wstr = make_string16(warg, wlen);
+		String arg = string16_to_string(a, wstr);
+		if (arg.len > 0) {
+			array_add(&args, arg);
+		}
+	}
+	return args;
+#else
+	auto args = array_make<String>(a, 0, argc);
+	for (isize i = 0; i < argc; i++) {
+		String arg = make_string_c(argv[i]);
+		if (arg.len > 0) {
+			array_add(&args, arg);
+		}
+	}
+	return args;
+#endif
+}
+
+
+
+
+
+void print_usage_line(i32 indent, char const *fmt, ...) {
+	while (indent --> 0) {
+		gb_printf_err("\t");
+	}
+	va_list va;
+	va_start(va, fmt);
+	gb_printf_err_va(fmt, va);
+	va_end(va);
+	gb_printf_err("\n");
+}
+
+void usage(String argv0) {
+	print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
+	print_usage_line(0, "Usage:");
+	print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
+	print_usage_line(0, "Commands:");
+	print_usage_line(1, "build     compile .odin file, or directory of .odin files, as an executable.");
+	print_usage_line(1, "          one must contain the program's entry point, all must be in the same package.");
+	print_usage_line(1, "run       same as 'build', but also then runs the newly compiled executable.");
+	print_usage_line(1, "check     parse and type check .odin file");
+	print_usage_line(1, "query     parse, type check, and output a .json file containing information about the program");
+	print_usage_line(1, "docs      generate documentation for a .odin file");
+	print_usage_line(1, "version   print version");
+	print_usage_line(0, "");
+	print_usage_line(0, "For more information of flags, apply the flag to see what is possible");
+	print_usage_line(1, "-help");
+}
+
+
+bool string_is_valid_identifier(String str) {
+	if (str.len <= 0) return false;
+
+	isize rune_count = 0;
+
+	isize w = 0;
+	isize offset = 0;
+	while (offset < str.len) {
+		Rune r = 0;
+		w = gb_utf8_decode(str.text, str.len, &r);
+		if (r == GB_RUNE_INVALID) {
+			return false;
+		}
+
+		if (rune_count == 0) {
+			if (!rune_is_letter(r)) {
+				return false;
+			}
+		} else {
+			if (!rune_is_letter(r) && !rune_is_digit(r)) {
+				return false;
+			}
+		}
+		rune_count += 1;
+		offset += w;
+	}
+
+	return true;
+}
+
+enum BuildFlagKind {
+	BuildFlag_Invalid,
+
+	BuildFlag_Help,
+
+	BuildFlag_OutFile,
+	BuildFlag_OptimizationLevel,
+	BuildFlag_ShowTimings,
+	BuildFlag_ShowMoreTimings,
+	BuildFlag_ThreadCount,
+	BuildFlag_KeepTempFiles,
+	BuildFlag_Collection,
+	BuildFlag_Define,
+	BuildFlag_BuildMode,
+	BuildFlag_Target,
+	BuildFlag_Debug,
+	BuildFlag_DisableAssert,
+	BuildFlag_NoBoundsCheck,
+	BuildFlag_NoCRT,
+	BuildFlag_UseLLD,
+	BuildFlag_Vet,
+	BuildFlag_UseLLVMApi,
+	BuildFlag_IgnoreUnknownAttributes,
+
+	BuildFlag_Compact,
+	BuildFlag_GlobalDefinitions,
+	BuildFlag_GoToDefinitions,
+
+#if defined(GB_SYSTEM_WINDOWS)
+	BuildFlag_ResourceFile,
+	BuildFlag_WindowsPdbName,
+	BuildFlag_Subsystem,
+#endif
+
+	BuildFlag_COUNT,
+};
+
+enum BuildFlagParamKind {
+	BuildFlagParam_None,
+
+	BuildFlagParam_Boolean,
+	BuildFlagParam_Integer,
+	BuildFlagParam_Float,
+	BuildFlagParam_String,
+
+	BuildFlagParam_COUNT,
+};
+
+struct BuildFlag {
+	BuildFlagKind      kind;
+	String             name;
+	BuildFlagParamKind param_kind;
+};
+
+void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
+	BuildFlag flag = {kind, name, param_kind};
+	array_add(build_flags, flag);
+}
+
+ExactValue build_param_to_exact_value(String name, String param) {
+	ExactValue value = {};
+	if (str_eq_ignore_case(param, str_lit("t")) ||
+	    str_eq_ignore_case(param, str_lit("true"))) {
+		value = exact_value_bool(true);
+	} else if (str_eq_ignore_case(param, str_lit("f")) ||
+	           str_eq_ignore_case(param, str_lit("false"))) {
+		value = exact_value_bool(false);
+	} else if (param.len > 0) {
 		if (param[0] == '"') {
 			value = exact_value_string(param);
 			if (value.kind == ExactValue_String) {
@@ -325,6 +635,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_NoCRT,             str_lit("no-crt"),            BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_UseLLD,            str_lit("lld"),               BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_Vet,               str_lit("vet"),               BuildFlagParam_None);
+	add_flag(&build_flags, BuildFlag_UseLLVMApi,        str_lit("llvm-api"),          BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None);
 
 	add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None);
@@ -349,6 +660,7 @@ bool parse_build_flags(Array<String> args) {
 			gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
 			continue;
 		}
+
 		String name = substring(flag, 1, flag.len);
 		isize end = 0;
 		for (; end < name.len; end++) {
@@ -694,6 +1006,10 @@ bool parse_build_flags(Array<String> args) {
 							build_context.vet = true;
 							break;
 
+						case BuildFlag_UseLLVMApi:
+							build_context.use_llvm_api = true;
+							break;
+
 						case BuildFlag_IgnoreUnknownAttributes:
 							build_context.ignore_unknown_attributes = true;
 							break;
@@ -1277,331 +1593,370 @@ int main(int arg_count, char const **arg_ptr) {
 		return 1;
 	}
 
-	irGen ir_gen = {0};
-	if (!ir_gen_init(&ir_gen, &checker)) {
-		return 1;
-	}
-	// defer (ir_gen_destroy(&ir_gen));
-
-
-	timings_start_section(timings, str_lit("llvm ir gen"));
-	ir_gen_tree(&ir_gen);
-
-	timings_start_section(timings, str_lit("llvm ir opt tree"));
-	ir_opt_tree(&ir_gen);
-
-	timings_start_section(timings, str_lit("llvm ir print"));
-	print_llvm_ir(&ir_gen);
+	if (build_context.use_llvm_api) {
+#if defined(LLVM_BACKEND_SUPPORT)
 
+		timings_start_section(timings, str_lit("LLVM API Code Gen"));
+		lbGenerator gen = {};
+		if (!lb_init_generator(&gen, &checker)) {
+			return 1;
+		}
+		lb_generate_code(&gen);
 
-	String output_name = ir_gen.output_name;
-	String output_base = ir_gen.output_base;
-
-	build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
+		i32 linker_stage_exit_count = linker_stage(&gen);
+		if (linker_stage_exit_count != 0) {
+			return linker_stage_exit_count;
+		}
 
-	i32 exit_code = 0;
+		if (build_context.show_timings) {
+			show_timings(&checker, timings);
+		}
 
-	timings_start_section(timings, str_lit("llvm-opt"));
-	exit_code = exec_llvm_opt(output_base);
-	if (exit_code != 0) {
-		return exit_code;
-	}
+		remove_temp_files(gen.output_base);
 
-	timings_start_section(timings, str_lit("llvm-llc"));
-	exit_code = exec_llvm_llc(output_base);
-	if (exit_code != 0) {
-		return exit_code;
-	}
+		if (run_output) {
+		#if defined(GB_SYSTEM_WINDOWS)
+			return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen.output_base), LIT(run_args_string));
+		#else
+			//NOTE(thebirk): This whole thing is a little leaky
+			String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
+			complete_path = path_to_full_path(heap_allocator(), complete_path);
+			return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+		#endif
+		}
 
-	if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#ifdef GB_SYSTEM_UNIX
-		system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s",
-				LIT(output_base), LIT(output_base), LIT(build_context.link_flags));
+		return 0;
 #else
-		gb_printf_err("Don't know how to cross compile to selected target.\n");
+		gb_printf_err("LLVM C API backend is not supported on this platform yet\n");
+		return 1;
 #endif
 	} else {
-	#if defined(GB_SYSTEM_WINDOWS)
-		timings_start_section(timings, str_lit("msvc-link"));
-
-		gbString lib_str = gb_string_make(heap_allocator(), "");
-		defer (gb_string_free(lib_str));
-		char lib_str_buf[1024] = {0};
-
-		char const *output_ext = "exe";
-		gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
-		defer (gb_string_free(link_settings));
-
-
-		// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
-		Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
-
-		if (find_result.windows_sdk_version == 0) {
-			gb_printf_err("Windows SDK not found.\n");
-			return 1;
-		}
-
-		// Add library search paths.
-		if (find_result.vs_library_path.len > 0) {
-			GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
-			GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
-
-			String path = {};
-			auto add_path = [&](String path) {
-				if (path[path.len-1] == '\\') {
-					path.len -= 1;
-				}
-				link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
-			};
-			add_path(find_result.windows_sdk_um_library_path);
-			add_path(find_result.windows_sdk_ucrt_library_path);
-			add_path(find_result.vs_library_path);
+		irGen ir_gen = {0};
+		if (!ir_gen_init(&ir_gen, &checker)) {
+			return 1;
 		}
+		// defer (ir_gen_destroy(&ir_gen));
 
-		for_array(i, ir_gen.module.foreign_library_paths) {
-			String lib = ir_gen.module.foreign_library_paths[i];
-			GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
-			isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
-			                        " \"%.*s\"", LIT(lib));
-			lib_str = gb_string_appendc(lib_str, lib_str_buf);
-		}
 
+		timings_start_section(timings, str_lit("llvm ir gen"));
+		ir_gen_tree(&ir_gen);
 
+		timings_start_section(timings, str_lit("llvm ir opt tree"));
+		ir_opt_tree(&ir_gen);
 
-		if (build_context.is_dll) {
-			output_ext = "dll";
-			link_settings = gb_string_append_fmt(link_settings, "/DLL");
-		} else {
-			link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
+		timings_start_section(timings, str_lit("llvm ir print"));
+		print_llvm_ir(&ir_gen);
+
+
+		String output_name = ir_gen.output_name;
+		String output_base = ir_gen.output_base;
+
+		build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
+
+		i32 exit_code = 0;
+
+		timings_start_section(timings, str_lit("llvm-opt"));
+		exit_code = exec_llvm_opt(output_base);
+		if (exit_code != 0) {
+			return exit_code;
 		}
 
-		if (build_context.pdb_filepath != "") {
-			link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+		timings_start_section(timings, str_lit("llvm-llc"));
+		exit_code = exec_llvm_llc(output_base);
+		if (exit_code != 0) {
+			return exit_code;
 		}
 
-		if (build_context.no_crt) {
-			link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
+		if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+	#ifdef GB_SYSTEM_UNIX
+			system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s",
+					LIT(output_base), LIT(output_base), LIT(build_context.link_flags));
+	#else
+			gb_printf_err("Don't know how to cross compile to selected target.\n");
+	#endif
 		} else {
-			link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
-		}
+		#if defined(GB_SYSTEM_WINDOWS)
+			timings_start_section(timings, str_lit("msvc-link"));
 
-		if (ir_gen.module.generate_debug_info) {
-			link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
-		}
+			gbString lib_str = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(lib_str));
+			char lib_str_buf[1024] = {0};
 
+			char const *output_ext = "exe";
+			gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
+			defer (gb_string_free(link_settings));
 
-		char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
-		if (!build_context.use_lld) { // msvc
-			if (build_context.has_resource) {
-				exit_code = system_exec_command_line_app("msvc-link",
-					"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
-					LIT(find_result.vs_exe_path),
-					LIT(output_base),
-					LIT(build_context.resource_filepath)
-				);
 
-	            if (exit_code != 0) {
-					return exit_code;
-				}
+			// NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+			Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
 
-				exit_code = system_exec_command_line_app("msvc-link",
-					"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
-					"/nologo /incremental:no /opt:ref /subsystem:%s "
-					" %.*s "
-					" %s "
-					"",
-					LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
-					link_settings,
-					subsystem_str,
-					LIT(build_context.link_flags),
-					lib_str
-				);
+			if (find_result.windows_sdk_version == 0) {
+				gb_printf_err("Windows SDK not found.\n");
+				return 1;
+			}
+
+			// Add library search paths.
+			if (find_result.vs_library_path.len > 0) {
+				GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
+				GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+
+				String path = {};
+				auto add_path = [&](String path) {
+					if (path[path.len-1] == '\\') {
+						path.len -= 1;
+					}
+					link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
+				};
+				add_path(find_result.windows_sdk_um_library_path);
+				add_path(find_result.windows_sdk_ucrt_library_path);
+				add_path(find_result.vs_library_path);
+			}
+
+			for_array(i, ir_gen.module.foreign_library_paths) {
+				String lib = ir_gen.module.foreign_library_paths[i];
+				GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
+				isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+				                        " \"%.*s\"", LIT(lib));
+				lib_str = gb_string_appendc(lib_str, lib_str_buf);
+			}
+
+
+
+			if (build_context.is_dll) {
+				output_ext = "dll";
+				link_settings = gb_string_append_fmt(link_settings, "/DLL");
+			} else {
+				link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
+			}
+
+			if (build_context.pdb_filepath != "") {
+				link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+			}
+
+			if (build_context.no_crt) {
+				link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
 			} else {
+				link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+			}
+
+			if (ir_gen.module.generate_debug_info) {
+				link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+			}
+
+
+			char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
+			if (!build_context.use_lld) { // msvc
+				if (build_context.has_resource) {
+					exit_code = system_exec_command_line_app("msvc-link",
+						"\"%.*src.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
+						LIT(find_result.vs_exe_path),
+						LIT(output_base),
+						LIT(build_context.resource_filepath)
+					);
+
+		            if (exit_code != 0) {
+						return exit_code;
+					}
+
+					exit_code = system_exec_command_line_app("msvc-link",
+						"\"%.*slink.exe\" \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+						"/nologo /incremental:no /opt:ref /subsystem:%s "
+						" %.*s "
+						" %s "
+						"",
+						LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), LIT(output_base), output_ext,
+						link_settings,
+						subsystem_str,
+						LIT(build_context.link_flags),
+						lib_str
+					);
+				} else {
+					exit_code = system_exec_command_line_app("msvc-link",
+						"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
+						"/nologo /incremental:no /opt:ref /subsystem:%s "
+						" %.*s "
+						" %s "
+						"",
+						LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
+						link_settings,
+						subsystem_str,
+						LIT(build_context.link_flags),
+						lib_str
+					);
+				}
+			} else { // lld
 				exit_code = system_exec_command_line_app("msvc-link",
-					"\"%.*slink.exe\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
+					"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
 					"/nologo /incremental:no /opt:ref /subsystem:%s "
 					" %.*s "
 					" %s "
 					"",
-					LIT(find_result.vs_exe_path), LIT(output_base), LIT(output_base), output_ext,
+					LIT(build_context.ODIN_ROOT),
+					LIT(output_base), LIT(output_base), output_ext,
 					link_settings,
 					subsystem_str,
 					LIT(build_context.link_flags),
 					lib_str
 				);
 			}
-		} else { // lld
-			exit_code = system_exec_command_line_app("msvc-link",
-				"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
-				"/nologo /incremental:no /opt:ref /subsystem:%s "
-				" %.*s "
-				" %s "
-				"",
-				LIT(build_context.ODIN_ROOT),
-				LIT(output_base), LIT(output_base), output_ext,
-				link_settings,
-				subsystem_str,
-				LIT(build_context.link_flags),
-				lib_str
-			);
-		}
-
-		if (exit_code != 0) {
-			return exit_code;
-		}
-
-		if (build_context.show_timings) {
-			show_timings(&checker, timings);
-		}
 
-		remove_temp_files(output_base);
+			if (exit_code != 0) {
+				return exit_code;
+			}
 
-		if (run_output) {
-			return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
-		}
-	#else
-		timings_start_section(timings, str_lit("ld-link"));
+			if (build_context.show_timings) {
+				show_timings(&checker, timings);
+			}
 
-		// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
-		char cwd[256];
-		getcwd(&cwd[0], 256);
-		//printf("%s\n", cwd);
+			remove_temp_files(output_base);
 
-		// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
-		//                files can be passed with -l:
-		gbString lib_str = gb_string_make(heap_allocator(), "-L/");
-		defer (gb_string_free(lib_str));
+			if (run_output) {
+				return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
+			}
+		#else
+			timings_start_section(timings, str_lit("ld-link"));
+
+			// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
+			char cwd[256];
+			getcwd(&cwd[0], 256);
+			//printf("%s\n", cwd);
+
+			// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
+			//                files can be passed with -l:
+			gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+			defer (gb_string_free(lib_str));
+
+			for_array(i, ir_gen.module.foreign_library_paths) {
+				String lib = ir_gen.module.foreign_library_paths[i];
+
+				// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
+				//   This allows you to specify '-f' in a #foreign_system_library,
+				//   without having to implement any new syntax specifically for MacOS.
+				#if defined(GB_SYSTEM_OSX)
+					if (string_ends_with(lib, str_lit(".framework"))) {
+						// framework thingie
+						String lib_name = lib;
+						lib_name = remove_extension_from_path(lib_name);
+						lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
+					} else if (string_ends_with(lib, str_lit(".a"))) {
+						// static libs, absolute full path relative to the file in which the lib was imported from
+						lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+					} else if (string_ends_with(lib, str_lit(".dylib"))) {
+						// dynamic lib
+						lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+					} else {
+						// dynamic or static system lib, just link regularly searching system library paths
+						lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+					}
+				#else
+					// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
+					//                since those are statically linked to at link time. shared libraries (.so) has to be
+					//                available at runtime wherever the executable is run, so we make require those to be
+					//                local to the executable (unless the system collection is used, in which case we search
+					//                the system library paths for the library file).
+					if (string_ends_with(lib, str_lit(".a"))) {
+						// static libs, absolute full path relative to the file in which the lib was imported from
+						lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
+					} else if (string_ends_with(lib, str_lit(".so"))) {
+						// dynamic lib, relative path to executable
+						// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
+						//                at runtimeto the executable
+						lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
+					} else {
+						// dynamic or static system lib, just link regularly searching system library paths
+						lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+					}
+				#endif
+			}
 
-		for_array(i, ir_gen.module.foreign_library_paths) {
-			String lib = ir_gen.module.foreign_library_paths[i];
+			// Unlike the Win32 linker code, the output_ext includes the dot, because
+			// typically executable files on *NIX systems don't have extensions.
+			String output_ext = {};
+			char const *link_settings = "";
+			char const *linker;
+			if (build_context.is_dll) {
+				// Shared libraries are .dylib on MacOS and .so on Linux.
+				#if defined(GB_SYSTEM_OSX)
+					output_ext = STR_LIT(".dylib");
+					link_settings = "-dylib -dynamic";
+				#else
+					output_ext = STR_LIT(".so");
+					link_settings = "-shared";
+				#endif
+			} else {
+				// TODO: Do I need anything here?
+				link_settings = "";
+			}
 
-			// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
-			//   This allows you to specify '-f' in a #foreign_system_library,
-			//   without having to implement any new syntax specifically for MacOS.
-			#if defined(GB_SYSTEM_OSX)
-				if (string_ends_with(lib, str_lit(".framework"))) {
-					// framework thingie
-					String lib_name = lib;
-					lib_name = remove_extension_from_path(lib_name);
-					lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
-				} else if (string_ends_with(lib, str_lit(".a"))) {
-					// static libs, absolute full path relative to the file in which the lib was imported from
-					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
-				} else if (string_ends_with(lib, str_lit(".dylib"))) {
-					// dynamic lib
-					lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
-				} else {
-					// dynamic or static system lib, just link regularly searching system library paths
-					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
-				}
-			#else
-				// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
-				//                since those are statically linked to at link time. shared libraries (.so) has to be
-				//                available at runtime wherever the executable is run, so we make require those to be
-				//                local to the executable (unless the system collection is used, in which case we search
-				//                the system library paths for the library file).
-				if (string_ends_with(lib, str_lit(".a"))) {
-					// static libs, absolute full path relative to the file in which the lib was imported from
-					lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
-				} else if (string_ends_with(lib, str_lit(".so"))) {
-					// dynamic lib, relative path to executable
-					// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
-					//                at runtimeto the executable
-					lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
-				} else {
-					// dynamic or static system lib, just link regularly searching system library paths
-					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+			if (build_context.out_filepath.len > 0) {
+				//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
+				isize pos = string_extension_position(build_context.out_filepath);
+				if (pos > 0) {
+					output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
 				}
-			#endif
-		}
+			}
 
-		// Unlike the Win32 linker code, the output_ext includes the dot, because
-		// typically executable files on *NIX systems don't have extensions.
-		String output_ext = {};
-		char const *link_settings = "";
-		char const *linker;
-		if (build_context.is_dll) {
-			// Shared libraries are .dylib on MacOS and .so on Linux.
 			#if defined(GB_SYSTEM_OSX)
-				output_ext = STR_LIT(".dylib");
-				link_settings = "-dylib -dynamic";
+				linker = "ld";
 			#else
-				output_ext = STR_LIT(".so");
-				link_settings = "-shared";
+				// TODO(zangent): Figure out how to make ld work on Linux.
+				//   It probably has to do with including the entire CRT, but
+				//   that's quite a complicated issue to solve while remaining distro-agnostic.
+				//   Clang can figure out linker flags for us, and that's good enough _for now_.
+				linker = "clang -Wno-unused-command-line-argument";
 			#endif
-		} else {
-			// TODO: Do I need anything here?
-			link_settings = "";
-		}
 
-		if (build_context.out_filepath.len > 0) {
-			//NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
-			isize pos = string_extension_position(build_context.out_filepath);
-			if (pos > 0) {
-				output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
+			exit_code = system_exec_command_line_app("ld-link",
+				"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
+				" %s "
+				" %.*s "
+				" %s "
+				#if defined(GB_SYSTEM_OSX)
+					// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+					// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+					//       make sure to also change the 'mtriple' param passed to 'opt'
+					" -macosx_version_min 10.8.0 "
+					// This points the linker to where the entry point is
+					" -e _main "
+				#endif
+				, linker, LIT(output_base), LIT(output_base), LIT(output_ext),
+				lib_str,
+				"-lc -lm",
+				LIT(build_context.link_flags),
+				link_settings);
+			if (exit_code != 0) {
+				return exit_code;
 			}
-		}
 
 		#if defined(GB_SYSTEM_OSX)
-			linker = "ld";
-		#else
-			// TODO(zangent): Figure out how to make ld work on Linux.
-			//   It probably has to do with including the entire CRT, but
-			//   that's quite a complicated issue to solve while remaining distro-agnostic.
-			//   Clang can figure out linker flags for us, and that's good enough _for now_.
-			linker = "clang -Wno-unused-command-line-argument";
-		#endif
-
-		exit_code = system_exec_command_line_app("ld-link",
-			"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
-			" %s "
-			" %.*s "
-			" %s "
-			#if defined(GB_SYSTEM_OSX)
-				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
-				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
-				//       make sure to also change the 'mtriple' param passed to 'opt'
-				" -macosx_version_min 10.8.0 "
-				// This points the linker to where the entry point is
-				" -e _main "
-			#endif
-			, linker, LIT(output_base), LIT(output_base), LIT(output_ext),
-			lib_str,
-			"-lc -lm",
-			LIT(build_context.link_flags),
-			link_settings);
-		if (exit_code != 0) {
-			return exit_code;
-		}
-
-	#if defined(GB_SYSTEM_OSX)
-		if (build_context.ODIN_DEBUG) {
-			// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
-			// to the symbols in the object file
-			exit_code = system_exec_command_line_app("dsymutil",
-				"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
-			);
+			if (build_context.ODIN_DEBUG) {
+				// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
+				// to the symbols in the object file
+				exit_code = system_exec_command_line_app("dsymutil",
+					"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
+				);
 
-			if (exit_code != 0) {
-				return exit_code;
+				if (exit_code != 0) {
+					return exit_code;
+				}
 			}
-		}
-	#endif
+		#endif
 
 
-		if (build_context.show_timings) {
-			show_timings(&checker, timings);
-		}
+			if (build_context.show_timings) {
+				show_timings(&checker, timings);
+			}
 
-		remove_temp_files(output_base);
+			remove_temp_files(output_base);
 
-		if (run_output) {
-			//NOTE(thebirk): This whole thing is a little leaky
-			String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
-			complete_path = path_to_full_path(heap_allocator(), complete_path);
-			return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+			if (run_output) {
+				//NOTE(thebirk): This whole thing is a little leaky
+				String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
+				complete_path = path_to_full_path(heap_allocator(), complete_path);
+				return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+			}
+		#endif
 		}
-	#endif
 	}
 
 	return 0;

+ 2 - 0
src/map.cpp

@@ -55,12 +55,14 @@ gb_inline HashKey hash_string(String s) {
 gb_inline HashKey hash_pointer(void *ptr) {
 	HashKey h = {HashKey_Ptr};
 	h.key = cast(u64)cast(uintptr)ptr;
+	// h.key = gb_fnv64a(&ptr, gb_size_of(void *));
 	h.ptr = ptr;
 	return h;
 }
 gb_inline HashKey hash_ptr_and_id(void *ptr, u64 id) {
 	HashKey h = {HashKey_PtrAndId};
 	h.key = cast(u64)cast(uintptr)ptr;
+	// h.key = gb_fnv64a(&ptr, gb_size_of(void *));
 	h.ptr_and_id.ptr = ptr;
 	h.ptr_and_id.id  = id;
 	return h;

+ 6 - 5
src/parser.hpp

@@ -98,7 +98,6 @@ struct AstFile {
 	Array<Ast *> imports; // 'import' 'using import'
 	isize        directive_count;
 
-
 	Ast *        curr_proc;
 	isize        error_count;
 
@@ -111,6 +110,9 @@ struct AstFile {
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;
 	TokenPos fix_prev_pos;
+
+	struct LLVMOpaqueMetadata *llvm_metadata;
+	struct LLVMOpaqueMetadata *llvm_metadata_scope;
 };
 
 
@@ -174,12 +176,10 @@ enum ProcCallingConvention {
 	ProcCC_StdCall,
 	ProcCC_FastCall,
 
-	// TODO(bill): Add extra calling conventions
-	// ProcCC_VectorCall,
-	// ProcCC_ClrCall,
-
 	ProcCC_None,
 
+	ProcCC_MAX,
+
 
 	ProcCC_ForeignBlockDefault = -1,
 };
@@ -251,6 +251,7 @@ enum StmtAllowFlag {
 		ProcInlining inlining; \
 		Token where_token; \
 		Array<Ast *> where_clauses; \
+		DeclInfo *decl; \
 	}) \
 	AST_KIND(CompoundLit, "compound literal", struct { \
 		Ast *type; \

+ 8 - 0
src/string.cpp

@@ -101,6 +101,14 @@ char *alloc_cstring(gbAllocator a, String s) {
 	return c_str;
 }
 
+char *cstring_duplicate(gbAllocator a, char const *s) {
+	isize len = gb_strlen(s);
+	char *c_str = gb_alloc_array(a, char, len+1);
+	gb_memmove(c_str, s, len);
+	c_str[len] = '\0';
+	return c_str;
+}
+
 
 
 gb_inline bool str_eq_ignore_case(String const &a, String const &b) {

+ 5 - 1
src/tokenizer.cpp

@@ -179,6 +179,10 @@ Token make_token_ident(String s) {
 	Token t = {Token_Ident, s};
 	return t;
 }
+Token make_token_ident(char const *s) {
+	Token t = {Token_Ident, make_string_c(s)};
+	return t;
+}
 
 
 struct ErrorCollector {
@@ -904,7 +908,7 @@ Token tokenizer_get_token(Tokenizer *t) {
 			}
 
 			if (token.kind == Token_Ident && token.string == "notin") {
-				token.kind = Token_not_in; 
+				token.kind = Token_not_in;
 			}
 		}