Browse Source

Fix push_* with better defer system

Ginger Bill 9 years ago
parent
commit
fa7d7938e1
9 changed files with 947 additions and 363 deletions
  1. 0 331
      code/demo.odin
  2. 334 0
      code/old_demos/demo001.odin
  3. 412 0
      code/old_demos/old_runtime.odin
  4. 4 1
      core/fmt.odin
  5. 131 0
      core/mem.odin
  6. 0 1
      core/os.odin
  7. 63 27
      src/codegen/ssa.cpp
  8. 2 2
      src/common.cpp
  9. 1 1
      src/main.cpp

+ 0 - 331
code/demo.odin

@@ -1,10 +1,6 @@
 #import "fmt.odin"
 #import "os.odin"
 #import "mem.odin"
-// #import "http_test.odin" as ht
-// #import "game.odin" as game
-// #import "punity.odin" as pn
-
 
 main :: proc() {
 
@@ -17,331 +13,4 @@ main :: proc() {
 		x^ = 1337
 		fmt.println(x^)
 	}
-
-
-	// struct_padding()
-	// bounds_checking()
-	// type_introspection()
-	// any_type()
-	// crazy_introspection()
-	// namespaces_and_files()
-	// miscellany()
-	// ht.run()
-	// game.run()
-	// {
-	// 	init :: proc(c: ^pn.Core) {}
-	// 	step :: proc(c: ^pn.Core) {}
-
-	// 	pn.run(init, step)
-	// }
-}
-
-struct_padding :: proc() {
-	{
-		A :: struct {
-			a: u8
-			b: u32
-			c: u16
-		}
-
-		B :: struct {
-			a: [7]u8
-			b: [3]u16
-			c: u8
-			d: u16
-		}
-
-		fmt.println("size_of(A):", size_of(A))
-		fmt.println("size_of(B):", size_of(B))
-
-		// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
-	}
-	{
-		A :: struct #ordered {
-			a: u8
-			b: u32
-			c: u16
-		}
-
-		B :: struct #ordered {
-			a: [7]u8
-			b: [3]u16
-			c: u8
-			d: u16
-		}
-
-		fmt.println("size_of(A):", size_of(A))
-		fmt.println("size_of(B):", size_of(B))
-
-		// C-style structure layout
-	}
-	{
-		A :: struct #packed {
-			a: u8
-			b: u32
-			c: u16
-		}
-
-		B :: struct #packed {
-			a: [7]u8
-			b: [3]u16
-			c: u8
-			d: u16
-		}
-
-		fmt.println("size_of(A):", size_of(A))
-		fmt.println("size_of(B):", size_of(B))
-
-		// Useful for explicit layout
-	}
-
-	// Member sorting by priority
-	// Alignment desc.
-	// Size desc.
-	// source order asc.
-
-	/*
-		A :: struct {
-			a: u8
-			b: u32
-			c: u16
-		}
-
-		B :: struct {
-			a: [7]u8
-			b: [3]u16
-			c: u8
-			d: u16
-		}
-
-		Equivalent too
-
-		A :: struct #ordered {
-			b: u32
-			c: u16
-			a: u8
-		}
-
-		B :: struct #ordered {
-			b: [3]u16
-			d: u16
-			a: [7]u8
-			c: u8
-		}
-	*/
-}
-
-bounds_checking :: proc() {
-	x: [4]int
-	// x[-1] = 0; // Compile Time
-	// x[4]  = 0; // Compile Time
-
-	{
-		a, b := -1, 4;
-		// x[a] = 0; // Runtime Time
-		// x[b] = 0; // Runtime Time
-	}
-
-	// Works for arrays, strings, slices, and related procedures & operations
-
-	{
-		base: [10]int
-		s := base[2:6]
-		a, b := -1, 6
-
-		#no_bounds_check {
-			s[a] = 0;
-			// #bounds_check s[b] = 0;
-		}
-
-	#no_bounds_check
-		if s[a] == 0 {
-			// Do whatever
-		}
-
-		// Bounds checking can be toggled explicit
-		// on a per statement basis.
-		// _any statement_
-	}
 }
