Browse Source

Begin working on proper expressions

gingerBill 2 years ago
parent
commit
ee8372145d
7 changed files with 630 additions and 80 deletions
  1. BIN
      src/tilde/tb.lib
  2. 131 47
      src/tilde_backend.cpp
  3. 10 1
      src/tilde_backend.hpp
  4. 80 17
      src/tilde_const.cpp
  5. 259 6
      src/tilde_expr.cpp
  6. 81 3
      src/tilde_proc.cpp
  7. 69 6
      src/tilde_stmt.cpp

BIN
src/tilde/tb.lib


+ 131 - 47
src/tilde_backend.cpp

@@ -33,7 +33,7 @@ gb_internal TB_DataType cg_data_type(Type *t) {
 		case Basic_typeid:
 		case Basic_typeid:
 			return TB_TYPE_INTN(cast(u16)(8*sz));
 			return TB_TYPE_INTN(cast(u16)(8*sz));
 
 
-		case Basic_f16: return TB_TYPE_I16;
+		case Basic_f16: return TB_TYPE_F16;
 		case Basic_f32: return TB_TYPE_F32;
 		case Basic_f32: return TB_TYPE_F32;
 		case Basic_f64: return TB_TYPE_F64;
 		case Basic_f64: return TB_TYPE_F64;
 
 
@@ -60,11 +60,11 @@ gb_internal TB_DataType cg_data_type(Type *t) {
 		case Basic_u128be:
 		case Basic_u128be:
 			return TB_TYPE_INTN(cast(u16)(8*sz));
 			return TB_TYPE_INTN(cast(u16)(8*sz));
 
 
-		case Basic_f16le: return TB_TYPE_I16;
+		case Basic_f16le: return TB_TYPE_F16;
 		case Basic_f32le: return TB_TYPE_F32;
 		case Basic_f32le: return TB_TYPE_F32;
 		case Basic_f64le: return TB_TYPE_F64;
 		case Basic_f64le: return TB_TYPE_F64;
 
 
-		case Basic_f16be: return TB_TYPE_I16;
+		case Basic_f16be: return TB_TYPE_F16;
 		case Basic_f32be: return TB_TYPE_F32;
 		case Basic_f32be: return TB_TYPE_F32;
 		case Basic_f64be: return TB_TYPE_F64;
 		case Basic_f64be: return TB_TYPE_F64;
 		}
 		}
@@ -173,9 +173,128 @@ gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_
 	return -1;
 	return -1;
 }
 }
 
 
-gb_internal void cg_create_global_variables(cgModule *m) {
+struct cgGlobalVariable {
+	cgValue var;
+	cgValue init;
+	DeclInfo *decl;
+	bool is_initialized;
+};
+
+// Returns already_has_entry_point
+gb_internal bool cg_global_variables_create(cgModule *m) {
+	isize global_variable_max_count = 0;
+	bool already_has_entry_point = false;
+
+	for (Entity *e : m->info->entities) {
+		String name = e->token.string;
+
+		if (e->kind == Entity_Variable) {
+			global_variable_max_count++;
+		} else if (e->kind == Entity_Procedure) {
+			if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
+				GB_ASSERT(e == m->info->entry_point);
+			}
+			if (build_context.command_kind == Command_test &&
+			    (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+				String link_name = e->Procedure.link_name;
+				if (e->pkg->kind == Package_Runtime) {
+					if (link_name == "main"           ||
+					    link_name == "DllMain"        ||
+					    link_name == "WinMain"        ||
+					    link_name == "wWinMain"       ||
+					    link_name == "mainCRTStartup" ||
+					    link_name == "_start") {
+						already_has_entry_point = true;
+					}
+				}
+			}
+		}
+	}
+	auto global_variables = array_make<cgGlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
+
+	auto *min_dep_set = &m->info->minimum_dependency_set;
+
+	for (DeclInfo *d : m->info->variable_init_order) {
+		Entity *e = d->entity;
+
+		if ((e->scope->flags & ScopeFlag_File) == 0) {
+			continue;
+		}
+
+		if (!ptr_set_exists(min_dep_set, e)) {
+			continue;
+		}
+
+		DeclInfo *decl = decl_info_of_entity(e);
+		if (decl == nullptr) {
+			continue;
+		}
+		GB_ASSERT(e->kind == Entity_Variable);
+
+		bool is_foreign = e->Variable.is_foreign;
+		bool is_export  = e->Variable.is_export;
+
+		String name = cg_get_entity_name(m, e);
+
+		TB_Linkage linkage = TB_LINKAGE_PRIVATE;
+
+		if (is_foreign) {
+			linkage = TB_LINKAGE_PUBLIC;
+			// lb_add_foreign_library_path(m, e->Variable.foreign_library);
+			// lb_set_wasm_import_attributes(g.value, e, name);
+		} else if (is_export) {
+			linkage = TB_LINKAGE_PUBLIC;
+		}
+		// lb_set_linkage_from_entity_flags(m, g.value, e->flags);
+
+		TB_DebugType *debug_type = cg_debug_type(m, e->type);
+		TB_Global *global = tb_global_create(m->mod, name.len, cast(char const *)name.text, debug_type, linkage);
+		cgValue g = cg_value(global, alloc_type_pointer(e->type));
+
+		TB_ModuleSection *section = tb_module_get_data(m->mod);
+
+		if (e->Variable.thread_local_model != "") {
+			section = tb_module_get_tls(m->mod);
+		}
+		if (e->Variable.link_section.len > 0) {
+			// TODO(bill): custom module sections
+			// LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
+		}
+
+		size_t max_objects = 0;
+		tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_objects);
+
+		cgGlobalVariable var = {};
+		var.var = g;
+		var.decl = decl;
+
+		if (decl->init_expr != nullptr) {
+			// TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
+			// if (!is_type_any(e->type) && !is_type_union(e->type)) {
+			// 	if (tav.mode != Addressing_Invalid) {
+			// 		if (tav.value.kind != ExactValue_Invalid) {
+			// 			ExactValue v = tav.value;
+			// 			lbValue init = lb_const_value(m, tav.type, v);
+			// 			LLVMSetInitializer(g.value, init.value);
+			// 			var.is_initialized = true;
+			// 		}
+			// 	}
+			// }
+			// if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
+			// 	var.is_initialized = true;
+			// }
+		}
+
+		array_add(&global_variables, var);
+
+		cg_add_entity(m, e, g);
+		cg_add_member(m, name, g);
+	}
+
+
+
 	if (build_context.no_rtti) {
 	if (build_context.no_rtti) {
-		return;
+		return already_has_entry_point;
 	}
 	}
 
 
 	CheckerInfo *info = m->info;
 	CheckerInfo *info = m->info;
@@ -256,9 +375,11 @@ gb_internal void cg_create_global_variables(cgModule *m) {
 			}
 			}
 		}
 		}
 	}
 	}
