Browse Source

Update Tilde to get procedure passing rules

gingerBill 2 years ago
parent
commit
e5f9458905
6 changed files with 217 additions and 115 deletions
  1. 12 83
      src/tilde.cpp
  2. 3 0
      src/tilde.hpp
  3. 43 19
      src/tilde/tb.h
  4. BIN
      src/tilde/tb.lib
  5. 120 12
      src/tilde_proc.cpp
  6. 39 1
      src/tilde_stmt.cpp

+ 12 - 83
src/tilde.cpp

@@ -1,5 +1,12 @@
 #include "tilde.hpp"
 #include "tilde.hpp"
 
 
+
+gb_global Slice<TB_Arena *> global_tb_arenas;
+
+gb_internal TB_Arena *cg_arena(void) {
+	return global_tb_arenas[current_thread_index()];
+}
+
 // returns TB_TYPE_VOID if not trivially possible
 // returns TB_TYPE_VOID if not trivially possible
 gb_internal TB_DataType cg_data_type(Type *t) {
 gb_internal TB_DataType cg_data_type(Type *t) {
 	GB_ASSERT(t != nullptr);
 	GB_ASSERT(t != nullptr);
@@ -672,6 +679,11 @@ gb_internal bool cg_generate_code(Checker *c) {
 	CheckerInfo *info = &c->info;
 	CheckerInfo *info = &c->info;
 	gb_unused(info);
 	gb_unused(info);
 
 
+	global_tb_arenas = slice_make<TB_Arena *>(permanent_allocator(), global_thread_pool.threads.count);
+	for_array(i, global_tb_arenas) {
+		global_tb_arenas[i] = tb_default_arena();
+	}
+
 	cgModule *m = cg_module_create(c);
 	cgModule *m = cg_module_create(c);
 	defer (cg_module_destroy(m));
 	defer (cg_module_destroy(m));
 
 
@@ -750,89 +762,6 @@ gb_internal bool cg_generate_code(Checker *c) {
 	char const *path = "W:/Odin/tilde_main.obj";
 	char const *path = "W:/Odin/tilde_main.obj";
 	GB_ASSERT(tb_export_buffer_to_file(export_buffer, path));
 	GB_ASSERT(tb_export_buffer_to_file(export_buffer, path));
 
 
-
-	////////////////////////////////////////////////////////////////////////////////////
-
-
-	// TB_Arena *arena = tb_default_arena();
-
-	// TB_Global *str = nullptr;
-	// {
-	// 	TB_Global *str_data = nullptr;
-	// 	{
-	// 		str_data = tb_global_create(m->mod, "csb$1", nullptr, TB_LINKAGE_PRIVATE);
-	// 		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str_data, 8, 1, 1);
-	// 		void *region = tb_global_add_region(m->mod, str_data, 0, 8);
-	// 		memcpy(region, "Hellope\x00", 8);
-	// 	}
-
-	// 	str = tb_global_create(m->mod, "global$str", nullptr, TB_LINKAGE_PRIVATE);
-	// 	tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), str, 16, 8, 2);
-	// 	tb_global_add_symbol_reloc(m->mod, str, 0, cast(TB_Symbol *)str_data);
-	// 	void *len = tb_global_add_region(m->mod, str, 8, 8);
-	// 	*cast(i64 *)len = 7;
-	// }
-
-	// {
-	// 	TB_PrototypeParam printf_ret = {TB_TYPE_I32};
-	// 	TB_PrototypeParam printf_params = {TB_TYPE_PTR};
-	// 	TB_FunctionPrototype *printf_proto = tb_prototype_create(m->mod, TB_STDCALL, 1, &printf_params, 1, &printf_ret, true);
-	// 	TB_External *printf_proc = tb_extern_create(m->mod, "printf", TB_EXTERNAL_SO_LOCAL);
-
-	// 	TB_PrototypeParam main_ret = {TB_TYPE_I32};
-	// 	TB_FunctionPrototype *main_proto = tb_prototype_create(m->mod, TB_STDCALL, 0, nullptr, 1, &main_ret, false);
-	// 	TB_Function *         p = tb_function_create(m->mod, "main", TB_LINKAGE_PUBLIC, TB_COMDAT_NONE);
-	// 	tb_function_set_prototype(p, main_proto, arena);
-
-
-	// 	auto str_ptr          = tb_inst_get_symbol_address(p, cast(TB_Symbol *)str);
-	// 	auto str_data_ptr_ptr = tb_inst_member_access(p, str_ptr, 0);
-	// 	auto str_data_ptr     = tb_inst_load(p, TB_TYPE_PTR, str_data_ptr_ptr, 1, false);
-	// 	auto str_len_ptr      = tb_inst_member_access(p, str_ptr, 8);
-	// 	auto str_len          = tb_inst_load(p, TB_TYPE_I64, str_len_ptr, 8, false);
-
-
-	// 	TB_Node *params[4] = {};
-	// 	params[0] = tb_inst_cstring(p, "%.*s %s!\n");
-	// 	params[1] = tb_inst_trunc(p, str_len, TB_TYPE_I32);
-	// 	params[2] = str_data_ptr;
-	// 	params[3] = tb_inst_cstring(p, "World");
-	// 	TB_MultiOutput output = tb_inst_call(p, printf_proto, tb_inst_get_symbol_address(p, cast(TB_Symbol *)printf_proc), gb_count_of(params), params);
-	// 	gb_unused(output);
-	// 	TB_Node *printf_return_value = output.single;
-
-	// 	TB_Node *zero = tb_inst_uint(p, TB_TYPE_I32, 0);
-	// 	TB_Node *one  = tb_inst_uint(p, TB_TYPE_I32, 1);
-
-	// 	TB_Node *prev_case = tb_inst_get_control(p);
-
-	// 	TB_Node *true_case  = tb_inst_region(p);
-	// 	TB_Node *false_case = tb_inst_region(p);
-
-	// 	TB_Node *cond = tb_inst_cmp_igt(p, printf_return_value, zero, true);
-	// 	tb_inst_if(p, cond, true_case, false_case);
-
-	// 	tb_inst_set_control(p, true_case);
-	// 	tb_inst_ret(p, 1, &zero);
-
-	// 	tb_inst_set_control(p, false_case);
-	// 	tb_inst_ret(p, 1, &one);
-
-	// 	tb_inst_set_control(p, prev_case);
-
-
-	// 	tb_module_compile_function(m->mod, p, TB_ISEL_FAST);
-
-	// 	tb_function_print(p, tb_default_print_callback, stdout);
-
-
-	// 	TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;
-	// 	TB_ExportBuffer export_buffer = tb_module_object_export(m->mod, debug_format);
-	// 	defer (tb_export_buffer_free(export_buffer));
-
-	// 	char const *path = "W:/Odin/tilde_main.obj";
-	// 	GB_ASSERT(tb_export_buffer_to_file(export_buffer, path));
-	// }
 	return true;
 	return true;
 }
 }
 
 

+ 3 - 0
src/tilde.hpp

@@ -248,6 +248,9 @@ gb_global isize cg_global_type_info_member_offsets_index = 0;
 gb_global isize cg_global_type_info_member_usings_index  = 0;
 gb_global isize cg_global_type_info_member_usings_index  = 0;
 gb_global isize cg_global_type_info_member_tags_index    = 0;
 gb_global isize cg_global_type_info_member_tags_index    = 0;
 
 
+
+gb_internal TB_Arena *cg_arena(void);
+
 gb_internal cgValue cg_value(TB_Global *  g,    Type *type);
 gb_internal cgValue cg_value(TB_Global *  g,    Type *type);
 gb_internal cgValue cg_value(TB_External *e,    Type *type);
 gb_internal cgValue cg_value(TB_External *e,    Type *type);
 gb_internal cgValue cg_value(TB_Function *f,    Type *type);
 gb_internal cgValue cg_value(TB_Function *f,    Type *type);

+ 43 - 19
src/tilde/tb.h

@@ -210,9 +210,6 @@ typedef enum TB_NodeTypeEnum {
     // projection
     // projection
     TB_PROJ,
     TB_PROJ,
 
 
-    // metadata
-    TB_KEEPALIVE,
-
     TB_CALL,  // normal call
     TB_CALL,  // normal call
     TB_SCALL, // system call
     TB_SCALL, // system call
 
 
@@ -640,14 +637,32 @@ TB_API void tb_module_set_tls_index(TB_Module* m, ptrdiff_t len, const char* nam
 TB_API void tb_module_layout_sections(TB_Module* m);
 TB_API void tb_module_layout_sections(TB_Module* m);
 
 
 ////////////////////////////////
 ////////////////////////////////
-// Exporter
+// Compiled code introspection
 ////////////////////////////////
 ////////////////////////////////
+enum { TB_ASSEMBLY_CHUNK_CAP = 4*1024 - sizeof(size_t[2]) };
+
+typedef struct TB_Assembly TB_Assembly;
+struct TB_Assembly {
+    TB_Assembly* next;
+
+    // nice chunk of text here
+    size_t length;
+    char data[];
+};
 
 
 // this is where the machine code and other relevant pieces go.
 // this is where the machine code and other relevant pieces go.
 typedef struct TB_FunctionOutput TB_FunctionOutput;
 typedef struct TB_FunctionOutput TB_FunctionOutput;
 
 
 // returns NULL if it fails
 // returns NULL if it fails
-TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode);
+TB_API TB_FunctionOutput* tb_module_compile_function(TB_Module* m, TB_Function* f, TB_ISelMode isel_mode, bool emit_asm);
+
+TB_API uint8_t* tb_output_get_code(TB_FunctionOutput* out, size_t* out_length);
+
+// returns NULL if no assembly was generated
+TB_API TB_Assembly* tb_output_get_asm(TB_FunctionOutput* out);
+
+// this is relative to the start of the function (the start of the prologue)
+TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip);
 
 
 ////////////////////////////////
 ////////////////////////////////
 // Exporter
 // Exporter
@@ -780,6 +795,27 @@ struct TB_FunctionPrototype {
 // matching signatures.
 // matching signatures.
 TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc, size_t param_count, const TB_PrototypeParam* params, size_t return_count, const TB_PrototypeParam* returns, bool has_varargs);
 TB_API TB_FunctionPrototype* tb_prototype_create(TB_Module* m, TB_CallingConv cc, size_t param_count, const TB_PrototypeParam* params, size_t return_count, const TB_PrototypeParam* returns, bool has_varargs);
 
 
+// same as tb_function_set_prototype except it will handle lowering from types like the TB_DebugType
+// into the correct ABI and exposing sane looking nodes to the parameters.
+//
+// returns the parameters
+TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count);
+TB_API TB_FunctionPrototype* tb_prototype_from_dbg(TB_Module* m, TB_DebugType* dbg);
+
+// used for ABI parameter passing
+typedef enum {
+    // needs a direct value
+    TB_PASSING_DIRECT,
+
+    // needs an address to the value
+    TB_PASSING_INDIRECT,
+
+    // doesn't use this parameter
+    TB_PASSING_IGNORE,
+} TB_PassingRule;
+
+TB_API TB_PassingRule tb_get_passing_rule_from_dbg(TB_Module* mod, TB_DebugType* param_type, bool is_return);
+
 ////////////////////////////////
 ////////////////////////////////
 // Globals
 // Globals
 ////////////////////////////////
 ////////////////////////////////
@@ -829,6 +865,8 @@ TB_API void tb_debug_record_end(TB_DebugType* type, TB_CharUnits size, TB_CharUn
 
 
 TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_t param_count, size_t return_count, bool has_varargs);
 TB_API TB_DebugType* tb_debug_create_func(TB_Module* m, TB_CallingConv cc, size_t param_count, size_t return_count, bool has_varargs);
 
 
+TB_API TB_DebugType* tb_debug_field_type(TB_DebugType* type);
+
 // you'll need to fill these if you make a function
 // you'll need to fill these if you make a function
 TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_params(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
 TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
@@ -839,12 +877,6 @@ TB_API TB_DebugType** tb_debug_func_returns(TB_DebugType* type);
 // it is an index to the input
 // it is an index to the input
 #define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++)
 #define TB_FOR_INPUT_IN_NODE(it, parent) for (TB_Node **it = parent->inputs, **__end = it + (parent)->input_count; it != __end; it++)
 
 
-////////////////////////////////
-// Compiled code introspection
-////////////////////////////////
-// this is relative to the start of the function (the start of the prologue)
-TB_API TB_Safepoint* tb_safepoint_get(TB_Function* f, uint32_t relative_ip);
-
 ////////////////////////////////
 ////////////////////////////////
 // Symbols
 // Symbols
 ////////////////////////////////
 ////////////////////////////////
@@ -879,13 +911,6 @@ TB_API void tb_symbol_set_name(TB_Symbol* s, ptrdiff_t len, const char* name);
 TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr);
 TB_API void tb_symbol_bind_ptr(TB_Symbol* s, void* ptr);
 TB_API const char* tb_symbol_get_name(TB_Symbol* s);
 TB_API const char* tb_symbol_get_name(TB_Symbol* s);
 
 
-// same as tb_function_set_prototype except it will handle lowering from types like the TB_DebugType
-// into the correct ABI and exposing sane looking nodes to the parameters.
-//
-// returns the parameters
-TB_API TB_Node** tb_function_set_prototype_from_dbg(TB_Function* f, TB_DebugType* dbg, TB_Arena* arena, size_t* out_param_count);
-TB_API TB_FunctionPrototype* tb_prototype_from_dbg(TB_Module* m, TB_DebugType* dbg);
-
 // if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources
 // if arena is NULL, defaults to module arena which is freed on tb_free_thread_resources
 TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena);
 TB_API void tb_function_set_prototype(TB_Function* f, TB_FunctionPrototype* p, TB_Arena* arena);
 TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f);
 TB_API TB_FunctionPrototype* tb_function_get_prototype(TB_Function* f);