-
-type_introspection :: proc() {
-	{
-		info: ^Type_Info
-		x: int
-
-		info = type_info(int) // by type
-		info = type_info_of_val(x) // by value
-		// See: runtime.odin
-
-		match type i : info {
-		case Type_Info.Integer:
-			fmt.println("integer!")
-		case Type_Info.Float:
-			fmt.println("float!")
-		default:
-			fmt.println("potato!")
-		}
-
-		// Unsafe cast
-		integer_info := info as ^Type_Info.Integer
-	}
-
-	{
-		Vector2 :: struct { x, y: f32 }
-		Vector3 :: struct { x, y, z: f32 }
-
-		v1: Vector2
-		v2: Vector3
-		v3: Vector3
-
-		t1 := type_info_of_val(v1)
-		t2 := type_info_of_val(v2)
-		t3 := type_info_of_val(v3)
-
-		fmt.println()
-		fmt.print("Type of v1 is:\n\t", t1)
-
-		fmt.println()
-		fmt.print("Type of v2 is:\n\t", t2)
-
-		fmt.println("\n")
-		fmt.println("t1 == t2:", t1 == t2)
-		fmt.println("t2 == t3:", t2 == t3)
-	}
-}
-
-any_type :: proc() {
-	a: any
-
-	x: int = 123
-	y: f64 = 6.28
-	z: string = "Yo-Yo Ma"
-	// All types can be implicit cast to `any`
-	a = x
-	a = y
-	a = z
-	a = a // This the "identity" type, it doesn't get converted
-
-	a = 123 // Literals are copied onto the stack first
-
-	// any has two members
-	// data      - rawptr to the data
-	// type_info - pointer to the type info
-
-	fmt.println(x, y, z)
-	// See: fmt.odin
-	// For variadic any procedures in action
-}
-
-crazy_introspection :: proc() {
-	{
-		Fruit :: enum {
-			APPLE,
-			BANANA,
-			GRAPE,
-			MELON,
-			PEACH,
-			TOMATO,
-		}
-
-		s: string
-		s = enum_to_string(Fruit.PEACH)
-		fmt.println(s)
-
-		f := Fruit.GRAPE
-		s = enum_to_string(f)
-		fmt.println(s)
-
-		fmt.println(f)
-		// See: runtime.odin
-	}
-
-
-	{
-		// NOTE(bill): This is not safe code and I would not recommend this at all
-		// I'd recommend you use `match type` to get the subtype rather than
-		// casting pointers
-
-		Fruit :: enum {
-			APPLE,
-			BANANA,
-			GRAPE,
-			MELON,
-			PEACH,
-			TOMATO,
-		}
-
-		fruit_ti := type_info(Fruit)
-		name := (fruit_ti as ^Type_Info.Named).name // Unsafe casts
-		info := type_info_base(fruit_ti) as ^Type_Info.Enum // Unsafe casts
-
-		fmt.printf("% :: enum % {\n", name, info.base);
-		for i := 0; i < info.values.count; i++ {
-			fmt.printf("\t%\t= %,\n", info.names[i], info.values[i])
-		}
-		fmt.printf("}\n")
-
-		// NOTE(bill): look at that type-safe printf!
-	}
-
-	{
-		Vector3 :: struct {x, y, z: f32}
-
-		a := Vector3{x = 1, y = 4, z = 9}
-		fmt.println(a)
-		b := Vector3{x = 9, y = 3, z = 1}
-		fmt.println(b)
-
-		// NOTE(bill): See fmt.odin
-	}
-
-	// n.b. This pretty much "solves" serialization (to strings)
-}
-
-// #import "test.odin"
-
-namespaces_and_files :: proc() {
-
-	// test.thing()
-	// test.format.println()
-	// test.println()
-	/*
-		// Non-exporting import
-		#import "file.odin"
-		#import "file.odin" as file
-		#import "file.odin" as .
-		#import "file.odin" as _
-
-		// Exporting import
-		#load "file.odin"
-	*/
-
-	// Talk about scope rules and diagram
-}
-
-miscellany :: proc() {
-	/*
-		win32 `__imp__` prefix
-		#dll_import
-		#dll_export
-
-		Change exported name/symbol for linking
-		#link_name
-
-		Custom calling conventions
-		#stdcall
-		#fastcall
-
-		Runtime stuff
-		#shared_global_scope
-	*/
-
-	// assert(false)
-	// compile_assert(false)
-	// panic("Panic message goes here")
-}
-
-
-
-

+ 334 - 0
code/old_demos/demo001.odin