+
+	return already_has_entry_point;
 }
 }
 
 
-cgModule *cg_module_create(Checker *c) {
+gb_internal cgModule *cg_module_create(Checker *c) {
 	cgModule *m = gb_alloc_item(permanent_allocator(), cgModule);
 	cgModule *m = gb_alloc_item(permanent_allocator(), cgModule);
 
 
 	m->checker = c;
 	m->checker = c;
@@ -286,7 +407,7 @@ cgModule *cg_module_create(Checker *c) {
 	return m;
 	return m;
 }
 }
 
 
-void cg_module_destroy(cgModule *m) {
+gb_internal void cg_module_destroy(cgModule *m) {
 	map_destroy(&m->values);
 	map_destroy(&m->values);
 	array_free(&m->procedures_to_generate);
 	array_free(&m->procedures_to_generate);
 	map_destroy(&m->file_id_map);
 	map_destroy(&m->file_id_map);
@@ -391,7 +512,7 @@ gb_internal String cg_mangle_name(cgModule *m, Entity *e) {
 	return mangled_name;
 	return mangled_name;
 }
 }
 
 
-String cg_get_entity_name(cgModule *m, Entity *e) {
+gb_internal String cg_get_entity_name(cgModule *m, Entity *e) {
 	if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
 	if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
 		return e->TypeName.ir_mangled_name;
 		return e->TypeName.ir_mangled_name;
 	}
 	}
@@ -438,14 +559,6 @@ String cg_get_entity_name(cgModule *m, Entity *e) {
 	return name;
 	return name;
 }
 }
 
 
-
-struct cgGlobalVariable {
-	cgValue var;
-	cgValue init;
-	DeclInfo *decl;
-	bool is_initialized;
-};
-
 #include "tilde_const.cpp"
 #include "tilde_const.cpp"
 #include "tilde_expr.cpp"
 #include "tilde_expr.cpp"
 #include "tilde_proc.cpp"
 #include "tilde_proc.cpp"
@@ -463,37 +576,8 @@ gb_internal bool cg_generate_code(Checker *c) {
 
 
 	TIME_SECTION("Tilde Global Variables");
 	TIME_SECTION("Tilde Global Variables");
 
 
-	cg_create_global_variables(m);
-
-	// isize global_variable_max_count = 0;
-	// bool already_has_entry_point = false;
-
-	// for (Entity *e : info->entities) {
-	// 	String name = e->token.string;
-
-	// 	if (e->kind == Entity_Variable) {
-	// 		global_variable_max_count++;
-	// 	} else if (e->kind == Entity_Procedure) {
-	// 		if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
-	// 			GB_ASSERT(e == info->entry_point);
-	// 		}
-	// 		if (build_context.command_kind == Command_test &&
-	// 		    (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
-	// 			String link_name = e->Procedure.link_name;
-	// 			if (e->pkg->kind == Package_Runtime) {
-	// 				if (link_name == "main"           ||
-	// 				    link_name == "DllMain"        ||
-	// 				    link_name == "WinMain"        ||
-	// 				    link_name == "wWinMain"       ||
-	// 				    link_name == "mainCRTStartup" ||
-	// 				    link_name == "_start") {
-	// 					already_has_entry_point = true;
-	// 				}
-	// 			}
-	// 		}
-	// 	}
-	// }
-	// auto global_variables = array_make<cgGlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
+	bool already_has_entry_point = cg_global_variables_create(m);
+	gb_unused(already_has_entry_point);
 
 
 	if (true) {
 	if (true) {
 		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
 		Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);

+ 10 - 1
src/tilde_backend.hpp

@@ -7,6 +7,7 @@
 
 
 #include "tilde/tb.h"
 #include "tilde/tb.h"
 
 
+#define TB_TYPE_F16    TB_DataType{ { TB_INT, 0, 16 } }
 #define TB_TYPE_I128   TB_DataType{ { TB_INT, 0, 128 } }
 #define TB_TYPE_I128   TB_DataType{ { TB_INT, 0, 128 } }
 #define TB_TYPE_INT    TB_TYPE_INTN(cast(u16)build_context.int_size)
 #define TB_TYPE_INT    TB_TYPE_INTN(cast(u16)build_context.int_size)
 #define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)build_context.ptr_size)
 #define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)build_context.ptr_size)
@@ -127,6 +128,7 @@ struct cgProcedure {
 	Array<cgProcedure *> children;
 	Array<cgProcedure *> children;
 
 
 	TB_Function *func;
 	TB_Function *func;
+	TB_FunctionPrototype *proto;
 	TB_Symbol *symbol;
 	TB_Symbol *symbol;
 
 
 	Entity *  entity;
 	Entity *  entity;
@@ -151,9 +153,12 @@ struct cgProcedure {
 
 
 	Scope *curr_scope;
 	Scope *curr_scope;
 	i32    scope_index;
 	i32    scope_index;
+	bool   in_multi_assignment;
 
 
 	Array<Scope *>       scope_stack;
 	Array<Scope *>       scope_stack;
 	Array<cgContextData> context_stack;
 	Array<cgContextData> context_stack;
+
+	PtrMap<Entity *, cgAddr> variable_map;
 };
 };
 
 
 
 
@@ -226,4 +231,8 @@ gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr);
 
 
 gb_internal cgValue cg_find_procedure_value_from_entity(cgModule *m, Entity *e);
 gb_internal cgValue cg_find_procedure_value_from_entity(cgModule *m, Entity *e);
 
 
-gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type);
+gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type);
+
+gb_internal String cg_get_entity_name(cgModule *m, Entity *e);
+
+gb_internal cgValue cg_typeid(cgModule *m, Type *t);