@@ -903,7 +928,6 @@ TB_API void tb_inst_set_region_name(TB_Node* n, ptrdiff_t len, const char* name)
 TB_API void tb_inst_unreachable(TB_Function* f);
 TB_API void tb_inst_unreachable(TB_Function* f);
 TB_API void tb_inst_debugbreak(TB_Function* f);
 TB_API void tb_inst_debugbreak(TB_Function* f);
 TB_API void tb_inst_trap(TB_Function* f);
 TB_API void tb_inst_trap(TB_Function* f);
-TB_API void tb_inst_keep_alive(TB_Function* f, TB_Node* src);
 TB_API TB_Node* tb_inst_poison(TB_Function* f);
 TB_API TB_Node* tb_inst_poison(TB_Function* f);
 
 
 TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id);
 TB_API TB_Node* tb_inst_param(TB_Function* f, int param_id);

BIN
src/tilde/tb.lib


+ 120 - 12
src/tilde_proc.cpp

@@ -87,12 +87,11 @@ gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool i
 	}
 	}
 
 
 	if (p->symbol == nullptr)  {
 	if (p->symbol == nullptr)  {
-		TB_Arena *arena = tb_default_arena();
 		p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
 		p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
 
 
 		size_t out_param_count = 0;
 		size_t out_param_count = 0;
 		p->debug_type = cg_debug_type_for_proc(m, p->type);
 		p->debug_type = cg_debug_type_for_proc(m, p->type);
-		TB_Node **params = tb_function_set_prototype_from_dbg(p->func, p->debug_type, arena, &out_param_count);
+		TB_Node **params = tb_function_set_prototype_from_dbg(p->func, p->debug_type, cg_arena(), &out_param_count);
 		p->param_nodes = {params, cast(isize)out_param_count};
 		p->param_nodes = {params, cast(isize)out_param_count};
 		p->proto = tb_function_get_prototype(p->func);
 		p->proto = tb_function_get_prototype(p->func);
 
 
@@ -141,12 +140,11 @@ gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &li
 
 
 	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
 	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
 
 
-	TB_Arena *arena = tb_default_arena();
 	p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
 	p->func = tb_function_create(m->mod, link_name.len, cast(char const *)link_name.text, linkage, TB_COMDAT_NONE);
 
 
 	size_t out_param_count = 0;
 	size_t out_param_count = 0;
 	p->debug_type = cg_debug_type_for_proc(m, p->type);
 	p->debug_type = cg_debug_type_for_proc(m, p->type);
-	TB_Node **params = tb_function_set_prototype_from_dbg(p->func, p->debug_type, arena, &out_param_count);
+	TB_Node **params = tb_function_set_prototype_from_dbg(p->func, p->debug_type, cg_arena(), &out_param_count);
 	p->param_nodes = {params, cast(isize)out_param_count};
 	p->param_nodes = {params, cast(isize)out_param_count};
 	p->proto = tb_function_get_prototype(p->func);
 	p->proto = tb_function_get_prototype(p->func);
 
 
@@ -275,17 +273,32 @@ gb_internal void cg_procedure_end(cgProcedure *p) {
 	if (tb_inst_get_control(p->func)) {
 	if (tb_inst_get_control(p->func)) {
 		tb_inst_ret(p->func, 0, nullptr);
 		tb_inst_ret(p->func, 0, nullptr);
 	}
 	}
+	bool emit_asm = false;
 	// if (p->name == "main") {
 	// if (p->name == "main") {
-	if (p->name == "bug" ABI_PKG_NAME_SEPARATOR "main") {
+	if (
+	    // p->name == "bug" ABI_PKG_NAME_SEPARATOR "main" ||
+	    p->name == "main" ||
+	    false
+	) {
 		TB_Arena *arena = tb_default_arena();
 		TB_Arena *arena = tb_default_arena();
 		defer (arena->free(arena));
 		defer (arena->free(arena));
 		TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
 		TB_FuncOpt *opt = tb_funcopt_enter(p->func, arena);
 		defer (tb_funcopt_exit(opt));
 		defer (tb_funcopt_exit(opt));
 		tb_funcopt_print(opt);
 		tb_funcopt_print(opt);
 
 
+		// emit_asm = true;
+
+		// GraphViz printing
 		// tb_function_print(p->func, tb_default_print_callback, stdout);
 		// tb_function_print(p->func, tb_default_print_callback, stdout);
 	}
 	}
-	tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST);
+	TB_FunctionOutput *output = tb_module_compile_function(p->module->mod, p->func, TB_ISEL_FAST, emit_asm);
+	if (emit_asm) {
+		TB_Assembly *assembly = tb_output_get_asm(output);
+		for (TB_Assembly *node = assembly; node != nullptr; node = node->next) {
+			gb_printf_err("%.*s", cast(int)node->length, node->data);
+		}
+		gb_printf_err("\n");
+	}
 }
 }
 
 
 gb_internal void cg_procedure_generate(cgProcedure *p) {
 gb_internal void cg_procedure_generate(cgProcedure *p) {
@@ -332,7 +345,9 @@ gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr) {
 	cgValue res = cg_build_call_expr_internal(p, expr);
 	cgValue res = cg_build_call_expr_internal(p, expr);
 
 
 	if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
 	if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
-		GB_PANIC("Handle optional_ok_one");
+		GB_ASSERT(res.kind == cgValue_Multi);
+		GB_ASSERT(res.multi->values.count == 2);
+		return res.multi->values[0];
 		// GB_ASSERT(is_type_tuple(res.type));
 		// GB_ASSERT(is_type_tuple(res.type));
 		// GB_ASSERT(res.type->Tuple.variables.count == 2);
 		// GB_ASSERT(res.type->Tuple.variables.count == 2);
 		// return cg_emit_struct_ev(p, res, 0);
 		// return cg_emit_struct_ev(p, res, 0);
@@ -345,18 +360,111 @@ gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue>
 		value = cg_value(tb_inst_get_symbol_address(p->func, value.symbol), value.type);
 		value = cg_value(tb_inst_get_symbol_address(p->func, value.symbol), value.type);
 	}
 	}
 	GB_ASSERT(value.kind == cgValue_Value);
 	GB_ASSERT(value.kind == cgValue_Value);
