Browse Source

Implicit Parameter Passing based `context` system (replacing Thread Local Storage (TLS) approach)

Ginger Bill 8 years ago
parent
commit
5957d7f7be
8 changed files with 178 additions and 87 deletions
  1. 48 39
      core/_preload.odin
  2. 2 1
      misc/shell.bat
  3. 5 0
      src/check_decl.cpp
  4. 5 1
      src/check_expr.cpp
  5. 7 5
      src/checker.cpp
  6. 69 26
      src/ir.cpp
  7. 30 8
      src/ir_print.cpp
  8. 12 7
      src/parser.cpp

+ 48 - 39
core/_preload.odin

@@ -32,11 +32,12 @@ type (
 	}
 	}
 	// NOTE(bill): This must match the compiler's
 	// NOTE(bill): This must match the compiler's
 	CallingConvention enum {
 	CallingConvention enum {
-		Invalid = 0,
-		Odin    = 1,
-		C       = 2,
-		Std     = 3,
-		Fast    = 4,
+		Invalid         = 0,
+		Odin            = 1,
+		Contextless     = 2,
+		C               = 3,
+		Std             = 4,
+		Fast            = 5,
 	}
 	}
 
 
 	TypeInfoRecord struct #ordered {
 	TypeInfoRecord struct #ordered {
@@ -182,7 +183,7 @@ type (
 	}
 	}
 )
 )
 
 
-#thread_local var __context: Context;
+// #thread_local var __context: Context;
 
 
 
 
 
 
@@ -192,7 +193,7 @@ type SourceCodeLocation struct {
 	procedure:             string,
 	procedure:             string,
 }
 }
 
 
-proc make_source_code_location(file: string, line, column: i64, procedure: string) -> SourceCodeLocation #inline {
+proc make_source_code_location(file: string, line, column: i64, procedure: string) -> SourceCodeLocation #cc_contextless #inline {
 	return SourceCodeLocation{file, line, column, procedure};
 	return SourceCodeLocation{file, line, column, procedure};
 }
 }
 
 
@@ -200,8 +201,14 @@ proc make_source_code_location(file: string, line, column: i64, procedure: strin
 
 
 const DEFAULT_ALIGNMENT = align_of([vector 4]f32);
 const DEFAULT_ALIGNMENT = align_of([vector 4]f32);
 
 
+proc __init_context_from_ptr(c: ^Context, other: ^Context) #cc_contextless {
+	if c == nil {
+		return;
+	}
+	c^ = other^;
+}
 
 
-proc __init_context(c: ^Context) {
+proc __init_context(c: ^Context) #cc_contextless {
 	if c == nil {
 	if c == nil {
 		return;
 		return;
 	}
 	}
@@ -214,12 +221,14 @@ proc __init_context(c: ^Context) {
 }
 }
 
 
 
 
+/*
 proc __check_context() {
 proc __check_context() {
 	__init_context(&__context);
 	__init_context(&__context);
 }
 }
+*/
 
 
 proc alloc(size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline {
 proc alloc(size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline {
-	__check_context();
+	// __check_context();
 	var a = context.allocator;
 	var a = context.allocator;
 	return a.procedure(a.data, AllocatorMode.Alloc, size, alignment, nil, 0, 0);
 	return a.procedure(a.data, AllocatorMode.Alloc, size, alignment, nil, 0, 0);
 }
 }
@@ -235,19 +244,19 @@ proc free_ptr_with_allocator(a: Allocator, ptr: rawptr) #inline {
 }
 }
 
 
 proc free_ptr(ptr: rawptr) #inline {
 proc free_ptr(ptr: rawptr) #inline {
-	__check_context();
+	// __check_context();
 	free_ptr_with_allocator(context.allocator, ptr);
 	free_ptr_with_allocator(context.allocator, ptr);
 }
 }
 
 
 proc free_all() #inline {
 proc free_all() #inline {
-	__check_context();
+	// __check_context();
 	var a = context.allocator;
 	var a = context.allocator;
 	a.procedure(a.data, AllocatorMode.FreeAll, 0, 0, nil, 0, 0);
 	a.procedure(a.data, AllocatorMode.FreeAll, 0, 0, nil, 0, 0);
 }
 }
 
 
 
 
 proc resize(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline {
 proc resize(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT) -> rawptr #inline {
-	__check_context();
+	// __check_context();
 	var a = context.allocator;
 	var a = context.allocator;
 	return a.procedure(a.data, AllocatorMode.Resize, new_size, alignment, ptr, old_size, 0);
 	return a.procedure(a.data, AllocatorMode.Resize, new_size, alignment, ptr, old_size, 0);
 }
 }
@@ -312,7 +321,7 @@ proc default_allocator() -> Allocator {
 }
 }
 
 
 
 
