Selaa lähdekoodia

Speed up SSA generation and clang compilation

Ginger Bill 9 vuotta sitten
vanhempi
commit
455820fc84
9 muutettua tiedostoa jossa 843 lisäystä ja 652 poistoa
  1. 1 1
      build.bat
  2. 674 561
      examples/demo.odin
  3. 8 10
      examples/game.odin
  4. 29 15
      examples/runtime.odin
  5. 7 1
      src/codegen/codegen.cpp
  6. 73 32
      src/codegen/print_llvm.cpp
  7. 10 5
      src/codegen/ssa.cpp
  8. 40 27
      src/main.cpp
  9. 1 0
      src/parser.cpp

+ 1 - 1
build.bat

@@ -51,7 +51,7 @@ pushd %build_dir%
 
 	cl %compiler_settings% "..\src\main.cpp" ^
 		/link %linker_settings% -OUT:%exe_name% ^
-	&& odin run ..\examples/demo.odin
+	&& odin ..\examples/demo.odin
 	rem odin run ..\examples/demo.odin
 
 

+ 674 - 561
examples/demo.odin

@@ -1,761 +1,874 @@
-// Demo 001
+// Demo 002
 #load "basic.odin"
 #load "game.odin"
 
-main :: proc() {
-	Entity :: type union {
-		Frog: struct {
-			jump_height: f32
-		}
-		Helicopter: struct {
-			weight:     f32
-			blade_code: int
-		}
-	}
+#thread_local tls_int: int
 
-	using Entity
-	f: Entity = Frog{137}
-	h: Entity = Helicopter{123, 4}
-
-	match type ^f -> e {
-	case Frog:
-		print_string("Frog!\n")
-		print_f32(e.jump_height); nl()
-		e.jump_height = 69
-		print_f32(e.jump_height); nl()
-	case Helicopter:
-		print_string("Helicopter!\n")
-		e.weight = 1337
-	default:
-		print_string("Unknown!\n")
-	}
-}
+main :: proc() {
+	// Forenotes
 
-nl :: proc() { print_nl() }
+	// Semicolons are now optional
+	// Rule for when a semicolon is expected after a statement
+	// - If the next token is not on the same line
+	// - if the next token is a closing brace }
+	// - Otherwise, a semicolon is needed
+	//
+	// Expections:
+	// for, if, match
+	// if x := thing(); x < 123 {}
+	// for i := 0; i < 123; i++ {}
 
-/*
-// Demo 001
-#load "basic.odin"
-#load "game.odin"
+	// Q: Should I use the new rule or go back to the old one without optional semicolons?
 
-main :: proc() {
-	// _ = hellope()
-	// procedures()
-	// variables()
-	// constants()
-	// types()
-	// data_control()
-	// using_fields()
-
-	run_game()
-}
 
+	// #thread_local - see runtime.odin and above at `tls_int`
+	// #foreign_system_library - see win32.odin
 
-hellope :: proc() -> int {
-	print_string("Hellope, 世界\n")
-	return 1
+	struct_compound_literals()
+	enumerations()
+	variadic_procedures()
+	new_builtins()
+	match_statement()
+	namespacing()
+	subtyping()
+	tagged_unions()
 }
 
+struct_compound_literals :: proc() {
+	Thing :: type struct {
+		id: int
+		x: f32
+		name: string
+	}
+	{
+		t1: Thing
+		t1.id = 1
 
-// Line comment
-/*
-	Block Comment
-*/
-/*
-	Nested /*
-		Block /*
-			Comment
-		*/
-	*/
-*/
-
-apple, banana, carrot: bool
-box, carboard: bool = true, false
-// hellope_value: int = hellope() // The procedure is ran just before `main`
+		t3 := Thing{}
+		t4 := Thing{1, 2, "Fred"}
+		// t5 := Thing{1, 2}
 
-variables :: proc() {
-	i: int // initialized with zero value
-	j: int = 1
-	x, y: int = 1, 2
+		t6 := Thing{
+			name = "Tom",
+			x = 23,
+		}
+	}
+}
 