+ 80 - 17
src/tilde_const.cpp

@@ -1,19 +1,31 @@
-gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type) {
+gb_internal bool cg_is_expr_constant_zero(Ast *expr) {
+	GB_ASSERT(expr != nullptr);
+	auto v = exact_value_to_integer(expr->tav.value);
+	if (v.kind == ExactValue_Integer) {
+		return big_int_cmp_zero(&v.value_integer) == 0;
+	}
+	return false;
+}
+
+gb_internal cgValue cg_const_nil(cgModule *m, cgProcedure *p, Type *type) {
 	Type *original_type = type;
 	Type *original_type = type;
 	type = core_type(type);
 	type = core_type(type);
 	i64 size = type_size_of(type);
 	i64 size = type_size_of(type);
 	i64 align = type_align_of(type);
 	i64 align = type_align_of(type);
 	TB_DataType dt = cg_data_type(type);
 	TB_DataType dt = cg_data_type(type);
 	if (TB_IS_VOID_TYPE(dt)) {
 	if (TB_IS_VOID_TYPE(dt)) {
-		TB_Module *m = p->module->mod;
 		char name[32] = {};
 		char name[32] = {};
-		gb_snprintf(name, 31, "cnil$%u", 1+p->module->const_nil_guid.fetch_add(1));
-		TB_Global *global = tb_global_create(m, -1, name, nullptr, TB_LINKAGE_PRIVATE);
-		tb_global_set_storage(m, tb_module_get_rdata(m), global, size, align, 0);
+		gb_snprintf(name, 31, "cnil$%u", 1+m->const_nil_guid.fetch_add(1));
+		TB_Global *global = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
+		tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), global, size, align, 0);
 
 
 		TB_Symbol *symbol = cast(TB_Symbol *)global;
 		TB_Symbol *symbol = cast(TB_Symbol *)global;
-		TB_Node *node = tb_inst_get_symbol_address(p->func, symbol);
-		return cg_lvalue_addr(node, type);
+		if (p) {
+			TB_Node *node = tb_inst_get_symbol_address(p->func, symbol);
+			return cg_lvalue_addr(node, type);
+		} else {
+			GB_PANIC("TODO(bill): cg_const_nil");
+		}
 	}
 	}
 
 
 	if (is_type_internally_pointer_like(type)) {
 	if (is_type_internally_pointer_like(type)) {
@@ -34,24 +46,71 @@ gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type) {
 	return {};
 	return {};
 }
 }
 
 
-gb_internal cgValue cg_const_value(cgModule *m, cgProcedure *p, Type *type, ExactValue const &value) {
+gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type) {
+	return cg_const_nil(p->module, p, type);
+}
+
+gb_internal cgValue cg_const_value(cgModule *m, cgProcedure *p, Type *type, ExactValue const &value, bool allow_local = true) {
 	TB_Node *node = nullptr;
 	TB_Node *node = nullptr;
 
 
-	if (value.kind == ExactValue_Invalid) {
+	bool is_local = allow_local && p != nullptr;
+	gb_unused(is_local);
+
+	TB_DataType dt = cg_data_type(type);
+
+	switch (value.kind) {
+	case ExactValue_Invalid:
 		return cg_const_nil(p, type);
 		return cg_const_nil(p, type);
+
+	case ExactValue_Typeid:
+		return cg_typeid(m, value.value_typeid);
+
+	case ExactValue_Procedure:
+		{
+			Ast *expr = unparen_expr(value.value_procedure);
+			Entity *e = entity_of_node(expr);
+			if (e != nullptr) {
+				cgValue found = cg_find_procedure_value_from_entity(m, e);
+				GB_ASSERT(are_types_identical(type, found.type));
+				return found;
+			}
+			GB_PANIC("TODO(bill): cg_const_value ExactValue_Procedure");
+		}
+		break;
 	}
 	}
 
 
-	if (value.kind == ExactValue_Procedure) {
-		Ast *expr = unparen_expr(value.value_procedure);
-		Entity *e = entity_of_node(expr);
-		if (e != nullptr) {
-			cgValue found = cg_find_procedure_value_from_entity(m, e);
-			GB_ASSERT(are_types_identical(type, found.type));
-			return found;
+	GB_ASSERT(!TB_IS_VOID_TYPE(dt));
+
+	switch (value.kind) {
+	case ExactValue_Bool:
+		return cg_value(tb_inst_uint(p->func, dt, value.value_bool), type);
+
+	case ExactValue_Integer:
+		GB_ASSERT(dt.raw != TB_TYPE_I128.raw);
+		if (is_type_unsigned(type)) {
+			u64 i = exact_value_to_u64(value);
+			return cg_value(tb_inst_uint(p->func, dt, i), type);
+		} else {
+			i64 i = exact_value_to_i64(value);
+			return cg_value(tb_inst_sint(p->func, dt, i), type);
 		}
 		}
+		break;
 
 
+	case ExactValue_Float:
+		GB_ASSERT(dt.raw != TB_TYPE_F16.raw);
+		GB_ASSERT(!is_type_different_to_arch_endianness(type));
+		{
+			f64 f = exact_value_to_f64(value);
+			if (type_size_of(type) == 8) {
+				return cg_value(tb_inst_float64(p->func, f), type);
+			} else {
+				return cg_value(tb_inst_float32(p->func, cast(f32)f), type);
+			}
+		}
+		break;
 	}
 	}
 
 
+
 	GB_ASSERT(node != nullptr);
 	GB_ASSERT(node != nullptr);
 	return cg_value(node, type);
 	return cg_value(node, type);
 }
 }
