Browse Source

Add DebugInfoArray as separate debug info type. Minor cleanup of various debug infos.

lachsinc 7 năm trước cách đây
mục cha
commit
1ee0fe7457
2 tập tin đã thay đổi với 195 bổ sung178 xóa
  1. 173 150
      src/ir.cpp
  2. 22 28
      src/ir_print.cpp

+ 173 - 150
src/ir.cpp

@@ -28,6 +28,7 @@ struct irModule {
 	irDebugInfo *         debug_compile_unit;
 
 
+
 	i32                   global_string_index;
 	i32                   global_array_index; // For ConstantSlice
 	i32                   global_generated_index;
@@ -255,7 +256,7 @@ gbAllocator ir_allocator(void) {
 	IR_INSTR_KIND(StartupRuntime, i32)                                \
 	IR_INSTR_KIND(DebugDeclare, struct {                              \
 		irDebugInfo *scope;                                           \
-		Ast *    expr;                                            \
+		Ast *        expr;                                            \
 		Entity *     entity;                                          \
 		bool         is_addr;                                         \
 		irValue *    value;                                           \
@@ -536,6 +537,7 @@ enum irDebugInfoKind {
 	irDebugInfo_GlobalVariable,
 	irDebugInfo_LocalVariable,
 
+	irDebugInfo_DebugInfoArray, // array of irDebugInfo *'s
 
 	irDebugInfo_Count,
 };
@@ -549,6 +551,8 @@ struct irDebugInfo {
 			AstFile *    file;
 			String       producer;
 			irDebugInfo *all_procs;
+			irDebugInfo *enums;   // DebugInfoArray
+			irDebugInfo *globals; // DebugInfoArray
 		} CompileUnit;
 		struct {
 			AstFile *file;
@@ -562,16 +566,16 @@ struct irDebugInfo {
 			Scope *      scope; // Actual scope
 		} Scope;
 		struct {
-			Entity *             entity;
-			String               name;
-			irDebugInfo *        file;
-			TokenPos             pos;
-			Array<irDebugInfo *> types; // !{return, return, param, param, param.. etc.}
+			Entity *      entity;
+			String        name;
+			irDebugInfo * file;
+			TokenPos      pos;
+			irDebugInfo * types; // !{return, return, param, param, param.. etc.}
 			// TODO(lachsinc): variables / retainedNodes ?
 		} Proc;
 		struct {
 			Array<irDebugInfo *> procs;
-		} AllProcs;
+		} AllProcs; // TODO(lach): Redundant w/ DebugInfoArray. Merge.
 
 		// NOTE(lachsinc): Many of the following fields could be removed/resolved as we print it?
 		struct {
@@ -606,7 +610,7 @@ struct irDebugInfo {
 			irDebugInfo *        base_type; // optional, used for enumeration_type.
 			i32                  size;
 			i32                  align;
-			Array<irDebugInfo *> elements;
+			irDebugInfo *        elements;
 			i32                  array_count; // TODO(lach): We could define a new !DISubrange and place ptr to it inside above elements array instead.
 		} CompositeType;
 		struct {
@@ -630,6 +634,9 @@ struct irDebugInfo {
 			i32          arg; // Non-zero if proc parameter
 			irDebugInfo *type;
 		} LocalVariable;
+		struct {
+			Array<irDebugInfo *> elements;
+		} DebugInfoArray;
 	};
 };
 
@@ -1418,8 +1425,8 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
 
 		// TODO(lachsinc): "Arg" is not used yet but should be eventually, if applicable, set to param index
 		// NOTE(lachsinc): The following call recurses through a type creating or finding the necessary debug info.
-		// This approach may be quite detrimental to perf esp. debug builds! 
-		// This may not be the most appropriate place to place this?
+		// This approach may be quite detrimental to perf?
+		// This may not be the most appropriate place to place this? (for proc non-value params etc.)
 		ir_add_debug_info_local(proc, di, e, 0);
 	}
 
@@ -1529,6 +1536,13 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type) {
 
 irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity *e, Type *type, irDebugInfo *file);
 
+irDebugInfo *ir_add_debug_info_array(irModule *module, isize count, isize capacity) {
+	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DebugInfoArray);
+	array_init(&di->DebugInfoArray.elements, ir_allocator(), count, capacity);
+	map_set(&module->debug_info, hash_pointer(di), di);
+	return di;
+}
+
 irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) {
 	// if (!proc->module->generate_debug_info) {
 	// 	return nullptr;
@@ -1653,6 +1667,140 @@ irDebugInfo *ir_add_debug_info_enumerator(irModule *module, Entity *e) {
 	return di;
 }
 
+irDebugInfo *ir_add_debug_info_dynamic_array(irModule *module, irDebugInfo *scope, Entity *e, Type *type, irDebugInfo *file) {
+	//
+	// TODO(lachsinc): Hardcode McGee.
+	//
+
+	// TODO(lachsinc): SPEED? I assume this will create a bunch of new debug infos for _every single_
+	// dynamic array type. Maybe that's what we want, but with ability to refer to the _same_
+	// derived types for the len/cap/allocator fields.
+
+	// TODO(lachsinc): HACK we should handle named's as derived types to
+	// minimise duplication of work / ir output
+	Type *named = nullptr;
+	if (is_type_named(type)) {
+		named = type;
+		type = base_type(type);
+	}
+	GB_ASSERT(type->kind == Type_DynamicArray);
+
+	// if (!module->debug_dynamic_array_type) {
+		irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+		di->CompositeType.name = named ? named->Named.name : str_lit("dynamic array");
+		di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+
+		// TODO(lachsinc): Necessary ?
+		di->CompositeType.size = 8*cast(i32)(type_size_of(t_rawptr) +
+											type_size_of(t_int) +
+											type_size_of(t_int) +
+											type_size_of(t_allocator)); // TODO(lachsinc): Allocator is correct size??
+		di->CompositeType.align = 8*cast(i32)type_align_of(t_rawptr);
+		di->CompositeType.elements = ir_add_debug_info_array(module, 0, 4);
+
+		// Data pointer type
+		irDebugInfo *data_ptr_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+		data_ptr_di->DerivedType.name = str_lit("ptr_type_name_todo");
+		data_ptr_di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+		data_ptr_di->DerivedType.size = 8*cast(i32)type_size_of(type->DynamicArray.elem);
+		data_ptr_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, type->DynamicArray.elem, file);
+
+		// Field "data"
+		irDebugInfo *data_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+		data_di->DerivedType.name = str_lit("data");
+		data_di->DerivedType.tag = irDebugBasicEncoding_member;
+		data_di->DerivedType.size = 8*cast(i32)type_size_of(t_rawptr);
+		data_di->DerivedType.offset = 0;
+		data_di->DerivedType.base_type = data_ptr_di;
+
+		// Field "len"
+		irDebugInfo *len_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+		len_di->DerivedType.name = str_lit("len");
+		len_di->DerivedType.tag = irDebugBasicEncoding_member;
+		len_di->DerivedType.size = 8*cast(i32)type_size_of(t_int);
+		len_di->DerivedType.offset = data_di->DerivedType.size;
+		len_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_int, file);
+
+		// Field "cap"
+		irDebugInfo *cap_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+		cap_di->DerivedType.name = str_lit("cap");
+		cap_di->DerivedType.tag = irDebugBasicEncoding_member;
+		cap_di->DerivedType.size = 8*cast(i32)type_size_of(t_int);
+		cap_di->DerivedType.offset = data_di->DerivedType.size + len_di->DerivedType.size;
+		cap_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_int, file);
+
+		// Field "allocator"
+		irDebugInfo *alloc_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+		alloc_di->DerivedType.name = str_lit("allocator");
+		alloc_di->DerivedType.tag = irDebugBasicEncoding_member;
+		alloc_di->DerivedType.size = 8*cast(i32)type_size_of(t_allocator);
+		alloc_di->DerivedType.offset = data_di->DerivedType.size + len_di->DerivedType.size + alloc_di->DerivedType.size;
+		alloc_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_allocator, file); // TODO(lachsinc): Highly doubtful t_allocator creates correct debug info!
+
+		array_add(&di->CompositeType.elements->DebugInfoArray.elements, data_di);
+		array_add(&di->CompositeType.elements->DebugInfoArray.elements, len_di);
+		array_add(&di->CompositeType.elements->DebugInfoArray.elements, cap_di);
+		array_add(&di->CompositeType.elements->DebugInfoArray.elements, alloc_di);
+
+		// NOTE(lach): This isn't particularly robust; we create a new one for every type. A potential workaround
+		// is to store a pointer for each of these "custom" types inside irModule, creating if not exists
+		// (and either adding to debug_info map, or assigning id's manually to them).
+		map_set(&module->debug_info, hash_pointer(data_ptr_di), data_ptr_di);
+		map_set(&module->debug_info, hash_pointer(data_di), data_di);
+		map_set(&module->debug_info, hash_pointer(len_di), len_di);
+		map_set(&module->debug_info, hash_pointer(cap_di), cap_di);
+		map_set(&module->debug_info, hash_pointer(alloc_di), alloc_di);
+
+		map_set(&module->debug_info, hash_type(named ? named : type), di);
+	// }
+
+	return di;
+}
+
+irDebugInfo *ir_add_debug_info_string(irModule *module, irDebugInfo *scope, Entity *e, Type *type, irDebugInfo *file) {
+	// TODO(lach): Is there a cleaner way to set up these types without hardcoding values ??
+	// Also we may want to just create hardcoded "base type" for things like strings etc.
+	// and just create a derived (named) type to "inherit" from. That way we can look them up directly
+	// inside irModule, and avoid lots of map lookups and array creations for their elements.
+	// In theory this should only occur once, as we hash the type t_string once and return it.
+
+	GB_ASSERT(type->kind == Type_Basic);
+	GB_ASSERT(type->Basic.kind == Basic_string);
+
+	irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+	di->CompositeType.name = type->Basic.name;
+	di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+	di->CompositeType.size = 8*cast(i32)type_size_of(t_string);
+	di->CompositeType.align = 8*cast(i32)type_align_of(t_string);
+	di->CompositeType.elements = ir_add_debug_info_array(module, 0, 2);
+
+	irDebugInfo *data_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+	data_di->DerivedType.name = str_lit("data"); // TODO(lachsinc):
+	data_di->DerivedType.tag = irDebugBasicEncoding_member;
+	data_di->DerivedType.size = 8*cast(i32)type_size_of(t_rawptr);
+	data_di->DerivedType.offset = 0;
+	data_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_cstring, file);
+
+	irDebugInfo *len_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+	len_di->DerivedType.name = str_lit("len"); // TODO(lachsinc):
+	len_di->DerivedType.tag = irDebugBasicEncoding_member;
+	len_di->DerivedType.size = 8*cast(i32)type_size_of(t_i64);
+	len_di->DerivedType.offset = data_di->DerivedType.size;
+	len_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_i64, file);
+
+	array_add(&di->CompositeType.elements->DebugInfoArray.elements, data_di);
+	array_add(&di->CompositeType.elements->DebugInfoArray.elements, len_di);
+
+	// NOTE(lach): This isn't particularly robust, it assumes all strings will be caught
+	// by the map lookup (ie this will only be created once).
+	map_set(&module->debug_info, hash_pointer(data_di), data_di);
+	map_set(&module->debug_info, hash_pointer(len_di), len_di);
+
+	map_set(&module->debug_info, hash_type(type), di);
+
+	return di;
+}
+
 irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity *e, Type *type, irDebugInfo *file) {
 	// if (!proc->module->generate_debug_info) {
 	// 	return nullptr;
@@ -1695,25 +1843,25 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity
 			} else {
 				di->CompositeType.name = str_lit("struct_name_todo");
 			}