@@ -0,0 +1,334 @@
+#import "fmt.odin"
+#import "os.odin"
+#import "mem.odin"
+// #import "http_test.odin" as ht
+// #import "game.odin" as game
+// #import "punity.odin" as pn
+
+main :: proc() {
+	// struct_padding()
+	// bounds_checking()
+	// type_introspection()
+	// any_type()
+	// crazy_introspection()
+	// namespaces_and_files()
+	// miscellany()
+	// ht.run()
+	// game.run()
+	// {
+	// 	init :: proc(c: ^pn.Core) {}
+	// 	step :: proc(c: ^pn.Core) {}
+
+	// 	pn.run(init, step)
+	// }
+}
+
+struct_padding :: proc() {
+	{
+		A :: struct {
+			a: u8
+			b: u32
+			c: u16
+		}
+
+		B :: struct {
+			a: [7]u8
+			b: [3]u16
+			c: u8
+			d: u16
+		}
+
+		fmt.println("size_of(A):", size_of(A))
+		fmt.println("size_of(B):", size_of(B))
+
+		// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
+	}
+	{
+		A :: struct #ordered {
+			a: u8
+			b: u32
+			c: u16
+		}
+
+		B :: struct #ordered {
+			a: [7]u8
+			b: [3]u16
+			c: u8
+			d: u16
+		}
+
+		fmt.println("size_of(A):", size_of(A))
+		fmt.println("size_of(B):", size_of(B))
+
+		// C-style structure layout
+	}
+	{
+		A :: struct #packed {
+			a: u8
+			b: u32
+			c: u16
+		}
+
+		B :: struct #packed {
+			a: [7]u8
+			b: [3]u16
+			c: u8
+			d: u16
+		}
+
+		fmt.println("size_of(A):", size_of(A))
+		fmt.println("size_of(B):", size_of(B))
+
+		// Useful for explicit layout
+	}
+
+	// Member sorting by priority
+	// Alignment desc.
+	// Size desc.
+	// source order asc.
+
+	/*
+		A :: struct {
+			a: u8
+			b: u32
+			c: u16
+		}
+
+		B :: struct {
+			a: [7]u8
+			b: [3]u16
+			c: u8
+			d: u16
+		}
+
+		Equivalent too
+
+		A :: struct #ordered {
+			b: u32
+			c: u16
+			a: u8
+		}
+
+		B :: struct #ordered {
+			b: [3]u16
+			d: u16
+			a: [7]u8
+			c: u8
+		}
+	*/
+}
+
+bounds_checking :: proc() {
+	x: [4]int
+	// x[-1] = 0; // Compile Time
+	// x[4]  = 0; // Compile Time
+
+	{
+		a, b := -1, 4;
+		// x[a] = 0; // Runtime Time
+		// x[b] = 0; // Runtime Time
+	}
+
+	// Works for arrays, strings, slices, and related procedures & operations
+
+	{
+		base: [10]int
+		s := base[2:6]
+		a, b := -1, 6
+
+		#no_bounds_check {
+			s[a] = 0;
+			// #bounds_check s[b] = 0;
+		}
+
+	#no_bounds_check
+		if s[a] == 0 {
+			// Do whatever
+		}
+
+		// Bounds checking can be toggled explicit
+		// on a per statement basis.
+		// _any statement_
+	}
+}
+
+type_introspection :: proc() {
+	{
+		info: ^Type_Info
+		x: int
+
+		info = type_info(int) // by type
+		info = type_info_of_val(x) // by value
+		// See: runtime.odin
+
+		match type i : info {
+		case Type_Info.Integer:
+			fmt.println("integer!")
+		case Type_Info.Float:
+			fmt.println("float!")
+		default:
+			fmt.println("potato!")
+		}
+
+		// Unsafe cast
+		integer_info := info as ^Type_Info.Integer
+	}
+
+	{
+		Vector2 :: struct { x, y: f32 }
+		Vector3 :: struct { x, y, z: f32 }
+
+		v1: Vector2
+		v2: Vector3
+		v3: Vector3
+
+		t1 := type_info_of_val(v1)
+		t2 := type_info_of_val(v2)
+		t3 := type_info_of_val(v3)
+
+		fmt.println()
+		fmt.print("Type of v1 is:\n\t", t1)
+
+		fmt.println()
+		fmt.print("Type of v2 is:\n\t", t2)
+
+		fmt.println("\n")
+		fmt.println("t1 == t2:", t1 == t2)
+		fmt.println("t2 == t3:", t2 == t3)
+	}
+}
+
+any_type :: proc() {
+	a: any
+
+	x: int = 123
+	y: f64 = 6.28
+	z: string = "Yo-Yo Ma"
+	// All types can be implicit cast to `any`
+	a = x
+	a = y
+	a = z
+	a = a // This the "identity" type, it doesn't get converted
+
+	a = 123 // Literals are copied onto the stack first
+
+	// any has two members
+	// data      - rawptr to the data
+	// type_info - pointer to the type info
+
+	fmt.println(x, y, z)
+	// See: fmt.odin
+	// For variadic any procedures in action
+}
+
+crazy_introspection :: proc() {
+	{
+		Fruit :: enum {
+			APPLE,
+			BANANA,
+			GRAPE,
+			MELON,
+			PEACH,
+			TOMATO,
+		}
+
+		s: string
+		s = enum_to_string(Fruit.PEACH)
+		fmt.println(s)
+
+		f := Fruit.GRAPE
+		s = enum_to_string(f)
+		fmt.println(s)
+
+		fmt.println(f)
+		// See: runtime.odin
+	}
+
+
+	{
+		// NOTE(bill): This is not safe code and I would not recommend this at all
+		// I'd recommend you use `match type` to get the subtype rather than
+		// casting pointers
+
+		Fruit :: enum {
+			APPLE,
+			BANANA,
+			GRAPE,
+			MELON,
+			PEACH,
+			TOMATO,
+		}
+
+		fruit_ti := type_info(Fruit)
+		name := (fruit_ti as ^Type_Info.Named).name // Unsafe casts
+		info := type_info_base(fruit_ti) as ^Type_Info.Enum // Unsafe casts
+
+		fmt.printf("% :: enum % {\n", name, info.base);
+		for i := 0; i < info.values.count; i++ {
+			fmt.printf("\t%\t= %,\n", info.names[i], info.values[i])
+		}
+		fmt.printf("}\n")
+
+		// NOTE(bill): look at that type-safe printf!
+	}
+
+	{
+		Vector3 :: struct {x, y, z: f32}
+
+		a := Vector3{x = 1, y = 4, z = 9}
+		fmt.println(a)
+		b := Vector3{x = 9, y = 3, z = 1}
+		fmt.println(b)
+
+		// NOTE(bill): See fmt.odin
+	}
+
+	// n.b. This pretty much "solves" serialization (to strings)
+}
+
+// #import "test.odin"
+
+namespaces_and_files :: proc() {
+
+	// test.thing()
+	// test.format.println()
+	// test.println()
+	/*
+		// Non-exporting import
+		#import "file.odin"
+		#import "file.odin" as file
+		#import "file.odin" as .
+		#import "file.odin" as _
+
+		// Exporting import
+		#load "file.odin"
+	*/
+
+	// Talk about scope rules and diagram
+}
+
+miscellany :: proc() {
+	/*
+		win32 `__imp__` prefix
+		#dll_import
+		#dll_export
+
+		Change exported name/symbol for linking
+		#link_name
+
+		Custom calling conventions
+		#stdcall
+		#fastcall
+
+		Runtime stuff
+		#shared_global_scope
+	*/
+
+	// assert(false)
+	// compile_assert(false)
+	// panic("Panic message goes here")
+}
+
+
+
+