@@ -59,4 +118,8 @@ gb_internal cgValue cg_const_value(cgModule *m, cgProcedure *p, Type *type, Exac
 gb_internal cgValue cg_const_value(cgProcedure *p, Type *type, ExactValue const &value) {
 gb_internal cgValue cg_const_value(cgProcedure *p, Type *type, ExactValue const &value) {
 	GB_ASSERT(p != nullptr);
 	GB_ASSERT(p != nullptr);
 	return cg_const_value(p->module, p, type, value);
 	return cg_const_value(p->module, p, type, value);
-}
+}
+
+gb_internal cgValue cg_const_int(cgProcedure *p, Type *type, i64 i) {
+	return cg_const_value(p, type, exact_value_i64(i));
+}

+ 259 - 6
src/tilde_expr.cpp

@@ -1,3 +1,11 @@
+gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value) {
+	if (value.kind == cgValue_Symbol) {
+		GB_ASSERT(is_type_internally_pointer_like(value.type));
+		value = cg_value(tb_inst_get_symbol_address(p->func, value.symbol), value.type);
+	}
+	return value;
+}
+
 gb_internal cgContextData *cg_push_context_onto_stack(cgProcedure *p, cgAddr ctx) {
 gb_internal cgContextData *cg_push_context_onto_stack(cgProcedure *p, cgAddr ctx) {
 	ctx.kind = cgAddr_Context;
 	ctx.kind = cgAddr_Context;
 	cgContextData *cd = array_add_and_get(&p->context_stack);
 	cgContextData *cd = array_add_and_get(&p->context_stack);
@@ -43,7 +51,7 @@ gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e) {
 		return *found;
 		return *found;
 	}
 	}
 
 
-	// GB_PANIC("\n\tError in: %s, missing value '%.*s'\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
+	GB_PANIC("\n\tError in: %s, missing value '%.*s'\n", token_pos_to_string(e->token.pos), LIT(e->token.string));
 	return {};
 	return {};
 }
 }
 
 
@@ -57,6 +65,10 @@ gb_internal cgAddr cg_build_addr_from_entity(cgProcedure *p, Entity *e, Ast *exp
 		return {};
 		return {};
 	}
 	}
 
 
+	cgAddr *local_found = map_get(&p->variable_map, e);
+	if (local_found) {
+		return *local_found;
+	}
 
 
 	cgValue v = {};
 	cgValue v = {};
 
 
@@ -78,7 +90,9 @@ gb_internal cgAddr cg_build_addr_from_entity(cgProcedure *p, Entity *e, Ast *exp
 
 
 
 
 	if (v.node == nullptr) {
 	if (v.node == nullptr) {
-		return cg_addr(cg_find_value_from_entity(m, e));
+		cgValue v = cg_find_value_from_entity(m, e);
+		v = cg_flatten_value(p, v);
+		return cg_addr(v);
 	}
 	}
 
 
 	return cg_addr(v);
 	return cg_addr(v);
@@ -90,6 +104,17 @@ gb_internal cgValue cg_typeid(cgModule *m, Type *t) {
 }
 }
 
 
 
 