+	TEMPORARY_ALLOCATOR_GUARD();
+
+	TB_Module *m = p->module->mod;
 
 
-	// TODO(bill): abstract out the function prototype stuff so that you handle the ABI correctly (at least for win64 at the moment)
-	TB_FunctionPrototype *proto = cg_procedure_type_as_prototype(p->module, value.type);
+
+	Type *type = base_type(value.type);
+	GB_ASSERT(type->kind == Type_Proc);
+	TypeProc *pt = &type->Proc;
+	gb_unused(pt);
+
+	TB_FunctionPrototype *proto = cg_procedure_type_as_prototype(p->module, type);
 	TB_Node *target = value.node;
 	TB_Node *target = value.node;
-	auto params = slice_make<TB_Node *>(temporary_allocator(), 0 /*proto->param_count*/);
-	for_array(i, params) {
-		// params[i] = proto
+	auto params = slice_make<TB_Node *>(temporary_allocator(), proto->param_count);
+
+
+	GB_ASSERT(build_context.metrics.os == TargetOs_windows);
+	// TODO(bill): Support more than Win64 ABI
+
+	bool is_odin_like_cc = is_calling_convention_odin(pt->calling_convention);
+	GB_ASSERT(is_odin_like_cc);
+
+	bool return_is_indirect = false;
+
+	isize param_index = 0;
+	if (pt->result_count != 0) {
+		Type *last_result = pt->results->Tuple.variables[pt->results->Tuple.variables.count-1]->type;
+
+		TB_DebugType *dbg = cg_debug_type(p->module, last_result);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(m, dbg, true);
+		if (rule == TB_PASSING_INDIRECT) {
+			return_is_indirect = true;
+			TB_CharUnits size = cast(TB_CharUnits)type_size_of(last_result);
+			TB_CharUnits align = cast(TB_CharUnits)type_align_of(last_result);
+			params[param_index++] = tb_inst_local(p->func, size, align);
+		}
+	}
+	for (cgValue arg : args) {
+		Type *param_type = pt->params->Tuple.variables[param_index]->type;
+		arg = cg_emit_conv(p, arg, param_type);
+		arg = cg_flatten_value(p, arg);
+
+		TB_Node *param = nullptr;
+
+		TB_DebugType *dbg = cg_debug_type(p->module, param_type);
+		TB_PassingRule rule = tb_get_passing_rule_from_dbg(m, dbg, false);
+		switch (rule) {
+		case TB_PASSING_DIRECT:
+			GB_ASSERT(arg.kind == cgValue_Value);
+			param = arg.node;
+			break;
+		case TB_PASSING_INDIRECT:
+			{
+				// indirect
+				cgValue arg_ptr = cg_address_from_load_or_generate_local(p, arg);
+				GB_ASSERT(arg_ptr.kind == cgValue_Value);
+				param = arg_ptr.node;
+			}
+			break;
+		case TB_PASSING_IGNORE:
+			continue;
+		}
+
+		params[param_index++] = param;
+	}
+
+	// Split returns
+	for (isize i = 0; i < pt->result_count-1; i++) {
+		Type *result = pt->results->Tuple.variables[i]->type;
+		TB_CharUnits size = cast(TB_CharUnits)type_size_of(result);
+		TB_CharUnits align = cast(TB_CharUnits)type_align_of(result);
+		params[param_index++] = tb_inst_local(p->func, size, align);
+	}
+
+	if (pt->calling_convention == ProcCC_Odin) {
+		cgValue ctx_ptr = cg_find_or_generate_context_ptr(p).addr;
+		GB_ASSERT(ctx_ptr.kind == cgValue_Value);
+		params[param_index++] = ctx_ptr.node;
+	}
+	GB_ASSERT_MSG(param_index == params.count, "%td vs %td\n %s %u %u",
+	              param_index, params.count,
+	              type_to_string(type),
+	              proto->return_count,
+	              proto->param_count);
+
+	for (TB_Node *param : params) {
+		GB_ASSERT(param != nullptr);
 	}
 	}
 
 
 	GB_ASSERT(target != nullptr);
 	GB_ASSERT(target != nullptr);
 	TB_MultiOutput multi_output = tb_inst_call(p->func, proto, target, params.count, params.data);
 	TB_MultiOutput multi_output = tb_inst_call(p->func, proto, target, params.count, params.data);
 	gb_unused(multi_output);
 	gb_unused(multi_output);