-			array_init(&di->CompositeType.elements, ir_allocator(), 0, base->Struct.fields.capacity);
+			di->CompositeType.elements = ir_add_debug_info_array(module, 0, base->Struct.fields.count);
 			for_array(field_index, base->Struct.fields) {
-				array_add(&di->CompositeType.elements, 
+				array_add(&di->CompositeType.elements->DebugInfoArray.elements,
 				          ir_add_debug_info_field(module, di, base->Struct.fields[field_index], cast(i32)field_index,
 						                          base->Struct.fields[field_index]->type, file));
 			}
 			di->CompositeType.tag = irDebugBasicEncoding_structure_type;
 		} else if (is_type_union(type)) {
-			di->CompositeType.name = str_lit("union_todo");
-			array_init(&di->CompositeType.elements, ir_allocator(), 0, 0);
+			di->CompositeType.name = str_lit("union_name_todo");
+			di->CompositeType.elements = nullptr; // ir_add_debug_info_array(module, 0, 0);
 			// TODO(lachsinc): Add elements for union
 			di->CompositeType.tag = irDebugBasicEncoding_union_type;
 		} else if (is_type_enum(type)) {
 			GB_ASSERT(type->kind == Type_Named);
 			di->CompositeType.name = type->Named.name;
 			di->CompositeType.base_type = ir_add_debug_info_type(module, scope, e, type->Named.base->Enum.base_type, file);
-			array_init(&di->CompositeType.elements, ir_allocator(), 0, base->Enum.fields.capacity);
+			di->CompositeType.elements = ir_add_debug_info_array(module, 0, base->Enum.fields.count);
 			for_array(field_index, base->Enum.fields) {
-				array_add(&di->CompositeType.elements,
+				array_add(&di->CompositeType.elements->DebugInfoArray.elements,
 				          ir_add_debug_info_enumerator(module, base->Enum.fields[field_index]));
 			}
 			di->CompositeType.tag = irDebugBasicEncoding_enumeration_type;
@@ -1735,7 +1883,7 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity
 		} else if (deref->kind == Type_Basic) {
 			di->DerivedType.name = deref->Basic.name;
 		} else {
-			di->DerivedType.name = str_lit("derived_name_todo");
+			di->DerivedType.name = str_lit("pointer_name_todo");
 		}
 		di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
 		di->DerivedType.scope = scope;
@@ -1759,92 +1907,7 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity
 	// We could get fancy and use a composite type along with
 	// DW_TAG_class_type / template debug stuff eventually.
 	if (is_type_dynamic_array(type)) {
-		//
-		// TODO(lachsinc): Hardcode McGee.
-		//
-
-		// TODO(lachsinc): SPEED? I assume this will create a bunch of new debug infos for _every single_
-		// dynamic array type. Maybe that's what we want, but with ability to refer to the _same_
-		// derived types for the len/cap/allocator fields.
-
-		// TODO(lachsinc): HACK we should handle named's as derived types to 
-		// minimise duplication of work / ir output
-		Type *named = nullptr;
-		if (is_type_named(type)) {
-			named = type;
-			type = base_type(type);
-		}
-
-		GB_ASSERT(type->kind == Type_DynamicArray);
-
-		irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
-		di->CompositeType.name = named ? named->Named.name : str_lit("dynamic array");
-		di->CompositeType.tag = irDebugBasicEncoding_structure_type;
-		// TODO(lachsinc): Necessary ?
-		di->CompositeType.size = 8*cast(i32)(type_size_of(t_rawptr) +
-		                                     type_size_of(t_int) +
-		                                     type_size_of(t_int) +
-		                                     type_size_of(t_allocator)); // TODO(lachsinc): Allocator is correct size??
-		di->CompositeType.align = 8*cast(i32)type_align_of(t_rawptr);
-
-		array_init(&di->CompositeType.elements, ir_allocator(), 0, 4);
-
-		// Data pointer type
-		irDebugInfo *data_ptr_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		data_ptr_di->DerivedType.name = str_lit("ptr_type_name_todo");
-		data_ptr_di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
-		data_ptr_di->DerivedType.size = 8*cast(i32)type_size_of(type->DynamicArray.elem);
-		data_ptr_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, type->DynamicArray.elem, file);
-
-		// Field "data"
-		irDebugInfo *data_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		data_di->DerivedType.name = str_lit("data");
-		data_di->DerivedType.tag = irDebugBasicEncoding_member;
-		data_di->DerivedType.size = 8*cast(i32)type_size_of(t_rawptr);
-		data_di->DerivedType.offset = 0;
-		data_di->DerivedType.base_type = data_ptr_di;
-
-		// Field "len"
-		irDebugInfo *len_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		len_di->DerivedType.name = str_lit("len");
-		len_di->DerivedType.tag = irDebugBasicEncoding_member;
-		len_di->DerivedType.size = 8*cast(i32)type_size_of(t_int);
-		len_di->DerivedType.offset = data_di->DerivedType.size;
-		len_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_int, file);
-
-		// Field "cap"
-		irDebugInfo *cap_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		cap_di->DerivedType.name = str_lit("cap");
-		cap_di->DerivedType.tag = irDebugBasicEncoding_member;
-		cap_di->DerivedType.size = 8*cast(i32)type_size_of(t_int);
-		cap_di->DerivedType.offset = data_di->DerivedType.size + len_di->DerivedType.size;
-		cap_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_int, file);
-
-		// Field "allocator"
-		irDebugInfo *alloc_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		alloc_di->DerivedType.name = str_lit("allocator");
-		alloc_di->DerivedType.tag = irDebugBasicEncoding_member;
-		alloc_di->DerivedType.size = 8*cast(i32)type_size_of(t_allocator);
-		alloc_di->DerivedType.offset = data_di->DerivedType.size + len_di->DerivedType.size + alloc_di->DerivedType.size;
-		alloc_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_allocator, file); // TODO(lachsinc): Highly doubtful t_allocator creates correct debug info!
-
-		array_add(&di->CompositeType.elements, data_di);
-		array_add(&di->CompositeType.elements, len_di);
-		array_add(&di->CompositeType.elements, cap_di);
-		array_add(&di->CompositeType.elements, alloc_di);
-
-		// NOTE(lach): This isn't particularly robust; we create a new one for every type. A potential workaround
-		// is to store a pointer for each of these "custom" types inside irModule, creating if not exists
-		// (and either adding to debug_info map, or assigning id's manually to them).
-		map_set(&module->debug_info, hash_pointer(data_ptr_di), data_ptr_di);
-		map_set(&module->debug_info, hash_pointer(data_di), data_di);
-		map_set(&module->debug_info, hash_pointer(len_di), len_di);
-		map_set(&module->debug_info, hash_pointer(cap_di), cap_di);
-		map_set(&module->debug_info, hash_pointer(alloc_di), alloc_di);
-
-		map_set(&module->debug_info, hash_type(named ? named : type), di);
-
-		return di;
+		return ir_add_debug_info_dynamic_array(module, scope, e, type, file);
 	}
 
 	if (is_type_array(type)) {
@@ -1878,48 +1941,7 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, irDebugInfo *scope, Entity
 	}
 
 	if (is_type_string(type)) {
-		// TODO(lach): Is there a cleaner way to set up these types without hardcoding values ??
-		// Also we may want to just create hardcoded "base type" for things like strings etc.
-		// and just create a derived (named) type to "inherit" from. That way we can look them up directly
-		// inside irModule, and avoid lots of map lookups and array creations for their elements.
-		// In theory this should only occur once, as we hash the type t_string once and return it.
-
-		GB_ASSERT(type->kind == Type_Basic);
-		GB_ASSERT(type->Basic.kind == Basic_string);
-
-		irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
-		di->CompositeType.name = type->Basic.name;
-		di->CompositeType.tag = irDebugBasicEncoding_structure_type;
-		di->CompositeType.size = 8*cast(i32)type_size_of(t_string);
-		di->CompositeType.align = 8*cast(i32)type_align_of(t_string);
-
-		array_init(&di->CompositeType.elements, ir_allocator(), 0, 2);
-
-		irDebugInfo *data_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		data_di->DerivedType.name = str_lit("data"); // TODO(lachsinc):
-		data_di->DerivedType.tag = irDebugBasicEncoding_member;
-		data_di->DerivedType.size = 8*cast(i32)type_size_of(t_rawptr);
-		data_di->DerivedType.offset = 0;
-		data_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_cstring, file);
-
-		irDebugInfo *len_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
-		len_di->DerivedType.name = str_lit("len"); // TODO(lachsinc):
-		len_di->DerivedType.tag = irDebugBasicEncoding_member;
-		len_di->DerivedType.size = 8*cast(i32)type_size_of(t_i64);
-		len_di->DerivedType.offset = data_di->DerivedType.size;
-		len_di->DerivedType.base_type = ir_add_debug_info_type(module, scope, e, t_i64, file);
-
-		array_add(&di->CompositeType.elements, data_di);
-		array_add(&di->CompositeType.elements, len_di);
-
-		// NOTE(lach): This isn't particularly robust, it assumes all strings will be caught
-		// by the map lookup (ie this will only be created once).
-		map_set(&module->debug_info, hash_pointer(data_di), data_di);
-		map_set(&module->debug_info, hash_pointer(len_di), len_di);
-
-		map_set(&module->debug_info, hash_type(type), di);
-
-		return di;
+		return ir_add_debug_info_string(module, scope, e, type, file);
 	}
 
 	//