+gb_internal cgValue cg_correct_endianness(cgProcedure *p, cgValue value) {
+	Type *src = core_type(value.type);
+	GB_ASSERT(is_type_integer(src) || is_type_float(src));
+	if (is_type_different_to_arch_endianness(src)) {
+		GB_PANIC("TODO(bill): cg_correct_endianness");
+		// Type *platform_src_type = integer_endian_type_to_platform_type(src);
+		// value = cg_emit_byte_swap(p, value, platform_src_type);
+	}
+	return value;
+}
+
 gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *type) {
 gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *type) {
 	// TODO(bill): cg_emit_conv
 	// TODO(bill): cg_emit_conv
 	return value;
 	return value;
@@ -98,10 +123,7 @@ gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *type) {
 gb_internal cgValue cg_emit_transmute(cgProcedure *p, cgValue value, Type *type) {
 gb_internal cgValue cg_emit_transmute(cgProcedure *p, cgValue value, Type *type) {
 	GB_ASSERT(type_size_of(value.type) == type_size_of(type));
 	GB_ASSERT(type_size_of(value.type) == type_size_of(type));
 
 
-	if (value.kind == cgValue_Symbol) {
-		GB_ASSERT(is_type_pointer(value.type));
-		value = cg_value(tb_inst_get_symbol_address(p->func, value.symbol), type);
-	}
+	value = cg_flatten_value(p, value);
 
 
 	i64 src_align = type_align_of(value.type);
 	i64 src_align = type_align_of(value.type);
 	i64 dst_align = type_align_of(type);
 	i64 dst_align = type_align_of(type);
@@ -133,6 +155,217 @@ gb_internal cgValue cg_emit_transmute(cgProcedure *p, cgValue value, Type *type)
 }
 }
 
 
 
 
+gb_internal cgAddr cg_build_addr_slice_expr(cgProcedure *p, Ast *expr) {
+	ast_node(se, SliceExpr, expr);
+
+	cgValue low  = cg_const_int(p, t_int, 0);
+	cgValue high = {};
+
+	if (se->low  != nullptr) {
+		low = cg_correct_endianness(p, cg_build_expr(p, se->low));
+	}
+	if (se->high != nullptr) {
+		high = cg_correct_endianness(p, cg_build_expr(p, se->high));
+	}
+
+	bool no_indices = se->low == nullptr && se->high == nullptr;
+	gb_unused(no_indices);
+
+	cgAddr addr = cg_build_addr(p, se->expr);
+	cgValue base = cg_addr_load(p, addr);
+	Type *type = base_type(base.type);
+
+	if (is_type_pointer(type)) {
+		type = base_type(type_deref(type));
+		addr = cg_addr(base);
+		base = cg_addr_load(p, addr);
+	}
+
+	switch (type->kind) {
+	case Type_Slice: {
+		// Type *slice_type = type;
+		// cgValue len = cg_slice_len(p, base);
+		// if (high.value == nullptr) high = len;
+
+		// if (!no_indices) {
+		// 	cg_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// }
+
+		// cgValue elem    = cg_emit_ptr_offset(p, cg_slice_elem(p, base), low);
+		// cgValue new_len = cg_emit_arith(p, Token_Sub, high, low, t_int);
+
+		// cgAddr slice = cg_add_local_generated(p, slice_type, false);
+		// cg_fill_slice(p, slice, elem, new_len);
+		// return slice;
+		GB_PANIC("cg_build_addr_slice_expr Type_Slice");
+		break;
+	}
+
+	case Type_RelativeSlice:
+		GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the cg_addr_load");
+		break;
+
+	case Type_DynamicArray: {
+		// Type *elem_type = type->DynamicArray.elem;
+		// Type *slice_type = alloc_type_slice(elem_type);
+
+		// lbValue len = lb_dynamic_array_len(p, base);
+		// if (high.value == nullptr) high = len;
+
+		// if (!no_indices) {
+		// 	lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// }
+
+		// lbValue elem    = lb_emit_ptr_offset(p, lb_dynamic_array_elem(p, base), low);
+		// lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+		// lbAddr slice = lb_add_local_generated(p, slice_type, false);
+		// lb_fill_slice(p, slice, elem, new_len);
+		// return slice;
+		GB_PANIC("cg_build_addr_slice_expr Type_DynamicArray");
+		break;
+	}
+
+	case Type_MultiPointer: {
+		// lbAddr res = lb_add_local_generated(p, type_of_expr(expr), false);
+		// if (se->high == nullptr) {
+		// 	lbValue offset = base;
+		// 	LLVMValueRef indices[1] = {low.value};
+		// 	offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, "");
+		// 	lb_addr_store(p, res, offset);
+		// } else {
+		// 	low = lb_emit_conv(p, low, t_int);
+		// 	high = lb_emit_conv(p, high, t_int);
+
+		// 	lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
+
+		// 	LLVMValueRef indices[1] = {low.value};
+		// 	LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, "");
+		// 	LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
+
+		// 	LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
+		// 	LLVMValueRef gep1 = lb_emit_struct_ep(p, res.addr, 1).value;
+		// 	LLVMBuildStore(p->builder, ptr, gep0);
+		// 	LLVMBuildStore(p->builder, len, gep1);
+		// }
+		// return res;
+		GB_PANIC("cg_build_addr_slice_expr Type_MultiPointer");
+		break;
+	}
+
+	case Type_Array: {
+		// Type *slice_type = alloc_type_slice(type->Array.elem);
+		// lbValue len = lb_const_int(p->module, t_int, type->Array.count);
+
+		// if (high.value == nullptr) high = len;
+
+		// bool low_const  = type_and_value_of_expr(se->low).mode  == Addressing_Constant;
+		// bool high_const = type_and_value_of_expr(se->high).mode == Addressing_Constant;
+
+		// if (!low_const || !high_const) {
+		// 	if (!no_indices) {
+		// 		lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// 	}
+		// }
+		// lbValue elem    = lb_emit_ptr_offset(p, lb_array_elem(p, lb_addr_get_ptr(p, addr)), low);
+		// lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+		// lbAddr slice = lb_add_local_generated(p, slice_type, false);
+		// lb_fill_slice(p, slice, elem, new_len);
+		// return slice;
+		GB_PANIC("cg_build_addr_slice_expr Type_Array");
+		break;
+	}
+
+	case Type_Basic: {
+		// GB_ASSERT(type == t_string);
+		// lbValue len = lb_string_len(p, base);
+		// if (high.value == nullptr) high = len;
+
+		// if (!no_indices) {
+		// 	lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// }
+
+		// lbValue elem    = lb_emit_ptr_offset(p, lb_string_elem(p, base), low);
+		// lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+
+		// lbAddr str = lb_add_local_generated(p, t_string, false);
+		// lb_fill_string(p, str, elem, new_len);
+		// return str;
+		GB_PANIC("cg_build_addr_slice_expr Type_Basic");
+		break;
+	}
+
+
+	case Type_Struct:
+		// if (is_type_soa_struct(type)) {
+		// 	lbValue len = lb_soa_struct_len(p, lb_addr_get_ptr(p, addr));
+		// 	if (high.value == nullptr) high = len;
+
+		// 	if (!no_indices) {
+		// 		lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr);
+		// 	}
+		// 	#if 1
+
+		// 	lbAddr dst = lb_add_local_generated(p, type_of_expr(expr), true);
+		// 	if (type->Struct.soa_kind == StructSoa_Fixed) {
+		// 		i32 field_count = cast(i32)type->Struct.fields.count;
+		// 		for (i32 i = 0; i < field_count; i++) {
+		// 			lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 			lbValue field_src = lb_emit_struct_ep(p, lb_addr_get_ptr(p, addr), i);
+		// 			field_src = lb_emit_array_ep(p, field_src, low);
+		// 			lb_emit_store(p, field_dst, field_src);
+		// 		}
+
+		// 		lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 		lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 		lb_emit_store(p, len_dst, new_len);
+		// 	} else if (type->Struct.soa_kind == StructSoa_Slice) {
+		// 		if (no_indices) {
+		// 			lb_addr_store(p, dst, base);
+		// 		} else {
+		// 			i32 field_count = cast(i32)type->Struct.fields.count - 1;
+		// 			for (i32 i = 0; i < field_count; i++) {
+		// 				lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 				lbValue field_src = lb_emit_struct_ev(p, base, i);
+		// 				field_src = lb_emit_ptr_offset(p, field_src, low);
+		// 				lb_emit_store(p, field_dst, field_src);
+		// 			}
+
+
+		// 			lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 			lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 			lb_emit_store(p, len_dst, new_len);
+		// 		}
+		// 	} else if (type->Struct.soa_kind == StructSoa_Dynamic) {
+		// 		i32 field_count = cast(i32)type->Struct.fields.count - 3;
+		// 		for (i32 i = 0; i < field_count; i++) {
+		// 			lbValue field_dst = lb_emit_struct_ep(p, dst.addr, i);
+		// 			lbValue field_src = lb_emit_struct_ev(p, base, i);
+		// 			field_src = lb_emit_ptr_offset(p, field_src, low);
+		// 			lb_emit_store(p, field_dst, field_src);
+		// 		}
+
+
+		// 		lbValue len_dst = lb_emit_struct_ep(p, dst.addr, field_count);
+		// 		lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int);
+		// 		lb_emit_store(p, len_dst, new_len);
+		// 	}
+
+		// 	return dst;
+		// 	#endif
+		// }
+		GB_PANIC("cg_build_addr_slice_expr Type_Struct");
+		break;
+
+	}
+
+	GB_PANIC("Unknown slicable type");
+	return {};
+}
+
+
+
 
 
 gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr);
 gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr);
 gb_internal cgValue cg_build_expr(cgProcedure *p, Ast *expr) {
 gb_internal cgValue cg_build_expr(cgProcedure *p, Ast *expr) {
@@ -252,6 +485,11 @@ gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) {
 			return cg_value(cast(TB_Node *)nullptr, e->type);
 			return cg_value(cast(TB_Node *)nullptr, e->type);
 		}
 		}
 		GB_ASSERT(e->kind != Entity_ProcGroup);
 		GB_ASSERT(e->kind != Entity_ProcGroup);