-proc assert(condition: bool, message = "", using location = #caller_location) -> bool {
+proc assert(condition: bool, message = "", using location = #caller_location) -> bool #cc_contextless {
 	if !condition {
 	if !condition {
 		if len(message) > 0 {
 		if len(message) > 0 {
 			fmt.printf("%s(%d:%d) Runtime assertion: %s\n", fully_pathed_filename, line, column, message);
 			fmt.printf("%s(%d:%d) Runtime assertion: %s\n", fully_pathed_filename, line, column, message);
@@ -324,7 +333,7 @@ proc assert(condition: bool, message = "", using location = #caller_location) ->
 	return condition;
 	return condition;
 }
 }
 
 
-proc panic(message = "", using location = #caller_location) {
+proc panic(message = "", using location = #caller_location) #cc_contextless {
 	if len(message) > 0 {
 	if len(message) > 0 {
 		fmt.printf("%s(%d:%d) Panic: %s\n", fully_pathed_filename, line, column, message);
 		fmt.printf("%s(%d:%d) Panic: %s\n", fully_pathed_filename, line, column, message);
 	} else {
 	} else {
@@ -336,7 +345,7 @@ proc panic(message = "", using location = #caller_location) {
 
 
 
 
 
 
-proc __string_eq(a, b: string) -> bool {
+proc __string_eq(a, b: string) -> bool #cc_contextless {
 	if len(a) != len(b) {
 	if len(a) != len(b) {
 		return false;
 		return false;
 	}
 	}
@@ -349,25 +358,25 @@ proc __string_eq(a, b: string) -> bool {
 	return __string_cmp(a, b) == 0;
 	return __string_cmp(a, b) == 0;
 }
 }
 
 
-proc __string_cmp(a, b: string) -> int {
+proc __string_cmp(a, b: string) -> int #cc_contextless {
 	return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
 	return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
 }
 }
 
 
-proc __string_ne(a, b: string) -> bool #inline { return !__string_eq(a, b); }
-proc __string_lt(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; }
-proc __string_gt(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; }
-proc __string_le(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; }
-proc __string_ge(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
+proc __string_ne(a, b: string) -> bool #cc_contextless #inline { return !__string_eq(a, b); }
+proc __string_lt(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) < 0; }
+proc __string_gt(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) > 0; }
+proc __string_le(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) <= 0; }
+proc __string_ge(a, b: string) -> bool #cc_contextless #inline { return __string_cmp(a, b) >= 0; }
 
 
 
 
-proc __complex64_eq (a, b: complex64)  -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
-proc __complex64_ne (a, b: complex64)  -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
+proc __complex64_eq (a, b: complex64)  -> bool #cc_contextless #inline { return real(a) == real(b) && imag(a) == imag(b); }
+proc __complex64_ne (a, b: complex64)  -> bool #cc_contextless #inline { return real(a) != real(b) || imag(a) != imag(b); }
 
 
-proc __complex128_eq(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
-proc __complex128_ne(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
+proc __complex128_eq(a, b: complex128) -> bool #cc_contextless #inline { return real(a) == real(b) && imag(a) == imag(b); }
+proc __complex128_ne(a, b: complex128) -> bool #cc_contextless #inline { return real(a) != real(b) || imag(a) != imag(b); }
 
 
 
 
-proc __bounds_check_error(file: string, line, column: int, index, count: int) {
+proc __bounds_check_error(file: string, line, column: int, index, count: int) #cc_contextless {
 	if 0 <= index && index < count {
 	if 0 <= index && index < count {
 		return;
 		return;
 	}
 	}
@@ -376,7 +385,7 @@ proc __bounds_check_error(file: string, line, column: int, index, count: int) {
 	__debug_trap();
 	__debug_trap();
 }
 }
 
 
-proc __slice_expr_error(file: string, line, column: int, low, high, max: int) {
+proc __slice_expr_error(file: string, line, column: int, low, high, max: int) #cc_contextless {
 	if 0 <= low && low <= high && high <= max {
 	if 0 <= low && low <= high && high <= max {
 		return;
 		return;
 	}
 	}
@@ -385,7 +394,7 @@ proc __slice_expr_error(file: string, line, column: int, low, high, max: int) {
 	__debug_trap();
 	__debug_trap();
 }
 }
 
 
-proc __substring_expr_error(file: string, line, column: int, low, high: int) {
+proc __substring_expr_error(file: string, line, column: int, low, high: int) #cc_contextless {
 	if 0 <= low && low <= high {
 	if 0 <= low && low <= high {
 		return;
 		return;
 	}
 	}
@@ -393,7 +402,7 @@ proc __substring_expr_error(file: string, line, column: int, low, high: int) {
 	            file, line, column, low, high);
 	            file, line, column, low, high);
 	__debug_trap();
 	__debug_trap();
 }
 }
-proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to: ^TypeInfo) {
+proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to: ^TypeInfo) #cc_contextless {
 	if !ok {
 	if !ok {
 		fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
 		fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
 		            file, line, column, from, to);
 		            file, line, column, from, to);
@@ -401,12 +410,12 @@ proc __type_assertion_check(ok: bool, file: string, line, column: int, from, to:
 	}
 	}
 }
 }
 
 
-proc __string_decode_rune(s: string) -> (rune, int) #inline {
+proc __string_decode_rune(s: string) -> (rune, int) #cc_contextless #inline {
 	return utf8.decode_rune(s);
 	return utf8.decode_rune(s);
 }
 }
 
 
 
 
-proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr {
+proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless {
 	when size_of(rawptr) == 8 {
 	when size_of(rawptr) == 8 {
 		foreign __llvm_core proc llvm_memset_64bit(dst: rawptr, val: u8, len: int, align: i32, is_volatile: bool) #link_name "llvm.memset.p0i8.i64";
 		foreign __llvm_core proc llvm_memset_64bit(dst: rawptr, val: u8, len: int, align: i32, is_volatile: bool) #link_name "llvm.memset.p0i8.i64";
 		llvm_memset_64bit(data, u8(value), len, 1, false);
 		llvm_memset_64bit(data, u8(value), len, 1, false);
@@ -417,10 +426,10 @@ proc __mem_set(data: rawptr, value: i32, len: int) -> rawptr {
 		return data;
 		return data;
 	}
 	}
 }
 }
-proc __mem_zero(data: rawptr, len: int) -> rawptr {
+proc __mem_zero(data: rawptr, len: int) -> rawptr #cc_contextless {
 	return __mem_set(data, 0, len);
 	return __mem_set(data, 0, len);
 }
 }
-proc __mem_copy(dst, src: rawptr, len: int) -> rawptr {
+proc __mem_copy(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
 	// NOTE(bill): This _must_ be implemented like C's memmove
 	// NOTE(bill): This _must_ be implemented like C's memmove
 	when size_of(rawptr) == 8 {
 	when size_of(rawptr) == 8 {
 		foreign __llvm_core proc llvm_memmove_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memmove.p0i8.p0i8.i64";
 		foreign __llvm_core proc llvm_memmove_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memmove.p0i8.p0i8.i64";
@@ -432,7 +441,7 @@ proc __mem_copy(dst, src: rawptr, len: int) -> rawptr {
 		return dst;
 		return dst;
 	}
 	}
 }
 }
-proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr {
+proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
 	// NOTE(bill): This _must_ be implemented like C's memcpy
 	// NOTE(bill): This _must_ be implemented like C's memcpy
 	when size_of(rawptr) == 8 {
 	when size_of(rawptr) == 8 {
 		foreign __llvm_core proc llvm_memcpy_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memcpy.p0i8.p0i8.i64";
 		foreign __llvm_core proc llvm_memcpy_64bit(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #link_name "llvm.memcpy.p0i8.p0i8.i64";
@@ -445,7 +454,7 @@ proc __mem_copy_non_overlapping(dst, src: rawptr, len: int) -> rawptr {
 	}
 	}
 }
 }
 
 
-proc __mem_compare(a, b: ^u8, n: int) -> int {
+proc __mem_compare(a, b: ^u8, n: int) -> int #cc_contextless {
 	for i in 0..<n {
 	for i in 0..<n {
 		match {
 		match {
 		case (a+i)^ < (b+i)^:
 		case (a+i)^ < (b+i)^:
@@ -461,11 +470,11 @@ foreign __llvm_core {
 	proc __sqrt_f32(x: f32) -> f32 #link_name "llvm.sqrt.f32";
 	proc __sqrt_f32(x: f32) -> f32 #link_name "llvm.sqrt.f32";
 	proc __sqrt_f64(x: f64) -> f64 #link_name "llvm.sqrt.f64";
 	proc __sqrt_f64(x: f64) -> f64 #link_name "llvm.sqrt.f64";
 }
 }
-proc __abs_complex64(x: complex64) -> f32 #inline {
+proc __abs_complex64(x: complex64) -> f32 #inline #cc_contextless {
 	var r, i = real(x), imag(x);
 	var r, i = real(x), imag(x);
 	return __sqrt_f32(r*r + i*i);
 	return __sqrt_f32(r*r + i*i);
 }
 }
-proc __abs_complex128(x: complex128) -> f64 #inline {
+proc __abs_complex128(x: complex128) -> f64 #inline #cc_contextless {
 	var r, i = real(x), imag(x);
 	var r, i = real(x), imag(x);
 	return __sqrt_f64(r*r + i*i);
 	return __sqrt_f64(r*r + i*i);
 }
 }
@@ -475,7 +484,7 @@ proc __abs_complex128(x: complex128) -> f64 #inline {
 
 
 proc __dynamic_array_make(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
 proc __dynamic_array_make(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
 	var array = ^raw.DynamicArray(array_);
 	var array = ^raw.DynamicArray(array_);
-	__check_context();
+	// __check_context();
 	array.allocator = context.allocator;
 	array.allocator = context.allocator;
 	assert(array.allocator.procedure != nil);
 	assert(array.allocator.procedure != nil);
 
 
@@ -492,7 +501,7 @@ proc __dynamic_array_reserve(array_: rawptr, elem_size, elem_align: int, cap: in
 		return true;
 		return true;
 	}
 	}
 
 
-	__check_context();
+	// __check_context();
 	if array.allocator.procedure == nil {
 	if array.allocator.procedure == nil {
 		array.allocator = context.allocator;
 		array.allocator = context.allocator;
 	}
 	}

+ 2 - 1
misc/shell.bat

@@ -1,9 +1,10 @@
 @echo off
 @echo off
 
 
 rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
 rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
-call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
+rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
 rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
 rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
 rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
 rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
+call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
 set _NO_DEBUG_HEAP=1
 set _NO_DEBUG_HEAP=1
 
 
 set path=w:\Odin\misc;%path%
 set path=w:\Odin\misc;%path%

+ 5 - 0
src/check_decl.cpp

@@ -333,6 +333,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 			error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
 			error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
 			gb_string_free(str);
 			gb_string_free(str);
 		}
 		}
+		if (proc_type->Proc.calling_convention != ProcCC_Odin &&
+		    proc_type->Proc.calling_convention != ProcCC_Contextless) {
+			error(e->token, "Procedure `main` cannot have a custom calling convention");
+		}
+		proc_type->Proc.calling_convention = ProcCC_Contextless;
 	}
 	}
 
 
 	if (is_inline && is_no_inline) {
 	if (is_inline && is_no_inline) {

+ 5 - 1
src/check_expr.cpp

@@ -1419,7 +1419,9 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a
 	if (abi_return_type == NULL) {
 	if (abi_return_type == NULL) {
 		return false;
 		return false;
 	}
 	}
-	if (cc == ProcCC_Odin) {
+	switch (cc) {
+	case ProcCC_Odin:
+	case ProcCC_Contextless:
 		return false;
 		return false;
 	}
 	}
 
 
@@ -1465,6 +1467,8 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 		if (end->flags&EntityFlag_CVarArg) {
 		if (end->flags&EntityFlag_CVarArg) {
 			if (pt->calling_convention == ProcCC_Odin) {
 			if (pt->calling_convention == ProcCC_Odin) {
 				error(end->token, "Odin calling convention does not support #c_vararg");
 				error(end->token, "Odin calling convention does not support #c_vararg");
+			} else if (pt->calling_convention == ProcCC_Contextless) {
+				error(end->token, "Odin's contextless calling convention does not support #c_vararg");
 			} else if (pt->calling_convention == ProcCC_Fast) {
 			} else if (pt->calling_convention == ProcCC_Fast) {
 				error(end->token, "Fast calling convention does not support #c_vararg");
 				error(end->token, "Fast calling convention does not support #c_vararg");
 			} else {
 			} else {

+ 7 - 5
src/checker.cpp

@@ -1779,14 +1779,16 @@ void check_all_global_entities(Checker *c) {
 			continue;
 			continue;
 		}
 		}
 
 
-		if (e->kind != Entity_Procedure && e->token.string == "main") {
-			if (e->scope->is_init) {
+		if (e->token.string == "main") {
+			if (e->kind != Entity_Procedure) {
+				if (e->scope->is_init) {
+					error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
+					continue;
+				}
+			} else if (e->scope->is_global) {
 				error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
 				error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
 				continue;
 				continue;
 			}
 			}
-		} else if (e->scope->is_global && e->token.string == "main") {
-			error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
-			continue;
 		}
 		}
 
 
 		CheckerContext prev_context = c->context;
 		CheckerContext prev_context = c->context;

+ 69 - 26
src/ir.cpp

@@ -28,6 +28,8 @@ struct irModule {
 	i32                   global_array_index; // For ConstantSlice
 	i32                   global_array_index; // For ConstantSlice
 	i32                   global_generated_index;
 	i32                   global_generated_index;
 
 
+	irValue *             global_default_context;
+
 	// NOTE(bill): To prevent strings from being copied a lot
 	// NOTE(bill): To prevent strings from being copied a lot
 	// Mainly used for file names
 	// Mainly used for file names
 	Map<irValue *>        const_strings; // Key: String
 	Map<irValue *>        const_strings; // Key: String
@@ -125,6 +127,9 @@ struct irProcedure {
 	irTargetList *        target_list;
 	irTargetList *        target_list;
 	Array<irValue *>      referrers;
 	Array<irValue *>      referrers;
 
 
+	Array<irValue *>      context_stack;
+
+
 	Array<irBranchBlocks> branch_blocks;
 	Array<irBranchBlocks> branch_blocks;
 
 
 	i32                   local_count;
 	i32                   local_count;
@@ -216,6 +221,7 @@ struct irProcedure {
 		irValue * return_ptr;                                         \
 		irValue * return_ptr;                                         \
 		irValue **args;                                               \
 		irValue **args;                                               \
 		isize     arg_count;                                          \
 		isize     arg_count;                                          \
+		irValue * context_ptr;                                        \
 	})                                                                \
 	})                                                                \
 	IR_INSTR_KIND(StartupRuntime, i32)                                \
 	IR_INSTR_KIND(StartupRuntime, i32)                                \
 	IR_INSTR_KIND(DebugDeclare, struct {                              \
 	IR_INSTR_KIND(DebugDeclare, struct {                              \
@@ -986,22 +992,23 @@ irValue *ir_instr_select(irProcedure *p, irValue *cond, irValue *t, irValue *f)
 	return v;
 	return v;
 }
 }
 
 
-irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, irValue **args, isize arg_count, Type *result_type) {
+irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, irValue **args, isize arg_count, Type *result_type, irValue *context_ptr) {
 	irValue *v = ir_alloc_instr(p, irInstr_Call);
 	irValue *v = ir_alloc_instr(p, irInstr_Call);
-	v->Instr.Call.value = value;
-	v->Instr.Call.return_ptr = return_ptr;
-	v->Instr.Call.args = args;
-	v->Instr.Call.arg_count = arg_count;
-	v->Instr.Call.type = result_type;
+	v->Instr.Call.value       = value;
+	v->Instr.Call.return_ptr  = return_ptr;
+	v->Instr.Call.args        = args;
+	v->Instr.Call.arg_count   = arg_count;
+	v->Instr.Call.type        = result_type;
+	v->Instr.Call.context_ptr = context_ptr;
 	return v;
 	return v;
 }
 }
 
 
 irValue *ir_instr_conv(irProcedure *p, irConvKind kind, irValue *value, Type *from, Type *to) {
 irValue *ir_instr_conv(irProcedure *p, irConvKind kind, irValue *value, Type *from, Type *to) {
 	irValue *v = ir_alloc_instr(p, irInstr_Conv);
 	irValue *v = ir_alloc_instr(p, irInstr_Conv);
-	v->Instr.Conv.kind = kind;
+	v->Instr.Conv.kind  = kind;
 	v->Instr.Conv.value = value;
 	v->Instr.Conv.value = value;
-	v->Instr.Conv.from = from;
-	v->Instr.Conv.to = to;
+	v->Instr.Conv.from  = from;
+	v->Instr.Conv.to    = to;
 	return v;
 	return v;
 }
 }
 
 
@@ -1472,11 +1479,30 @@ irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) {
 
 
 irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
 irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t);
 
 
+
+irValue *ir_emit_global_call(irProcedure *proc, char *name_, irValue **args, isize arg_count);
+
+irValue *ir_find_or_generate_context_ptr(irProcedure *proc) {
+	if (proc->context_stack.count > 0) {
+		return proc->context_stack[proc->context_stack.count-1];
+	}
+	irValue *c = ir_add_local_generated(proc, t_context);
+	ir_emit_store(proc, c, ir_emit_load(proc, proc->module->global_default_context));
+	array_add(&proc->context_stack, c);
+	return c;
+}
+
+
 irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) {
 irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) {
 	Type *pt = base_type(ir_type(value));
 	Type *pt = base_type(ir_type(value));
 	GB_ASSERT(pt->kind == Type_Proc);
 	GB_ASSERT(pt->kind == Type_Proc);
 	Type *results = pt->Proc.results;
 	Type *results = pt->Proc.results;
 
 
+	irValue *context_ptr = NULL;
+	if (pt->Proc.calling_convention == ProcCC_Odin) {
+		context_ptr = ir_find_or_generate_context_ptr(p);
+	}
+
 	isize param_count = pt->Proc.param_count;
 	isize param_count = pt->Proc.param_count;
 	if (pt->Proc.c_vararg) {
 	if (pt->Proc.c_vararg) {
 		GB_ASSERT(param_count-1 <= arg_count);
 		GB_ASSERT(param_count-1 <= arg_count);
@@ -1500,11 +1526,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_
 	if (pt->Proc.return_by_pointer) {
 	if (pt->Proc.return_by_pointer) {
 		irValue *return_ptr = ir_add_local_generated(p, rt);
 		irValue *return_ptr = ir_add_local_generated(p, rt);
 		GB_ASSERT(is_type_pointer(ir_type(return_ptr)));
 		GB_ASSERT(is_type_pointer(ir_type(return_ptr)));
-		ir_emit(p, ir_instr_call(p, value, return_ptr, args, arg_count, NULL));
+		ir_emit(p, ir_instr_call(p, value, return_ptr, args, arg_count, NULL, context_ptr));
 		return ir_emit_load(p, return_ptr);
 		return ir_emit_load(p, return_ptr);
 	}
 	}
 
 
-	irValue *result = ir_emit(p, ir_instr_call(p, value, NULL, args, arg_count, abi_rt));
+	irValue *result = ir_emit(p, ir_instr_call(p, value, NULL, args, arg_count, abi_rt, context_ptr));
 	if (abi_rt != results) {
 	if (abi_rt != results) {
 		result = ir_emit_transmute(p, result, rt);
 		result = ir_emit_transmute(p, result, rt);
 	}
 	}
@@ -4865,7 +4891,8 @@ irAddr ir_build_addr(irProcedure *proc, AstNode *expr) {
 		irValue *v = NULL;
 		irValue *v = NULL;
 		switch (i->kind) {
 		switch (i->kind) {
 		case Token_context:
 		case Token_context:
-			v = ir_find_global_variable(proc, str_lit("__context"));
+			v = ir_find_or_generate_context_ptr(proc);
+			// v = ir_find_global_variable(proc, str_lit("__context"));
 			break;
 			break;
 		}
 		}
 
 
@@ -6716,13 +6743,13 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		ir_emit_comment(proc, str_lit("PushAllocator"));
 		ir_emit_comment(proc, str_lit("PushAllocator"));
 		ir_open_scope(proc);
 		ir_open_scope(proc);
 
 
-		irValue *context_ptr = ir_find_global_variable(proc, str_lit("__context"));
-		irValue *prev_context = ir_add_local_generated(proc, t_context);
-		ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr));
+		irValue *prev = ir_find_or_generate_context_ptr(proc);
+		irValue *next = ir_add_local_generated(proc, t_context);
+		ir_emit_store(proc, next, ir_emit_load(proc, prev));
+		array_add(&proc->context_stack, next);
+		defer (array_pop(&proc->context_stack));
 
 
-		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false));
-
-		irValue *gep = ir_emit_struct_ep(proc, context_ptr, 1);
+		irValue *gep = ir_emit_struct_ep(proc, next, 1);
 		ir_emit_store(proc, gep, ir_build_expr(proc, pa->expr));
 		ir_emit_store(proc, gep, ir_build_expr(proc, pa->expr));
 
 
 		ir_build_stmt(proc, pa->body);
 		ir_build_stmt(proc, pa->body);
@@ -6735,13 +6762,12 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		ir_emit_comment(proc, str_lit("PushContext"));
 		ir_emit_comment(proc, str_lit("PushContext"));
 		ir_open_scope(proc);
 		ir_open_scope(proc);
 
 
-		irValue *context_ptr = ir_find_global_variable(proc, str_lit("__context"));
-		irValue *prev_context = ir_add_local_generated(proc, t_context);
-		ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr));
-
-		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false));
+		irValue *prev = ir_find_or_generate_context_ptr(proc);
+		irValue *next = ir_add_local_generated(proc, t_context);
+		array_add(&proc->context_stack, next);
+		defer (array_pop(&proc->context_stack));
 
 
-		ir_emit_store(proc, context_ptr, ir_build_expr(proc, pc->expr));
+		ir_emit_store(proc, next, ir_build_expr(proc, pc->expr));
 
 
 		ir_build_stmt(proc, pc->body);
 		ir_build_stmt(proc, pc->body);
 
 