+ 412 - 0
code/old_demos/old_runtime.odin

@@ -0,0 +1,412 @@
+#load "win32.odin"
+
+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"
+
+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)
+	for i := 0; i < len; i++ {
+		d[i] = 0
+	}
+}
+
+memory_compare :: proc(dst, src: rawptr, len: int) -> int {
+	s1, s2: ^byte = dst, src
+	for i := 0; i < len; i++ {
+		a := ptr_offset(s1, i)^
+		b := ptr_offset(s2, i)^
+		if a != b {
+			return (a - b) as int
+		}
+	}
+	return 0
+}
+
+memory_copy :: proc(dst, src: rawptr, n: int) #inline {
+	if dst == src {
+		return
+	}
+
+	v128b :: type {4}u32
+	compile_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_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^
+		}
+	}
+}
+
+__string_eq :: proc(a, b: string) -> bool {
+	if len(a) != len(b) {
+		return false
+	}
+	if ^a[0] == ^b[0] {
+		return true
+	}
+	return memory_compare(^a[0], ^b[0], len(a)) == 0
+}
+
+__string_cmp :: proc(a, b : string) -> int {
+	min_len := len(a)
+	if len(b) < min_len {
+		min_len = len(b)
+	}
+	for i := 0; i < min_len; i++ {
+		x := a[i]
+		y := b[i]
+		if x < y {
+			return -1
+		} else if x > y {
+			return +1
+		}
+	}
+
+	if len(a) < len(b) {
+		return -1
+	} else if len(a) > len(b) {
+		return +1
+	}
+	return 0
+}
+
+__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
+__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
+__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
+__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
+__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
+
+
+
+
+Allocation_Mode :: type 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 {
+	procedure: Allocator_Proc;
+	data:      rawptr
+}
+
+
+Context :: type struct {
+	thread_ptr: rawptr
+
+	user_data:  rawptr
+	user_index: int
+
+	allocator: Allocator
+}
+
+#thread_local context: Context
+
+DEFAULT_ALIGNMENT :: 2*size_of(int)
+
+
+__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()
+	}
+}
+
+
+alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
+
+alloc_align :: proc(size, alignment: int) -> rawptr #inline {
+	__check_context()
+	a := context.allocator
+	return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
+}
+
+dealloc :: proc(ptr: rawptr) #inline {
+	__check_context()
+	a := context.allocator
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
+}
+dealloc_all :: proc(ptr: rawptr) #inline {
+	__check_context()
+	a := context.allocator
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
+}
+
+
+resize       :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
+resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
+	__check_context()
+	a := context.allocator
+	return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
+}
+
+
+
+default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
+	if old_memory == null {
+		return alloc_align(new_size, alignment)
+	}
+
+	if new_size == 0 {
+		dealloc(old_memory)
+		return null
+	}
+
+	if new_size == old_size {
+		return old_memory
+	}
+
+	new_memory := alloc_align(new_size, alignment)
+	if new_memory == null {
+		return null
+	}
+
+	memory_copy(new_memory, old_memory, min(old_size, new_size));
+	dealloc(old_memory)
+	return new_memory
+}
+
+
+__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
+                                 size, alignment: int,
+                                 old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
+	using Allocation_Mode
+	match mode {
+	case ALLOC:
+		return heap_alloc(size)
+	case RESIZE:
+		return default_resize_align(old_memory, old_size, size, alignment)
+	case DEALLOC:
+		heap_dealloc(old_memory)
+	case DEALLOC_ALL:
+		// NOTE(bill): Does nothing
+	}
+
+	return null
+}
+
+__default_allocator :: proc() -> Allocator {
+	return Allocator{
+		__default_allocator_proc,
+		null,
+	}
+}
+
+
+
+
+__assert :: proc(msg: string) {
+	file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
+	// TODO(bill): Which is better?
+	// __trap()
+	__debug_trap()
+}