+
+		cgAddr *addr = map_get(&p->variable_map, e);
+		if (addr) {
+			return cg_addr_load(p, *addr);
+		}
 		// return cg_find_ident(p, m, e, expr);
 		// return cg_find_ident(p, m, e, expr);
 		GB_PANIC("TODO: cg_find_ident");
 		GB_PANIC("TODO: cg_find_ident");
 		return {};
 		return {};
@@ -327,6 +565,17 @@ gb_internal cgValue cg_build_expr_internal(cgProcedure *p, Ast *expr) {
 		cgValue value = cg_build_expr(p, ac->expr);
 		cgValue value = cg_build_expr(p, ac->expr);
 		return cg_emit_conv(p, value, type);
 		return cg_emit_conv(p, value, type);
 	case_end;
 	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		if (is_type_slice(type_of_expr(se->expr))) {
+			// NOTE(bill): Quick optimization
+			if (se->high == nullptr &&
+			    (se->low == nullptr || cg_is_expr_constant_zero(se->low))) {
+				return cg_build_expr(p, se->expr);
+			}
+		}
+		return cg_addr_load(p, cg_build_addr(p, expr));
+	case_end;
 	}
 	}
 	GB_PANIC("TODO(bill): cg_build_expr_internal %.*s", LIT(ast_strings[expr->kind]));
 	GB_PANIC("TODO(bill): cg_build_expr_internal %.*s", LIT(ast_strings[expr->kind]));
 	return {};
 	return {};
@@ -384,6 +633,10 @@ gb_internal cgAddr cg_build_addr_internal(cgProcedure *p, Ast *expr) {
 		Entity *e = entity_of_node(expr);
 		Entity *e = entity_of_node(expr);
 		return cg_build_addr_from_entity(p, e, expr);
 		return cg_build_addr_from_entity(p, e, expr);
 	case_end;
 	case_end;
+
+	case_ast_node(se, SliceExpr, expr);
+		return cg_build_addr_slice_expr(p, expr);
+	case_end;
 	}
 	}
 
 
 	TokenPos token_pos = ast_token(expr).pos;
 	TokenPos token_pos = ast_token(expr).pos;

