Browse Source

Begin Type_Info
Missing stuff in records, procedures, and tuples

Ginger Bill 9 years ago
parent
commit
61fcfd6f3d

+ 1 - 1
build.bat

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

+ 29 - 867
examples/demo.odin

@@ -1,874 +1,36 @@
-// Demo 002
 #load "basic.odin"
-#load "game.odin"
-
-#thread_local tls_int: int
-
-main :: proc() {
-	// Forenotes
-
-	// 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++ {}
-
-	// Q: Should I use the new rule or go back to the old one without optional semicolons?
-
-
-	// #thread_local - see runtime.odin and above at `tls_int`
-	// #foreign_system_library - see win32.odin
-
-	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
-
-		t3 := Thing{}
-		t4 := Thing{1, 2, "Fred"}
-		// t5 := Thing{1, 2}
-
-		t6 := Thing{
-			name = "Tom",
-			x = 23,
-		}
-	}
-}
-
-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,
-		}
-
-		Fruit2 :: type enum u8 {
-			APPLE,
-			BANANA,
-			PEAR,
-		}
-
-		Fruit3 :: type enum u8 {
-			APPLE = 1,
-			BANANA, // 2
-			PEAR  = 5,
-			TOMATO, // 6
-		}
-	}
-}
-
-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_ints(); nl()
-	print_ints(1); nl()
-	print_ints(1, 2, 3); nl()
-
-	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])
-		}
-	}
-
-	print_prefix_f32s("a"); nl()
-	print_prefix_f32s("b", 1); nl()
-	print_prefix_f32s("c", 1, 2, 3); nl()
-
-	// Internally, the variadic procedures get allocated to an array on the stack,
-	// and this array is passed a slice
-
-	// 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)
-
-		defer delete(a)
-		defer delete(b)
-		defer delete(c)
-
-		// NOTE(bill): These use the current context's allocator not the default allocator
-		// see runtime.odin
-
-		// Q: Should this be `free` rather than `delete` and should I overload it for slices too?
-	}
-
-	{
-		a: int = 123
-		b: type_of_val(a) = 321
-
-		// 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
-	}
-
-	{
-		// Compile time assert
-		COND :: true
-		assert(COND)
-		// assert(!COND)
-
-		// Runtime assert
-		x := true
-		assert(x)
-		// assert(!x)
-	}
-
-	{
-		x: ^u32 = null;
-		y := ptr_offset(x, 100)
-		z := ptr_sub(y, x)
-		w := slice_ptr(x, 12)
-		t := slice_ptr(x, 12, 16)
-
-		// NOTE(bill): These are here because I've removed:
-		// pointer arithmetic
-		// pointer indexing
-		// pointer slicing
-
-		// Reason
-
-		a: [16]int
-		a[1] = 1;
-		b := ^a
-		// Auto pointer deref
-		// consistent with record members
-		assert(b[1] == 1)
-
-		// Q: Should I add them back in at the cost of inconsitency?
-	}
-
-	{
-		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()
-	}
-}
-
-
-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
-
-		case 3, 4: // multiple cases
-			print_string("3 or 4!\n")
-
-		case 5:
-			print_string("5!\n")
-			fallthrough // explicit fallthrough
-
-		default:
-			print_string("default!\n")
-		}
-
-
-
-		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")
-		}
-
-
-
-
-
-
-		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
-		}
-
-		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
-
-
-			guid:   Guid
-			name:   string
-			pos:    Vector3
-			vel:    Vector3
-			nested: Nested
-		}
-
-		guid: Entity.Guid = Entity.CONSTANT
-		i: Entity.Nested.MyInt
-
-
-
-		{
-			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()
-		}
-
-		{
-			using e: Entity
-			guid = 78456
-			name = "Thing"
-
-			print_int(e.guid as int); nl()
-			print_string(e.name); nl()
-		}
-	}
-
-	{
-		Entity :: type struct {
-			Guid :: type int
-			Nested :: type struct {
-				MyInt :: type int
-				i: int
-			}
-
-			CONSTANT :: 123
-
-
-			guid:      Guid
-			name:      string
-			using pos: Vector3
-			vel:       Vector3
-			using nested: ^Nested
-		}
-
-		e := Entity{nested = new(Entity.Nested)}
-		e.x = 123
-		e.i = Entity.CONSTANT
-	}
-
-
-
-	{
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		print_pos_1 :: proc(entity: ^Entity) {
-			print_string("print_pos_1: ")
-			print_floats(entity.position.x, entity.position.y, entity.position.z)
-		}
-
-		print_pos_2 :: proc(entity: ^Entity) {
-			using entity
-			print_string("print_pos_2: ")
-			print_floats(position.x, position.y, position.z)
-		}
-
-		print_pos_3 :: proc(using entity: ^Entity) {
-			print_string("print_pos_3: ")
-			print_floats(position.x, position.y, position.z)
-		}
-
-		print_pos_4 :: proc(using entity: ^Entity) {
-			using position
-			print_string("print_pos_4: ")
-			print_floats(x, y, z)
-		}
-
-		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
-	}
-}
-
-subtyping :: proc() {
-	{
-		// C way for subtyping/subclassing
-
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		Frog :: type struct {
-			entity: Entity
-			jump_height: f32
-		}
-
-		f: Frog
-		f.entity.position = Vector3{1, 2, 3}
-
-		using f.entity
-		position = Vector3{1, 2, 3}
-
-	}
-
-	{
-		// C++ way for subtyping/subclassing
-
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		Frog :: type struct {
-			using entity: Entity
-			jump_height: f32
-		}
-
-		f: Frog
-		f.position = Vector3{1, 2, 3}
-
-
-		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)
-
-		// Subtype Polymorphism
-	}
-
-	{
-		// More than C++ way for subtyping/subclassing
-
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		Frog :: type struct {
-			jump_height: f32
-			using entity: ^Entity // Doesn't have to be first member!
-		}
-
-		f: Frog
-		f.entity = new(Entity)
-		f.position = Vector3{1, 2, 3}
-
-
-		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)
-		print_pos(f)
-	}
-
-	{
-		// More efficient subtyping
-
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		Frog :: type struct {
-			jump_height: f32
-			using entity: ^Entity
-		}
-
-		MAX_ENTITES :: 64
-		entities: [MAX_ENTITES]Entity
-		entity_count := 0
-
-		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}
-
-		using f.position
-		print_floats(x, y, z)
-	}
-
-	{
-		// Down casting
-
-		Entity :: type struct {
-			position: Vector3
-		}
-
-		Frog :: type struct {
-			jump_height: f32
-			using entity: Entity
-		}
-
-		f: Frog
-		f.jump_height = 564
-		e := ^f.entity
-
-		frog := e down_cast ^Frog
-		print_string("down_cast: ")
-		print_f32(frog.jump_height); nl()
-
-		// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
-	}
-
-	{
-		// Multiple "inheritance"
-
-		Entity :: type struct {
-			position: Vector3
-		}
-		Climber :: type struct {
-			speed: f32
-		}
-
-		Frog :: type struct {
-			using entity:  Entity
-			using climber: Climber
-		}
+#load "math.odin"
+
+
+print_type_info_kind :: proc(info: ^Type_Info) {
+	using Type_Info
+	match type i : info {
+	case Named:     print_string("Named\n")
+	case Integer:   print_string("Integer\n")
+	case Float:     print_string("Float\n")
+	case String:    print_string("String\n")
+	case Boolean:   print_string("Boolean\n")
+	case Pointer:   print_string("Pointer\n")
+	case Procedure: print_string("Procedure\n")
+	case Array:     print_string("Array\n")
+	case Slice:     print_string("Slice\n")
+	case Vector:    print_string("Vector\n")
+	case Struct:    print_string("Struct\n")
+	case Union:     print_string("Union\n")
+	case Raw_Union: print_string("RawUnion\n")
+	case Enum:      print_string("Enum\n")
+	default:        print_string("void\n")
 	}
 }
 
-tagged_unions :: proc() {
-	{
-		EntityKind :: type enum {
-			INVALID,
-			FROG,
-			GIRAFFE,
-			HELICOPTER,
-		}
-
-		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
-				}
-			}
-		}
-
-		e: Entity
-		e.kind = EntityKind.FROG
-		e.frog.jump_height = 12
-
-		f: type_of_val(e.frog);
-
-		// But this is very unsafe and extremely cumbersome to write
-		// In C++, I use macros to alleviate this but it's not a solution
-	}
-
-	{
-		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
-			}
-		}
-
-		using Entity
-		f1: Frog = Frog{12, 0xff9900}
-		f2: Entity = Frog{12, 0xff9900} // Implicit cast
-		f3 := Frog{12, 0xff9900} as Entity // Explicit cast
-
-		// f3.Frog.jump_height = 12 // There are "members" of a union
-
-
-
-		e, f, g, h: Entity
-		f = Frog{12, 0xff9900}
-		g = Giraffe{2.1, 23}
-		h = Helicopter{4, 1000, "Frank"}
-
-
-
-
-		// 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
-
-
-		// Advantage over subtyping version
-		MAX_ENTITES :: 64
-		entities: [MAX_ENTITES]Entity
-
-		entities[0] = Frog{}
-		entities[1] = Helicopter{}
-		// etc.
-	}
-
-
-	{
-		// 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 {}
-
-		EntityKind :: type enum {
-			Invalid,
-			Constant,
-			Variable,
-			UsingVariable,
-			TypeName,
-			Procedure,
-			Builtin,
-			Count,
-		}
-
-		Entity :: type struct {
-			Guid :: type i64
-
-			kind: EntityKind
-			guid: Guid
-
-			scope: ^Scope
-			token: Token
-			type_: ^Type
-
-			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
-	}
-
-	{
-		Type       :: type struct {}
-		Scope      :: type struct {}
-		Token      :: type struct {}
-		AstNode    :: type struct {}
-		ExactValue :: type struct {}
-
-
-		EntityBase :: type struct {
-			Guid :: type i64
-			guid: Guid
-
-			scope: ^Scope
-			token: Token
-			type_: ^Type
-		}
-
-		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
-			}
-		}
-
-		using Entity
-
-		e: Entity
-
-		e = Variable{
-			base = EntityBase{},
-			used = true,
-			anonymous = false,
-		}
-
-
-
-		// Q: Allow a "base" type to be added to a union?
+main :: proc() {
+	i: int
+	s: struct {
+		x, y, z: f32
 	}
+	p := ^s
 
-
-	{
-		// `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
-		}
-
-		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?
-	}
+	print_type_info_kind(type_info(i))
+	print_type_info_kind(type_info(s))
+	print_type_info_kind(type_info(p))
 }