+ 4 - 1
core/fmt.odin

@@ -170,7 +170,10 @@ print_bool_to_buffer :: proc(buffer: ^[]byte, b : bool) {
 	else { print_string_to_buffer(buffer, "false") }
 }
 
-print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline { print_uint_base_to_buffer(buffer, p as uint, 16, 0, #rune " ") }
+print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline {
+	print_string_to_buffer(buffer, "0x")
+	print_uint_base_to_buffer(buffer, p as uint, 16, size_of(int), #rune "0")
+}
 
 print_f32_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 7) }
 print_f64_to_buffer :: proc(buffer: ^[]byte, f: f64) #inline { print__f64(buffer, f, 10) }

+ 131 - 0
core/mem.odin

@@ -0,0 +1,131 @@
+#import "fmt.odin"
+#import "os.odin"
+
+
+kilobytes :: proc(x: int) -> int #inline { return          (x) * 1024 }
+megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024 }
+gigabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024 }
+terabytes :: proc(x: int) -> int #inline { return terabytes(x) * 1024 }
+
+is_power_of_two :: proc(x: int) -> bool {
+	if x <= 0 {
+		return false
+	}
+	return (x & (x-1)) == 0
+}
+
+align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
+	assert(is_power_of_two(align))
+
+	a := align as uint
+	p := ptr as uint
+	modulo := p & (a-1)
+	if modulo != 0 {
+		p += a - modulo
+	}
+	return p as rawptr
+}
+
+
+
+
+
+// Custom allocators
+
+Arena :: struct {
+	backing:    Allocator
+	memory:     []u8
+	temp_count: int
+}
+
+Temp_Arena_Memory :: struct {
+	arena:          ^Arena
+	original_count: int
+}
+
+
+
+init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
+	backing    = Allocator{}
+	memory     = data[:0]
+	temp_count = 0
+}
+
+init_arena_from_context :: proc(using a: ^Arena, size: int) {
+	backing = current_context().allocator
+	memory = new_slice(u8, 0, size)
+	temp_count = 0
+}
+
+init_sub_arena :: proc(sub, parent: ^Arena, size: int) {
+	push_allocator arena_allocator(parent) {
+		init_arena_from_context(sub, size)
+	}
+}
+
+free_arena :: proc(using a: ^Arena) {
+	if backing.procedure != null {
+		push_allocator backing {
+			free(memory.data)
+			memory = memory[0:0:0]
+		}
+	}
+}
+
+arena_allocator :: proc(arena: ^Arena) -> Allocator {
+	return Allocator{
+		procedure = arena_allocator_proc,
+		data = arena,
+	}
+}
+
+arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
+                             size, alignment: int,
+                             old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
+	arena := allocator_data as ^Arena
+
+	using Allocator.Mode
+	match mode {
+	case ALLOC:
+		total_size := size + alignment
+
+		if arena.memory.count + total_size > arena.memory.capacity {
+			fmt.fprintln(os.stderr, "Arena out of memory")
+			return null
+		}
+
+		#no_bounds_check end := ^arena.memory[arena.memory.count]
+
+		ptr := align_forward(end, alignment)
+		arena.memory.count += total_size
+		memory_zero(ptr, size)
+		return ptr
+
+	case FREE:
+		// NOTE(bill): Free all at once
+		// Use Temp_Arena_Memory if you want to free a block
+
+	case FREE_ALL:
+		arena.memory.count = 0
+
+	case RESIZE:
+		return default_resize_align(old_memory, old_size, size, alignment)
+	}
+
+	return null
+}
+
+begin_temp_arena_memory :: proc(a: ^Arena) -> Temp_Arena_Memory {
+	tmp: Temp_Arena_Memory
+	tmp.arena = a
+	tmp.original_count = a.memory.count
+	a.temp_count++
+	return tmp
+}
+
+end_temp_arena_memory :: proc(using tmp: Temp_Arena_Memory) {
+	assert(arena.memory.count >= original_count)
+	assert(arena.temp_count > 0)
+	arena.memory.count = original_count
+	arena.temp_count--
+}