+
+	switch (pt->result_count) {
+	case 0:
+		return {};
+	case 1:
+		if (return_is_indirect) {
+			return cg_lvalue_addr(params[0], pt->results->Tuple.variables[0]->type);
+		}
+		GB_ASSERT(multi_output.count == 1);
+		return cg_value(multi_output.single, pt->results->Tuple.variables[0]->type);
+	}
+
+
+
 	return {};
 	return {};
 }
 }
 
 

+ 39 - 1
src/tilde_stmt.cpp

@@ -252,7 +252,45 @@ gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value) {
 	} else if (addr.kind == cgAddr_Map) {
 	} else if (addr.kind == cgAddr_Map) {
 		GB_PANIC("TODO(bill): cgAddr_Map");
 		GB_PANIC("TODO(bill): cgAddr_Map");
 	} else if (addr.kind == cgAddr_Context) {
 	} else if (addr.kind == cgAddr_Context) {
-		GB_PANIC("TODO(bill): cgAddr_Context");
+		cgAddr old_addr = cg_find_or_generate_context_ptr(p);
+
+		bool create_new = true;
+		for_array(i, p->context_stack) {
+			cgContextData *ctx_data = &p->context_stack[i];
+			if (ctx_data->ctx.addr.node == old_addr.addr.node) {
+				if (ctx_data->uses > 0) {
+					create_new = true;
+				} else if (p->scope_index > ctx_data->scope_index) {
+					create_new = true;
+				} else {
+					// gb_printf_err("%.*s (curr:%td) (ctx:%td) (uses:%td)\n", LIT(p->name), p->scope_index, ctx_data->scope_index, ctx_data->uses);
+					create_new = false;
+				}
+				break;
+			}
+		}
+
+		cgValue next = {};
+		if (create_new) {
+			cgValue old = cg_addr_load(p, old_addr);
+			cgAddr next_addr = cg_add_local(p, t_context, nullptr, true);
+			cg_addr_store(p, next_addr, old);
+			cg_push_context_onto_stack(p, next_addr);
+			next = next_addr.addr;
+		} else {
+			next = old_addr.addr;
+		}
+
+		if (addr.ctx.sel.index.count > 0) {
+			cgValue lhs = cg_emit_deep_field_gep(p, next, addr.ctx.sel);
+			cgValue rhs = cg_emit_conv(p, value, type_deref(lhs.type));
+			cg_emit_store(p, lhs, rhs);
+		} else {
+			cgValue lhs = next;
+			cgValue rhs = cg_emit_conv(p, value, cg_addr_type(addr));
+			cg_emit_store(p, lhs, rhs);
+		}
+		return;
 	} else if (addr.kind == cgAddr_SoaVariable) {
 	} else if (addr.kind == cgAddr_SoaVariable) {
 		GB_PANIC("TODO(bill): cgAddr_SoaVariable");
 		GB_PANIC("TODO(bill): cgAddr_SoaVariable");
 	} else if (addr.kind == cgAddr_Swizzle) {
 	} else if (addr.kind == cgAddr_Swizzle) {