-
-nl :: proc() { print_nl() }

+ 101 - 223
examples/runtime.odin

@@ -1,10 +1,68 @@
 #load "win32.odin"
 
+// IMPORTANT NOTE(bill): Do not change the order of any of this data
+// The compiler relies upon this _exact_ order
+Type_Info :: union {
+	Member :: struct {
+		name:   string
+		type_:  ^Type_Info
+		offset: int
+	}
+	Record :: struct {
+		fields: []Member
+	}
+
+
+	Named: struct {
+		name: string
+		base: ^Type_Info
+	}
+	Integer: struct {
+		bits: int
+		signed: bool
+	}
+	Float: struct {
+		bits: int
+	}
+	String:  struct {}
+	Boolean: struct {}
+	Pointer: struct {
+		elem: ^Type_Info
+	}
+	Procedure: struct{}
+	Array: struct {
+		elem: ^Type_Info
+		count: int
+	}
+	Slice: struct {
+		elem: ^Type_Info
+	}
+	Vector: struct {
+		elem: ^Type_Info
+		count: int
+	}
+	Struct:    Record
+	Union:     Record
+	Raw_Union: Record
+	Enum: struct {
+		base: ^Type_Info
+	}
+}
+
+Any :: struct {
+	type_info: ^Type_Info
+	// pointer to the data stored
+	data: rawptr
+}
+
+
+
+
 assume :: proc(cond: bool) #foreign "llvm.assume"
 