@@ -1968,7 +1990,8 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
 
 	isize result_count = proc->type->Proc.result_count;
 	isize param_count = proc->type->Proc.param_count;
-	array_init(&di->Proc.types, ir_allocator(), 0, gb_max(result_count, 1) + param_count); // TODO(lachsinc): ir_allocator() safe to use?
+	// gb_max(result_count, 1) because llvm expects explicit "null" return type
+	di->Proc.types = ir_add_debug_info_array(proc->module, 0, gb_max(result_count, 1) + param_count);
 
 	// Result/return types
 	if (result_count >= 1) {
@@ -1981,12 +2004,12 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
 
 			irDebugInfo *type_di = ir_add_debug_info_type(proc->module, di, e, e->type, file);
 			GB_ASSERT_NOT_NULL(type_di);
-			array_add(&di->Proc.types, type_di);
+			array_add(&di->Proc.types->DebugInfoArray.elements, type_di);
 		}
 	} else {
 		// llvm expects "!{null}" for a function without return type, use nullptr to represent it.
 		// TODO(lachsinc): Is there a specific "void" type we should refer to?
-		array_add(&di->Proc.types, (irDebugInfo*)nullptr);
+		array_add(&di->Proc.types->DebugInfoArray.elements, (irDebugInfo*)nullptr);
 	}
 
 	// Param types
