Browse Source

Implicit Context and #thread_local

gingerBill 9 years ago
parent
commit
680274b6f1

+ 1 - 0
build.bat

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

+ 1 - 1
c_libs/stb_image.c

@@ -414,7 +414,7 @@ extern "C" {
 #ifdef STB_IMAGE_STATIC
 #define STBIDEF static
 #else
-#define STBIDEF extern "C" __declspec(dllexport)
+#define STBIDEF extern
 #endif
 
 //////////////////////////////////////////////////////////////////////////////

+ 138 - 60
examples/demo001.odin

@@ -1,6 +1,7 @@
+// Demo 001
 #load "basic.odin"
 #load "math.odin"
-// #load "game.odin"
+#load "game.odin"
 
 main :: proc() {
 	_ = hellope();
@@ -9,7 +10,8 @@ main :: proc() {
 	constants();
 	types();
 	data_control();
-	// run_game();
+
+	run_game();
 }
 
 hellope :: proc() -> int {
@@ -17,9 +19,22 @@ hellope :: proc() -> int {
 	return 1;
 }
 
+
+// Line comment
+/*
+	Block Comment
+*/
+/*
+	Nested /*
+		Block /*
+			Comment
+		*/
+	*/
+*/
+
 apple, banana, carrot: bool;
 box, carboard: bool = true, false;
-hellope_value := hellope();
+hellope_value: int = hellope();
 
 variables :: proc() {
 	i: int; // initialized with zero value
@@ -39,21 +54,23 @@ variables :: proc() {
 	//
 	// f32 f64
 	//
-	// int uint (size_of(int) = size_of(rawptr))
+	// int uint (size_of(int) == size_of(uint) == size_of(rawptr))
 	//
-	// rawptr
+	// rawptr (equivalent to void * in C/C++)
 	//
 	// string
 	//
 	// byte - alias for u8
 	// rune - alias for i32 // Unicode Codepoint
 	//
-	// untyped bool      - "untyped" types can implicitly convert to any of the "typed" types
-	// untyped integer
-	// untyped float
-	// untyped pointer
-	// untyped string
-	// untyped rune
+	// "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
@@ -62,6 +79,7 @@ variables :: proc() {
 	zero_pointer := null;
 	zero_string1 := ""; // Escaped string
 	zero_string2 := ``; // Raw string
+	// Compound types have a different kind of zero value
 
 	// Unary operators
 	// +a
@@ -70,28 +88,28 @@ variables :: proc() {
 	// !a
 
 	// Binary operators
-	// a + b
-	// a - b
-	// a ~ b
-	// a | b
-
-	// a * b
-	// a / b
-	// a % b
-	// a & b
-	// a &~ b   == a & (~b)
-	// a << b
-	// a >> b
-
-	// a as Type
-	// a transmute Type
-
-	// a == b
-	// a != b
-	// a < b
-	// a > b
-	// a <= b
-	// a >= b
+	// 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
 
 }
 
@@ -115,10 +133,11 @@ procedures :: proc() {
 	print_string(b);
 
 	a, b = b, a; // Quirk of grammar the of multiple assignments
+	             // Swap variables
 	print_string(a);
 	print_string(b);
 
-	// Not hints, it's mandatory
+	// 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);
 	}
@@ -136,6 +155,16 @@ constants :: proc() {
 
 	TAU_32 : f32 : 6.28318530718;
 	TAU_AS_32 :: 6.28318530718 as f32;
+
+	PI :: TAU / 2;
+
+	CLOSE_TO_PI :: 3;
+
+	DIFF :: (PI - CLOSE_TO_PI) / PI; // Evaluated at compile time
+
+	a := TAU;         // the constant's value becomes typed as f32
+	b := CLOSE_TO_PI; // the constant's value becomes typed as int
+	c := DIFF;
 }
 
 nl :: proc() { print_rune('\n'); }
@@ -147,13 +176,22 @@ types :: proc() {
 	// z: f32 = x; // invalid
 	z: f32 = x as f32;
 
-	ptr_z := ^z; // Pascal notation
+
+	ptr_z := ^z;  // Pascal notation
 	ptr_z^ = 123; // Derefence Notation
+	w: f32 = ptr_z^;  // 123
 	print_f32(z); nl();
 
 	// ^z - pointer to z
 	// z^ - z from pointer
 
+	// Implicit conversion to and from rawptr
+	r_ptr: rawptr = ptr_z;
+	ptr_z = r_ptr;
+
+
+
+
 	f32_array: [12]f32; // Array of 12 f32
 	f32_array[0] = 2;
 	f32_array[1] = 3;
@@ -162,15 +200,28 @@ types :: proc() {
 	f32_array_len := len(f32_array); // builtin procedure
 	f32_array_cap := cap(f32_array); // == len(f32_array)
 
+
+	mda: [2][3][4]int; // Column-major
+	// mda[x][y][z]
+
+
+
 	api: [2]^f32;
 	papi: ^[2]^f32;
 
-	f32_slice: []f32; // Array reference
+
+
+
+	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, capacity:7
+	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);
@@ -178,8 +229,16 @@ types :: proc() {
 	_ = copy(f32_array[0:2], f32_array[2:4]); // You can use memcpy/memmove if you want
 
 
+
+
+
+
 	s := "Hellope World";
-	sub_string := s[5:10];
+	sub_string: string = s[5:10];
+
+
+
+
 
 	v0: {4}f32; // Vector of 4 f32
 	v0[0] = 1;
@@ -199,6 +258,11 @@ types :: proc() {
 	// LLVM rant?
 
 
+
+
+
+
+
 	type Vec4: {4}f32;
 	type Array3Int: [3]int;
 
@@ -218,7 +282,7 @@ types :: proc() {
 		b: u16,
 		c: u32,
 	}
-	static_assert(size_of(Packed) == 7);
+	static_assert(size_of(Packed) == 7); // builtin procedure
 
 
 	{
@@ -261,15 +325,17 @@ types :: proc() {
 		}
 
 		// transmute only works if the size of the types are equal
-		/{
+		/*
 			// in C
 			union {
 				i32 i;
 				f32 y;
 			};
-		 }/
+		 */
 	}
 
+
+
 	{ // Compound Literals
 		a := [3]int{1, 2, 3};
 		b := [3]int{};
@@ -293,7 +359,10 @@ types :: proc() {
 		print_f32(i[1]); print_rune('\n');
 	}
 
-	{
+
+
+	{ // First class procedures
+
 		do_thing :: proc(p: proc(a, b: int) -> int) {
 			print_int(p(3, 4)); nl();
 		}
@@ -309,11 +378,13 @@ types :: proc() {
 
 		do_thing(add);
 		do_thing(add_lambda);
-		do_thing(proc(a, b: int) -> int {
+		do_thing(proc(a, b: int) -> int { // Anonymous
 			return a * b;
 		});
 	}
 
+
+
 	{ // strings and runes
 		escaped := "Hellope World\n";
 		raw     := `Hellope World\n`;
@@ -346,11 +417,12 @@ void main() {
 }`;
 
 
-		knot1 := '⌘';
-		knot2 := '\u2318';     // 16 bit
-		knot3 := '\U00002318'; // 32 bit
-		knot4 := "\xe2\x8c\x98"; // Note it's a string, should I allow untyped string -> untyped rune casts?
+		hearts1 := '💕';
+		hearts2 := '\U0001f495'; // 32 bit
+		hearts3 := "\xf0\x9f\x92\x95"; // Note it's a string, should I allow untyped string -> untyped rune casts?
 
+		㐒 := '㐒';
+		㐒16 := '\u4db5'; // 16 bit
 		// String ideas "nicked" from Go, so far. I think I might change how some of it works later.
 	}
 
@@ -439,7 +511,7 @@ data_control :: proc() {
 
 
 
-	{
+	{ // Defer statement
 		defer print_string("日本語\n");
 		print_string("Japanese\n");
 	}
@@ -451,21 +523,26 @@ data_control :: proc() {
 	}
 
 	{
+		prev_allocator := context.allocator;
+		context.allocator = __default_allocator();
+		defer context.allocator = prev_allocator;
+
 		// C strings, yuk!
-		to_c_string :: proc(s: string) -> ^u8 {
-			c := heap_alloc(len(s)+1) as ^u8;
+		to_c_string := proc(s: string) -> ^u8 {
+			c := alloc(len(s)+1) as ^u8;
 			mem_copy(c, ^s[0], len(s));
 			c[len(s)] = 0;
 			return c;
-		}
+		};
+
 
 		fopen  :: proc(filename, mode: ^u8) -> rawptr #foreign
 		fclose :: proc(f: rawptr) -> i32 #foreign
 
 		filename := to_c_string("../examples/base.odin");
 		mode := to_c_string("rb");
-		defer heap_free(filename);
-		defer heap_free(mode);
+		defer dealloc(filename);
+		defer dealloc(mode);
 
 		f := fopen(filename, mode);
 		if f == null {
@@ -478,10 +555,10 @@ data_control :: proc() {
 		// rest of code
 
 		// Better version
-		/{
+		/*
 			type File: struct { filename: string }
 			type FileError: int
-			open_file :: proc(filename: string) -> (File, FileError) { ... }
+			open_file  :: proc(filename: string) -> (File, FileError) { ... }
 			close_file :: proc(f: ^File) { ... }
 			f, err := open_file("Test");
 			if err != 0 {
@@ -489,17 +566,17 @@ data_control :: proc() {
 			}
 			defer close_file(^f);
 
-		}/
+		 */
 
 
 	}
 
 	for i := 0; i < 100; i++ {
-		blah := heap_alloc(100 * size_of(int)) as ^int;
+		blah := alloc(100 * size_of(int)) as ^int;
 		defer {
 			defer print_string("!");
-			defer print_string("heap_free");
-			heap_free(blah);
+			defer print_string("dealloc");
+			dealloc(blah);
 		}
 
 		if i == 3 {
@@ -509,7 +586,7 @@ data_control :: proc() {
 
 		if i == 5 {
 			// defers called
-			return;
+			return; // End of procedure
 		}
 
 		if i == 8 {
@@ -523,3 +600,4 @@ data_control :: proc() {
 	print_string("It'll never happen, mate 3");
 }
 
+

+ 49 - 23
examples/game.odin

@@ -35,13 +35,12 @@ to_c_string :: proc(s: string) -> ^u8 {
 
 
 type Window: struct {
-	width, height: int,
-	wc: WNDCLASSEXA,
-	dc: HDC,
-	hwnd: HWND,
-	opengl_context: rawptr,
-	rc: HGLRC,
-	c_title: ^u8,
+	width, height:      int,
+	wc:                 WNDCLASSEXA,
+	dc:                 HDC,
+	hwnd:               HWND,
+	opengl_context, rc: HGLRC,
+	c_title:            ^u8,
 }
 
 make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) {
@@ -55,11 +54,11 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 	instance := GetModuleHandleA(null);
 
 	w.wc = WNDCLASSEXA{
-		cbSize    = size_of(WNDCLASSEXA) as u32,
-		style     = CS_VREDRAW | CS_HREDRAW,
-		hInstance = instance as HINSTANCE,
-		className = c_class_name,
-		wndProc   = window_proc,
+		size       = size_of(WNDCLASSEXA) as u32,
+		style      = CS_VREDRAW | CS_HREDRAW,
+		instance   = instance as HINSTANCE,
+		class_name = c_class_name,
+		wnd_proc   = window_proc,
 	};
 
 	if RegisterClassExA(^w.wc) == 0 {
@@ -82,15 +81,15 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 
 	{
 		pfd := PIXELFORMATDESCRIPTOR{
-			nSize        = size_of(PIXELFORMATDESCRIPTOR) as u32,
-			nVersion     = 1,
-			dwFlags      = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
-			iPixelType   = PFD_TYPE_RGBA,
-			cColorBits   = 32,
-			cAlphaBits   = 8,
-			cDepthBits   = 24,
-			cStencilBits = 8,
-			iLayerType   = PFD_MAIN_PLANE,
+			size         = size_of(PIXELFORMATDESCRIPTOR) as u32,
+			version      = 1,
+			flags        = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+			pixel_type   = PFD_TYPE_RGBA,
+			color_bits   = 32,
+			alpha_bits   = 8,
+			depth_bits   = 24,
+			stencil_bits = 8,
+			layer_type   = PFD_MAIN_PLANE,
 		};
 
 		SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), null);
@@ -124,6 +123,11 @@ display_window :: proc(w: ^Window) {
 }
 
 
+type Entity: struct {
+	pos: Vec2,
+	dim: Vec2,
+}
+
 
 run_game :: proc() {
 	win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
@@ -143,6 +147,9 @@ run_game :: proc() {
 
 	prev_time := time_now();
 	running := true;
+
+	pos := Vec2{100, 100};
+
 	for running {
 		curr_time := time_now();
 		dt := (curr_time - prev_time) as f32;
@@ -157,12 +164,32 @@ run_game :: proc() {
 			_ = DispatchMessageA(^msg);
 		}
 
+		if is_key_down(VK_ESCAPE) {
+			running = false;
+		}
+
+		{
+			SPEED :: 500;
+			v: Vec2;
+
+			if is_key_down(VK_RIGHT) { v[0] += 1; }
+			if is_key_down(VK_LEFT)  { v[0] -= 1; }
+			if is_key_down(VK_UP)    { v[1] += 1; }
+			if is_key_down(VK_DOWN)  { v[1] -= 1; }
+
+			v = vec2_norm0(v);
+
+			pos += v * Vec2{SPEED * dt};
+		}
+
+
 		glClearColor(0.5, 0.7, 1.0, 1.0);
 		glClear(GL_COLOR_BUFFER_BIT);
 
 		glLoadIdentity();
 		glOrtho(0, window.width as f64,
 		        0, window.height as f64, 0, 1);
+
 		draw_rect :: proc(x, y, w, h: f32) {
 			glBegin(GL_TRIANGLES);
 
@@ -177,8 +204,7 @@ run_game :: proc() {
 			glEnd();
 		}
 
-		x, y : f32 = 100+50*sinf(curr_time as f32), 100;
-		draw_rect(x, y, 50, 50);
+		draw_rect(pos[0], pos[1], 50, 50);
 
 		display_window(^window);
 		ms_to_sleep := (16 - 1000*dt) as i32;

+ 142 - 7
examples/math.odin

@@ -1,19 +1,154 @@
+MATH_TAU          :: 6.28318530717958647692528676655900576;
+MATH_PI           :: 3.14159265358979323846264338327950288;
+MATH_ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
+MATH_ONE_OVER_PI  :: 0.159154943091895335768883763372514362;
+
+MATH_E            :: 2.71828182845904523536;
+MATH_SQRT_TWO     :: 1.41421356237309504880168872420969808;
+MATH_SQRT_THREE   :: 1.73205080756887729352744634150587236;
+MATH_SQRT_FIVE    :: 2.23606797749978969640917366873127623;
+
+MATH_LOG_TWO      :: 0.693147180559945309417232121458176568;
+MATH_LOG_TEN      :: 2.30258509299404568401799145468436421;
+
+MATH_EPSILON      :: 1.19209290e-7;
+
+
 type Vec2: {2}f32
 type Vec3: {3}f32
 type Vec4: {4}f32
-type Mat2: {4}f32
+
+type Mat2:  {4}f32
+type Mat3:  {9}f32
+type Mat4: {16}f32
+
+
+fsqrt    :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
+fsin     :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
+fcos     :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
+flerp    :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
+fclamp   :: proc(x, lower, upper: f32) -> f32 { return fmin(fmax(x, lower), upper); }
+fclamp01 :: proc(x: f32) -> f32 { return fclamp(x, 0, 1); }
+fabs     :: proc(x: f32) -> f32 { if x < 0 { x = -x; } return x; }
+fsign    :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
+
+fmin     :: proc(a, b: f32) -> f32 { if a < b { return a; } return b; }
+fmax     :: proc(a, b: f32) -> f32 { if a > b { return a; } return b; }
+fmin3    :: proc(a, b, c: f32) -> f32 { return fmin(fmin(a, b), c); }
+fmax3    :: proc(a, b, c: f32) -> f32 { return fmax(fmax(a, b), c); }
+
+
+copy_sign :: proc(x, y: f32) -> f32 {
+	ix := x transmute u32;
+	iy := y transmute u32;
+	ix &= 0x7fffffff;
+	ix |= iy & 0x80000000;
+	return ix transmute f32;
+}
+
+
+round :: proc(x: f32) -> f32 {
+	if x >= 0 {
+		return floor(x + 0.5);
+	}
+	return ceil(x - 0.5);
+}
+floor :: proc(x: f32) -> f32 {
+	if x >= 0 {
+		return x as int as f32;
+	}
+	return (x-0.5) as int as f32;
+}
+ceil  :: proc(x: f32) -> f32 {
+	if x < 0 {
+		return x as int as f32;
+	}
+	return ((x as int)+1) as f32;
+}
+
+
+
+
+remainder :: proc(x, y: f32) -> f32 {
+	return x - round(x/y) * y;
+}
+
+fmod :: proc(x, y: f32) -> f32 {
+	y = fabs(y);
+	result := remainder(fabs(x), y);
+	if fsign(result) < 0 {
+		result += y;
+	}
+	return copy_sign(result, x);
+}
+
+
+to_radians :: proc(degrees: f32) -> f32 { return degrees * MATH_TAU / 360; }
+to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / MATH_TAU; }
 
 
 
-sqrtf :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
-sinf  :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
-cosf  :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
 
 vec2_dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c[0] + c[1]; }
 vec3_dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c[0] + c[1] + c[2]; }
+vec4_dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c[0] + c[1] + c[2] + c[3]; }
+
+
+vec2_mag :: proc(v: Vec2) -> f32 { return fsqrt(vec2_dot(v, v)); }
+vec3_mag :: proc(v: Vec3) -> f32 { return fsqrt(vec3_dot(v, v)); }
+vec4_mag :: proc(v: Vec4) -> f32 { return fsqrt(vec4_dot(v, v)); }
+
+vec2_norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{vec2_mag(v)}; }
+vec3_norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{vec3_mag(v)}; }
+vec4_norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{vec4_mag(v)}; }
+
+vec2_norm0 :: proc(v: Vec2) -> Vec2 {
+	m := vec2_mag(v);
+	if m == 0 {
+		return Vec2{0};
+	}
+	return v / Vec2{m};
+}
+
+vec3_norm0 :: proc(v: Vec3) -> Vec3 {
+	m := vec3_mag(v);
+	if m == 0 {
+		return Vec3{0};
+	}
+	return v / Vec3{m};
+}
+
+vec4_norm0 :: proc(v: Vec4) -> Vec4 {
+	m := vec4_mag(v);
+	if m == 0 {
+		return Vec4{0};
+	}
+	return v / Vec4{m};
+}
 
-lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
 
-vec2_mag :: proc(a: Vec2) -> f32 { return sqrtf(vec2_dot(a, a)); }
-vec3_mag :: proc(a: Vec3) -> f32 { return sqrtf(vec3_dot(a, a)); }
+F32_DIG        :: 6;
+F32_EPSILON    :: 1.192092896e-07;
+F32_GUARD      :: 0;
+F32_MANT_DIG   :: 24;
+F32_MAX        :: 3.402823466e+38;
+F32_MAX_10_EXP :: 38;
+F32_MAX_EXP    :: 128;
+F32_MIN        :: 1.175494351e-38;
+F32_MIN_10_EXP :: -37;
+F32_MIN_EXP    :: -125;
+F32_NORMALIZE  :: 0;
+F32_RADIX      :: 2;
+F32_ROUNDS     :: 1;
 
+F64_DIG        :: 15;                      // # of decimal digits of precision
+F64_EPSILON    :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
+F64_MANT_DIG   :: 53;                      // # of bits in mantissa
+F64_MAX        :: 1.7976931348623158e+308; // max value
+F64_MAX_10_EXP :: 308;                     // max decimal exponent
+F64_MAX_EXP    :: 1024;                    // max binary exponent
+F64_MIN        :: 2.2250738585072014e-308; // min positive value
+F64_MIN_10_EXP :: -307;                    // min decimal exponent
+F64_MIN_EXP    :: -1021;                   // min binary exponent
+F64_RADIX      :: 2;                       // exponent radix
+F64_ROUNDS     :: 1;                       // addition rounding: near

+ 127 - 3
examples/runtime.odin

@@ -1,14 +1,16 @@
 putchar :: proc(c: i32) -> i32 #foreign
 
-heap_alloc  :: proc(sz: int) -> rawptr #foreign "malloc"
-heap_free   :: proc(ptr: rawptr)       #foreign "free"
-
 mem_compare :: proc(dst, src : rawptr, len: int) -> i32 #foreign "memcmp"
 mem_copy    :: proc(dst, src : rawptr, len: int) -> i32 #foreign "memcpy"
 mem_move    :: proc(dst, src : rawptr, len: int) -> i32 #foreign "memmove"
 
 debug_trap :: proc() #foreign "llvm.debugtrap"
 
+// TODO(bill): make custom heap procedures
+heap_realloc :: proc(ptr: rawptr, sz: int) -> rawptr #foreign "realloc"
+heap_alloc   :: proc(sz: int) -> rawptr { return heap_realloc(null, sz); }
+heap_free    :: proc(ptr: rawptr)       { _ = heap_realloc(ptr, 0); }
+
 
 __string_eq :: proc(a, b : string) -> bool {
 	if len(a) != len(b) {
@@ -50,3 +52,125 @@ __string_lt :: proc(a, b : string) -> bool { return __string_cmp(a, b) < 0; }
 __string_gt :: proc(a, b : string) -> bool { return __string_cmp(a, b) > 0; }
 __string_le :: proc(a, b : string) -> bool { return __string_cmp(a, b) <= 0; }
 __string_ge :: proc(a, b : string) -> bool { return __string_cmp(a, b) >= 0; }
+
+
+type AllocationMode: int;
+ALLOCATION_ALLOC       :: 0;
+ALLOCATION_DEALLOC     :: 1;
+ALLOCATION_DEALLOC_ALL :: 2;
+ALLOCATION_RESIZE      :: 3;
+
+
+type AllocatorProc: proc(allocator_data: rawptr, mode: AllocationMode,
+                         size, alignment: int,
+                         old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
+
+type Allocator: struct {
+	procedure: AllocatorProc,
+	data:      rawptr,
+}
+
+
+type Context: struct {
+	thread_id: i32,
+
+	user_index: i32,
+	user_data:  rawptr,
+
+	allocator: Allocator,
+}
+
+#thread_local context: Context;
+
+DEFAULT_ALIGNMENT :: 2*size_of(int);
+
+
+__check_context :: proc() {
+	if context.allocator.procedure == null {
+		context.allocator = __default_allocator();
+	}
+
+	ptr := __check_context as rawptr;
+}
+
+
+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_ALLOC, size, alignment, null, 0, 0);
+}
+
+dealloc :: proc(ptr: rawptr) #inline {
+	__check_context();
+	a := context.allocator;
+	_ = a.procedure(a.data, ALLOCATION_DEALLOC, 0, 0, ptr, 0, 0);
+}
+dealloc_all :: proc(ptr: rawptr) #inline {
+	__check_context();
+	a := context.allocator;
+	_ = a.procedure(a.data, ALLOCATION_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_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 {
+		new_size = old_size;
+	}
+
+	if old_size == new_size {
+		return old_memory;
+	}
+
+	new_memory := alloc_align(new_size, alignment);
+	if new_memory == null {
+		return null;
+	}
+	_ = copy((new_memory as ^u8)[:new_size], (old_memory as ^u8)[:old_size]);
+	dealloc(old_memory);
+	return new_memory;
+}
+
+
+__default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode,
+                                 size, alignment: int,
+                                 old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
+	if mode == ALLOCATION_ALLOC {
+		return heap_alloc(size);
+	} else if mode == ALLOCATION_RESIZE {
+		return heap_realloc(old_memory, size);
+	} else if mode == ALLOCATION_DEALLOC {
+		heap_free(old_memory);
+	} else if mode == ALLOCATION_DEALLOC_ALL {
+		// NOTE(bill): Does nothing
+	}
+
+	return null;
+}
+
+__default_allocator :: proc() -> Allocator {
+	return Allocator{
+		__default_allocator_proc,
+		null,
+	};
+}
+

+ 204 - 42
examples/win32.odin

@@ -42,24 +42,24 @@ type POINT: struct { x, y: i32 }
 type WNDPROC: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
 
 type WNDCLASSEXA: struct {
-	cbSize, style: u32,
-	wndProc: WNDPROC,
-	cbClsExtra, cbWndExtra: i32,
-	hInstance: HINSTANCE,
-	hIcon: HICON,
-	hCursor: HCURSOR,
-	hbrBackground: HBRUSH,
-	menuName, className: ^u8,
-	hIconSm: HICON,
+	size, style:           u32,
+	wnd_proc:              WNDPROC,
+	cls_extra, wnd_extra:  i32,
+	instance:              HINSTANCE,
+	icon:                  HICON,
+	cursor:                HCURSOR,
+	background:            HBRUSH,
+	menu_name, class_name: ^u8,
+	sm:                    HICON,
 }
 
 type MSG: struct {
-	hwnd: HWND,
+	hwnd:    HWND,
 	message: u32,
-	wparam: WPARAM,
-	lparam: LPARAM,
-	time: u32,
-	pt: POINT,
+	wparam:  WPARAM,
+	lparam:  LPARAM,
+	time:    u32,
+	pt:      POINT,
 }
 
 
@@ -145,34 +145,34 @@ type wglCreateContextAttribsARBType: proc(hdc: HDC, hshareContext: rawptr, attri
 
 
 type PIXELFORMATDESCRIPTOR: struct  {
-	nSize,
-	nVersion,
-	dwFlags: u32,
-
-	iPixelType,
-	cColorBits,
-	cRedBits,
-	cRedShift,
-	cGreenBits,
-	cGreenShift,
-	cBlueBits,
-	cBlueShift,
-	cAlphaBits,
-	cAlphaShift,
-	cAccumBits,
-	cAccumRedBits,
-	cAccumGreenBits,
-	cAccumBlueBits,
-	cAccumAlphaBits,
-	cDepthBits,
-	cStencilBits,
-	cAuxBuffers,
-	iLayerType,
-	bReserved: byte,
-
-	dwLayerMask,
-	dwVisibleMask,
-	dwDamageMask: u32,
+	size,
+	version,
+	flags: u32,
+
+	pixel_type,
+	color_bits,
+	red_bits,
+	red_shift,
+	green_bits,
+	green_shift,
+	blue_bits,
+	blue_shift,
+	alpha_bits,
+	alpha_shift,
+	accum_bits,
+	accum_red_bits,
+	accum_green_bits,
+	accum_blue_bits,
+	accum_alpha_bits,
+	depth_bits,
+	stencil_bits,
+	aux_buffers,
+	layer_type,
+	reserved: byte,
+
+	layer_mask,
+	visible_mask,
+	damage_mask: u32,
 }
 
 GetDC             :: proc(h: HANDLE) -> HDC #foreign
@@ -190,3 +190,165 @@ wglCreateContext  :: proc(hdc: HDC) -> HGLRC #foreign
 wglMakeCurrent    :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign
 wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign
 wglDeleteContext  :: proc(hglrc: HGLRC) -> BOOL #foreign
+
+
+
+GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign
+
+is_key_down :: proc(key: int) -> bool {
+	return GetAsyncKeyState(key as i32) < 0;
+}
+
+
+VK_LBUTTON    :: 0x01;
+VK_RBUTTON    :: 0x02;
+VK_CANCEL     :: 0x03;
+VK_MBUTTON    :: 0x04;
+
+VK_BACK       :: 0x08;
+VK_TAB        :: 0x09;
+
+VK_CLEAR      :: 0x0C;
+VK_RETURN     :: 0x0D;
+
+VK_SHIFT      :: 0x10;
+VK_CONTROL    :: 0x11;
+VK_MENU       :: 0x12;
+VK_PAUSE      :: 0x13;
+VK_CAPITAL    :: 0x14;
+
+VK_KANA       :: 0x15;
+VK_HANGEUL    :: 0x15;
+VK_HANGUL     :: 0x15;
+VK_JUNJA      :: 0x17;
+VK_FINAL      :: 0x18;
+VK_HANJA      :: 0x19;
+VK_KANJI      :: 0x19;
+
+VK_ESCAPE     :: 0x1B;
+
+VK_CONVERT    :: 0x1C;
+VK_NONCONVERT :: 0x1D;
+VK_ACCEPT     :: 0x1E;
+VK_MODECHANGE :: 0x1F;
+
+VK_SPACE      :: 0x20;
+VK_PRIOR      :: 0x21;
+VK_NEXT       :: 0x22;
+VK_END        :: 0x23;
+VK_HOME       :: 0x24;
+VK_LEFT       :: 0x25;
+VK_UP         :: 0x26;
+VK_RIGHT      :: 0x27;
+VK_DOWN       :: 0x28;
+VK_SELECT     :: 0x29;
+VK_PRINT      :: 0x2A;
+VK_EXECUTE    :: 0x2B;
+VK_SNAPSHOT   :: 0x2C;
+VK_INSERT     :: 0x2D;
+VK_DELETE     :: 0x2E;
+VK_HELP       :: 0x2F;
+
+VK_0 :: '0';
+VK_1 :: '1';
+VK_2 :: '2';
+VK_3 :: '3';
+VK_4 :: '4';
+VK_5 :: '5';
+VK_6 :: '6';
+VK_7 :: '7';
+VK_8 :: '8';
+VK_9 :: '9';
+
+VK_A :: 'A';
+VK_B :: 'B';
+VK_C :: 'C';
+VK_D :: 'D';
+VK_E :: 'E';
+VK_F :: 'F';
+VK_G :: 'G';
+VK_H :: 'H';
+VK_I :: 'I';
+VK_J :: 'J';
+VK_K :: 'K';
+VK_L :: 'L';
+VK_M :: 'M';
+VK_N :: 'N';
+VK_O :: 'O';
+VK_P :: 'P';
+VK_Q :: 'Q';
+VK_R :: 'R';
+VK_S :: 'S';
+VK_T :: 'T';
+VK_U :: 'U';
+VK_V :: 'V';
+VK_W :: 'W';
+VK_X :: 'X';
+VK_Y :: 'Y';
+VK_Z :: 'Z';
+
+VK_LWIN       :: 0x5B;
+VK_RWIN       :: 0x5C;
+VK_APPS       :: 0x5D;
+
+VK_NUMPAD0    :: 0x60;
+VK_NUMPAD1    :: 0x61;
+VK_NUMPAD2    :: 0x62;
+VK_NUMPAD3    :: 0x63;
+VK_NUMPAD4    :: 0x64;
+VK_NUMPAD5    :: 0x65;
+VK_NUMPAD6    :: 0x66;
+VK_NUMPAD7    :: 0x67;
+VK_NUMPAD8    :: 0x68;
+VK_NUMPAD9    :: 0x69;
+VK_MULTIPLY   :: 0x6A;
+VK_ADD        :: 0x6B;
+VK_SEPARATOR  :: 0x6C;
+VK_SUBTRACT   :: 0x6D;
+VK_DECIMAL    :: 0x6E;
+VK_DIVIDE     :: 0x6F;
+VK_F1         :: 0x70;
+VK_F2         :: 0x71;
+VK_F3         :: 0x72;
+VK_F4         :: 0x73;
+VK_F5         :: 0x74;
+VK_F6         :: 0x75;
+VK_F7         :: 0x76;
+VK_F8         :: 0x77;
+VK_F9         :: 0x78;
+VK_F10        :: 0x79;
+VK_F11        :: 0x7A;
+VK_F12        :: 0x7B;
+VK_F13        :: 0x7C;
+VK_F14        :: 0x7D;
+VK_F15        :: 0x7E;
+VK_F16        :: 0x7F;
+VK_F17        :: 0x80;
+VK_F18        :: 0x81;
+VK_F19        :: 0x82;
+VK_F20        :: 0x83;
+VK_F21        :: 0x84;
+VK_F22        :: 0x85;
+VK_F23        :: 0x86;
+VK_F24        :: 0x87;
+
+VK_NUMLOCK    :: 0x90;
+VK_SCROLL     :: 0x91;
+
+VK_LSHIFT     :: 0xA0;
+VK_RSHIFT     :: 0xA1;
+VK_LCONTROL   :: 0xA2;
+VK_RCONTROL   :: 0xA3;
+VK_LMENU      :: 0xA4;
+VK_RMENU      :: 0xA5;
+VK_PROCESSKEY :: 0xE5;
+VK_ATTN       :: 0xF6;
+VK_CRSEL      :: 0xF7;
+VK_EXSEL      :: 0xF8;
+VK_EREOF      :: 0xF9;
+VK_PLAY       :: 0xFA;
+VK_ZOOM       :: 0xFB;
+VK_NONAME     :: 0xFC;
+VK_PA1        :: 0xFD;
+VK_OEM_CLEAR  :: 0xFE;
+

+ 2 - 1
src/checker/checker.cpp

@@ -38,9 +38,9 @@ struct DeclInfo {
 	AstNode *type_expr;
 	AstNode *init_expr;
 	AstNode *proc_decl; // AstNode_ProcDecl
+	u32 var_decl_tags;
 
 	Map<b32> deps; // Key: Entity *
-	i32 mark;
 };
 
 
@@ -605,6 +605,7 @@ void check_parsed_files(Checker *c) {
 							d = make_declaration_info(gb_heap_allocator(), c->global_scope);
 							d->type_expr = vd->type;
 							d->init_expr = init_expr;
+							d->var_decl_tags = vd->tags;
 						}
 
 						add_file_entity(c, name, e, d);

+ 24 - 1
src/checker/expr.cpp

@@ -874,6 +874,12 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) {
 		return true;
 	}
 
+
+	// proc -> rawptr
+	if (is_type_proc(xb), is_type_rawptr(yb)) {
+		return true;
+	}
+
 	return false;
 }
 
@@ -912,7 +918,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 			gbString type_str = type_to_string(type);
 			defer (gb_string_free(expr_str));
 			defer (gb_string_free(type_str));
-			error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` to `%s`", expr_str, type_str);
+			error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s`", expr_str, type_str);
 
 			x->mode = Addressing_Invalid;
 			return;
@@ -1199,8 +1205,18 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
 			convert_untyped_error(c, operand, target_type);
 			return;
 		}
+		break;
 
+	case Type_Proc:
+		switch (operand->type->basic.kind) {
+		case Basic_UntypedPointer:
+			break;
+		default:
+			convert_untyped_error(c, operand, target_type);
+			return;
+		}
 		break;
+
 	default:
 		convert_untyped_error(c, operand, target_type);
 		return;
@@ -1992,6 +2008,13 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 			i64 max = 0;
 			for (AstNode *elem = cl->elem_list; elem != NULL; elem = elem->next, index++) {
 				AstNode *e = elem;
+				if (e->kind == AstNode_FieldValue) {
+					error(&c->error_collector, ast_node_token(e),
+					      "`field = value` is only allowed in structure literals");
+					continue;
+				}
+
+
 				if (t->kind == Type_Array &&
 				    t->array.count >= 0 &&
 				    index >= t->array.count) {

+ 2 - 2
src/checker/stmt.cpp

@@ -119,7 +119,9 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) {
 		}
 	}
 
+
 	return false;
+
 }
 
 
@@ -742,8 +744,6 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		}
 	case_end;
 
-
-// Declarations
 	case_ast_node(vd, VarDecl, node);
 		isize entity_count = vd->name_count;
 		isize entity_index = 0;

+ 39 - 28
src/checker/type.cpp

@@ -80,7 +80,13 @@ String const type_strings[] = {
 #undef TYPE_KIND
 };
 
+enum TypeFlag {
+	TypeFlag_thread_local = GB_BIT(0),
+	TypeFlag_volatile     = GB_BIT(1),
+};
+
 struct Type {
+	u32 flags;
 	TypeKind kind;
 	union {
 		BasicType basic;
@@ -206,43 +212,46 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun
 
 
 Type *type_deref(Type *t) {
-	if (t != NULL && t->kind == Type_Pointer)
-		return t->pointer.elem;
+	if (t != NULL) {
+		Type *bt = get_base_type(t);
+		if (bt != NULL && bt->kind == Type_Pointer)
+			return bt->pointer.elem;
+	}
 	return t;
 }
 
 
 #define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
 gb_global Type basic_types[] = {
-	{Type_Basic, {Basic_Invalid,        0,                                      STR_LIT("invalid type")}},
-	{Type_Basic, {Basic_bool,           BasicFlag_Boolean,                      STR_LIT("bool")}},
-	{Type_Basic, {Basic_i8,             BasicFlag_Integer,                      STR_LIT("i8")}},
-	{Type_Basic, {Basic_i16,            BasicFlag_Integer,                      STR_LIT("i16")}},
-	{Type_Basic, {Basic_i32,            BasicFlag_Integer,                      STR_LIT("i32")}},
-	{Type_Basic, {Basic_i64,            BasicFlag_Integer,                      STR_LIT("i64")}},
-	{Type_Basic, {Basic_i128,           BasicFlag_Integer,                      STR_LIT("i128")}},
-	{Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
-	{Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
-	{Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
-	{Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
-	{Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u128")}},
-	{Type_Basic, {Basic_f32,            BasicFlag_Float,                        STR_LIT("f32")}},
-	{Type_Basic, {Basic_f64,            BasicFlag_Float,                        STR_LIT("f64")}},
-	{Type_Basic, {Basic_int,            BasicFlag_Integer,                      STR_LIT("int")}},
-	{Type_Basic, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}},
-	{Type_Basic, {Basic_rawptr,         BasicFlag_Pointer,                      STR_LIT("rawptr")}},
-	{Type_Basic, {Basic_string,         BasicFlag_String,                       STR_LIT("string")}},
-	{Type_Basic, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,  STR_LIT("untyped bool")}},
-	{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped integer")}},
-	{Type_Basic, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,  STR_LIT("untyped float")}},
-	{Type_Basic, {Basic_UntypedPointer, BasicFlag_Pointer | BasicFlag_Untyped,  STR_LIT("untyped pointer")}},
-	{Type_Basic, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,  STR_LIT("untyped string")}},
-	{Type_Basic, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped rune")}},
+	{0, Type_Basic, {Basic_Invalid,        0,                                      STR_LIT("invalid type")}},
+	{0, Type_Basic, {Basic_bool,           BasicFlag_Boolean,                      STR_LIT("bool")}},
+	{0, Type_Basic, {Basic_i8,             BasicFlag_Integer,                      STR_LIT("i8")}},
+	{0, Type_Basic, {Basic_i16,            BasicFlag_Integer,                      STR_LIT("i16")}},
+	{0, Type_Basic, {Basic_i32,            BasicFlag_Integer,                      STR_LIT("i32")}},
+	{0, Type_Basic, {Basic_i64,            BasicFlag_Integer,                      STR_LIT("i64")}},
+	{0, Type_Basic, {Basic_i128,           BasicFlag_Integer,                      STR_LIT("i128")}},
+	{0, Type_Basic, {Basic_u8,             BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
+	{0, Type_Basic, {Basic_u16,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
+	{0, Type_Basic, {Basic_u32,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
+	{0, Type_Basic, {Basic_u64,            BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
+	{0, Type_Basic, {Basic_u128,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u128")}},
+	{0, Type_Basic, {Basic_f32,            BasicFlag_Float,                        STR_LIT("f32")}},
+	{0, Type_Basic, {Basic_f64,            BasicFlag_Float,                        STR_LIT("f64")}},
+	{0, Type_Basic, {Basic_int,            BasicFlag_Integer,                      STR_LIT("int")}},
+	{0, Type_Basic, {Basic_uint,           BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}},
+	{0, Type_Basic, {Basic_rawptr,         BasicFlag_Pointer,                      STR_LIT("rawptr")}},
+	{0, Type_Basic, {Basic_string,         BasicFlag_String,                       STR_LIT("string")}},
+	{0, Type_Basic, {Basic_UntypedBool,    BasicFlag_Boolean | BasicFlag_Untyped,  STR_LIT("untyped bool")}},
+	{0, Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped integer")}},
+	{0, Type_Basic, {Basic_UntypedFloat,   BasicFlag_Float   | BasicFlag_Untyped,  STR_LIT("untyped float")}},
+	{0, Type_Basic, {Basic_UntypedPointer, BasicFlag_Pointer | BasicFlag_Untyped,  STR_LIT("untyped pointer")}},
+	{0, Type_Basic, {Basic_UntypedString,  BasicFlag_String  | BasicFlag_Untyped,  STR_LIT("untyped string")}},
+	{0, Type_Basic, {Basic_UntypedRune,    BasicFlag_Integer | BasicFlag_Untyped,  STR_LIT("untyped rune")}},
 };
 
 gb_global Type basic_type_aliases[] = {
-	{Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("byte")}},
-	{Type_Basic, {Basic_rune, BasicFlag_Integer,                      STR_LIT("rune")}},
+	{0, Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("byte")}},
+	{0, Type_Basic, {Basic_rune, BasicFlag_Integer,                      STR_LIT("rune")}},
 };
 
 gb_global Type *t_invalid         = &basic_types[Basic_Invalid];
@@ -392,6 +401,8 @@ b32 is_type_comparable(Type *t) {
 		return is_type_comparable(t->array.elem);
 	case Type_Vector:
 		return is_type_comparable(t->vector.elem);
+	case Type_Proc:
+		return true;
 	}
 	return false;
 }

+ 5 - 3
src/codegen/codegen.cpp

@@ -77,8 +77,10 @@ void ssa_gen_code(ssaGen *s) {
 		} break;
 
 		case Entity_Variable: {
-			// TODO(bill): global runtime initialization
 			ssaValue *g = ssa_make_value_global(a, e, NULL);
+			if (decl->var_decl_tags & VarDeclTag_thread_local) {
+				g->Global.is_thread_local = true;
+			}
 			ssaGlobalVariable var = {};
 			var.var = g;
 			var.decl = decl;
@@ -95,7 +97,7 @@ void ssa_gen_code(ssaGen *s) {
 				name = pd->foreign_name;
 			}
 			ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name);
-			p->proc.tags = pd->tags;
+			p->Proc.tags = pd->tags;
 
 			map_set(&m->values, hash_pointer(e), p);
 			map_set(&m->members, hash_string(name), p);
@@ -127,7 +129,7 @@ void ssa_gen_code(ssaGen *s) {
 		map_set(&m->values, hash_pointer(e), p);
 		map_set(&m->members, hash_string(name), p);
 
-		ssaProcedure *proc = &p->proc;
+		ssaProcedure *proc = &p->Proc;
 		proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea?
 
 		ssa_begin_procedure_body(proc);

+ 72 - 68
src/codegen/print_llvm.cpp

@@ -230,10 +230,10 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type
 		if (is_type_float(type) && type->basic.kind == Basic_f32) {
 			// IMPORTANT NOTE(bill): LLVM requires all floating point constants to be
 			// a 64 bit number if bits_of(float type) <= 64.
-			// For some bizarre reason, you need to clear the bottom 28 bits
+			// For some bizarre reason, you need to clear the bottom 29 bits
 			// https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M
-			u >>= 28;
-			u <<= 28;
+			u >>= 29;
+			u <<= 29;
 		}
 		ssa_fprintf(f, "0x%016llx", u);
 	} break;
@@ -262,19 +262,19 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint)
 	}
 	switch (value->kind) {
 	case ssaValue_Constant:
-		ssa_print_exact_value(f, m, value->constant.value, type_hint);
+		ssa_print_exact_value(f, m, value->Constant.value, type_hint);
 		break;
 	case ssaValue_TypeName:
-		ssa_print_encoded_local(f, value->type_name.name);
+		ssa_print_encoded_local(f, value->TypeName.name);
 		break;
 	case ssaValue_Global:
-		ssa_print_encoded_global(f, value->global.entity->token.string);
+		ssa_print_encoded_global(f, value->Global.entity->token.string);
 		break;
 	case ssaValue_Param:
-		ssa_print_encoded_local(f, value->param.entity->token.string);
+		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);
 		break;
 	case ssaValue_Instr:
 		ssa_fprintf(f, "%%%d", value->id);
@@ -284,7 +284,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint)
 
 void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	GB_ASSERT(value->kind == ssaValue_Instr);
-	ssaInstr *instr = &value->instr;
+	ssaInstr *instr = &value->Instr;
 
 	ssa_fprintf(f, "\t");
 
@@ -294,14 +294,14 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Local: {
-		Type *type = instr->local.entity->type;
+		Type *type = instr->Local.entity->type;
 		ssa_fprintf(f, "%%%d = alloca ", value->id);
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, ", align %lld ", type_align_of(m->sizes, m->allocator, type));
 		{
-			String str = instr->local.entity->token.string;
+			String str = instr->Local.entity->token.string;
 			if (str.len > 0)
-			ssa_fprintf(f, "; %.*s", LIT(instr->local.entity->token.string));
+			ssa_fprintf(f, "; %.*s", LIT(instr->Local.entity->token.string));
 		}
 		ssa_fprintf(f, "\n");
 		ssa_fprintf(f, "\tstore ");
@@ -312,33 +312,33 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Store: {
-		Type *type = ssa_value_type(instr->store.address);
+		Type *type = ssa_type(instr->Store.address);
 		ssa_fprintf(f, "store ");
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->store.value, type);
+		ssa_print_value(f, m, instr->Store.value, type);
 		ssa_fprintf(f, ", ");
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, "* ");
-		ssa_print_value(f, m, instr->store.address, type);
+		ssa_print_value(f, m, instr->Store.address, type);
 		ssa_fprintf(f, "\n");
 	} break;
 
 	case ssaInstr_Load: {
-		Type *type = instr->load.type;
+		Type *type = instr->Load.type;
 		ssa_fprintf(f, "%%%d = load ", value->id);
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, ", ");
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, "* ");
-		ssa_print_value(f, m, instr->load.address, type);
+		ssa_print_value(f, m, instr->Load.address, type);
 		ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, m->allocator, type));
 	} break;
 
 	case ssaInstr_GetElementPtr: {
-		Type *et = instr->get_element_ptr.elem_type;
+		Type *et = instr->GetElementPtr.elem_type;
 		ssa_fprintf(f, "%%%d = getelementptr ", value->id);
-		if (instr->get_element_ptr.inbounds) {
+		if (instr->GetElementPtr.inbounds) {
 			ssa_fprintf(f, "inbounds ");
 		}
 
@@ -346,10 +346,10 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 		ssa_fprintf(f, ", ");
 		ssa_print_type(f, m->sizes, et);
 		ssa_fprintf(f, "* ");
-		ssa_print_value(f, m, instr->get_element_ptr.address, et);
-		for (isize i = 0; i < instr->get_element_ptr.index_count; i++) {
-			ssaValue *index = instr->get_element_ptr.indices[i];
-			Type *t = ssa_value_type(index);
+		ssa_print_value(f, m, instr->GetElementPtr.address, et);
+		for (isize i = 0; i < instr->GetElementPtr.index_count; i++) {
+			ssaValue *index = instr->GetElementPtr.indices[i];
+			Type *t = ssa_type(index);
 			ssa_fprintf(f, ", ");
 			ssa_print_type(f, m->sizes, t);
 			ssa_fprintf(f, " ");
@@ -359,13 +359,13 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_ExtractValue: {
-		Type *et = instr->extract_value.elem_type;
+		Type *et = instr->ExtractValue.elem_type;
 		ssa_fprintf(f, "%%%d = extractvalue ", value->id);
 
 		ssa_print_type(f, m->sizes, et);
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->extract_value.address, et);
-		ssa_fprintf(f, ", %d\n", instr->extract_value.index);
+		ssa_print_value(f, m, instr->ExtractValue.address, et);
+		ssa_fprintf(f, ", %d\n", instr->ExtractValue.index);
 	} break;
 
 	case ssaInstr_NoOp: {;
@@ -374,28 +374,28 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 
 	case ssaInstr_Br: {;
 		ssa_fprintf(f, "br ");
-		if (instr->br.cond != NULL) {
+		if (instr->Br.cond != NULL) {
 			ssa_print_type(f, m->sizes, t_bool);
 			ssa_fprintf(f, " ");
-			ssa_print_value(f, m, instr->br.cond, t_bool);
-			ssa_fprintf(f, ", ", instr->br.cond->id);
+			ssa_print_value(f, m, instr->Br.cond, t_bool);
+			ssa_fprintf(f, ", ", instr->Br.cond->id);
 		}
 		ssa_fprintf(f, "label ");
-		ssa_fprintf(f, "%%"); ssa_print_block_name(f, instr->br.true_block);
-		if (instr->br.false_block != NULL) {
+		ssa_fprintf(f, "%%"); ssa_print_block_name(f, instr->Br.true_block);
+		if (instr->Br.false_block != NULL) {
 			ssa_fprintf(f, ", label ");
-			ssa_fprintf(f, "%%"); ssa_print_block_name(f, instr->br.false_block);
+			ssa_fprintf(f, "%%"); ssa_print_block_name(f, instr->Br.false_block);
 		}
 		ssa_fprintf(f, "\n");
 	} break;
 
 	case ssaInstr_Ret: {
-		auto *ret = &instr->ret;
+		auto *ret = &instr->Ret;
 		ssa_fprintf(f, "ret ");
 		if (ret->value == NULL) {
 			ssa_fprintf(f, "void");
 		} else {
-			Type *t = ssa_value_type(ret->value);
+			Type *t = ssa_type(ret->value);
 			ssa_print_type(f, m->sizes, t);
 			ssa_fprintf(f, " ");
 			ssa_print_value(f, m, ret->value, t);
@@ -406,7 +406,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Conv: {
-		auto *c = &instr->conv;
+		auto *c = &instr->Conv;
 		ssa_fprintf(f, "%%%d = %.*s ", value->id, LIT(ssa_conv_strings[c->kind]));
 		ssa_print_type(f, m->sizes, c->from);
 		ssa_fprintf(f, " ");
@@ -422,8 +422,8 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_BinaryOp: {
-		auto *bo = &value->instr.binary_op;
-		Type *type = get_base_type(ssa_value_type(bo->left));
+		auto *bo = &value->Instr.BinaryOp;
+		Type *type = get_base_type(ssa_type(bo->left));
 		Type *elem_type = type;
 		while (elem_type->kind == Type_Vector) {
 			elem_type = get_base_type(elem_type->vector.elem);
@@ -528,7 +528,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 	} break;
 
 	case ssaInstr_Call: {
-		auto *call = &instr->call;
+		auto *call = &instr->Call;
 		Type *result_type = call->type;
 		if (result_type) {
 			ssa_fprintf(f, "%%%d = ", value->id);
@@ -545,7 +545,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 
 		ssa_fprintf(f, "(");
 		if (call->arg_count > 0) {
-			Type *proc_type = get_base_type(ssa_value_type(call->value));
+			Type *proc_type = get_base_type(ssa_type(call->value));
 			GB_ASSERT(proc_type->kind == Type_Proc);
 			auto *params = &proc_type->proc.params->tuple;
 			for (isize i = 0; i < call->arg_count; i++) {
@@ -567,15 +567,15 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 
 	case ssaInstr_Select: {
 		ssa_fprintf(f, "%%%d = select i1 ", value->id);
-		ssa_print_value(f, m, instr->select.cond, t_bool);
+		ssa_print_value(f, m, instr->Select.cond, t_bool);
 		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m->sizes, ssa_value_type(instr->select.true_value));
+		ssa_print_type(f, m->sizes, ssa_type(instr->Select.true_value));
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->select.true_value, ssa_value_type(instr->select.true_value));
+		ssa_print_value(f, m, instr->Select.true_value, ssa_type(instr->Select.true_value));
 		ssa_fprintf(f, ", ");
-		ssa_print_type(f, m->sizes, ssa_value_type(instr->select.false_value));
+		ssa_print_type(f, m->sizes, ssa_type(instr->Select.false_value));
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->select.false_value, ssa_value_type(instr->select.false_value));
+		ssa_print_value(f, m, instr->Select.false_value, ssa_type(instr->Select.false_value));
 		ssa_fprintf(f, "\n");
 	} break;
 
@@ -583,39 +583,39 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 		ssa_fprintf(f, "call void @llvm.memmove.p0i8.p0i8.");
 		ssa_print_type(f, m->sizes, t_int);
 		ssa_fprintf(f, "(i8* ");
-		ssa_print_value(f, m, instr->copy_memory.dst, t_rawptr);
+		ssa_print_value(f, m, instr->CopyMemory.dst, t_rawptr);
 		ssa_fprintf(f, ", i8* ");
-		ssa_print_value(f, m, instr->copy_memory.src, t_rawptr);
+		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->copy_memory.len, t_int);
+		ssa_print_value(f, m, instr->CopyMemory.len, t_int);
 		char *vol_str = "false";
-		if (instr->copy_memory.is_volatile) {
+		if (instr->CopyMemory.is_volatile) {
 			vol_str = "true";
 		}
-		ssa_fprintf(f, ", i32 %d, i1 %s)\n", instr->copy_memory.align, vol_str);
+		ssa_fprintf(f, ", i32 %d, i1 %s)\n", instr->CopyMemory.align, vol_str);
 	} break;
 
 
 	case ssaInstr_ExtractElement: {
-		Type *vt = ssa_value_type(instr->extract_element.vector);
+		Type *vt = ssa_type(instr->ExtractElement.vector);
 		ssa_fprintf(f, "%%%d = extractelement ", value->id);
 
 		ssa_print_type(f, m->sizes, vt);
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->extract_element.vector, vt);
+		ssa_print_value(f, m, instr->ExtractElement.vector, vt);
 		ssa_fprintf(f, ", ");
-		Type *it = ssa_value_type(instr->extract_element.index);
+		Type *it = ssa_type(instr->ExtractElement.index);
 		ssa_print_type(f, m->sizes, it);
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, instr->extract_element.index, it);
+		ssa_print_value(f, m, instr->ExtractElement.index, it);
 		ssa_fprintf(f, "\n");
 	} break;
 
 	case ssaInstr_InsertElement: {
-		auto *ie = &instr->insert_element;
-		Type *vt = ssa_value_type(ie->vector);
+		auto *ie = &instr->InsertElement;
+		Type *vt = ssa_type(ie->vector);
 		ssa_fprintf(f, "%%%d = insertelement ", value->id);
 
 		ssa_print_type(f, m->sizes, vt);
@@ -623,21 +623,21 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) {
 		ssa_print_value(f, m, ie->vector, vt);
 		ssa_fprintf(f, ", ");
 
-		ssa_print_type(f, m->sizes, ssa_value_type(ie->elem));
+		ssa_print_type(f, m->sizes, ssa_type(ie->elem));
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, ie->elem, ssa_value_type(ie->elem));
+		ssa_print_value(f, m, ie->elem, ssa_type(ie->elem));
 		ssa_fprintf(f, ", ");
 
-		ssa_print_type(f, m->sizes, ssa_value_type(ie->index));
+		ssa_print_type(f, m->sizes, ssa_type(ie->index));
 		ssa_fprintf(f, " ");
-		ssa_print_value(f, m, ie->index, ssa_value_type(ie->index));
+		ssa_print_value(f, m, ie->index, ssa_type(ie->index));
 
 		ssa_fprintf(f, "\n");
 	} break;
 
 	case ssaInstr_ShuffleVector: {
-		auto *sv = &instr->shuffle_vector;
-		Type *vt = ssa_value_type(sv->vector);
+		auto *sv = &instr->ShuffleVector;
+		Type *vt = ssa_type(sv->vector);
 		ssa_fprintf(f, "%%%d = shufflevector ", value->id);
 
 		ssa_print_type(f, m->sizes, vt);
@@ -738,9 +738,9 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) {
 
 void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) {
 	GB_ASSERT(v->kind == ssaValue_TypeName);
-	ssa_print_encoded_local(f, v->type_name.name);
+	ssa_print_encoded_local(f, v->TypeName.name);
 	ssa_fprintf(f, " = type ");
-	ssa_print_type(f, m->sizes, get_base_type(v->type_name.type));
+	ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type));
 	ssa_fprintf(f, "\n");
 }
 
@@ -763,9 +763,9 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 		ssaValue *v = entry->value;
 		switch (v->kind) {
 		case ssaValue_TypeName: {
-			ssa_print_encoded_local(f, v->type_name.name);
+			ssa_print_encoded_local(f, v->TypeName.name);
 			ssa_fprintf(f, " = type ");
-			ssa_print_type(f, m->sizes, get_base_type(v->type_name.type));
+			ssa_print_type(f, m->sizes, get_base_type(v->TypeName.type));
 			ssa_fprintf(f, "\n");
 		} break;
 		}
@@ -790,16 +790,20 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 		ssaValue *v = entry->value;
 		switch (v->kind) {
 		case ssaValue_Global: {
-			auto *g = &v->global;
+			auto *g = &v->Global;
 			ssa_print_encoded_global(f, g->entity->token.string);
 			ssa_fprintf(f, " = ");
+			if (g->is_thread_local) {
+				ssa_fprintf(f, "thread_local ");
+			}
 			if (g->is_constant) {
 				ssa_fprintf(f, "private constant ");
 			} else {
 				ssa_fprintf(f, "global ");
 			}
 
-			ssa_print_type(f, m->sizes, get_base_type(g->entity->type));
+
+			ssa_print_type(f, m->sizes, g->entity->type);
 			ssa_fprintf(f, " ");
 			if (g->value != NULL) {
 				ssa_print_value(f, m, g->value, g->entity->type);
@@ -810,7 +814,7 @@ void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 		} break;
 
 		case ssaValue_Proc: {
-			ssa_print_proc(f, m, &v->proc);
+			ssa_print_proc(f, m, &v->Proc);
 		} break;
 		}
 	}

File diff suppressed because it is too large
+ 225 - 273
src/codegen/ssa.cpp


+ 21 - 1
src/parser.cpp

@@ -77,6 +77,10 @@ enum ProcTag {
 	ProcTag_no_inline = GB_BIT(2),
 };
 
+enum VarDeclTag {
+	VarDeclTag_thread_local = GB_BIT(0),
+};
+
 #define AST_NODE_KINDS \
 	AST_NODE_KIND(Invalid, struct{}) \
 	AST_NODE_KIND(BasicLit, Token) \
@@ -165,6 +169,7 @@ AST_NODE_KIND(_DeclBegin,      struct{}) \
 	AST_NODE_KIND(BadDecl, struct { Token begin, end; }) \
 	AST_NODE_KIND(VarDecl, struct { \
 			DeclKind kind; \
+			u32      tags; \
 			AstNode *name_list; \
 			AstNode *type; \
 			AstNode *value_list; \
@@ -177,7 +182,7 @@ AST_NODE_KIND(_DeclBegin,      struct{}) \
 			u64     tags;            \
 			String  foreign_name;    \
 		}) \
-	AST_NODE_KIND(TypeDecl,   struct { Token token; AstNode *name, *type; }) \
+	AST_NODE_KIND(TypeDecl, struct { Token token; AstNode *name, *type; }) \
 	AST_NODE_KIND(LoadDecl, struct { Token token, filepath; }) \
 AST_NODE_KIND(_DeclEnd, struct{}) \
 AST_NODE_KIND(_TypeBegin, struct{}) \
@@ -1994,7 +1999,22 @@ AstNode *parse_stmt(AstFile *f) {
 			}
 			ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, file_path);
+		} else if (are_strings_equal(s->TagStmt.name.string, make_string("thread_local"))) {
+			AstNode *var_decl = parse_simple_stmt(f);
+			if (var_decl->kind != AstNode_VarDecl ||
+			    var_decl->VarDecl.kind != Declaration_Mutable) {
+				ast_file_err(f, token, "#thread_local may only be applied to variable declarations");
+				return make_bad_decl(f, token, ast_node_token(var_decl));
+			}
+			if (f->curr_scope != f->file_scope) {
+				ast_file_err(f, token, "#thread_local is only allowed at the file scope.");
+				return make_bad_decl(f, token, ast_node_token(var_decl));
+			}
+			var_decl->VarDecl.tags |= VarDeclTag_thread_local;
+			return var_decl;
 		}
+
+
 		s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument
 		return s;
 	} break;

+ 4 - 4
src/tokenizer.cpp

@@ -371,25 +371,25 @@ void tokenizer_skip_whitespace(Tokenizer *t) {
 			if (t->read_curr[0] == '/') { // Line comment //
 				while (t->curr_rune != '\n')
 					advance_to_next_rune(t);
-			} else if (t->read_curr[0] == '{') { // (Nested) Block comment /{}/
+			} else if (t->read_curr[0] == '*') { // (Nested) Block comment /**/
 				isize comment_scope = 1;
 				for (;;) {
 					advance_to_next_rune(t);
 					if (t->curr_rune == '/') {
 						advance_to_next_rune(t);
-						if (t->curr_rune == '{') {
+						if (t->curr_rune == '*') {
 							advance_to_next_rune(t);
 							comment_scope++;
 						}
 					}
-					if (t->curr_rune == '}') {
+					if (t->curr_rune == '*') {
 						advance_to_next_rune(t);
 						if (t->curr_rune == '/') {
 							advance_to_next_rune(t);
 							comment_scope--;
 						}
 					}
-					if (comment_scope == 0)
+					if (comment_scope <= 0)
 						break;
 				}
 			} else {

Some files were not shown because too many files changed in this diff