@@ -6785,12 +6811,14 @@ void ir_number_proc_registers(irProcedure *proc) {
 }
 }
 
 
 void ir_begin_procedure_body(irProcedure *proc) {
 void ir_begin_procedure_body(irProcedure *proc) {
+	gbAllocator a = proc->module->allocator;
 	array_add(&proc->module->procs, proc);
 	array_add(&proc->module->procs, proc);
 
 
 	array_init(&proc->blocks,           heap_allocator());
 	array_init(&proc->blocks,           heap_allocator());
 	array_init(&proc->defer_stmts,      heap_allocator());
 	array_init(&proc->defer_stmts,      heap_allocator());
 	array_init(&proc->children,         heap_allocator());
 	array_init(&proc->children,         heap_allocator());
 	array_init(&proc->branch_blocks,    heap_allocator());
 	array_init(&proc->branch_blocks,    heap_allocator());
+	array_init(&proc->context_stack,    heap_allocator());
 
 
 	DeclInfo *decl = decl_info_of_entity(proc->module->info, proc->entity);
 	DeclInfo *decl = decl_info_of_entity(proc->module->info, proc->entity);
 	if (decl != NULL) {
 	if (decl != NULL) {
@@ -6808,7 +6836,6 @@ void ir_begin_procedure_body(irProcedure *proc) {
 
 
 	if (proc->type->Proc.return_by_pointer) {
 	if (proc->type->Proc.return_by_pointer) {
 		// NOTE(bill): this must be the first parameter stored
 		// NOTE(bill): this must be the first parameter stored
-		gbAllocator a = proc->module->allocator;
 		Type *ptr_type = make_type_pointer(a, reduce_tuple_to_single_type(proc->type->Proc.results));
 		Type *ptr_type = make_type_pointer(a, reduce_tuple_to_single_type(proc->type->Proc.results));
 		Entity *e = make_entity_param(a, NULL, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
 		Entity *e = make_entity_param(a, NULL, make_token_ident(str_lit("agg.result")), ptr_type, false, false);
 		e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
 		e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
@@ -6848,6 +6875,13 @@ void ir_begin_procedure_body(irProcedure *proc) {
 	}
 	}
 
 
 
 
+	if (proc->type->Proc.calling_convention == ProcCC_Odin) {
+		Entity *e = make_entity_param(a, NULL, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false);
+		e->flags |= EntityFlag_NoAlias;
+		irValue *param = ir_value_param(a, proc, e, e->type);
+		ir_module_add_value(proc->module, e, param);
+		array_add(&proc->context_stack, param);
+	}
 }
 }
 
 
 
 
@@ -7222,6 +7256,9 @@ void ir_gen_tree(irGen *s) {
 		}
 		}
 	}
 	}
 
 
+	{ // Add global default context
+		m->global_default_context = ir_add_global_generated(m, t_context, NULL);
+	}
 	struct irGlobalVariable {
 	struct irGlobalVariable {
 		irValue *var, *init;
 		irValue *var, *init;
 		DeclInfo *decl;
 		DeclInfo *decl;
@@ -7478,7 +7515,7 @@ void ir_gen_tree(irGen *s) {
 		String name = str_lit(IR_STARTUP_RUNTIME_PROC_NAME);
 		String name = str_lit(IR_STARTUP_RUNTIME_PROC_NAME);
 		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
 		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
 		                                 NULL, 0,
 		                                 NULL, 0,
-		                                 NULL, 0, false, ProcCC_Odin);
+		                                 NULL, 0, false, ProcCC_Contextless);
 		AstNode *body = gb_alloc_item(a, AstNode);
 		AstNode *body = gb_alloc_item(a, AstNode);
 		Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0);
 		Entity *e = make_entity_procedure(a, NULL, make_token_ident(name), proc_type, 0);
 		irValue *p = ir_value_procedure(a, m, e, proc_type, NULL, body, name);
 		irValue *p = ir_value_procedure(a, m, e, proc_type, NULL, body, name);
@@ -7492,6 +7529,12 @@ void ir_gen_tree(irGen *s) {
 
 
 		ir_begin_procedure_body(proc);
 		ir_begin_procedure_body(proc);
 
 
+		{
+			irValue **args = gb_alloc_array(a, irValue *, 1);
+			args[0] = m->global_default_context;
+			ir_emit_global_call(proc, "__init_context", args, 1);
+		}
+
 		// TODO(bill): Should do a dependency graph do check which order to initialize them in?
 		// TODO(bill): Should do a dependency graph do check which order to initialize them in?
 		for_array(i, global_variables) {
 		for_array(i, global_variables) {
 			irGlobalVariable *var = &global_variables[i];
 			irGlobalVariable *var = &global_variables[i];

+ 30 - 8
src/ir_print.cpp

@@ -199,6 +199,12 @@ void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
 			ir_print_type(f, m, t->Proc.abi_compat_params[i]);
 			ir_print_type(f, m, t->Proc.abi_compat_params[i]);
 		}
 		}
 	}
 	}
+	if (t->Proc.calling_convention == ProcCC_Odin) {
+		if (param_count > 0) {
+			ir_fprintf(f, ", ");
+		}
+		ir_print_type(f, m, t_context_ptr);
+	}
 	ir_fprintf(f, ")");
 	ir_fprintf(f, ")");
 }
 }
 
 