@@ -2000,10 +2023,10 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String na
 			// TODO(lach): Could technically be a local?
 			irDebugInfo *type_di = ir_add_debug_info_type(proc->module, di, e, e->type, file);
 			GB_ASSERT_NOT_NULL(type_di);
-			array_add(&di->Proc.types, type_di);
+			array_add(&di->Proc.types->DebugInfoArray.elements, type_di);
 		}
 	}
-	
+
 	proc->debug_scope = di;
 
 	map_set(&proc->module->debug_info, hash_entity(entity), di);

+ 22 - 28
src/ir_print.cpp

@@ -1974,30 +1974,16 @@ void print_llvm_ir(irGen *ir) {
 				            ", flags: DIFlagPrototyped"
 				            ", isOptimized: false"
 				            ", unit: !%d"
-				            ", type: !DISubroutineType(types: !{",
+				            ", type: !DISubroutineType(types: !%d)",
 				            LIT(di->Proc.entity->token.string),
 				            LIT(di->Proc.name),
 				            di->Proc.file->id, // TODO(lachsinc): HACK For now lets pretend all procs scope's == file.
 				            di->Proc.file->id,
 				            di->Proc.pos.line,
 				            di->Proc.pos.line, // NOTE(lachsinc): Assume scopeLine always same as line.
-				            m->debug_compile_unit->id);
-				if (di->Proc.types.count > 0) {
-					for_array(type_index, di->Proc.types) {
-						if (type_index > 0) {
-							ir_write_byte(f, ',');
-						}
-						if (di->Proc.types[type_index]) {
-							ir_fprintf(f, "!%d", di->Proc.types[type_index]->id);
-						} else {
-							ir_write_str_lit(f, "null");
-						}
-					}
-				} else {
-					ir_write_str_lit(f, "null");
-				}
-				ir_write_str_lit(f, "})"); // !DISubroutineTypes close
-				ir_write_byte(f, ')');
+				            m->debug_compile_unit->id,
+							di->Proc.types->id);
+				ir_write_byte(f, ')'); // !DISubprogram(
 				break;
 			case irDebugInfo_LocalVariable: {
 				ir_fprintf(f, "!DILocalVariable("
@@ -2074,7 +2060,7 @@ void print_llvm_ir(irGen *ir) {
 					            di->CompositeType.size,
 					            di->CompositeType.align);
 					ir_print_debug_encoding(f, irDebugInfo_CompositeType, di->CompositeType.tag);
-					if (di->CompositeType.scope) {
+					if (di->CompositeType.scope != nullptr) {
 						ir_fprintf(f, ", scope: !%d"
 						              ", file: !%d"
 						              ", line: %td",
@@ -2082,18 +2068,12 @@ void print_llvm_ir(irGen *ir) {
 						              di->CompositeType.file->id,
 						              di->CompositeType.pos.line);
 					}
-					if (di->CompositeType.base_type) {
+					if (di->CompositeType.base_type != nullptr) {
 						GB_ASSERT(di->CompositeType.tag == irDebugBasicEncoding_enumeration_type);
 						ir_fprintf(f, ", baseType: !%d", di->CompositeType.base_type->id);
 					}
-					if (di->CompositeType.elements.count > 0) {
-						ir_write_str_lit(f, ", elements: !{");
-						for_array(element_index, di->CompositeType.elements) {
-							ir_fprintf(f, "%s!%d",
-							           element_index > 0 ? ", " : "",
-							           di->CompositeType.elements[element_index]->id);
-						}
-						ir_write_byte(f, '}');
+					if (di->CompositeType.elements != nullptr) {
+						ir_fprintf(f, ", elements: !%d", di->CompositeType.elements->id);
 					}
 					ir_write_byte(f, ')');
 				}
@@ -2108,6 +2088,7 @@ void print_llvm_ir(irGen *ir) {
 				ir_write_byte(f, ')');
 				break;
 			}
+			// TODO(lach): Merge w/ DebugInfoArray
 			case irDebugInfo_AllProcs:
 				ir_fprintf(f, "!{");
 				for_array(proc_index, di->AllProcs.procs) {
@@ -2117,6 +2098,19 @@ void print_llvm_ir(irGen *ir) {
 				}
 				ir_write_byte(f, '}');
 				break;
+			case irDebugInfo_DebugInfoArray:
+				ir_fprintf(f, "!{");
+				for_array(element_index, di->DebugInfoArray.elements) {
+					irDebugInfo *elem = di->DebugInfoArray.elements[element_index];
+					if (element_index > 0) {ir_write_str_lit(f, ", ");}
+					if (elem) {
+						ir_fprintf(f, "!%d", elem->id);
+					} else {
+						ir_fprintf(f, "null"); // NOTE(lachsinc): Proc's can contain "nullptr" entries to represent void return values.
+					}
+				}
+				ir_write_byte(f, '}');
+				break;
 
 			default:
 				GB_PANIC("Unhandled irDebugInfo kind %d", di->kind);