+ 81 - 3
src/tilde_proc.cpp

@@ -113,7 +113,7 @@ gb_internal TB_FunctionPrototype *cg_procedure_type_as_prototype(cgModule *m, Ty
 			}
 			}
 		}
 		}
 
 
-		if (param.dt.width != 0) {
+		if (param.dt.raw != 0) {
 			if (is_blank_ident(e->token)) {
 			if (is_blank_ident(e->token)) {
 				param.name = alloc_cstring(temporary_allocator(), e->token.string);
 				param.name = alloc_cstring(temporary_allocator(), e->token.string);
 			}
 			}
@@ -183,6 +183,7 @@ gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool i
 	// p->branch_blocks.allocator = a;
 	// p->branch_blocks.allocator = a;
 	p->context_stack.allocator = a;
 	p->context_stack.allocator = a;
 	p->scope_stack.allocator   = a;
 	p->scope_stack.allocator   = a;
+	map_init(&p->variable_map);
 	// map_init(&p->tuple_fix_map, 0);
 	// map_init(&p->tuple_fix_map, 0);
 
 
 	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
 	TB_Linkage linkage = TB_LINKAGE_PRIVATE;
@@ -196,8 +197,10 @@ 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);
-		tb_function_set_prototype(p->func, cg_procedure_type_as_prototype(m, p->type), tb_default_arena());
+		p->proto = cg_procedure_type_as_prototype(m, p->type);
+		tb_function_set_prototype(p->func, p->proto, arena);
 		p->symbol = cast(TB_Symbol *)p->func;
 		p->symbol = cast(TB_Symbol *)p->func;
 	}
 	}
 
 
@@ -235,13 +238,16 @@ gb_internal cgProcedure *cg_procedure_create_dummy(cgModule *m, String const &li
 	// p->branch_blocks.allocator = a;
 	// p->branch_blocks.allocator = a;
 	p->scope_stack.allocator = a;
 	p->scope_stack.allocator = a;
 	p->context_stack.allocator = a;
 	p->context_stack.allocator = a;
+	map_init(&p->variable_map);
 	// map_init(&p->tuple_fix_map, 0);
 	// map_init(&p->tuple_fix_map, 0);
 
 
 
 
 	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);
-	tb_function_set_prototype(p->func, cg_procedure_type_as_prototype(m, p->type), tb_default_arena());
+	p->proto = cg_procedure_type_as_prototype(m, p->type);
+	tb_function_set_prototype(p->func, p->proto, arena);
 	p->symbol = cast(TB_Symbol *)p->func;
 	p->symbol = cast(TB_Symbol *)p->func;
 
 
 	cgValue proc_value = cg_value(p->symbol, p->type);
 	cgValue proc_value = cg_value(p->symbol, p->type);
@@ -255,6 +261,78 @@ gb_internal void cg_procedure_begin(cgProcedure *p) {
 	if (p == nullptr || p->func == nullptr) {
 	if (p == nullptr || p->func == nullptr) {
 		return;
 		return;
 	}
 	}
+
+	if (p->body == nullptr) {
+		return;
+	}
+
+	GB_ASSERT(p->type->kind == Type_Proc);
+	TypeProc *pt = &p->type->Proc;
+	if (pt->params) {
+		int param_index = 0;
+		for (Entity *e : pt->params->Tuple.variables) {
+			if (e->kind != Entity_Variable) {
+				continue;
+			}
+
+			if (param_index >= p->proto->param_count) {
+				break;
+			}
+
+			TB_Node *ptr = tb_inst_param_addr(p->func, param_index);
+			cgValue local = cg_value(ptr, alloc_type_pointer(e->type));
+
+			if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+				// NOTE(bill): for debugging purposes only
+				String name = e->token.string;
+				TB_DebugType *debug_type = cg_debug_type(p->module, e->type);
+				tb_node_append_attrib(ptr, tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type));
+
+			}
+			cgAddr addr = cg_addr(local);
+			if (e) {
+				map_set(&p->variable_map, e, addr);
+			}
+
+			// if (arg_type->kind == lbArg_Ignore) {
+			// 	continue;
+			// } else if (arg_type->kind == lbArg_Direct) {
+			// 	if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+			// 		LLVMTypeRef param_type = lb_type(p->module, e->type);
+			// 		LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index);
+			// 		LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type);
+
+			// 		lbValue param = {};
+			// 		param.value = value;
+			// 		param.type = e->type;
+
+			// 		map_set(&p->direct_parameters, e, param);
+
+			// 		lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+			// 		GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
+			// 		lb_add_entity(p->module, e, ptr);
+
+			// 		lbBlock *block = p->decl_block;
+			// 		if (original_value != value) {
+			// 			block = p->curr_block;
+			// 		}
+			// 		LLVMValueRef debug_storage_value = value;
+			// 		if (original_value != value && LLVMIsALoadInst(value)) {
+			// 			debug_storage_value = LLVMGetOperand(value, 0);
+			// 		}
+			// 		lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block, arg_type->kind);
+			// 	}
+			// } else if (arg_type->kind == lbArg_Indirect) {
+			// 	if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+			// 		lbValue ptr = {};
+			// 		ptr.value = LLVMGetParam(p->value, param_offset+param_index);
+			// 		ptr.type = alloc_type_pointer(e->type);
+			// 		lb_add_entity(p->module, e, ptr);
+			// 		lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block, arg_type->kind);
+			// 	}
+			// }
+		}
+	}
 }
 }
 
 
 gb_internal void cg_procedure_end(cgProcedure *p) {
 gb_internal void cg_procedure_end(cgProcedure *p) {

+ 69 - 6
src/tilde_stmt.cpp

@@ -131,6 +131,9 @@ gb_internal cgValue cg_address_from_load(cgProcedure *p, cgValue value) {
 		GB_PANIC("Symbol is an invalid use case for cg_address_from_load");
 		GB_PANIC("Symbol is an invalid use case for cg_address_from_load");
 		return {};
 		return {};
 	}
 	}
+	GB_PANIC("Invalid cgValue for cg_address_from_load");
+	return {};
+
 }
 }
 
 
 gb_internal bool cg_addr_is_empty(cgAddr const &addr) {
 gb_internal bool cg_addr_is_empty(cgAddr const &addr) {
@@ -174,7 +177,14 @@ gb_internal Type *cg_addr_type(cgAddr const &addr) {
 }
 }
 
 
 gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr) {
 gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr) {
-	GB_PANIC("TODO(bill): cg_addr_load");
+	if (addr.addr.node == nullptr) {
+		return {};
+	}
+	switch (addr.kind) {
+	case cgAddr_Default:
+		return cg_emit_load(p, addr.addr);
+	}
+	GB_PANIC("TODO(bill): cg_addr_load %p", addr.addr.node);
 	return {};
 	return {};
 }
 }
 
 