+ 0 - 1
core/os.odin

@@ -26,7 +26,6 @@ create :: proc(name: string) -> (File, bool) {
 	return f, success
 }
 
-
 close :: proc(using f: ^File) {
 	win32.CloseHandle(handle)
 }

+ 63 - 27
src/codegen/ssa.cpp

@@ -85,15 +85,24 @@ struct ssaTargetList {
 	ssaBlock *     fallthrough_;
 };
 
+enum ssaDeferExitKind {
+	ssaDeferExit_Default,
+	ssaDeferExit_Return,
+	ssaDeferExit_Branch,
+};
 enum ssaDeferKind {
-	ssaDefer_Default,
-	ssaDefer_Return,
-	ssaDefer_Branch,
+	ssaDefer_Node,
+	ssaDefer_Instr,
 };
+
 struct ssaDefer {
-	AstNode *stmt;
+	ssaDeferKind kind;
 	isize scope_index;
 	ssaBlock *block;
+	union {
+		AstNode *stmt;
+		ssaValue *instr;
+	};
 };
 
 struct ssaProcedure {
@@ -361,6 +370,26 @@ ssaDebugInfo *ssa_alloc_debug_info(gbAllocator a, ssaDebugInfoKind kind) {
 	return di;
 }
 
+
+ssaDefer ssa_add_defer_node(ssaProcedure *proc, isize scope_index, AstNode *stmt) {
+	ssaDefer d = {ssaDefer_Node};
+	d.scope_index = scope_index;
+	d.block = proc->curr_block;
+	d.stmt = stmt;
+	gb_array_append(proc->defer_stmts, d);
+	return d;
+}
+
+
+ssaDefer ssa_add_defer_instr(ssaProcedure *proc, isize scope_index, ssaValue *instr) {
+	ssaDefer d = {ssaDefer_Instr};
+	d.scope_index = proc->scope_index;
+	d.block = proc->curr_block;
+	d.instr = cast(ssaValue *)gb_alloc_copy(proc->module->allocator, instr, gb_size_of(ssaValue));
+	gb_array_append(proc->defer_stmts, d);
+	return d;
+}
+
 void ssa_init_module(ssaModule *m, Checker *c) {
 	// TODO(bill): Determine a decent size for the arena
 	isize token_count = c->parser->total_token_count;
@@ -972,17 +1001,21 @@ void ssa_build_defer_stmt(ssaProcedure *proc, ssaDefer d) {
 	gb_array_append(proc->blocks, b);
 	proc->curr_block = b;
 	ssa_emit_comment(proc, make_string("defer"));
-	ssa_build_stmt(proc, d.stmt);
+	if (d.kind == ssaDefer_Node) {
+		ssa_build_stmt(proc, d.stmt);
+	} else if (d.kind == ssaDefer_Instr) {
+		ssa_emit(proc, d.instr);
+	}
 }
 
-void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferKind kind, ssaBlock *block) {
+void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferExitKind kind, ssaBlock *block) {
 	isize count = gb_array_count(proc->defer_stmts);
 	isize i = count;
 	while (i --> 0) {
 		ssaDefer d = proc->defer_stmts[i];
-		if (kind == ssaDefer_Return) {
+		if (kind == ssaDeferExit_Return) {
 			ssa_build_defer_stmt(proc, d);
-		} else if (kind == ssaDefer_Default) {
+		} else if (kind == ssaDeferExit_Default) {
 			if (proc->scope_index == d.scope_index &&
 			    d.scope_index > 1) {
 				ssa_build_defer_stmt(proc, d);
@@ -991,7 +1024,7 @@ void ssa_emit_defer_stmts(ssaProcedure *proc, ssaDeferKind kind, ssaBlock *block
 			} else {
 				break;
 			}
-		} else if (kind == ssaDefer_Branch) {
+		} else if (kind == ssaDeferExit_Branch) {
 			GB_ASSERT(block != NULL);
 			isize lower_limit = block->scope_index+1;
 			if (lower_limit < d.scope_index) {
@@ -1009,7 +1042,7 @@ void ssa_emit_unreachable(ssaProcedure *proc) {
 }
 
 void ssa_emit_ret(ssaProcedure *proc, ssaValue *v) {
-	ssa_emit_defer_stmts(proc, ssaDefer_Return, NULL);
+	ssa_emit_defer_stmts(proc, ssaDeferExit_Return, NULL);
 	ssa_emit(proc, ssa_make_instr_ret(proc, v));
 }
 
@@ -3316,7 +3349,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 	case_ast_node(bs, BlockStmt, node);
 		proc->scope_index++;
 		ssa_build_stmt_list(proc, bs->stmts);
-		ssa_emit_defer_stmts(proc, ssaDefer_Default, NULL);
+		ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 		proc->scope_index--;
 	case_end;
 
@@ -3325,8 +3358,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		isize scope_index = proc->scope_index;
 		if (ds->stmt->kind == AstNode_BlockStmt)
 			scope_index--;
-		ssaDefer d = {ds->stmt, scope_index, proc->curr_block};
-		gb_array_append(proc->defer_stmts, d);
+		ssa_add_defer_node(proc, scope_index, ds->stmt);
 	case_end;
 
 	case_ast_node(rs, ReturnStmt, node);
@@ -3377,7 +3409,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 		proc->scope_index++;
 		ssa_build_stmt(proc, is->body);
-		ssa_emit_defer_stmts(proc, ssaDefer_Default, NULL);
+		ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 		proc->scope_index--;
 
 		ssa_emit_jump(proc, done);
@@ -3387,7 +3419,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			proc->scope_index++;
 			ssa_build_stmt(proc, is->else_stmt);
-			ssa_emit_defer_stmts(proc, ssaDefer_Default, NULL);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 			proc->scope_index--;
 
 
@@ -3429,7 +3461,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 		proc->scope_index++;
 		ssa_build_stmt(proc, fs->body);
-		ssa_emit_defer_stmts(proc, ssaDefer_Default, NULL);
+		ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 		proc->scope_index--;
 
 		ssa_pop_target_list(proc);
@@ -3524,7 +3556,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			proc->scope_index++;
 			ssa_push_target_list(proc, done, NULL, fall);
 			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_emit_defer_stmts(proc, ssaDefer_Default, body);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Default, body);
 			ssa_pop_target_list(proc);
 			proc->scope_index--;
 
@@ -3541,7 +3573,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			proc->scope_index++;
 			ssa_push_target_list(proc, done, NULL, default_fall);
 			ssa_build_stmt_list(proc, default_stmts);
-			ssa_emit_defer_stmts(proc, ssaDefer_Default, default_block);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Default, default_block);
 			ssa_pop_target_list(proc);
 			proc->scope_index--;
 		}
@@ -3631,7 +3663,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			proc->scope_index++;
 			ssa_push_target_list(proc, done, NULL, NULL);
 			ssa_build_stmt_list(proc, cc->stmts);
-			ssa_emit_defer_stmts(proc, ssaDefer_Default, body);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Default, body);
 			ssa_pop_target_list(proc);
 			proc->scope_index--;
 
@@ -3647,7 +3679,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			proc->scope_index++;
 			ssa_push_target_list(proc, done, NULL, NULL);
 			ssa_build_stmt_list(proc, default_stmts);
-			ssa_emit_defer_stmts(proc, ssaDefer_Default, default_block);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Default, default_block);
 			ssa_pop_target_list(proc);
 			proc->scope_index--;
 		}
@@ -3671,7 +3703,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		// TODO(bill): Handle fallthrough scope exit correctly
 		// if (block != NULL && bs->token.kind != Token_fallthrough) {
 		if (block != NULL) {
-			ssa_emit_defer_stmts(proc, ssaDefer_Branch, block);
+			ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block);
 		}
 		switch (bs->token.kind) {
 		case Token_break:       ssa_emit_comment(proc, make_string("break"));       break;
@@ -3686,35 +3718,39 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_ast_node(pa, PushAllocator, node);
 		ssa_emit_comment(proc, make_string("PushAllocator"));
+		proc->scope_index++;
+		defer (proc->scope_index--);
 
 		ssaValue *context_ptr = *map_get(&proc->module->members, hash_string(make_string("__context")));
 		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
 		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-		defer (ssa_emit_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
+
+		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
 
 		ssaValue *gep = ssa_emit_struct_gep(proc, context_ptr, 1, t_allocator_ptr);
 		ssa_emit_store(proc, gep, ssa_build_expr(proc, pa->expr));
 
-		proc->scope_index++;
 		ssa_build_stmt(proc, pa->body);
-		proc->scope_index--;
+		ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 
 	case_end;
 
 
 	case_ast_node(pa, PushContext, node);
 		ssa_emit_comment(proc, make_string("PushContext"));
+		proc->scope_index++;
+		defer (proc->scope_index--);
 
 		ssaValue *context_ptr = *map_get(&proc->module->members, hash_string(make_string("__context")));
 		ssaValue *prev_context = ssa_add_local_generated(proc, t_context);
 		ssa_emit_store(proc, prev_context, ssa_emit_load(proc, context_ptr));
-		defer (ssa_emit_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
+
+		ssa_add_defer_instr(proc, proc->scope_index, ssa_make_instr_store(proc, context_ptr, ssa_emit_load(proc, prev_context)));
 
 		ssa_emit_store(proc, context_ptr, ssa_build_expr(proc, pa->expr));
 
-		proc->scope_index++;
 		ssa_build_stmt(proc, pa->body);
-		proc->scope_index--;
+		ssa_emit_defer_stmts(proc, ssaDeferExit_Default, NULL);
 	case_end;
 
 

+ 2 - 2
src/common.cpp

@@ -12,7 +12,7 @@ String get_module_dir(gbAllocator a) {
 	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
 	defer (gb_temp_arena_memory_end(tmp));
 
-	
+
 	wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
 
 	String16 str = {text, len};
@@ -25,7 +25,7 @@ String get_module_dir(gbAllocator a) {
 		}
 		path.len--;
 	}
-	
+
 	return path;
 }
 

+ 1 - 1
src/main.cpp

@@ -49,7 +49,7 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 	}
 }
 
-#define DISPLAY_TIMING
+// #define DISPLAY_TIMING
 #if defined(DISPLAY_TIMING)
 #define INIT_TIMER() f64 start_time = gb_time_now(), end_time = 0, total_time = 0
 #define PRINT_TIMER(section) do { \