-__debug_trap           :: proc()           #foreign "llvm.debugtrap"
-__trap                 :: proc()           #foreign "llvm.trap"
-read_cycle_counter     :: proc() -> u64    #foreign "llvm.readcyclecounter"
+__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"
@@ -22,218 +80,29 @@ 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)
-	for i := 0; i < len; i++ {
-		d[i] = 0
-	}
+	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
+	llvm_memset_64bit(data, 0, len, 1, false)
 }
 
 memory_compare :: proc(dst, src: rawptr, len: int) -> int {
-	s1, s2: ^byte = dst, src
+	// TODO(bill): make a faster `memory_compare`
+	a, b := slice_ptr(dst as ^byte, len), slice_ptr(src as ^byte, len)
 	for i := 0; i < len; i++ {
-		a := ptr_offset(s1, i)^
-		b := ptr_offset(s2, i)^
-		if a != b {
-			return (a - b) as int
+		if a[i] != b[i] {
+			return (a[i] - b[i]) as int
 		}
 	}
 	return 0
 }
 
-memory_copy :: proc(dst, src: rawptr, n: int) #inline {
-	if dst == src {
-		return
-	}
-
-	v128b :: type {4}u32
-	assert(align_of(v128b) == 16)
-
-	d, s: ^byte = dst, src
-
-	for ; s as uint % 16 != 0 && n != 0; n-- {
-		d^ = s^
-		d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-	}
-
-	if d as uint % 16 == 0 {
-		for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 {
-			(d as ^v128b)^ = (s as ^v128b)^
-		}
-
-		if n&8 != 0 {
-			(d as ^u64)^ = (s as ^u64)^
-			d, s = ptr_offset(d, 8), ptr_offset(s, 8)
-		}
-		if n&4 != 0 {
-			(d as ^u32)^ = (s as ^u32)^;
-			d, s = ptr_offset(d, 4), ptr_offset(s, 4)
-		}
-		if n&2 != 0 {
-			(d as ^u16)^ = (s as ^u16)^
-			d, s = ptr_offset(d, 2), ptr_offset(s, 2)
-		}
-		if n&1 != 0 {
-			d^ = s^
-			d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-		}
-		return;
-	}
-
-	// IMPORTANT NOTE(bill): Little endian only
-	LS :: proc(a, b: u32) -> u32 #inline { return a << b }
-	RS :: proc(a, b: u32) -> u32 #inline { return a >> b }
-	/* NOTE(bill): Big endian version
-	LS :: proc(a, b: u32) -> u32 #inline { return a >> b; }
-	RS :: proc(a, b: u32) -> u32 #inline { return a << b; }
-	*/
-
-	w, x: u32
-
-	if d as uint % 4 == 1 {
-		w = (s as ^u32)^
-		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
-		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
-		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
-		n -= 3
-
-		for n > 16 {
-			d32 := d as ^u32
-			s32 := ptr_offset(s, 1) as ^u32
-			x = s32^; d32^ = LS(w, 24) | RS(x, 8)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 24) | RS(w, 8)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			x = s32^; d32^ = LS(w, 24) | RS(x, 8)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 24) | RS(w, 8)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
-			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
-		}
-
-	} else if d as uint % 4 == 2 {
-		w = (s as ^u32)^
-		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
-		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
-		n -= 2
-
-		for n > 17 {
-			d32 := d as ^u32
-			s32 := ptr_offset(s, 2) as ^u32
-			x = s32^; d32^ = LS(w, 16) | RS(x, 16)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 16) | RS(w, 16)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			x = s32^; d32^ = LS(w, 16) | RS(x, 16)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 16) | RS(w, 16)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
-			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
-		}
-
-	} else if d as uint % 4 == 3 {
-		w = (s as ^u32)^
-		d^ = s^
-		n -= 1
-
-		for n > 18 {
-			d32 := d as ^u32
-			s32 := ptr_offset(s, 3) as ^u32
-			x = s32^; d32^ = LS(w, 8) | RS(x, 24)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 8) | RS(w, 24)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			x = s32^; d32^ = LS(w, 8) | RS(x, 24)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-			w = s32^; d32^ = LS(x, 8) | RS(w, 24)
-			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
-			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
-		}
-	}
-
-	if n&16 != 0 {
-		(d as ^v128b)^ = (s as ^v128b)^
-		d, s = ptr_offset(d, 16), ptr_offset(s, 16)
-	}
-	if n&8 != 0 {
-		(d as ^u64)^ = (s as ^u64)^
-		d, s = ptr_offset(d, 8), ptr_offset(s, 8)
-	}
-	if n&4 != 0 {
-		(d as ^u32)^ = (s as ^u32)^;
-		d, s = ptr_offset(d, 4), ptr_offset(s, 4)
-	}
-	if n&2 != 0 {
-		(d as ^u16)^ = (s as ^u16)^
-		d, s = ptr_offset(d, 2), ptr_offset(s, 2)
-	}
-	if n&1 != 0 {
-		d^  = s^
-	}
+memory_copy :: proc(dst, src: rawptr, len: int) #inline {
+	llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64"
+	llvm_memcpy_64bit(dst, src, len, 1, false)
 }
 