@@ -737,10 +743,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
 
 
 void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConvention cc) {
 void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConvention cc) {
 	switch (cc) {
 	switch (cc) {
-	case ProcCC_Odin: ir_fprintf(f, "");       break;
-	case ProcCC_C:    ir_fprintf(f, "ccc ");   break;
-	case ProcCC_Std:  ir_fprintf(f, "cc 64 "); break;
-	case ProcCC_Fast: ir_fprintf(f, "cc 65 "); break;
+	case ProcCC_Odin:        ir_fprintf(f, "");       break;
+	case ProcCC_Contextless: ir_fprintf(f, "");       break;
+	case ProcCC_C:           ir_fprintf(f, "ccc ");   break;
+	case ProcCC_Std:         ir_fprintf(f, "cc 64 "); break;
+	case ProcCC_Fast:        ir_fprintf(f, "cc 65 "); break;
 	default: GB_PANIC("unknown calling convention: %d", cc);
 	default: GB_PANIC("unknown calling convention: %d", cc);
 	}
 	}
 }
 }
@@ -1261,8 +1268,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 
 
 
 
 		if (call->arg_count > 0) {
 		if (call->arg_count > 0) {
-			Type *proc_type = base_type(ir_type(call->value));
-			GB_ASSERT(proc_type->kind == Type_Proc);
 			TypeTuple *params = &proc_type->Proc.params->Tuple;
 			TypeTuple *params = &proc_type->Proc.params->Tuple;
 			if (proc_type->Proc.c_vararg) {
 			if (proc_type->Proc.c_vararg) {
 				isize i = 0;
 				isize i = 0;
@@ -1293,7 +1298,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 					ir_print_value(f, m, arg, t);
 					ir_print_value(f, m, arg, t);
 				}
 				}
 			} else {
 			} else {
-				for (isize i = 0; i < call->arg_count; i++) {
+				GB_ASSERT(call->arg_count == params->variable_count);
+				isize param_count = params->variable_count;
+				for (isize i = 0; i < param_count; i++) {
 					Entity *e = params->variables[i];
 					Entity *e = params->variables[i];
 					GB_ASSERT(e != NULL);
 					GB_ASSERT(e != NULL);
 					irValue *arg = call->args[i];
 					irValue *arg = call->args[i];
@@ -1310,6 +1317,14 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 				}
 				}
 			}
 			}
 		}
 		}