@@ -285,19 +295,23 @@ gb_internal cgAddr cg_add_local(cgProcedure *p, Type *type, Entity *e, bool zero
 
 
 	if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
 	if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
 		// NOTE(bill): for debugging purposes only
 		// NOTE(bill): for debugging purposes only
-		// String name = e->token.string;
-		// TB_DebugType *debug_type = cg_debug_type(p->module, type);
-		// tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type);
+		String name = e->token.string;
+		TB_DebugType *debug_type = cg_debug_type(p->module, type);
+		tb_node_append_attrib(local, tb_function_attrib_variable(p->func, name.len, cast(char const *)name.text, debug_type));
 	}
 	}
 
 
 	if (zero_init) {
 	if (zero_init) {
 		bool is_volatile = false;
 		bool is_volatile = false;
-		TB_Node *zero  = tb_inst_uint(p->func, TB_TYPE_I8,  0);
+		TB_Node *zero = tb_inst_uint(p->func, TB_TYPE_I8, 0);
 		TB_Node *count = tb_inst_uint(p->func, TB_TYPE_I32, cast(u64)size);
 		TB_Node *count = tb_inst_uint(p->func, TB_TYPE_I32, cast(u64)size);
 		tb_inst_memset(p->func, local, zero, count, alignment, is_volatile);
 		tb_inst_memset(p->func, local, zero, count, alignment, is_volatile);
 	}
 	}
 
 
-	return cg_addr(cg_value(local, alloc_type_pointer(type)));
+	cgAddr addr = cg_addr(cg_value(local, alloc_type_pointer(type)));
+	if (e) {
+		map_set(&p->variable_map, e, addr);
+	}
+	return addr;
 }
 }
 
 
 
 
@@ -313,10 +327,59 @@ gb_internal void cg_emit_defer_stmts(cgProcedure *p, cgDeferExitKind kind, TB_No
 	// TODO(bill): cg_emit_defer_stmts
 	// TODO(bill): cg_emit_defer_stmts
 }
 }
 
 
+
+gb_internal isize cg_append_tuple_values(cgProcedure *p, Array<cgValue> *dst_values, cgValue src_value) {
+	isize init_count = dst_values->count;
+	Type *t = src_value.type;
+	if (t && t->kind == Type_Tuple) {
+		GB_PANIC("TODO(bill): tuple assignments");
+		// cgTupleFix *tf = map_get(&p->tuple_fix_map, src_value.value);
+		// if (tf) {
+		// 	for (cgValue const &value : tf->values) {
+		// 		array_add(dst_values, value);
+		// 	}
+		// } else {
+		// 	for_array(i, t->Tuple.variables) {
+		// 		cgValue v = cg_emit_tuple_ev(p, src_value, cast(i32)i);
+		// 		array_add(dst_values, v);
+		// 	}
+		// }
+	} else {
+		array_add(dst_values, src_value);
+	}
+	return dst_values->count - init_count;
+}
 gb_internal void cg_build_assignment(cgProcedure *p, Array<cgAddr> const &lvals, Slice<Ast *> const &values) {
 gb_internal void cg_build_assignment(cgProcedure *p, Array<cgAddr> const &lvals, Slice<Ast *> const &values) {
 	if (values.count == 0) {
 	if (values.count == 0) {
 		return;
 		return;
 	}
 	}
+
+	auto inits = array_make<cgValue>(permanent_allocator(), 0, lvals.count);
+
+	for (Ast *rhs : values) {
+		cgValue init = cg_build_expr(p, rhs);
+		cg_append_tuple_values(p, &inits, init);
+	}
+
+	bool prev_in_assignment = p->in_multi_assignment;
+
+	isize lval_count = 0;
+	for (cgAddr const &lval : lvals) {
+		if (!cg_addr_is_empty(lval)) {
+			// check if it is not a blank identifier
+			lval_count += 1;
+		}
+	}
+	p->in_multi_assignment = lval_count > 1;
+
+	GB_ASSERT(lvals.count == inits.count);
+	for_array(i, inits) {
+		cgAddr lval = lvals[i];
+		cgValue init = inits[i];
+		cg_addr_store(p, lval, init);
+	}
+
+	p->in_multi_assignment = prev_in_assignment;
 }
 }
 
 
 gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) {
 gb_internal void cg_build_assign_stmt(cgProcedure *p, AstAssignStmt *as) {