-	// Type inference
-	apple, banana, 世界 := true, 123, "world"
+enumerations :: proc() {
+	{
+		Fruit :: type enum {
+			APPLE,  // 0
+			BANANA, // 1
+			PEAR,   // 2
+		}
 
+		f := Fruit.APPLE
+		// g: int = Fruit.BANANA
+		g: int = Fruit.BANANA as int
+	}
+	{
+		Fruit1 :: type enum int {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
 
-	// Basic Types of the Language
-	//
-	// bool
-	//
-	// i8 i16 i32 i64 i128
-	// u8 u16 u32 u64 u128
-	//
-	// f32 f64
-	//
-	// int uint (size_of(int) == size_of(uint) == size_of(rawptr))
-	//
-	// rawptr (equivalent to void * in C/C++)
-	//
-	// string
-	//
-	// byte - alias for u8
-	// rune - alias for i32 // Unicode Codepoint
-	//
-	// "untyped" types can implicitly convert to any of the "typed" types
-	//                      Default Type
-	// untyped bool      -  bool
-	// untyped integer   -  int
-	// untyped float     -  f64
-	// untyped pointer   -  rawptr
-	// untyped string    -  string
-	// untyped rune      -  rune/i32
-
-
-	// Zero values
-	zero_numeric := 0
-	zero_boolean := false
-	zero_pointer := null
-	zero_string1 := "" // Escaped string
-	zero_string2 := `` // Raw string
-	// Compound types have a different kind of zero value
-
-	// Unary operators
-	// +a
-	// -a
-	// ~a
-	// !a
-
-	// Binary operators
-	// a + b    add
-	// a - b    sub
-	// a ~ b    xor
-	// a | b     or
-
-	// a * b    mul
-	// a / b    quo
-	// a % b    mod
-	// a & b    and
-	// a &~ b   bitclear == a & (~b)
-	// a << b   shl
-	// a >> b   shr
-
-	// a as Type         // Type cast
-	// a transmute Type  // Bit  cast
-
-	// a == b   eq
-	// a != b   ne
-	// a < b    lt
-	// a > b    gt
-	// a <= b   le
-	// a >= b   ge
+		Fruit2 :: type enum u8 {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
 
+		Fruit3 :: type enum u8 {
+			APPLE = 1,
+			BANANA, // 2
+			PEAR  = 5,
+			TOMATO, // 6
+		}
+	}
 }
 
-procedures :: proc() {
-	add :: proc(x: int, y: int) -> int {
-		return x + y
+variadic_procedures :: proc() {
+	print_ints :: proc(args: ..int) {
+		for i := 0; i < len(args); i++ {
+			if i > 0 {
+				print_string(", ")
+			}
+			print_int(args[i])
+		}
 	}
-	print_int(add(3, 4)) // 7
-	print_nl()
 
-	add_v2 :: proc(x, y: int) -> int {
-		return x + y
-	}
+	print_ints(); nl()
+	print_ints(1); nl()
+	print_ints(1, 2, 3); nl()
 
-	fibonacci :: proc(n: int) -> int {
-		if n < 2 {
-			return n
+	print_prefix_f32s :: proc(prefix: string, args: ..f32) {
+		print_string(prefix)
+		print_string(": ")
+		for i := 0; i < len(args); i++ {
+			if i > 0 {
+				print_string(", ")
+			}
+			print_f32(args[i])
 		}
-		return fibonacci(n-1) + fibonacci(n-2)
 	}
-	print_int(fibonacci(12)); nl()
 
+	print_prefix_f32s("a"); nl()
+	print_prefix_f32s("b", 1); nl()
+	print_prefix_f32s("c", 1, 2, 3); nl()
 
-	swap_strings :: proc(x, y: string) -> (string, string) {
-		return y, x
-	}
-	a, b := swap_strings("Hellope\n", "World\n")
-	print_string(a)
-	print_string(b)
-
-	a, b = b, a // Quirk of grammar the of multiple assignments
-	             // Swap variables
-	print_string(a)
-	print_string(b)
-
-	// Not a hint like C/C++, it's mandatory (unless it cannot do it but it will warn)
-	proc1 :: proc(a, b: int) #inline {
-		print_int(a + b)
-	}
-	proc2 :: proc(a, b: int) #no_inline {
-		print_int(a + b)
-	}
+	// Internally, the variadic procedures get allocated to an array on the stack,
+	// and this array is passed a slice
 
-	print_int(3 ''add 4)     // Infix style
-	print_nl()
-	print_int(12 'fibonacci) // Postfix style
-	print_nl()
+	// This is first step for a `print` procedure but I do not have an `any` type
+	// yet as this requires a few other things first - i.e. introspection
 }
 
+new_builtins :: proc() {
+	{
+		a := new(int)
+		b := new_slice(int, 12)
+		c := new_slice(int, 12, 16)
 
-TAU :: 6.28318530718
+		defer delete(a)
+		defer delete(b)
+		defer delete(c)
 
-constants :: proc() {
-	TAU :: 6.28318530718 // untyped float
-	WORLD_JAPANESE :: "世界" // untyped string
+		// NOTE(bill): These use the current context's allocator not the default allocator
+		// see runtime.odin
 
-	TAU_32 : f32 : 6.28318530718
-	TAU_AS_32 :: 6.28318530718 as f32
+		// Q: Should this be `free` rather than `delete` and should I overload it for slices too?
+	}
 
-	PI :: TAU / 2
+	{
+		a: int = 123
+		b: type_of_val(a) = 321
 
-	CLOSE_TO_PI :: 3
+		// NOTE(bill): This matches the current naming scheme
+		// size_of
+		// align_of
+		// offset_of
+		//
+		// size_of_val
+		// align_of_val
+		// offset_of_val
+		// type_of_val
+	}
 
-	DIFF :: (PI - CLOSE_TO_PI) / PI // Evaluated at compile time
+	{
+		// Compile time assert
+		COND :: true
+		assert(COND)
+		// assert(!COND)
 
-	a := TAU         // the constant's value becomes typed as f32
-	b := CLOSE_TO_PI // the constant's value becomes typed as int
-	c := DIFF
-}
+		// Runtime assert
+		x := true
+		assert(x)
+		// assert(!x)
+	}
 
-nl :: proc() { print_nl() }
+	{
+		x: ^u32 = null;
+		y := ptr_offset(x, 100)
+		z := ptr_sub(y, x)
+		w := slice_ptr(x, 12)
+		t := slice_ptr(x, 12, 16)
 
-types :: proc() {
+		// NOTE(bill): These are here because I've removed:
+		// pointer arithmetic
+		// pointer indexing
+		// pointer slicing
 
-	x: int = 123
-	y := x // y: int = x
-	// z: f32 = x // invalid
-	z: f32 = x as f32
+		// Reason
 
+		a: [16]int
+		a[1] = 1;
+		b := ^a
+		// Auto pointer deref
+		// consistent with record members
+		assert(b[1] == 1)
 
-	ptr_z := ^z  // Pascal notation
-	ptr_z^ = 123 // Derefence Notation
-	w: f32 = ptr_z^  // 123
-	print_f32(z); nl()
+		// Q: Should I add them back in at the cost of inconsitency?
+	}
 
-	// ^z - pointer to z
-	// z^ - z from pointer
+	{
+		a, b := -1, 2
+		print_int(min(a, b)); nl()
+		print_int(max(a, b)); nl()
+		print_int(abs(a)); nl()
+
+		// These work at compile time too
+		A :: -1
+		B :: 2
+		C :: min(A, B)
+		D :: max(A, B)
+		E :: abs(A)
+
+		print_int(C); nl()
+		print_int(D); nl()
+		print_int(E); nl()
+	}
+}
 
-	// Implicit conversion to and from rawptr
-	r_ptr: rawptr = ptr_z
-	ptr_z = r_ptr
 
+match_statement :: proc() {
+	// NOTE(bill): `match` statements are similar to `switch` statements
+	// in other languages but there are few differences
 
+	{
+		match x := 2; x {
+		case 1: // cases must be constant expression
+			print_string("1!\n")
+			// break by default
 
+		case 2:
+			s := "2!\n"; // Each case has its own scope
+			print_string(s)
+			break // explicit break
 
-	f32_array: [12]f32 // Array of 12 f32
-	f32_array[0] = 2
-	f32_array[1] = 3
-	// f32_array[-1] = 2 // Error - compile time check
-	// f32_array[13]  = 2 // Error - compile time check
-	f32_array_len := len(f32_array) // builtin procedure
-	f32_array_cap := cap(f32_array) // == len(f32_array)
+		case 3, 4: // multiple cases
+			print_string("3 or 4!\n")
 
+		case 5:
+			print_string("5!\n")
+			fallthrough // explicit fallthrough
 
-	mda: [2][3][4]int // Column-major
-	// mda[x][y][z]
+		default:
+			print_string("default!\n")
+		}
 
 
 
-	api: [2]^f32
-	papi: ^[2]^f32
+		match x := 1.5; x {
+		case 1.5:
+			print_string("1.5!\n")
+			// break by default
+		case MATH_TAU:
+			print_string("τ!\n")
+		default:
+			print_string("default!\n")
+		}
 
 
 
+		match x := "Hello"; x {
+		case "Hello":
+			print_string("greeting\n")
+			// break by default
+		case "Goodbye":
+			print_string("farewell\n")
+		default:
+			print_string("???\n")
+		}
 
-	f32_slice: []f32 // Slice / Array reference
-	f32_slice = f32_array[0:5]
-	f32_slice = f32_array[:5]
-	f32_slice = f32_array[:] // f32_array[0:len(f32_array)-1]
 
-	f32_slice = f32_array[1:5:7] // low:1, high:5, max:7
-	                              // len: 5-1 == 4
-	                              // cap: 7-1 == 6
 
 
 
-	append_success := append(^f32_slice, 1)
-	_ = append(^f32_slice, 2)
 
-	_ = copy(f32_array[0:2], f32_array[2:4]) // You can use memcpy/memmove if you want
+		a := 53
+		match {
+		case a == 1:
+			print_string("one\n")
+		case a == 2:
+			print_string("a couple\n")
+		case a < 7, a == 7:
+			print_string("a few\n")
+		case a < 12: // intentional bug
+			print_string("several\n")
+		case a >= 12 && a < 100:
+			print_string("dozens\n")
+		case a >= 100 && a < 1000:
+			print_string("hundreds\n")
+		default:
+			print_string("a fuck ton\n")
+		}
 
+		// Identical to this
+
+		b := 53
+		if b == 1 {
+			print_string("one\n")
+		} else if b == 2 {
+			print_string("a couple\n")
+		} else if b < 7 || b == 7 {
+			print_string("a few\n")
+		} else if b < 12 { // intentional bug
+			print_string("several\n")
+		} else if b >= 12 && b < 100 {
+			print_string("dozens\n")
+		} else if b >= 100 && b < 1000 {
+			print_string("hundreds\n")
+		} else {
+			print_string("a fuck ton\n")
+		}
 
+		// However, match statements allow for `break` and `fallthrough` unlike
+		// an if statement
+	}
+}
 
+Vector3 :: type struct {
+	x, y, z: f32
+}
 
+print_floats :: proc(args: ..f32) {
+	for i := 0; i < len(args); i++ {
+		if i > 0 {
+			print_string(", ")
+		}
+		print_f32(args[i])
+	}
+	print_nl()
+}
 
+namespacing :: proc() {
+	{
+		Thing :: type struct {
+			x: f32
+			name: string
+		}
 
-	s := "Hellope World"
-	sub_string: string = s[5:10]
+		a: Thing
+		a.x = 3
+		{
+			Thing :: type struct {
+				y: int
+				test: bool
+			}
 
+			b: Thing // Uses this scope's Thing
+			b.test = true
+		}
+	}
 
+	{
+		Entity :: type struct {
+			Guid :: type int
+			Nested :: type struct {
+				MyInt :: type int
+				i: int
+			}
 
+			CONSTANT :: 123
 
 
-	v0: {4}f32 // Vector of 4 f32
-	v0[0] = 1
-	v0[1] = 3
-	v0[2] = 6
-	v0[3] = 10
+			guid:   Guid
+			name:   string
+			pos:    Vector3
+			vel:    Vector3
+			nested: Nested
+		}
 
-	v1 := v0 + v0 // Simd Arithmetic
-	v1 = v1 - v0
-	v1 *= v0 // i.e. hadamard product
-	v1 /= v0
+		guid: Entity.Guid = Entity.CONSTANT
+		i: Entity.Nested.MyInt
 
-	// builtin procedure
-	v2 := swizzle(v0, 3, 2, 1, 0) // {10, 6, 3, 1}
 
-	v3: {4}bool = v0 == v2
-	// LLVM rant?
 
+		{
+			using Entity
+			guid: Guid = CONSTANT
+			using Nested
+			i: MyInt
+		}
 
 
+		{
+			using Entity.Nested
+			guid: Entity.Guid = Entity.CONSTANT
+			i: MyInt
+		}
 
 
+		{
+			e: Entity
+			using e
+			guid = 78456
+			name = "Thing"
 
+			print_int(e.guid as int); nl()
+			print_string(e.name); nl()
+		}
 
-	Vec4 :: type {4}f32
-	Array3Int :: type [3]int
+		{
+			using e: Entity
+			guid = 78456
+			name = "Thing"
 
-	Vec3 :: type struct {
-		x, y, z: f32
+			print_int(e.guid as int); nl()
+			print_string(e.name); nl()
+		}
 	}
 
-	BinaryNode :: type struct {
-		left, right: ^BinaryNode // same format as procedure argument
-		data: rawptr
-	}
+	{
+		Entity :: type struct {
+			Guid :: type int
+			Nested :: type struct {
+				MyInt :: type int
+				i: int
+			}
 
-	AddProc :: type proc(a, b: int) -> int
+			CONSTANT :: 123
 
-	Packed :: type struct #packed {
-		a: u8
-		b: u16
-		c: u32
-	}
-	assert(size_of(Packed) == 7) // builtin procedure
-	{
-		a, b: ^BinaryNode
-		a = alloc(size_of(BinaryNode)) as ^BinaryNode
-		b = alloc(size_of(BinaryNode)) as ^BinaryNode
 
-		c := BinaryNode{a, b, null}
-		c.left^.data = null
-		c.left.data = null // No need to deference
+			guid:      Guid
+			name:      string
+			using pos: Vector3
+			vel:       Vector3
+			using nested: ^Nested
+		}
 
-		dealloc(a)
-		dealloc(b)
+		e := Entity{nested = new(Entity.Nested)}
+		e.x = 123
+		e.i = Entity.CONSTANT
 	}
 
-	{
-		MyInt :: type int
-		x: int = 1
-		y: MyInt = 2
-		// z := x + y // Failure - types cannot implicit convert*
-		z := x as MyInt + y // Type cast using `as`
-	}
 
 
 	{
-		// From: Quake III Arena
-		Q_rsqrt :: proc(number: f32) -> f32 {
-			i: i32
-			x2, y: f32
-			THREE_HALFS :: 1.5
+		Entity :: type struct {
+			position: Vector3
+		}
 
-			x2 = number * 0.5
-			y = number
-			i = (^y as ^i32)^                      // evil floating point bit level hacking
-			i = 0x5f3759df - i>>1                  // what the fuck?
-			y = (^i as ^f32)^
-			y = y * (THREE_HALFS - (x2 * y *y))    // 1st iteration
-		//	y = y * (THREE_HALFS - (x2 * y *y))    // 2nd iteration, this can be removed
-			return y
+		print_pos_1 :: proc(entity: ^Entity) {
+			print_string("print_pos_1: ")
+			print_floats(entity.position.x, entity.position.y, entity.position.z)
 		}
 
-		Q_rsqrt_v2 :: proc(number: f32) -> f32 {
-			THREE_HALFS :: 1.5
+		print_pos_2 :: proc(entity: ^Entity) {
+			using entity
+			print_string("print_pos_2: ")
+			print_floats(position.x, position.y, position.z)
+		}
 
-			x2 := number * 0.5
-			y := number
-			i := y transmute i32                   // evil floating point bit level hacking
-			i = 0x5f3759df - i>>1                  // what the fuck?
-			y = i transmute f32
-			y = y * (THREE_HALFS - (x2 * y *y))    // 1st iteration
-		//	y = y * (THREE_HALFS - (x2 * y *y))    // 2nd iteration, this can be removed
-			return y
+		print_pos_3 :: proc(using entity: ^Entity) {
+			print_string("print_pos_3: ")
+			print_floats(position.x, position.y, position.z)
 		}
 
-		// NOTE(bill): transmute only works if the size of the types are equal
+		print_pos_4 :: proc(using entity: ^Entity) {
+			using position
+			print_string("print_pos_4: ")
+			print_floats(x, y, z)
+		}
 
-		/*
-			// in C
-			union {
-				i32 i
-				f32 y
-			}
-		 */
+		e := Entity{position = Vector3{1, 2, 3}}
+		print_pos_1(^e)
+		print_pos_2(^e)
+		print_pos_3(^e)
+		print_pos_4(^e)
+
+		// This is similar to C++'s `this` pointer that is implicit and only available in methods
 	}
+}
 
-	{ // Enumeration
-		Thing :: type enum {
-			APPLE,
-			FROG,
-			TREE,
-			TOMB,
-		}
-		a := Thing.APPLE
+subtyping :: proc() {
+	{
+		// C way for subtyping/subclassing
 
-		Sized :: type enum u64 {
-			APPLE,
-			FROG,
-			TREE,
-			TOMB,
+		Entity :: type struct {
+			position: Vector3
 		}
-		assert(size_of(Sized) == size_of(u64))
 
-		Certain :: type enum {
-			APPLE = 3,
-			FROG,
-			TREE = 7,
-			TOMB,
+		Frog :: type struct {
+			entity: Entity
+			jump_height: f32
 		}
-		assert(Certain.TOMB == 8)
-	}
 
-	{ // Untagged union
-		BitHack :: type raw_union {
-			i: i32
-			f: f32
-		}
-		b: BitHack
-		b.f = 123
-		print_int(b.i as int); print_nl()
+		f: Frog
+		f.entity.position = Vector3{1, 2, 3}
 
+		using f.entity
+		position = Vector3{1, 2, 3}
 
+	}
 
-		// Manually tagged union
+	{
+		// C++ way for subtyping/subclassing
 
-		EntityKind :: type enum {
-			Invalid,
-			Constant,
-			Variable,
-			TypeName,
-			Procedure,
-			Builtin,
-			Count,
+		Entity :: type struct {
+			position: Vector3
 		}
 
-		Entity :: type struct {
-			kind: EntityKind
-			guid: u64
+		Frog :: type struct {
+			using entity: Entity
+			jump_height: f32
+		}
 
-			// Other data
+		f: Frog
+		f.position = Vector3{1, 2, 3}
 
-			/*using*/
-			data: union {
-				constant: struct{}
-				variable: struct{
-					visited, is_field, used, anonymous: bool
-				}
-				procedure: struct { used: bool }
-				buitlin: struct { id: i32 }
-			}
+
+		print_pos :: proc(using entity: Entity) {
+			print_string("print_pos: ")
+			print_floats(position.x, position.y, position.z)
 		}
 
+		print_pos(f.entity)
+		print_pos(f)
 
-		// NOTE(bill): Tagged unions are not added yet but are planned
+		// Subtype Polymorphism
 	}
 
+	{
+		// More than C++ way for subtyping/subclassing
 
+		Entity :: type struct {
+			position: Vector3
+		}
 
-	{ // Compound Literals
-		a := [3]int{1, 2, 3}
-		b := [3]int{}
-		c := [..]int{1, 2, 3}
-
-		d := []int{1, 2, 3} // slice
-
-		e := {4}f32{1, 2, 3, 4}
-		f := {4}f32{1} // broadcasts to all
-		// g := {4}f32{1, 2} // require either 1 or 4 elements
+		Frog :: type struct {
+			jump_height: f32
+			using entity: ^Entity // Doesn't have to be first member!
+		}
 
-		Vec2 :: type {2}f32
+		f: Frog
+		f.entity = new(Entity)
+		f.position = Vector3{1, 2, 3}
 
-		h := Vec2{1, 2}
 
-		i := Vec2{5} * h // For strong type safety
-		// FORENOTE: 5 * h was originally allowed but it was an edge case in the
-		// compiler I didn't think it was enough to justify have it it.
+		print_pos :: proc(using entity: ^Entity) {
+			print_string("print_pos: ")
+			print_floats(position.x, position.y, position.z)
+		}
 
-		print_f32(i[0]); print_rune(#rune ",")
-		print_f32(i[1]); print_nl()
+		print_pos(f.entity)
+		print_pos(^f)
+		print_pos(f)
 	}
 
+	{
+		// More efficient subtyping
 
-
-	{ // First class procedures
-
-		do_thing :: proc(p: proc(a, b: int) -> int) {
-			print_int(p(3, 4)); nl()
+		Entity :: type struct {
+			position: Vector3
 		}
 
-		add :: proc(a, b: int) -> int {
-			return a + b
+		Frog :: type struct {
+			jump_height: f32
+			using entity: ^Entity
 		}
 
+		MAX_ENTITES :: 64
+		entities: [MAX_ENTITES]Entity
+		entity_count := 0
 
-		add_lambda := proc(a, b: int) -> int {
-			return a - b
-		} // note semicolon
-
-		do_thing(add)
-		do_thing(add_lambda)
-		do_thing(proc(a, b: int) -> int { // Anonymous
-			return a * b
-		})
-	}
-
+		next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
+			e := ^entities[entity_count^]
+			entity_count^++
+			return e
+		}
 
+		f: Frog
+		f.entity = next_entity(entities[:], ^entity_count)
+		f.position = Vector3{3, 4, 6}
 
-	{ // strings and runes
-		escaped := "Hellope World\n"
-		raw     := `Hellope World\n`
-		print_string(escaped)
-		print_string(raw); nl()
+		using f.position
+		print_floats(x, y, z)
+	}
 
-		// Crap shader example
-		shader_string :=
-`#version 410
+	{
+		// Down casting
 
-layout (location = 0) in vec3 a_position
-layout (location = 1) in vec3 a_normal;
-layout (location = 2) in vec2 a_tex_coord;
+		Entity :: type struct {
+			position: Vector3
+		}
 
-out vec3 v_position;
-out vec3 v_normal;
-out vec2 v_tex_coord;
+		Frog :: type struct {
+			jump_height: f32
+			using entity: Entity
+		}
 
-uniform mat4 u_model_view;
-uniform mat3 u_normal;
-uniform mat4 u_proj;
-uniform mat4 u_mvp;
+		f: Frog
+		f.jump_height = 564
+		e := ^f.entity
 
-void main() {
-    v_tex_coord = a_tex_coord;
-    v_normal = normalize(u_normal * a_normal);
-    v_position = vec3(u_model_view * vec4(a_position, 1.0));
+		frog := e down_cast ^Frog
+		print_string("down_cast: ")
+		print_f32(frog.jump_height); nl()
 
-    gl_Position = u_mvp * vec4(a_position, 1.0);
-}`;
+		// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
+	}
 
+	{
+		// Multiple "inheritance"
 
-		hearts1 := #rune "💕";
-		hearts2 := #rune "\U0001f495"; // 32 bit
-		hearts3 := #rune "\xf0\x9f\x92\x95";
+		Entity :: type struct {
+			position: Vector3
+		}
+		Climber :: type struct {
+			speed: f32
+		}
 
-		㐒 := #rune "㐒";
-		㐒16 := #rune "\u4db5"; // 16 bit but will be `rune`
-		// String ideas "nicked" from Go, so far. I think I might change how some of it works later.
+		Frog :: type struct {
+			using entity:  Entity
+			using climber: Climber
+		}
 	}
+}
 
-
-	{ // size, align, offset
-		Thing :: type struct {
-			a: u8;
-			b: u16;
-			c, d, e: u32;
+tagged_unions :: proc() {
+	{
+		EntityKind :: type enum {
+			INVALID,
+			FROG,
+			GIRAFFE,
+			HELICOPTER,
 		}
 
-		s := size_of(Thing);
-		a := align_of(Thing);
-		o := offset_of(Thing, b);
+		Entity :: type struct {
+			kind: EntityKind
+			using data: raw_union {
+				frog: struct {
+					jump_height: f32
+					colour: u32
+				}
+				giraffe: struct {
+					neck_length: f32
+					spot_count: int
+				}
+				helicopter: struct {
+					blade_count: int
+					weight: f32
+					pilot_name: string
+				}
+			}
+		}
 
-		t: Thing;
+		e: Entity
+		e.kind = EntityKind.FROG
+		e.frog.jump_height = 12
 
-		sv := size_of_val(t);
-		av := align_of_val(t);
-		ov := offset_of_val(t.b);
-	}
-}
+		f: type_of_val(e.frog);
 
-data_control :: proc() {
-	sum := 0
-	for i := 0; i < 12; i++ {
-		sum += 1
+		// But this is very unsafe and extremely cumbersome to write
+		// In C++, I use macros to alleviate this but it's not a solution
 	}
-	print_string("sum = "); print_int(sum); nl()
 
-	sum = 1
-	for ; sum < 1000000; {
-		sum += sum
-	}
-	print_string("sum = "); print_int(sum); nl()
+	{
+		Entity :: type union {
+			Frog: struct {
+				jump_height: f32
+				colour: u32
+			}
+			Giraffe: struct {
+				neck_length: f32
+				spot_count: int
+			}
+			Helicopter: struct {
+				blade_count: int
+				weight: f32
+				pilot_name: string
+			}
+		}
 
-	sum = 1
-	for sum < 1000000 {
-		sum += sum
-	}
-	print_string("sum = "); print_int(sum); nl()
+		using Entity
+		f1: Frog = Frog{12, 0xff9900}
+		f2: Entity = Frog{12, 0xff9900} // Implicit cast
+		f3 := Frog{12, 0xff9900} as Entity // Explicit cast
 
-	// loop
-	// for { } == for true {}
+		// f3.Frog.jump_height = 12 // There are "members" of a union
 
-	// Question: Should I separate all these concepts and rename it?
-	//
-	// range - iterable
-	// for   - c style
-	// while
-	// loop  - while true
 
-	// Notes:
-	// conditions _must_ a boolean expression
-	// i++ and i-- are statements, not expressions
 
+		e, f, g, h: Entity
+		f = Frog{12, 0xff9900}
+		g = Giraffe{2.1, 23}
+		h = Helicopter{4, 1000, "Frank"}
 
-	x := 2
-	if x < 3 {
-		print_string("x < 2\n")
-	}
 
-	// Unified initializer syntax - same as for statements
-	if x := 2; x < 3 {
-		print_string("x < 2\n")
-	}
 
-	if x := 4; x < 3 {
-		print_string("Never called\n")
-	} else {
-		print_string("This is called\n")
-	}
 
-	{ // String comparison
-		a := "Hellope"
-		b := "World"
-		if a < b {
-			print_string("a < b\n")
-		}
-		if a != b {
-			print_string("a != b\n")
+		// Requires a pointer to the union
+		// `x` will be a pointer to type of the case
+		// Q: Allow for a non pointer version with takes a copy instead?
+		match type ^f -> x {
+		case Frog:
+			print_string("Frog!\n")
+			print_f32(x.jump_height); nl()
+			x.jump_height = 3
+			print_f32(x.jump_height); nl()
+		case Giraffe:
+			print_string("Giraffe!\n")
+		case Helicopter:
+			print_string("ROFLCOPTER!\n")
+		default:
+			print_string("invalid entity\n")
 		}
 
-	}
+		fp := ^f as ^Frog // Unsafe
+		print_f32(fp.jump_height); nl()
 
 
+		// Internals of a tagged union
+		/*
+			struct {
+				data: [size_of_biggest_tag]u8
+				tag_index: int
+			}
+		*/
+		// This is to allow for pointer casting if needed
 
 
-	{ // Defer statement
-		defer print_string("日本語\n")
-		print_string("Japanese\n")
-	}
+		// Advantage over subtyping version
+		MAX_ENTITES :: 64
+		entities: [MAX_ENTITES]Entity
 
-	{
-		defer print_string("1\n")
-		defer print_string("2\n")
-		defer print_string("3\n")
+		entities[0] = Frog{}
+		entities[1] = Helicopter{}
+		// etc.
 	}
 
+
 	{
-		prev_allocator := context.allocator
-		context.allocator = __default_allocator()
-		defer context.allocator = prev_allocator
+		// Transliteration of code from this actual compiler
+		// Some stuff is missing
+		Type       :: type struct {}
+		Scope      :: type struct {}
+		Token      :: type struct {}
+		AstNode    :: type struct {}
+		ExactValue :: type struct {}
 
-		File :: type struct { filename: string }
-		FileError :: type int;
-		open_file  :: proc(filename: string) -> (File, FileError) {
-			return File{}, 0
-		}
-		close_file :: proc(f: ^File) {}
-		f, err := open_file("Test")
-		if err != 0 {
-			// handle error
+		EntityKind :: type enum {
+			Invalid,
+			Constant,
+			Variable,
+			UsingVariable,
+			TypeName,
+			Procedure,
+			Builtin,
+			Count,
 		}
-		defer close_file(^f)
-	}
 
-	for i := 0; i < 100; i++ {
-		blah := new_slice(int, 100)
-		defer {
-			defer print_string("!")
-			defer print_string("dealloc")
-			delete(blah)
-		}
+		Entity :: type struct {
+			Guid :: type i64
 
-		if i == 3 {
-			// defers called
-			continue
-		}
+			kind: EntityKind
+			guid: Guid
 
-		if i == 5 {
-			// defers called
-			return // End of procedure
-		}
+			scope: ^Scope
+			token: Token
+			type_: ^Type
 
-		if i == 8 {
-			// defers called
-			break // never happens
+			using data: raw_union {
+				Constant: struct {
+					value: ExactValue
+				}
+				Variable: struct {
+					visited:   bool // Cycle detection
+					used:      bool // Variable is used
+					is_field:  bool // Is struct field
+					anonymous: bool // Variable is an anonymous
+				}
+				UsingVariable: struct {
+				}
+				TypeName: struct {
+				}
+				Procedure: struct {
+					used: bool
+				}
+				Builtin: struct {
+					id: int
+				}
+			}
 		}
+
+		// Plus all the constructing procedures that go along with them!!!!
+		// It's a nightmare
 	}
 
-	defer print_string("It'll never happen, mate 1")
-	print_string("It'll never happen, mate 2")
-	print_string("It'll never happen, mate 3")
-}
+	{
+		Type       :: type struct {}
+		Scope      :: type struct {}
+		Token      :: type struct {}
+		AstNode    :: type struct {}
+		ExactValue :: type struct {}
 
 
-using_fields :: proc() {
-	{ // Everyday stuff
-		Vec3 :: type struct { x, y, z: f32; }
+		EntityBase :: type struct {
+			Guid :: type i64
+			guid: Guid
 
-		Entity :: type struct {
-			name: string;
-			using pos: Vec3;
-			vel: Vec3;
+			scope: ^Scope
+			token: Token
+			type_: ^Type
 		}
-		t: Entity;
-		t.y = 456;
-		print_f32(t.y);     print_nl();
-		print_f32(t.pos.y); print_nl();
-		print_f32(t.vel.y); print_nl();
-
 
-		Frog :: type struct { // Subtype (kind of)
-			using entity: Entity;
-			colour: u32;
-			jump_height: f32;
+		Entity :: type union {
+			Constant: struct {
+				using base: EntityBase
+				value: ExactValue
+			}
+			Variable: struct {
+				using base: EntityBase
+				visited:   bool // Cycle detection
+				used:      bool // Variable is used
+				is_field:  bool // Is struct field
+				anonymous: bool // Variable is an anonymous
+			}
+			UsingVariable: struct {
+				using base: EntityBase
+			}
+			TypeName: struct {
+				using base: EntityBase
+			}
+			Procedure: struct {
+				using base: EntityBase
+				used: bool
+			}
+			Builtin: struct {
+				using base: EntityBase
+				id: int
+			}
 		}
 
-		f: Frog;
-		f.y = 1337;
-		print_f32(f.y);     print_nl();
-		print_f32(f.pos.y); print_nl();
-		print_f32(f.vel.y); print_nl();
+		using Entity
 
+		e: Entity
 
-		Buffalo :: type struct {
-			using entity: Entity;
-			speed: f32;
-			noise_level: f32;
+		e = Variable{
+			base = EntityBase{},
+			used = true,
+			anonymous = false,
 		}
+
+
+
+		// Q: Allow a "base" type to be added to a union?
 	}
 
 
-	{ // Crazy Shit
-		Vec2 :: type raw_union {
-			using _xy: struct {x, y: f32};
-			e: [2]f32;
-			v: {2}f32;
+	{
+		// `Raw` unions still have uses, especially for mathematic types
+
+		Vector2 :: type raw_union {
+			using xy_: struct { x, y: f32 }
+			e: [2]f32
+			v: {2}f32
 		}
 
-		Entity :: type struct {
-			using pos: ^Vec2;
-			name: string;
-		}
-		t: Entity;
-		t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)?
-		t.x = 123;
-		print_f32(t._xy.x);     print_nl();
-		print_f32(t.pos.x);     print_nl();
-		print_f32(t.pos._xy.x); print_nl();
+		Vector3 :: type raw_union {
+			using xyz_: struct { x, y, z: f32 }
+			xy: Vector2
+			e: [3]f32
+			v: {3}f32
+		}
+
+		v2: Vector2
+		v2.x = 1
+		v2.e[0] = 1
+		v2.v[0] = 1
+
+		v3: Vector3
+		v3.x = 1
+		v3.e[0] = 1
+		v3.v[0] = 1
+		v3.xy.x = 1
+
+		// Q: If I applied using to a vector element in a raw_union,
+		// should that type now act as if it was a vector?
 	}
 }
-*/
+
+nl :: proc() { print_nl() }

+ 8 - 10
examples/game.odin

@@ -6,9 +6,7 @@ TWO_HEARTS :: #rune "💕"
 
 win32_perf_count_freq := GetQueryPerformanceFrequency()
 time_now :: proc() -> f64 {
-	if win32_perf_count_freq == 0 {
-		debug_trap()
-	}
+	assert(win32_perf_count_freq != 0)
 
 	counter: i64
 	_ = QueryPerformanceCounter(^counter)
@@ -25,10 +23,10 @@ win32_print_last_error :: proc() {
 }
 
 // Yuk!
-to_c_string :: proc(s: string) -> ^u8 {
-	c_str: ^u8 = alloc(len(s)+1)
-	memory_copy(c_str, ^s[0], len(s))
-	ptr_offset(c_str, len(s))^ = 0
+to_c_string :: proc(s: string) -> []u8 {
+	c_str := new_slice(u8, len(s)+1)
+	_ = copy(c_str, s as []byte)
+	c_str[len(s)] = 0
 	return c_str
 }
 
@@ -39,7 +37,7 @@ Window :: type struct {
 	dc:                 HDC
 	hwnd:               HWND
 	opengl_context, rc: HGLRC
-	c_title:            ^u8
+	c_title:            []u8
 }
 
 make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) {
@@ -65,7 +63,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 	}
 
 	w.hwnd = CreateWindowExA(0,
-	                         c_class_name, w.c_title,
+	                         c_class_name, ^w.c_title[0],
 	                         WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
 	                         CW_USEDEFAULT, CW_USEDEFAULT,
 	                         w.width as i32, w.height as i32,
@@ -114,7 +112,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 }
 
 destroy_window :: proc(w: ^Window) {
-	heap_free(w.c_title)
+	delete(w.c_title)
 }
 
 display_window :: proc(w: ^Window) {

+ 29 - 15
examples/runtime.odin

@@ -1,15 +1,25 @@
 #load "win32.odin"
 
-debug_trap :: proc() #foreign "llvm.debugtrap"
+assume :: proc(cond: bool) #foreign "llvm.assume"
 
-// TODO(bill): make custom heap procedures
-heap_alloc :: proc(len: int) -> rawptr {
-	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len)
-}
-heap_free :: proc(ptr: rawptr) {
-	_ = HeapFree(GetProcessHeap(), 0, ptr)
-}
+__debug_trap           :: proc()           #foreign "llvm.debugtrap"
+__trap                 :: proc()           #foreign "llvm.trap"
+read_cycle_counter     :: proc() -> u64    #foreign "llvm.readcyclecounter"
+
+bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
+bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
+bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
+
+byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
+byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
+byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
 
+fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
+fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
+
+// TODO(bill): make custom heap procedures
+heap_alloc   :: proc(len: int)   -> rawptr #foreign "malloc"
+heap_dealloc :: proc(ptr: rawptr)          #foreign "free"
 
 memory_zero :: proc(data: rawptr, len: int) {
 	d := slice_ptr(data as ^byte, len)
@@ -288,10 +298,10 @@ Allocator :: type struct {
 
 
 Context :: type struct {
-	thread_id: int
+	thread_ptr: rawptr
 
-	user_index: int
 	user_data:  rawptr
+	user_index: int
 
 	allocator: Allocator
 }
@@ -305,6 +315,10 @@ __check_context :: proc() {
 	if context.allocator.procedure == null {
 		context.allocator = __default_allocator()
 	}
+	if context.thread_ptr == null {
+		// TODO(bill):
+		// context.thread_ptr = current_thread_pointer()
+	}
 }
 
 
@@ -368,13 +382,11 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
 	using Allocation_Mode
 	match mode {
 	case ALLOC:
-		data := heap_alloc(size)
-		memory_zero(data, size)
-		return data
+		return heap_alloc(size)
 	case RESIZE:
 		return default_resize_align(old_memory, old_size, size, alignment)
 	case DEALLOC:
-		heap_free(old_memory)
+		heap_dealloc(old_memory)
 	case DEALLOC_ALL:
 		// NOTE(bill): Does nothing
 	}
@@ -394,5 +406,7 @@ __default_allocator :: proc() -> Allocator {
 
 __assert :: proc(msg: string) {
 	file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
-	debug_trap()
+	// TODO(bill): Which is better?
+	// __trap()
+	__debug_trap()
 }

+ 7 - 1
src/codegen/codegen.cpp

@@ -118,6 +118,7 @@ void ssa_gen_tree(ssaGen *s) {
 			}
 
 			if (are_strings_equal(name, original_name)) {
+			#if 0
 				Scope *scope = *map_get(&info->scopes, hash_pointer(pd->type));
 				isize count = multi_map_count(&scope->elements, hash_string(original_name));
 				if (count > 1) {
@@ -127,6 +128,7 @@ void ssa_gen_tree(ssaGen *s) {
 					name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(name), e->guid);
 					name = make_string(name_text, name_len-1);
 				}
+			#endif
 			}
 
 			ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name);
@@ -208,5 +210,9 @@ void ssa_gen_tree(ssaGen *s) {
 
 
 void ssa_gen_ir(ssaGen *s) {
-	ssa_print_llvm_ir(&s->output_file, &s->module);
+	ssaFileBuffer buf = {};
+	ssa_file_buffer_init(&buf, &s->output_file);
+	defer (ssa_file_buffer_destroy(&buf));
+
+	ssa_print_llvm_ir(&buf, &s->module);
 }

+ 73 - 32
src/codegen/print_llvm.cpp

@@ -1,20 +1,53 @@
-#define SSA_PRINT_TO_STDOUT 0
+struct ssaFileBuffer {
+	gbVirtualMemory vm;
+	isize offset;
+	gbFile *output;
+};
+
+void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) {
+	isize size = 8*gb_virtual_memory_page_size(NULL);
+	f->vm = gb_vm_alloc(NULL, size);
+	f->offset = 0;
+	f->output = output;
+}
+
+void ssa_file_buffer_destroy(ssaFileBuffer *f) {
+	if (f->offset > 0) {
+		// NOTE(bill): finish writing buffered data
+		gb_file_write(f->output, f->vm.data, f->offset);
+	}
+
+	gb_vm_free(f->vm);
+}
+
+void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) {
+	if (len > f->vm.size) {
+		gb_file_write(f->output, data, len);
+		return;
+	}
+
+	if ((f->vm.size - f->offset) < len) {
+		gb_file_write(f->output, f->vm.data, f->offset);
+		f->offset = 0;
+	}
+	u8 *cursor = cast(u8 *)f->vm.data + f->offset;
+	gb_memcopy(cursor, data, len);
+	f->offset += len;
+}
+
 
-void ssa_fprintf(gbFile *f, char *fmt, ...) {
+void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) {
 	va_list va;
 	va_start(va, fmt);
-	gb_fprintf_va(f, fmt, va);
-#if SSA_PRINT_TO_STDOUT
-	gb_printf_va(fmt, va);
-#endif
+	char buf[4096] = {};
+	isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+	ssa_file_buffer_write(f, buf, len-1);
 	va_end(va);
 }
 
-void ssa_file_write(gbFile *f, void *data, isize len) {
-	gb_file_write(f, data, len);
-#if SSA_PRINT_TO_STDOUT
-	gb_file_write(gb_file_get_standard(gbFileStandard_Output), data, len);
-#endif
+
+void ssa_file_write(ssaFileBuffer *f, void *data, isize len) {
+	ssa_file_buffer_write(f, data, len);
 }
 
 b32 ssa_valid_char(u8 c) {
@@ -35,7 +68,7 @@ b32 ssa_valid_char(u8 c) {
 	return false;
 }
 
-void ssa_print_escape_string(gbFile *f, String name, b32 print_quotes) {
+void ssa_print_escape_string(ssaFileBuffer *f, String name, b32 print_quotes) {
 	isize extra = 0;
 	for (isize i = 0; i < name.len; i++) {
 		u8 c = name.text[i];
@@ -81,18 +114,21 @@ void ssa_print_escape_string(gbFile *f, String name, b32 print_quotes) {
 
 
 
-void ssa_print_encoded_local(gbFile *f, String name) {
+void ssa_print_encoded_local(ssaFileBuffer *f, String name) {
 	ssa_fprintf(f, "%%");
 	ssa_print_escape_string(f, name, true);
 }
 
-void ssa_print_encoded_global(gbFile *f, String name) {
+void ssa_print_encoded_global(ssaFileBuffer *f, String name, b32 global_scope = false) {
 	ssa_fprintf(f, "@");
+	if (!global_scope) {
+		ssa_fprintf(f, ".");
+	}
 	ssa_print_escape_string(f, name, true);
 }
 
 
-void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
+void ssa_print_type(ssaFileBuffer *f, BaseTypeSizes s, Type *t) {
 	i64 word_bits = 8*s.word_size;
 	GB_ASSERT_NOT_NULL(t);
 	t = default_type(t);
@@ -113,8 +149,8 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 		case Basic_u128:   ssa_fprintf(f, "u128");                    break;
 		case Basic_f32:    ssa_fprintf(f, "float");                   break;
 		case Basic_f64:    ssa_fprintf(f, "double");                  break;
-		case Basic_rawptr: ssa_fprintf(f, "%%.rawptr");               break;
-		case Basic_string: ssa_fprintf(f, "%%.string");               break;
+		case Basic_rawptr: ssa_fprintf(f, "%%..rawptr");              break;
+		case Basic_string: ssa_fprintf(f, "%%..string");              break;
 		case Basic_uint:   ssa_fprintf(f, "i%lld", word_bits);        break;
 		case Basic_int:    ssa_fprintf(f, "i%lld", word_bits);        break;
 		}
@@ -213,7 +249,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 	}
 }
 
-void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type) {
+void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) {
 	type = get_base_type(type);
 	if (is_type_float(type)) {
 		value = exact_value_to_float(value);
@@ -276,12 +312,12 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type
 	}
 }
 
-void ssa_print_block_name(gbFile *f, ssaBlock *b) {
+void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) {
 	ssa_print_escape_string(f, b->label, false);
 	ssa_fprintf(f, "..%d", b->id);
 }
 
-void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) {
+void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) {
 	if (value == NULL) {
 		ssa_fprintf(f, "!!!NULL_VALUE");
 		return;
@@ -300,7 +336,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint)
 		ssa_print_encoded_local(f, value->Param.entity->token.string);
 		break;
 	case ssaValue_Proc:
-		ssa_print_encoded_global(f, value->Proc.name);
+		ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & ProcTag_foreign) != 0);
 		break;
 	case ssaValue_Instr:
 		ssa_fprintf(f, "%%%d", value->id);
@@ -308,7 +344,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint)
 	}
 }
 
-void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
+void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) {
 	GB_ASSERT(value->kind == ssaValue_Instr);
 	ssaInstr *instr = &value->Instr;
 
@@ -316,7 +352,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 
 	switch (instr->kind) {
 	case ssaInstr_StartupRuntime: {
-		ssa_fprintf(f, "call void @" SSA_STARTUP_RUNTIME_PROC_NAME "()\n");
+		ssa_fprintf(f, "call void @." SSA_STARTUP_RUNTIME_PROC_NAME "()\n");
 	} break;
 
 	case ssaInstr_Comment:
@@ -471,7 +507,9 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 				case Token_GtEq:  runtime_proc = "__string_gt"; break;
 				}
 
-				ssa_fprintf(f, " @%s(", runtime_proc);
+				ssa_fprintf(f, " ");
+				ssa_print_encoded_global(f, make_string(runtime_proc), false);
+				ssa_fprintf(f, "(");
 				ssa_print_type(f, m->sizes, type);
 				ssa_fprintf(f, " ");
 				ssa_print_value(f, m, bo->left, type);
@@ -605,7 +643,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_MemCopy: {
-		ssa_fprintf(f, "call void @memory_move");
+		ssa_fprintf(f, "call void @.memory_move");
 		ssa_fprintf(f, "(i8* ");
 		ssa_print_value(f, m, instr->CopyMemory.dst, t_rawptr);
 		ssa_fprintf(f, ", i8* ");
@@ -689,7 +727,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	}
 }
 
-void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
+void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) {
 	if (proc->body == NULL) {
 		ssa_fprintf(f, "\ndeclare ");
 	} else {
@@ -705,8 +743,11 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
 	}
 
 	ssa_fprintf(f, " ");
-
-	ssa_print_encoded_global(f, proc->name);
+	if (are_strings_equal(proc->name, make_string("main"))) {
+		ssa_print_encoded_global(f, proc->name, true);
+	} else {
+		ssa_print_encoded_global(f, proc->name, (proc->tags & ProcTag_foreign) != 0);
+	}
 	ssa_fprintf(f, "(");
 
 	if (proc_type->param_count > 0) {
@@ -760,7 +801,7 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
 	}
 }
 
-void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) {
+void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) {
 	GB_ASSERT(v->kind == ssaValue_TypeName);
 	Type *base_type = get_base_type(ssa_type(v));
 	if (!is_type_struct(base_type) && !is_type_union(base_type)) {
@@ -772,17 +813,17 @@ void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) {
 	ssa_fprintf(f, "\n");
 }
 
-void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
+void ssa_print_llvm_ir(ssaFileBuffer *f, ssaModule *m) {
 	if (m->layout.len > 0) {
 		ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
 	}
 
-	ssa_print_encoded_local(f, make_string(".string"));
+	ssa_print_encoded_local(f, make_string("..string"));
 	ssa_fprintf(f, " = type {i8*, ");
 	ssa_print_type(f, m->sizes, t_int);
 	ssa_fprintf(f, "} ; Basic_string\n");
 
-	ssa_print_encoded_local(f, make_string(".rawptr"));
+	ssa_print_encoded_local(f, make_string("..rawptr"));
 	ssa_fprintf(f, " = type i8* ; Basic_rawptr\n\n");
 
 	gb_for_array(member_index, m->members.entries) {

+ 10 - 5
src/codegen/ssa.cpp

@@ -742,9 +742,10 @@ ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize
 	return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
 }
 
-ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name, ssaValue **args, isize arg_count) {
-	ssaValue **found = map_get(&proc->module->members, hash_string(make_string(name)));
-	GB_ASSERT_MSG(found != NULL, "%s", name);
+ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) {
+	String name = make_string(name_);
+	ssaValue **found = map_get(&proc->module->members, hash_string(name));
+	GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name));
 	ssaValue *gp = *found;
 	return ssa_emit_call(proc, gp, args, arg_count);
 }
@@ -1229,7 +1230,7 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) {
 
 	isize max_len = 4+8+1;
 	u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len);
-	isize len = gb_snprintf(cast(char *)str, max_len, ".str%x", proc->module->global_string_index);
+	isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", proc->module->global_string_index);
 	proc->module->global_string_index++;
 
 	String name = make_string(str, len-1);
@@ -1238,7 +1239,8 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) {
 	Type *type = make_type_array(a, t_u8, value.value_string.len);
 	Entity *entity = make_entity_constant(a, NULL, token, type, value);
 	ssaValue *g = ssa_make_value_global(a, entity, ssa_make_value_constant(a, type, value));
-	g->Global.is_private = true;
+	g->Global.is_private  = true;
+	g->Global.is_constant = true;
 
 	map_set(&proc->module->values, hash_pointer(entity), g);
 	map_set(&proc->module->members, hash_string(name), g);
@@ -2780,6 +2782,9 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			Entity *f = *map_get(&info->foreign_procs, hash_string(name));
 			ssaValue *value = ssa_make_value_procedure(proc->module->allocator,
 			                                           proc->module, e->type, pd->type, pd->body, name);
+
+			value->Proc.tags = pd->tags;
+
 			ssa_module_add_value(proc->module, e, value);
 			ssa_build_proc(value, proc);
 			if (e == f) {

+ 40 - 27
src/main.cpp

@@ -40,14 +40,29 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 	}
 }
 
+
+#define INIT_TIMER() u64 start_time, end_time = 0, total_time = 0; start_time = gb_utc_time_now()
+#define PRINT_TIMER(section) do { \
+	u64 diff; \
+	end_time = gb_utc_time_now(); \
+	diff = end_time - start_time; \
+	total_time += diff; \
+	gb_printf_err("%s: %lld ms\n", section, diff/1000); \
+	start_time = gb_utc_time_now(); \
+} while (0)
+
+#define PRINT_ACCUMULATION() do { \
+	gb_printf_err("Total compilation time: %lld ms\n", total_time/1000); \
+} while (0)
+
+
 int main(int argc, char **argv) {
 	if (argc < 2) {
 		gb_printf_err("Please specify a .odin file\n");
 		return 1;
 	}
 
-	u64 start_time, end_time;
-	start_time = gb_utc_time_now();
+	INIT_TIMER();
 
 	init_universal_scope();
 
@@ -67,9 +82,7 @@ int main(int argc, char **argv) {
 	if (parse_files(&parser, init_filename) != ParseFile_None)
 		return 1;
 
-	end_time = gb_utc_time_now();
-	gb_printf_err("Parser: %lld ms\n", (end_time - start_time)/1000);
-	start_time = gb_utc_time_now();
+	PRINT_TIMER("Syntax Parser");
 
 	// print_ast(parser.files[0].decls, 0);
 
@@ -81,9 +94,7 @@ int main(int argc, char **argv) {
 	check_parsed_files(&checker);
 
 
-	// end_time = gb_utc_time_now();
-	// gb_printf_err("Checker: %lld ms\n", (end_time - start_time)/1000);
-	// start_time = gb_utc_time_now();
+	PRINT_TIMER("Semantic Checker");
 
 #if 1
 	ssaGen ssa = {};
@@ -93,17 +104,12 @@ int main(int argc, char **argv) {
 
 	ssa_gen_tree(&ssa);
 
-	// end_time = gb_utc_time_now();
-	// gb_printf_err("ssa tree: %lld ms\n", (end_time - start_time)/1000);
-	// start_time = gb_utc_time_now();
+	PRINT_TIMER("SSA Tree");
 
 	// TODO(bill): Speedup writing to file for IR code
 	ssa_gen_ir(&ssa);
 
-	// end_time = gb_utc_time_now();
-	// gb_printf_err("ssa ir: %lld ms\n", (end_time - start_time)/1000);
-	// start_time = gb_utc_time_now();
-
+	PRINT_TIMER("SSA IR");
 
 	char const *output_name = ssa.output_file.filename;
 	isize base_name_len = gb_path_extension(output_name)-1 - output_name;
@@ -112,16 +118,23 @@ int main(int argc, char **argv) {
 
 	i32 exit_code = 0;
 	exit_code = win32_exec_command_line_app(
-		"../misc/llvm-bin/opt -mem2reg %s -o %.*s.bc",
+		"../misc/llvm-bin/opt %s -o %.*s.bc "
+		"-memcpyopt "
+		"-mem2reg "
+		"-die -dse "
+		"-dce "
+		// "-S "
+		// "-debug-pass=Arguments "
+		"",
 		output_name, cast(int)base_name_len, output_name);
 	if (exit_code != 0)
 		return exit_code;
 
-	// end_time = gb_utc_time_now();
-	// gb_printf_err("llvm-opt: %lld ms\n", (end_time - start_time)/1000);
-	// start_time = gb_utc_time_now();
+	PRINT_TIMER("llvm-opt");
 
+#if 1
 	gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib");
+	defer (gb_string_free(lib_str));
 	char lib_str_buf[1024] = {};
 	gb_for_array(i, parser.system_libraries) {
 		String lib = parser.system_libraries[i];
@@ -130,26 +143,26 @@ int main(int argc, char **argv) {
 		lib_str = gb_string_appendc(lib_str, lib_str_buf);
 	}
 
-
 	exit_code = win32_exec_command_line_app(
-		"clang -o %.*s.exe %.*s.bc "
+		"clang %.*s.bc -o %.*s.o "
+		"-O0 "
 		"-Wno-override-module "
-		// "-nostartfiles "
-		"%s "
-		,
+		"%s",
 		cast(int)base_name_len, output_name,
 		cast(int)base_name_len, output_name,
 		lib_str);
-	gb_string_free(lib_str);
+
 	if (exit_code != 0)
 		return exit_code;
 
-	// end_time = gb_utc_time_now();
-	// gb_printf_err("clang: %lld ms\n\n\n", (end_time - start_time)/1000);
+	PRINT_TIMER("clang-compiler");
+
+	PRINT_ACCUMULATION();
 
 	if (run_output) {
 		win32_exec_command_line_app("%.*s.exe", cast(int)base_name_len, output_name);
 	}
+#endif
 #endif
 
 	return 0;

+ 1 - 0
src/parser.cpp

@@ -33,6 +33,7 @@ struct AstFile {
 
 	ErrorCollector error_collector;
 
+	// TODO(bill): Error recovery
 	// NOTE(bill): Error recovery
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;