+		if (proc_type->Proc.calling_convention == ProcCC_Odin) {
+			if (proc_type->Proc.param_count > 0) {
+				ir_fprintf(f, ", ");
+			}
+			ir_print_type(f, m, t_context_ptr);
+			ir_fprintf(f, " noalias nonnull");
+			ir_print_value(f, m, call->context_ptr, t_context_ptr);
+		}
 		ir_fprintf(f, ")\n");
 		ir_fprintf(f, ")\n");
 
 
 	} break;
 	} break;
@@ -1538,7 +1553,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 
 
 	if (param_count > 0) {
 	if (param_count > 0) {
 		TypeTuple *params = &proc_type->params->Tuple;
 		TypeTuple *params = &proc_type->params->Tuple;
-		for (isize i = 0; i < params->variable_count; i++) {
+		for (isize i = 0; i < param_count; i++) {
 			Entity *e = params->variables[i];
 			Entity *e = params->variables[i];
 			Type *original_type = e->type;
 			Type *original_type = e->type;
 			Type *abi_type = proc_type->abi_compat_params[i];
 			Type *abi_type = proc_type->abi_compat_params[i];
@@ -1564,6 +1579,13 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 			}
 			}
 		}
 		}
 	}
 	}
+	if (proc_type->calling_convention == ProcCC_Odin) {
+		if (param_count > 0) {
+			ir_fprintf(f, ", ");
+		}
+		ir_print_type(f, m, t_context_ptr);
+		ir_fprintf(f, " noalias nonnull %%__.context_ptr");
+	}
 
 
 	ir_fprintf(f, ") ");
 	ir_fprintf(f, ") ");
 
 