-memory_move :: proc(dst, src: rawptr, n: int) #inline {
-	d, s: ^byte = dst, src
-	if d == s {
-		return
-	}
-	if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s {
-		memory_copy(d, s, n)
-		return
-	}
-
-	// TODO(bill): Vectorize the shit out of this
-	if d < s {
-		if s as int % size_of(int) == d as int % size_of(int) {
-			for d as int % size_of(int) != 0 {
-				if n == 0 {
-					return
-				}
-				n--
-				d^ = s^
-				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-			}
-			di, si := d as ^int, s as ^int
-			for n >= size_of(int) {
-				di^ = si^
-				di, si = ptr_offset(di, 1), ptr_offset(si, 1)
-				n -= size_of(int)
-			}
-		}
-		for ; n > 0; n-- {
-			d^ = s^
-			d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-		}
-	} else {
-		if s as int % size_of(int) == d as int % size_of(int) {
-			for ptr_offset(d, n) as int % size_of(int) != 0 {
-				if n == 0 {
-					return
-				}
-				n--
-				d^ = s^
-				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-			}
-			for n >= size_of(int) {
-				n -= size_of(int)
-				di := ptr_offset(d, n) as ^int
-				si := ptr_offset(s, n) as ^int
-				di^ = si^
-			}
-			for ; n > 0; n-- {
-				d^ = s^
-				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
-			}
-		}
-		for n > 0 {
-			n--
-			dn := ptr_offset(d, n)
-			sn := ptr_offset(s, n)
-			dn^ = sn^
-		}
-	}
+memory_move :: proc(dst, src: rawptr, len: int) #inline {
+	llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64"
+	llvm_memmove_64bit(dst, src, len, 1, false)
 }
 
 __string_eq :: proc(a, b: string) -> bool {
@@ -247,25 +116,36 @@ __string_eq :: proc(a, b: string) -> bool {
 }
 
 __string_cmp :: proc(a, b : string) -> int {
-	min_len := len(a)
-	if len(b) < min_len {
-		min_len = len(b)
+	// Translation of http://mgronhol.github.io/fast-strcmp/
+	n := min(len(a), len(b))
+
+	fast := n/size_of(int) + 1
+	offset := (fast-1)*size_of(int)
+	curr_block := 0
+	if n <= size_of(int) {
+		fast = 0
 	}
-	for i := 0; i < min_len; i++ {
-		x := a[i]
-		y := b[i]
-		if x < y {
-			return -1
-		} else if x > y {
-			return +1
+
+	la := slice_ptr(^a[0] as ^int, fast)
+	lb := slice_ptr(^b[0] as ^int, fast)
+
+	for ; curr_block < fast; curr_block++ {
+		if (la[curr_block] ~ lb[curr_block]) != 0 {
+			for pos := curr_block*size_of(int); pos < n; pos++ {
+				if (a[pos] ~ b[pos]) != 0 {
+					return a[pos] as int - b[pos] as int
+				}
+			}
 		}
+
 	}
 
-	if len(a) < len(b) {
-		return -1
-	} else if len(a) > len(b) {
-		return +1
+	for ; offset < n; offset++ {
+		if (a[offset] ~ b[offset]) != 0 {
+			return a[offset] as int - b[offset] as int
+		}
 	}
+
 	return 0
 }
 
@@ -278,26 +158,24 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >
 
 
 
-Allocation_Mode :: type enum {
+Allocation_Mode :: enum {
 	ALLOC,
 	DEALLOC,
 	DEALLOC_ALL,
 	RESIZE,
 }
 
-
-
 Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
                             size, alignment: int,
                             old_memory: rawptr, old_size: int, flags: u64) -> rawptr
 
-Allocator :: type struct {
+Allocator :: struct {
 	procedure: Allocator_Proc;
 	data:      rawptr
 }
 
 
-Context :: type struct {
+Context :: struct {
 	thread_ptr: rawptr
 
 	user_data:  rawptr

+ 3 - 3
examples/win32.odin

@@ -42,7 +42,7 @@ INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE
 
 WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
 
-WNDCLASSEXA :: type struct {
+WNDCLASSEXA :: struct {
 	size, style:           u32
 	wnd_proc:              WNDPROC
 	cls_extra, wnd_extra:  i32
@@ -54,7 +54,7 @@ WNDCLASSEXA :: type struct {
 	sm:                    HICON
 }
 
-MSG :: type struct {
+MSG :: struct {
 	hwnd:    HWND
 	message: u32
 	wparam:  WPARAM
@@ -191,7 +191,7 @@ PROC  :: type proc()
 wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC
 
 
-PIXELFORMATDESCRIPTOR :: type struct  {
+PIXELFORMATDESCRIPTOR :: struct  {
 	size,
 	version,
 	flags: u32

BIN
misc/llvm-bin/lli.exe


BIN
misc/llvm-bin/opt.exe


+ 41 - 14
src/checker/checker.cpp

@@ -150,6 +150,8 @@ enum BuiltinProcId {
 	BuiltinProc_max,
 	BuiltinProc_abs,
 
+	BuiltinProc_type_info,
+
 	BuiltinProc_Count,
 };
 struct BuiltinProc {
@@ -188,6 +190,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("min"),              2, false, Expr_Expr},
 	{STR_LIT("max"),              2, false, Expr_Expr},
 	{STR_LIT("abs"),              1, false, Expr_Expr},
+
+	{STR_LIT("type_info"),        1, false, Expr_Expr},
 };
 
 struct CheckerContext {
@@ -197,13 +201,14 @@ struct CheckerContext {
 
 // NOTE(bill): Symbol tables
 struct CheckerInfo {
-	Map<TypeAndValue>      types;         // Key: AstNode * | Expression -> Type (and value)
-	Map<Entity *>          definitions;   // Key: AstNode * | Identifier -> Entity
-	Map<Entity *>          uses;          // Key: AstNode * | Identifier -> Entity
-	Map<Scope *>           scopes;        // Key: AstNode * | Node       -> Scope
-	Map<ExpressionInfo>    untyped;       // Key: AstNode * | Expression -> ExpressionInfo
-	Map<DeclInfo *>        entities;      // Key: Entity *
-	Map<Entity *>          foreign_procs; // Key: String
+	Map<TypeAndValue>      types;           // Key: AstNode * | Expression -> Type (and value)
+	Map<Entity *>          definitions;     // Key: AstNode * | Identifier -> Entity
+	Map<Entity *>          uses;            // Key: AstNode * | Identifier -> Entity
+	Map<Scope *>           scopes;          // Key: AstNode * | Node       -> Scope
+	Map<ExpressionInfo>    untyped;         // Key: AstNode * | Expression -> ExpressionInfo
+	Map<DeclInfo *>        entities;        // Key: Entity *
+	Map<Entity *>          foreign_procs;   // Key: String
+	Map<Type *>            type_info_types; // Key: Type *
 };
 
 struct Checker {
@@ -383,6 +388,26 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
 	add_global_entity(entity);
 }
 
+
+Type *t_type_info           = NULL;
+Type *t_type_info_ptr       = NULL;
+
+Type *t_type_info_named     = NULL;
+Type *t_type_info_integer   = NULL;
+Type *t_type_info_float     = NULL;
+Type *t_type_info_string    = NULL;
+Type *t_type_info_boolean   = NULL;
+Type *t_type_info_pointer   = NULL;
+Type *t_type_info_procedure = NULL;
+Type *t_type_info_array     = NULL;
+Type *t_type_info_slice     = NULL;
+Type *t_type_info_vector    = NULL;
+Type *t_type_info_struct    = NULL;
+Type *t_type_info_union     = NULL;
+Type *t_type_info_raw_union = NULL;
+Type *t_type_info_enum      = NULL;
+
+
 void init_universal_scope(void) {
 	// NOTE(bill): No need to free these
 	gbAllocator a = gb_heap_allocator();
@@ -425,13 +450,14 @@ void init_universal_scope(void) {
 
 void init_checker_info(CheckerInfo *i) {
 	gbAllocator a = gb_heap_allocator();
-	map_init(&i->types,         a);
-	map_init(&i->definitions,   a);
-	map_init(&i->uses,          a);
-	map_init(&i->scopes,        a);
-	map_init(&i->entities,      a);
-	map_init(&i->untyped,       a);
-	map_init(&i->foreign_procs, a);
+	map_init(&i->types,           a);
+	map_init(&i->definitions,     a);
+	map_init(&i->uses,            a);
+	map_init(&i->scopes,          a);
+	map_init(&i->entities,        a);
+	map_init(&i->untyped,         a);
+	map_init(&i->foreign_procs,   a);
+	map_init(&i->type_info_types, a);
 
 }
 
@@ -443,6 +469,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_destroy(&i->entities);
 	map_destroy(&i->untyped);
 	map_destroy(&i->foreign_procs);
+	map_destroy(&i->type_info_types);
 }
 
 

+ 68 - 0
src/checker/expr.cpp

@@ -1900,6 +1900,42 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 	return NULL;
 }
 
+void add_type_info_type(Checker *c, Type *t) {
+	if (t == NULL) {
+		return;
+	}
+	t = default_type(t);
+	if (map_get(&c->info.type_info_types, hash_pointer(t)) != NULL) {
+		// Types have already been added
+		return;
+	}
+
+	map_set(&c->info.type_info_types, hash_pointer(t), t);
+	Type *bt = get_base_type(t);
+	switch (bt->kind) {
+	case Type_Named:   add_type_info_type(c, bt->Named.base);   break;
+	case Type_Array:   add_type_info_type(c, bt->Array.elem);   break;
+	case Type_Slice:   add_type_info_type(c, bt->Slice.elem);   break;
+	case Type_Vector:  add_type_info_type(c, bt->Vector.elem);  break;
+	case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break;
+	case Type_Record: {
+		switch (bt->Record.kind) {
+		case TypeRecord_Enum:
+			add_type_info_type(c, bt->Record.enum_base);
+			break;
+		default:
+			for (isize i = 0; i < bt->Record.field_count; i++) {
+				Entity *f = bt->Record.fields[i];
+				add_type_info_type(c, f->type);
+			}
+			break;
+		}
+	} break;
+	}
+	// TODO(bill): Type info for procedures and tuples
+	// TODO(bill): Remove duplicate identical types efficiently
+}
+
 b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) {
 	GB_ASSERT(call->kind == AstNode_CallExpr);
 	ast_node(ce, CallExpr, call);
@@ -2652,6 +2688,38 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		operand->type = type;
 	} break;
 
+
+	case BuiltinProc_type_info: {
+		if (t_type_info == NULL) {
+			String type_info_str = make_string("Type_Info");
+			Entity **found = map_get(&c->global_scope->elements, hash_string(type_info_str));
+			GB_ASSERT_MSG(found != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`");
+			Entity *e = *found;
+			t_type_info = e->type;
+			t_type_info_ptr = make_type_pointer(c->allocator, t_type_info);
+
+			auto *record = &get_base_type(e->type)->Record;
+			GB_ASSERT(record->field_count == 15);
+			t_type_info_named     = record->fields[ 1]->type;
+			t_type_info_integer   = record->fields[ 2]->type;
+			t_type_info_float     = record->fields[ 3]->type;
+			t_type_info_string    = record->fields[ 4]->type;
+			t_type_info_boolean   = record->fields[ 5]->type;
+			t_type_info_pointer   = record->fields[ 6]->type;
+			t_type_info_procedure = record->fields[ 7]->type;
+			t_type_info_array     = record->fields[ 8]->type;
+			t_type_info_slice     = record->fields[ 9]->type;
+			t_type_info_vector    = record->fields[10]->type;
+			t_type_info_struct    = record->fields[11]->type;
+			t_type_info_union     = record->fields[12]->type;
+			t_type_info_raw_union = record->fields[13]->type;
+			t_type_info_enum      = record->fields[14]->type;
+		}
+
+		add_type_info_type(c, operand->type);
+		operand->mode = Addressing_Value;
+		operand->type = t_type_info_ptr;
+	} break;
 	}
 
 	return true;

+ 143 - 21
src/codegen/codegen.cpp

@@ -31,27 +31,6 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) {
 	if (err != gbFileError_None)
 		return false;
 
-#if 0
-	Map<i32> type_map; // Key: Type *
-	map_init(&type_map, gb_heap_allocator());
-	i32 index = 0;
-	gb_for_array(i, c->info.types.entries) {
-		TypeAndValue tav = c->info.types.entries[i].value;
-		Type *type = tav.type;
-		HashKey key = hash_pointer(type);
-		auto found = map_get(&type_map, key);
-		if (!found) {
-			map_set(&type_map, key, index);
-			index++;
-		}
-	}
-	gb_for_array(i, type_map.entries) {
-		auto *e = &type_map.entries[i];
-		Type *t = cast(Type *)cast(uintptr)e->key.key;
-		gb_printf("%s\n", type_to_string(t));
-	}
-#endif
-
 	return true;
 }
 
@@ -199,6 +178,149 @@ void ssa_gen_tree(ssaGen *s) {
 			}
 		}
 
+		{ // NOTE(bill): Setup type_info data
+			ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data")));
+			GB_ASSERT(found != NULL);
+			ssaValue *type_info_data = *found;
+			CheckerInfo *info = proc->module->info;
+
+
+			Type *t_int_ptr           = make_type_pointer(a, t_int);
+			Type *t_bool_ptr          = make_type_pointer(a, t_bool);
+			Type *t_string_ptr        = make_type_pointer(a, t_string);
+			Type *t_type_info_ptr_ptr = make_type_pointer(a, t_type_info_ptr);
+
+			auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * {
+				auto *info = proc->module->info;
+				MapFindResult fr = map__find(&info->type_info_types, hash_pointer(type));
+				GB_ASSERT(fr.entry_index >= 0);
+				return ssa_emit_struct_gep(proc, type_info_data, fr.entry_index, t_type_info_ptr);
+			};
+
+
+			gb_for_array(entry_index, info->type_info_types.entries) {
+				auto *entry = &info->type_info_types.entries[entry_index];
+				Type *t = entry->value;
+
+				ssaValue *tag = NULL;
+
+				switch (t->kind) {
+				case Type_Named: {
+					tag = ssa_add_local_generated(proc, t_type_info_named);
+
+					ssaValue *gsa  = ssa_add_global_string_array(proc, make_exact_value_string(t->Named.name));
+					ssaValue *elem = ssa_array_elem(proc, gsa);
+					ssaValue *len  = ssa_array_len(proc, ssa_emit_load(proc, gsa));
+					ssaValue *name = ssa_emit_string(proc, elem, len);
+
+					ssaValue *gep  = get_type_info_ptr(proc, type_info_data, t->Named.base);
+
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero,  t_string_ptr), name);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_one32, t_type_info_ptr), gep);
+				} break;
+
+				case Type_Basic:
+					switch (t->Basic.kind) {
+					case Basic_bool:
+						tag = ssa_add_local_generated(proc, t_type_info_boolean);
+						break;
+					case Basic_i8:
+					case Basic_i16:
+					case Basic_i32:
+					case Basic_i64:
+					case Basic_i128:
+					case Basic_u8:
+					case Basic_u16:
+					case Basic_u32:
+					case Basic_u64:
+					case Basic_u128:
+					case Basic_int:
+					case Basic_uint: {
+						tag = ssa_add_local_generated(proc, t_type_info_integer);
+						b32 is_unsigned = (basic_types[t->Basic.kind].flags & BasicFlag_Unsigned) != 0;
+						ssaValue *bits      = ssa_make_value_constant(a, t_int, make_exact_value_integer(8*type_size_of(m->sizes, a, t)));
+						ssaValue *is_signed = ssa_make_value_constant(a, t_bool, make_exact_value_bool(!is_unsigned));
+						ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_int_ptr),  bits);
+						ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_one32,  t_bool_ptr), is_signed);
+					} break;
+
+					case Basic_f32:
+					case Basic_f64: {
+						tag = ssa_add_local_generated(proc, t_type_info_float);
+						ssaValue *bits = ssa_make_value_constant(a, t_int, make_exact_value_integer(8*type_size_of(m->sizes, a, t)));
+						ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_int_ptr), bits);
+					} break;
+
+					case Basic_rawptr:
+						tag = ssa_add_local_generated(proc, t_type_info_pointer);
+						break;
+
+					case Basic_string:
+						tag = ssa_add_local_generated(proc, t_type_info_string);
+						break;
+					}
+					break;
+
+				case Type_Pointer: {
+					tag = ssa_add_local_generated(proc, t_type_info_pointer);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+				} break;
+				case Type_Array: {
+					tag = ssa_add_local_generated(proc, t_type_info_array);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+				} break;
+				case Type_Slice: {
+					tag = ssa_add_local_generated(proc, t_type_info_slice);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+				} break;
+				case Type_Vector: {
+					tag = ssa_add_local_generated(proc, t_type_info_vector);
+					ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+				} break;
+				case Type_Record: {
+					switch (t->Record.kind) {
+					// TODO(bill): Record members for `Type_Info`
+					case TypeRecord_Struct:
+						tag = ssa_add_local_generated(proc, t_type_info_struct);
+						break;
+					case TypeRecord_Union:
+						tag = ssa_add_local_generated(proc, t_type_info_union);
+						break;
+					case TypeRecord_RawUnion:
+						tag = ssa_add_local_generated(proc, t_type_info_raw_union);
+						break;
+					case TypeRecord_Enum: {
+						tag = ssa_add_local_generated(proc, t_type_info_enum);
+						Type *enum_base = t->Record.enum_base;
+						if (enum_base == NULL) {
+							enum_base = t_int;
+						}
+						ssaValue *gep = get_type_info_ptr(proc, type_info_data, enum_base);
+						ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep);
+					} break;
+					}
+				} break;
+
+				case Type_Tuple:
+					// TODO(bill): Type_Info for tuples
+					break;
+				case Type_Proc:
+					// TODO(bill): Type_Info for procedures
+					break;
+				}
+
+				if (tag != NULL) {
+					ssaValue *gep = ssa_emit_struct_gep(proc, type_info_data, entry_index, t_type_info_ptr);
+					ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info);
+					ssa_emit_store(proc, gep, val);
+				}
+			}
+		}
+
 		ssa_end_procedure_body(proc);
 	}
 

+ 4 - 16
src/codegen/print_llvm.cpp

@@ -314,7 +314,7 @@ void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Typ
 
 void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) {
 	ssa_print_escape_string(f, b->label, false);
-	ssa_fprintf(f, "..%d", b->id);
+	ssa_fprintf(f, "-%d", b->id);
 }
 
 void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) {
@@ -352,7 +352,9 @@ void ssa_print_instr(ssaFileBuffer *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_print_encoded_global(f, make_string(SSA_STARTUP_RUNTIME_PROC_NAME));
+		ssa_fprintf(f, "()\n");
 	} break;
 
 	case ssaInstr_Comment:
@@ -642,20 +644,6 @@ void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) {
 		ssa_fprintf(f, "\n");
 	} break;
 
-	case ssaInstr_MemCopy: {
-		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* ");
-		ssa_print_value(f, m, instr->CopyMemory.src, t_rawptr);
-		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m->sizes, t_int);
-		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->CopyMemory.len, t_int);
-		ssa_fprintf(f, ")\n");
-	} break;
-
-
 	case ssaInstr_ExtractElement: {
 		Type *vt = ssa_type(instr->ExtractElement.vector);
 		ssa_fprintf(f, "%%%d = extractelement ", value->id);

+ 64 - 46
src/codegen/ssa.cpp

@@ -85,7 +85,6 @@ struct ssaProcedure {
 	SSA_INSTR_KIND(Unreachable), \
 	SSA_INSTR_KIND(BinaryOp), \
 	SSA_INSTR_KIND(Call), \
-	SSA_INSTR_KIND(MemCopy), \
 	SSA_INSTR_KIND(NoOp), \
 	SSA_INSTR_KIND(ExtractElement), \
 	SSA_INSTR_KIND(InsertElement), \
@@ -198,13 +197,6 @@ struct ssaInstr {
 			ssaValue **args;
 			isize arg_count;
 		} Call;
-		struct {
-			ssaValue *dst, *src;
-			ssaValue *len;
-			i32 align;
-			b32 is_volatile;
-		} CopyMemory;
-
 		struct {
 			ssaValue *vector;
 			ssaValue *index;
@@ -315,6 +307,25 @@ void ssa_module_init(ssaModule *m, Checker *c) {
 
 	map_init(&m->values,  gb_heap_allocator());
 	map_init(&m->members, gb_heap_allocator());
+
+	{
+		// Add type info data
+		ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value);
+
+
+		String name = make_string("__type_info_data");
+		Token token = {};
+		token.kind = Token_Identifier;
+		token.string = name;
+
+
+		isize count = gb_array_count(c->info.type_info_types.entries);
+		Entity *e = make_entity_variable(m->allocator, NULL, token, make_type_array(m->allocator, t_type_info, count));
+		ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
+		g->Global.is_private  = true;
+		map_set(&m->values, hash_pointer(e), g);
+		map_set(&m->members, hash_string(name), g);
+	}
 }
 
 void ssa_module_destroy(ssaModule *m) {
@@ -356,9 +367,6 @@ Type *ssa_type(ssaInstr *instr) {
 		}
 		return NULL;
 	} break;
-	case ssaInstr_MemCopy:
-		return t_int;
-
 	case ssaInstr_ExtractElement: {
 		Type *vt = ssa_type(instr->ExtractElement.vector);
 		Type *bt = base_vector_type(get_base_type(vt));
@@ -545,16 +553,6 @@ ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args,
 	return v;
 }
 
-ssaValue *ssa_make_instr_copy_memory(ssaProcedure *p, ssaValue *dst, ssaValue *src, ssaValue *len, i32 align, b32 is_volatile) {
-	ssaValue *v = ssa_alloc_instr(p, ssaInstr_MemCopy);
-	v->Instr.CopyMemory.dst = dst;
-	v->Instr.CopyMemory.src = src;
-	v->Instr.CopyMemory.len = len;
-	v->Instr.CopyMemory.align = align;
-	v->Instr.CopyMemory.is_volatile = is_volatile;
-	return v;
-}
-
 ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) {
 	ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv);
 	v->Instr.Conv.kind = kind;
@@ -925,7 +923,6 @@ void ssa_end_procedure_body(ssaProcedure *proc) {
 			case ssaInstr_Br:
 			case ssaInstr_Ret:
 			case ssaInstr_Unreachable:
-			case ssaInstr_MemCopy:
 			case ssaInstr_StartupRuntime:
 				continue;
 			case ssaInstr_Call:
@@ -2008,11 +2005,13 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					                                              make_exact_value_integer(size_of_elem));
 					ssaValue *byte_count = ssa_emit_arith(proc, mul, len, elem_size, t_int);
 
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
+					args[0] = dst;
+					args[1] = src;
+					args[2] = byte_count;
 
-					i32 align = cast(i32)type_align_of(proc->module->sizes, proc->module->allocator, elem_type);
-					b32 is_volatile = false;
+					ssa_emit_global_call(proc, "memory_move", args, 3);
 
-					ssa_emit(proc, ssa_make_instr_copy_memory(proc, dst, src, byte_count, align, is_volatile));
 					return len;
 				} break;
 				case BuiltinProc_append: {
@@ -2056,7 +2055,12 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					item = ssa_emit_ptr_offset(proc, item, v_zero);
 					item = ssa_emit_conv(proc, item, t_rawptr, true);
 
-					ssa_emit(proc, ssa_make_instr_copy_memory(proc, offset, item, byte_count, 1, false));
+					ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3);
+					args[0] = offset;
+					args[1] = item;
+					args[2] = byte_count;
+
+					ssa_emit_global_call(proc, "memory_move", args, 3);
 
 					// Increment slice length
 					Token add = {Token_Add};
@@ -2172,6 +2176,22 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero);
 					return ssa_emit_select(proc, cond, neg_x, x);
 				} break;
+
+
+				case BuiltinProc_type_info: {
+					ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data")));
+					GB_ASSERT(found != NULL);
+					ssaValue *type_info_data = *found;
+					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
+					Type *t = default_type(type_of_expr(proc->module->info, ce->arg_list));
+					MapFindResult fr = map__find(&proc->module->info->type_info_types, hash_pointer(t));
+					GB_ASSERT(fr.entry_index >= 0);
+					// Zero is null and void
+					isize offset = fr.entry_index;
+
+					ssaValue *gep = ssa_emit_struct_gep(proc, type_info_data, offset, t_type_info_ptr);
+					return gep;
+				} break;
 				}
 			}
 		}
@@ -3153,11 +3173,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		Type *union_type = type_deref(ssa_type(parent));
 		GB_ASSERT(is_type_union(union_type));
 
+		ssa_emit_comment(proc, make_string("get union's tag"));
 		ssaValue *tag_index = ssa_emit_struct_gep(proc, parent, v_one32, make_type_pointer(allocator, t_int));
 		tag_index = ssa_emit_load(proc, tag_index);
 
 		ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr);
 
+		ssaBlock *start_block = ssa_add_block(proc, node, make_string("type-match.case.first"));
+		ssa_emit_jump(proc, start_block);
+		proc->curr_block = start_block;
+
 		ssaBlock *done = ssa__make_block(proc, node, make_string("type-match.done")); // NOTE(bill): Append later
 
 		ast_node(body, BlockStmt, ms->body);
@@ -3173,30 +3198,22 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		for (AstNode *clause = body->list;
 		     clause != NULL;
 		     clause = clause->next, i++) {
-			ssaBlock *body = NULL;
-			b32 append_body = false;
-
 			ast_node(cc, CaseClause, clause);
 
-			if (body == NULL) {
-				append_body = true;
-				if (cc->list == NULL) {
-					body = ssa__make_block(proc, clause, make_string("type-match.dflt.body"));
-				} else {
-					body = ssa__make_block(proc, clause, make_string("type-match.case.body"));
-				}
-			}
-
-
 			if (cc->list == NULL) {
 				// default case
 				default_stmts  = cc->stmts;
-				default_block = body;
+				default_block = ssa__make_block(proc, clause, make_string("type-match.dflt.body"));
 				continue;
 			}
 
+
+			ssaBlock *body = ssa__make_block(proc, clause, make_string("type-match.case.body"));
+
+
 			Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause));
 			Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
+			GB_ASSERT(tag_var_entity != NULL);
 			ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
 			ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type);
 			ssa_emit_store(proc, tag_var, data_ptr);
@@ -3205,10 +3222,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			Type *base_type = type_deref(tag_var_entity->type);
 			ssaValue *index = NULL;
-			for (isize i = 0; i < get_base_type(union_type)->Record.field_count; i++) {
-				Entity *f = get_base_type(union_type)->Record.fields[i];
+			Type *ut = get_base_type(union_type);
+			GB_ASSERT(ut->Record.kind == TypeRecord_Union);
+			for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) {
+				Entity *f = get_base_type(union_type)->Record.fields[field_index];
 				if (are_types_identical(f->type, base_type)) {
-					index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i));
+					index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(field_index));
+					break;
 				}
 			}
 			GB_ASSERT(index != NULL);
@@ -3220,9 +3240,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			gb_array_append(proc->blocks, next_cond);
 			proc->curr_block = next_cond;
 
-			if (append_body) {
-				gb_array_append(proc->blocks, body);
-			}
+			gb_array_append(proc->blocks, body);
 			proc->curr_block = body;
 			ssa_push_target_list(proc, done, NULL, NULL);
 			ssa_build_stmt_list(proc, cc->stmts);

+ 10 - 2
src/main.cpp

@@ -41,6 +41,7 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 }
 
 
+#if 0
 #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; \
@@ -54,6 +55,11 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 #define PRINT_ACCUMULATION() do { \
 	gb_printf_err("Total compilation time: %lld ms\n", total_time/1000); \
 } while (0)
+#else
+#define INIT_TIMER()
+#define PRINT_TIMER(section)
+#define PRINT_ACCUMULATION()
+#endif
 
 
 int main(int argc, char **argv) {
@@ -117,8 +123,10 @@ int main(int argc, char **argv) {
 
 
 	i32 exit_code = 0;
+	// For more passes arguments: http://llvm.org/docs/Passes.html
 	exit_code = win32_exec_command_line_app(
-		"../misc/llvm-bin/opt %s -o %.*s.bc "
+		// "../misc/llvm-bin/opt %s -o %.*s.bc "
+		"opt %s -o %.*s.bc "
 		"-memcpyopt "
 		"-mem2reg "
 		"-die -dse "
@@ -144,7 +152,7 @@ int main(int argc, char **argv) {
 	}
 
 	exit_code = win32_exec_command_line_app(
-		"clang %.*s.bc -o %.*s.o "
+		"clang %.*s.bc -o %.*s.exe "
 		"-O0 "
 		"-Wno-override-module "
 		"%s",

+ 13 - 4
src/parser.cpp

@@ -2063,8 +2063,15 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) {
 			declaration_kind = Declaration_Immutable;
 		next_token(f);
 
-		if (f->cursor[0].kind == Token_type) {
-			Token token = expect_token(f, Token_type);
+		if (f->cursor[0].kind == Token_type ||
+		    f->cursor[0].kind == Token_struct ||
+		    f->cursor[0].kind == Token_enum ||
+		    f->cursor[0].kind == Token_union ||
+		    f->cursor[0].kind == Token_raw_union) {
+			Token token = f->cursor[0];
+			if (token.kind == Token_type) {
+				next_token(f);
+			}
 			if (name_count != 1) {
 				ast_file_err(f, ast_node_token(name_list), "You can only declare one type at a time");
 				return make_bad_decl(f, name_list->Ident, token);
@@ -2290,9 +2297,9 @@ AstNode *parse_match_stmt(AstFile *f) {
 	Token open, close;
 
 	if (allow_token(f, Token_type)) {
-		tag = parse_expr(f, true);
-		expect_token(f, Token_ArrowRight);
 		AstNode *var = parse_identifier(f);
+		expect_token(f, Token_Colon);
+		tag = parse_simple_stmt(f);
 
 		open = expect_token(f, Token_OpenBrace);
 		AstNode *list = NULL;
@@ -2307,6 +2314,8 @@ AstNode *parse_match_stmt(AstFile *f) {
 
 		close = expect_token(f, Token_CloseBrace);
 		body = make_block_stmt(f, list, list_count, open, close);
+
+		tag = convert_stmt_to_expr(f, tag, make_string("type match expression"));
 		return make_type_match_stmt(f, token, tag, var, body);
 	} else {
 		if (f->cursor[0].kind != Token_OpenBrace) {