+ 12 - 7
src/parser.cpp

@@ -91,11 +91,12 @@ enum ProcTag {
 };
 };
 
 
 enum ProcCallingConvention {
 enum ProcCallingConvention {
-	ProcCC_Invalid = 0,
-	ProcCC_Odin    = 1,
-	ProcCC_C       = 2,
-	ProcCC_Std     = 3,
-	ProcCC_Fast    = 4,
+	ProcCC_Invalid     = 0,
+	ProcCC_Odin        = 1,
+	ProcCC_Contextless = 2,
+	ProcCC_C           = 3,
+	ProcCC_Std         = 4,
+	ProcCC_Fast        = 5,
 };
 };
 
 
 enum VarDeclFlag {
 enum VarDeclFlag {
@@ -2070,14 +2071,18 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven
 		ELSE_IF_ADD_TAG(no_bounds_check)
 		ELSE_IF_ADD_TAG(no_bounds_check)
 		ELSE_IF_ADD_TAG(inline)
 		ELSE_IF_ADD_TAG(inline)
 		ELSE_IF_ADD_TAG(no_inline)
 		ELSE_IF_ADD_TAG(no_inline)
-		// ELSE_IF_ADD_TAG(dll_import)
-		// ELSE_IF_ADD_TAG(dll_export)
 		else if (tag_name == "cc_odin") {
 		else if (tag_name == "cc_odin") {
 			if (cc == ProcCC_Invalid) {
 			if (cc == ProcCC_Invalid) {
 				cc = ProcCC_Odin;
 				cc = ProcCC_Odin;
 			} else {
 			} else {
 				syntax_error(tag_expr, "Multiple calling conventions for procedure type");
 				syntax_error(tag_expr, "Multiple calling conventions for procedure type");
 			}
 			}
+		} else if (tag_name == "cc_contextless") {
+			if (cc == ProcCC_Invalid) {
+				cc = ProcCC_Contextless;
+			} else {
+				syntax_error(tag_expr, "Multiple calling conventions for procedure type");
+			}
 		} else if (tag_name == "cc_c") {
 		} else if (tag_name == "cc_c") {
 			if (cc == ProcCC_Invalid) {
 			if (cc == ProcCC_Invalid) {
 				cc = ProcCC_C;
 				cc = ProcCC_C;