aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2018-10-24 09:40:05 +0100
committerGitHub <noreply@github.com>2018-10-24 09:40:05 +0100
commit2bd85e764e32beba498addad93aef6f618395394 (patch)
tree1b65f3cdf2a043761019c0acb9e8adcab73c5a70 /src
parent822e4894f2a37d372dcb46db5185ad2c4d140d98 (diff)
parentd894fb37087a100eeea2a0de1da89bda390e1306 (diff)
Merge pull request #260 from lachsinc/master
[WIP] Provide llvm with more debug info (for Visual Studio debugger support)
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp17
-rw-r--r--src/ir.cpp1203
-rw-r--r--src/ir_print.cpp305
-rw-r--r--src/main.cpp6
4 files changed, 1360 insertions, 171 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 90a80c158..ec2f27343 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -589,10 +589,23 @@ void init_build_context(void) {
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
- gbString opt_flags = gb_string_make_reserve(heap_allocator(), 16);
+ gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
+ opt_flags = gb_string_append_fmt(opt_flags, "-O%d ", bc->optimization_level);
if (bc->optimization_level != 0) {
- opt_flags = gb_string_append_fmt(opt_flags, "-O%d", bc->optimization_level);
+ // NOTE(lachsinc): The following options were previously passed during call
+ // to opt in main.cpp:exec_llvm_opt().
+ // -die: Dead instruction elimination
+ // -memcpyopt: MemCpy optimization
+ opt_flags = gb_string_appendc(opt_flags, "-memcpyopt -die ");
}
+
+ // NOTE(lachsinc): This optimization option was previously required to get
+ // around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
+ // is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.
+ // if (bc->ODIN_DEBUG == false) {
+ // opt_flags = gb_string_appendc(opt_flags, "-mem2reg ");
+ // }
+
bc->opt_flags = make_string_c(opt_flags);
diff --git a/src/ir.cpp b/src/ir.cpp
index 78236bd35..b1c39a6f8 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -26,6 +26,7 @@ struct irModule {
Map<irValue *> anonymous_proc_lits; // Key: Ast *
irDebugInfo * debug_compile_unit;
+ Array<irDebugInfo *> debug_location_stack;
i32 global_string_index;
@@ -109,11 +110,6 @@ struct irBranchBlocks {
};
-struct irDebugLocation {
- TokenPos pos;
- irDebugInfo *debug_scope;
-};
-
struct irContextData {
irValue *value;
isize scope_index;
@@ -271,8 +267,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; \
@@ -438,7 +433,7 @@ struct irValue {
irValueKind kind;
i32 index;
bool index_set;
- irDebugLocation loc;
+ irDebugInfo * loc;
union {
irValueConstant Constant;
irValueConstantSlice ConstantSlice;
@@ -526,6 +521,9 @@ enum irDebugEncoding {
irDebugBasicEncoding_unsigned = 6,
irDebugBasicEncoding_unsigned_char = 7,
+ // TODO(lachsinc): Should the following be renamed from basic -> tag to mirror their DW_TAG_*
+ // counterparts? Perhaps separate out if they truly have different meaning.
+
irDebugBasicEncoding_member = 13,
irDebugBasicEncoding_pointer_type = 15,
irDebugBasicEncoding_typedef = 22,
@@ -534,7 +532,10 @@ enum irDebugEncoding {
irDebugBasicEncoding_enumeration_type = 4,
irDebugBasicEncoding_structure_type = 19,
irDebugBasicEncoding_union_type = 23,
+};
+enum irDebugInfoFlags {
+ irDebugInfoFlag_Bitfield = (1 << 19),
};
enum irDebugInfoKind {
@@ -542,18 +543,21 @@ enum irDebugInfoKind {
irDebugInfo_CompileUnit,
irDebugInfo_File,
- irDebugInfo_Scope,
irDebugInfo_Proc,
+ irDebugInfo_ProcType,
+ irDebugInfo_Location,
+ irDebugInfo_LexicalBlock,
irDebugInfo_AllProcs,
- irDebugInfo_BasicType, // basic types
- irDebugInfo_ProcType,
- irDebugInfo_DerivedType, // pointer, typedef
- irDebugInfo_CompositeType, // array, struct, enum, (raw_)union
+ irDebugInfo_BasicType, // primitive types
+ irDebugInfo_DerivedType, // pointer, distinct etc.
+ irDebugInfo_CompositeType, // array, struct, enum, union etc.
irDebugInfo_Enumerator, // For irDebugInfo_CompositeType if enum
+ irDebugInfo_GlobalVariableExpression, // used to describe if global is const or not
irDebugInfo_GlobalVariable,
irDebugInfo_LocalVariable,
+ irDebugInfo_DebugInfoArray, // array of irDebugInfo *'s
irDebugInfo_Count,
};
@@ -566,7 +570,8 @@ struct irDebugInfo {
struct {
AstFile * file;
String producer;
- irDebugInfo *all_procs;
+ irDebugInfo *enums; // DebugInfoArray
+ irDebugInfo *globals; // DebugInfoArray
} CompileUnit;
struct {
AstFile *file;
@@ -574,21 +579,26 @@ struct irDebugInfo {
String directory;
} File;
struct {
- irDebugInfo *parent;
- irDebugInfo *file;
- TokenPos pos;
- Scope * scope; // Actual scope
- } Scope;
+ Entity * entity;
+ String name;
+ irDebugInfo * file;
+ TokenPos pos;
+ irDebugInfo * type;
+ // TODO(lachsinc): variables / retainedNodes ?
+ } Proc;
+ struct {
+ irDebugInfo * types; // !{return, return, param, param, param.. etc.}
+ } ProcType;
struct {
- Entity * entity;
- String name;
- irDebugInfo *file;
TokenPos pos;
- } Proc;
+ irDebugInfo *scope;
+ } Location;
struct {
- Array<irDebugInfo *> procs;
- } AllProcs;
-
+ TokenPos pos;
+ irDebugInfo *file;
+ irDebugInfo *scope;
+ } LexicalBlock;
+
struct {
String name;
i32 size;
@@ -596,35 +606,44 @@ struct irDebugInfo {
irDebugEncoding encoding;
} BasicType;
struct {
- irDebugInfo * return_type;
- Array<irDebugInfo *> param_types;
- } ProcType;
- struct {
- irDebugInfo * base_type;
- irDebugEncoding encoding;
+ irDebugEncoding tag;
+ irDebugInfo * base_type;
+ String name;
+ irDebugInfo * scope;
+ irDebugInfo * file;
+ TokenPos pos;
+ i32 size;
+ i32 align;
+ i32 offset;
+ irDebugInfoFlags flags; // Used only for DIFlagBitField.
} DerivedType;
struct {
- irDebugEncoding encoding;
- String name;
- String identifier;
- irDebugInfo * file;
- TokenPos pos;
- i32 size;
- i32 align;
- Array<irDebugInfo *> elements;
+ irDebugEncoding tag;
+ String name;
+ irDebugInfo * scope;
+ irDebugInfo * file;
+ TokenPos pos;
+ irDebugInfo * base_type; // optional, used for enumeration_type.
+ i32 size;
+ i32 align;
+ irDebugInfo * elements;
+ i32 array_count; // for DISubrange
} CompositeType;
struct {
String name;
i64 value;
} Enumerator;
struct {
+ irDebugInfo *var;
+ } GlobalVariableExpression;
+ struct {
String name;
String linkage_name;
irDebugInfo *scope;
irDebugInfo *file;
TokenPos pos;
+ irDebugInfo *type;
irValue *variable;
- irDebugInfo *declaration;
} GlobalVariable;
struct {
String name;
@@ -634,6 +653,9 @@ struct irDebugInfo {
i32 arg; // Non-zero if proc parameter
irDebugInfo *type;
} LocalVariable;
+ struct {
+ Array<irDebugInfo *> elements; // TODO(lachsinc): Leak?
+ } DebugInfoArray;
};
};
@@ -821,8 +843,12 @@ irAddr ir_build_addr (irProcedure *proc, Ast *expr);
void ir_build_proc (irValue *value, irProcedure *parent);
void ir_gen_global_type_name(irModule *m, Entity *e, String name);
irValue *ir_get_type_info_ptr (irProcedure *proc, Type *type);
-
-
+void ir_value_set_debug_location(irProcedure *proc, irValue *v);
+void ir_push_debug_location (irModule *m, Ast *node, irDebugInfo *scope);
+void ir_pop_debug_location (irModule *m);
+irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id);
+irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file);
+irDebugInfo *ir_add_debug_info_proc(irProcedure *proc);
irValue *ir_alloc_value(irValueKind kind) {
@@ -1190,15 +1216,13 @@ irValue *ir_instr_comment(irProcedure *p, String text) {
return v;
}
-irValue *ir_instr_debug_declare(irProcedure *p, irDebugInfo *scope, Ast *expr, Entity *entity, bool is_addr, irValue *value) {
+irValue *ir_instr_debug_declare(irProcedure *p, Ast *expr, Entity *entity, bool is_addr, irValue *value) {
irValue *v = ir_alloc_instr(p, irInstr_DebugDeclare);
- v->Instr.DebugDeclare.scope = scope;
v->Instr.DebugDeclare.expr = expr;
v->Instr.DebugDeclare.entity = entity;
v->Instr.DebugDeclare.is_addr = is_addr;
v->Instr.DebugDeclare.value = value;
return v;
-
}
@@ -1222,6 +1246,7 @@ irValue *ir_value_constant_slice(Type *type, irValue *backing_array, i64 count)
irValue *ir_emit(irProcedure *proc, irValue *instr) {
GB_ASSERT(instr->kind == irValue_Instr);
+ irModule *m = proc->module;
irBlock *b = proc->curr_block;
instr->Instr.block = b;
if (b != nullptr) {
@@ -1232,6 +1257,9 @@ irValue *ir_emit(irProcedure *proc, irValue *instr) {
} else if (instr->Instr.kind != irInstr_Unreachable) {
GB_PANIC("ir_emit: Instruction missing parent block");
}
+ if (m->generate_debug_info) {
+ ir_value_set_debug_location(proc, instr);
+ }
return instr;
}
@@ -1470,7 +1498,7 @@ void ir_push_context_onto_stack(irProcedure *proc, irValue *ctx) {
array_add(&proc->context_stack, cd);
}
-irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initialized) {
+irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initialized, i32 param_index = 0) {
irBlock *b = proc->decl_block; // all variables must be in the first block
irValue *instr = ir_instr_local(proc, e, true);
instr->Instr.block = b;
@@ -1482,9 +1510,15 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
ir_emit_zero_init(proc, instr, expr);
}
- if (expr != nullptr && proc->entity != nullptr) {
- irDebugInfo *di = *map_get(&proc->module->debug_info, hash_entity(proc->entity));
- ir_emit(proc, ir_instr_debug_declare(proc, di, expr, e, true, instr));
+ // if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
+ if (proc->module->generate_debug_info && proc->entity != nullptr) {
+ // GB_ASSERT_NOT_NULL(proc->debug_scope);
+ if (expr != nullptr) {
+ ir_emit(proc, ir_instr_debug_declare(proc, expr, e, true, instr));
+ }
+ if (e->scope != nullptr && proc->debug_scope != nullptr) {
+ irDebugInfo *di_local = ir_add_debug_info_local(proc, e, param_index);
+ }
}
return instr;
@@ -1549,13 +1583,16 @@ irValue *ir_add_global_generated(irModule *m, Type *type, irValue *value) {
}
-irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type) {
+irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) {
irValue *v = ir_value_param(proc, e, abi_type);
irValueParam *p = &v->Param;
+ ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope);
+ defer (ir_pop_debug_location(proc->module));
+
switch (p->kind) {
case irParamPass_Value: {
- irValue *l = ir_add_local(proc, e, expr, false);
+ irValue *l = ir_add_local(proc, e, expr, false, index);
irValue *x = v;
if (abi_type == t_llvm_bool) {
x = ir_emit_conv(proc, x, t_bool);
@@ -1568,7 +1605,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type) {
return ir_emit_load(proc, v);
case irParamPass_Integer: {
- irValue *l = ir_add_local(proc, e, expr, false);
+ irValue *l = ir_add_local(proc, e, expr, false, index);
irValue *iptr = ir_emit_conv(proc, l, alloc_type_pointer(p->type));
ir_emit_store(proc, iptr, v);
return ir_emit_load(proc, l);
@@ -1591,11 +1628,26 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type) {
//
////////////////////////////////////////////////////////////////
-irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) {
+irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irDebugInfo *scope, 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(irModule *module, AstFile *file) {
// if (!proc->module->generate_debug_info) {
// return nullptr;
// }
+ irDebugInfo **existing = map_get(&module->debug_info, hash_ast_file(file));
+ if (existing != nullptr) {
+ GB_ASSERT((*existing)->kind == irDebugInfo_File);
+ return *existing;
+ }
+
GB_ASSERT(file != nullptr);
irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_File);
di->File.file = file;
@@ -1618,36 +1670,950 @@ irDebugInfo *ir_add_debug_info_file(irProcedure *proc, AstFile *file) {
di->File.filename = filename;
di->File.directory = directory;
- map_set(&proc->module->debug_info, hash_ast_file(file), di);
+ map_set(&module->debug_info, hash_ast_file(file), di);
return di;
}
+irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) {
+ switch (kind) {
+ case Basic_llvm_bool:
+ case Basic_bool:
+ case Basic_b8:
+ case Basic_b16:
+ case Basic_b32:
+ case Basic_b64:
+ return irDebugBasicEncoding_boolean;
+
+ case Basic_i8:
+ return irDebugBasicEncoding_signed_char;
+
+ case Basic_u8:
+ return irDebugBasicEncoding_unsigned_char;
+
+ case Basic_i16:
+ case Basic_i32:
+ case Basic_i64:
+ case Basic_int:
+ case Basic_rune:
+ case Basic_typeid:
+ return irDebugBasicEncoding_signed;
+
+ case Basic_u16:
+ case Basic_u32:
+ case Basic_u64:
+ case Basic_uint:
+ case Basic_uintptr:
+ return irDebugBasicEncoding_unsigned;
+
+ // case Basic_f16:
+ case Basic_f32:
+ case Basic_f64:
+ return irDebugBasicEncoding_float;
+
+ // case Basic_complex32:
+ case Basic_complex64:
+ case Basic_complex128:
+ case Basic_cstring:
+ case Basic_string:
+ case Basic_any:
+ case Basic_rawptr:
+ break; // not a "DIBasicType"
+ }
-irDebugInfo *ir_add_debug_info_proc(irProcedure *proc, Entity *entity, String name, irDebugInfo *file) {
- // if (!proc->module->generate_debug_info) {
- // return nullptr;
- // }
+ GB_PANIC("Unreachable");
+ return irDebugBasicEncoding_Invalid;
+}
+
+i32 ir_debug_info_bits(i64 size) {
+ return 8*cast(i32)size;
+}
+
+i32 ir_debug_size_bits(Type *type) {
+ return ir_debug_info_bits(type_size_of(type));
+}
+
+i32 ir_debug_align_bits(Type *type) {
+ return ir_debug_info_bits(type_align_of(type));
+}
+
+irDebugInfo *ir_add_debug_info_field_internal(irModule *module, String name, Type *type, i32 offset_bits, Entity *e, irDebugInfo *scope) {
+ // NOTE(lachsinc): Caller is expected to insert the returned value into map themselves.
+ // "scope", if set, should be inserted into map prior to calling to ensure no cyclical dependency issues.
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ di->DerivedType.name = name;
+ di->DerivedType.tag = irDebugBasicEncoding_member;
+ di->DerivedType.size = ir_debug_size_bits(type);
+ di->DerivedType.offset = offset_bits;
+ di->DerivedType.scope = scope;
+
+ // NOTE(lachsinc): It is "safe" to overwrite this base_type after a call to this function,
+ // if you need to set a specific type for this field.
+ di->DerivedType.base_type = ir_add_debug_info_type(module, type, e, scope, nullptr);
+ GB_ASSERT_NOT_NULL(di->DerivedType.base_type);
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_field(irModule *module, irDebugInfo *scope, Entity *e, Type *scope_type, i32 index, Type *type, irDebugInfo *file) {
+ // NOTE(lachsinc): This lookup will only work for struct fields!!
+ if (e) {
+ irDebugInfo **existing = map_get(&module->debug_info, hash_entity(e));
+ if (existing != nullptr) {
+ return *existing;
+ }
+ }
+
+ irDebugInfo *di = ir_add_debug_info_field_internal(module, make_string(nullptr, 0), type, 0, e, scope);
+ void *ptr_to_hash = nullptr;
+ if (scope_type) {
+ Type *scope_base = base_type(scope_type);
+ if (is_type_struct(scope_type) || is_type_tuple(scope_type)) {
+ if (is_type_struct(scope_type) && scope_base->Struct.are_offsets_set) {
+ di->DerivedType.offset = ir_debug_info_bits(scope_base->Struct.offsets[index]);
+ } else if (is_type_tuple(scope_type) && scope_base->Tuple.are_offsets_set) {
+ di->DerivedType.offset = ir_debug_info_bits(scope_base->Tuple.offsets[index]);
+ } else {
+ di->DerivedType.offset = ir_debug_info_bits(type_offset_of(scope_base, index));
+ }
+ if (e) {
+ ptr_to_hash = e;
+ di->DerivedType.name = e->token.string;
+ if (e->token.string.len == 0) {
+ // If no name available for field, use its field index as its name.
+ isize max_len = 8;
+ u8 *str = cast(u8 *)gb_alloc_array(heap_allocator(), u8, max_len);
+ isize len = gb_snprintf(cast(char *)str, 8, "%d", index);
+ di->DerivedType.name = make_string(str, len-1);
+ }
+ di->DerivedType.pos = e->token.pos;
+ } else {
+ GB_PANIC("Unreachable"); // struct field Entity's should be provided.
+ }
+ } else if (is_type_union(scope_base)) {
+ // TODO(lachsinc): Handle this in a more generic manner/pass in??...
+ // Token token = base_type(scope_base)->Union.node->UnionType.token;
+ // di->DerivedType.name = token.string;
+ // di->DerivedType.pos = token.pos;
+ if (is_type_named(type)) {
+ di->DerivedType.name = type->kind == Type_Named ? type->Named.name : type->Basic.name;
+ }
+ ptr_to_hash = di;
+ }
+ }
+
+ di->DerivedType.file = file;
+
+ GB_ASSERT_NOT_NULL(ptr_to_hash);
+ map_set(&module->debug_info, hash_pointer(ptr_to_hash), di);
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_enumerator(irModule *module, Entity *e) {
+ irDebugInfo **existing = map_get(&module->debug_info, hash_entity(e));
+ if (existing != nullptr) {
+ return *existing;
+ }
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Enumerator);
+ di->Enumerator.name = e->token.string;
+ GB_ASSERT(e->kind == Entity_Constant);
+ GB_ASSERT(e->Constant.value.kind == ExactValue_Integer);
+ di->Enumerator.value = big_int_to_i64(&e->Constant.value.value_integer);
+
+ map_set(&module->debug_info, hash_entity(e), di);
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_type_dynamic_array(irModule *module, Type *type, Entity *e, irDebugInfo *scope, irDebugInfo *file) {
+ GB_ASSERT(type->kind == Type_DynamicArray);
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = str_lit("dynamic_array"); // TODO(lachsinc): [dynamic] .. type->DynamicArray.elem name
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(t_rawptr) +
+ ir_debug_size_bits(t_int) +
+ ir_debug_size_bits(t_int) +
+ ir_debug_size_bits(t_allocator);
+ di->CompositeType.align = ir_debug_align_bits(t_rawptr);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ // Data pointer type
+ // TODO(lachsinc): Perhaps lookup/alloc-a-fake Type_Pointer type and go via ir_add_debug_info_type() with it.
+ irDebugInfo *data_ptr_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ data_ptr_di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+ data_ptr_di->DerivedType.size = ir_debug_size_bits(t_rawptr);
+ map_set(&module->debug_info, hash_pointer(data_ptr_di), data_ptr_di);
+ data_ptr_di->DerivedType.base_type = ir_add_debug_info_type(module, type->DynamicArray.elem, e, scope, file);
+
+ irDebugInfo *data_di = ir_add_debug_info_field_internal(module, str_lit("data"), t_rawptr,
+ 0,
+ nullptr,
+ di);
+ data_di->DerivedType.base_type = data_ptr_di;
+ map_set(&module->debug_info, hash_pointer(data_di), data_di);
+
+ irDebugInfo *len_di = ir_add_debug_info_field_internal(module, str_lit("len"), t_int,
+ data_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(len_di), len_di);
+
+ irDebugInfo *cap_di = ir_add_debug_info_field_internal(module, str_lit("cap"), t_int,
+ data_di->DerivedType.size +
+ len_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(cap_di), cap_di);
+
+ irDebugInfo *alloc_di = ir_add_debug_info_field_internal(module, str_lit("allocator"), t_allocator,
+ data_di->DerivedType.size +
+ len_di->DerivedType.size +
+ cap_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(alloc_di), alloc_di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 4);
+ array_add(&elements_di->DebugInfoArray.elements, data_di);
+ array_add(&elements_di->DebugInfoArray.elements, len_di);
+ array_add(&elements_di->DebugInfoArray.elements, cap_di);
+ array_add(&elements_di->DebugInfoArray.elements, alloc_di);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_type_bit_field(irModule *module, Type *type, Entity *e, irDebugInfo *scope) {
+ GB_ASSERT(type->kind == Type_BitField || (type->kind == Type_Named && type->Named.base->kind == Type_BitField));
+
+ Type *bf_type = base_type(type);
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = is_type_named(type) ? type->Named.name : str_lit("bit_field");
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(bf_type);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ GB_ASSERT(bf_type->BitField.fields.count == bf_type->BitField.offsets.count &&
+ bf_type->BitField.fields.count == bf_type->BitField.sizes.count);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, bf_type->BitField.fields.count);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ for_array(field_index, bf_type->BitField.fields) {
+ Entity *field = bf_type->BitField.fields[field_index];
+ u32 offset = bf_type->BitField.offsets[field_index];
+ u32 size = bf_type->BitField.sizes[field_index];
+ String name = str_lit("field_todo");
+ if (field != nullptr && field->token.string.len > 0) {
+ name = field->token.string;
+ }
+ // TODO(lachsinc): t_i64 may not be safe to use for all bitfields?
+ irDebugInfo *field_di = ir_add_debug_info_field_internal(module, name, t_i64,
+ 0,
+ nullptr,
+ di);
+ // NOTE(lachsinc): Above calls BitFieldValues type_size_of() which returns size in bits,
+ // replace with its true bit value here..
+ field_di->DerivedType.size = size;
+ field_di->DerivedType.offset = offset; // Offset stored in bits already, no need to convert
+ field_di->DerivedType.flags = irDebugInfoFlag_Bitfield;
+ map_set(&module->debug_info, hash_pointer(field_di), field_di);
+ array_add(&elements_di->DebugInfoArray.elements, field_di);
+ }
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_type_bit_set(irModule *module, Type *type, Entity *e, irDebugInfo *scope) {
+ GB_ASSERT(type->kind == Type_BitSet || type->kind == Type_Named);
+
+ Type *base = base_type(type);
+
+ Type *named = nullptr;
+ if (type->kind == Type_Named) {
+ named = type;
+ }
+
+ Type *elem_type = nullptr;
+ if (base->BitSet.elem != nullptr) {
+ // TODO(lachsinc): Do bitsets have integration with non-primitive types other than enums?
+ elem_type = base->BitSet.elem;
+ if (elem_type->kind == Type_Enum) {
+ GB_ASSERT(elem_type->Enum.fields.count == base->BitSet.upper + 1);
+ }
+ }
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = named != nullptr ? named->Named.name : str_lit("bit_set");
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(base);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, base->BitSet.upper + 1);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ for (i64 i = 0; i <= base->BitSet.upper; ++i) {
+ u32 offset = cast(u32)i;
+ // TODO(lachsinc): Maybe name these fields numbered ascending?
+ String name = str_lit("field_todo");
+ if (elem_type != nullptr && is_type_enum(elem_type)) {
+ name = base_type(elem_type)->Enum.fields[i]->token.string;
+ }
+ irDebugInfo *field_di = ir_add_debug_info_field_internal(module, name, t_u32, // TODO(lachsinc): u32 fine??
+ 0,
+ nullptr,
+ di);
+ field_di->DerivedType.size = 1;
+ field_di->DerivedType.offset = offset; // Offset stored in bits already, no need to convert
+ field_di->DerivedType.flags = irDebugInfoFlag_Bitfield;
+ map_set(&module->debug_info, hash_pointer(field_di), field_di);
+ array_add(&elements_di->DebugInfoArray.elements, field_di);
+ }
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_type_string(irModule *module, irDebugInfo *scope, Entity *e, Type *type) {
+ // TODO(lachsinc): Does this only occur once ??
+ irDebugInfo **existing = map_get(&module->debug_info, hash_type(t_string));
+ if (existing != nullptr) {
+ GB_ASSERT((*existing)->kind == irDebugInfo_CompositeType);
+ return *existing;
+ } else {
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = type->Basic.name;
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(t_string);
+ di->CompositeType.align = ir_debug_align_bits(t_string);
+
+ map_set(&module->debug_info, hash_type(type), di);
+
+ // Field "data"
+ irDebugInfo *data_di = ir_add_debug_info_field_internal(module, str_lit("data"), t_cstring,
+ 0,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(data_di), data_di);
+
+ // Field "len"
+ irDebugInfo *len_di = ir_add_debug_info_field_internal(module, str_lit("len"), t_i64,
+ data_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(len_di), len_di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2);
+ array_add(&elements_di->DebugInfoArray.elements, data_di);
+ array_add(&elements_di->DebugInfoArray.elements, len_di);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ return di;
+ }
+}
+
+irDebugInfo *ir_add_debug_info_type_any(irModule *module) {
+ irDebugInfo **existing = map_get(&module->debug_info, hash_type(t_any));
+ if (existing != nullptr) {
+ GB_ASSERT((*existing)->kind == irDebugInfo_CompositeType);
+ return *existing;
+ } else {
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = t_any->Basic.name;
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(t_any);
+ di->CompositeType.align = ir_debug_align_bits(t_any);
+
+ map_set(&module->debug_info, hash_type(t_any), di);
+
+ // Field "data"
+ irDebugInfo *data_di = ir_add_debug_info_field_internal(module, str_lit("data"), t_rawptr,
+ 0,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(data_di), data_di);
+
+ // Field "id"
+ irDebugInfo *id_di = ir_add_debug_info_field_internal(module, str_lit("id"), t_typeid,
+ data_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(id_di), id_di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2);
+ array_add(&elements_di->DebugInfoArray.elements, data_di);
+ array_add(&elements_di->DebugInfoArray.elements, id_di);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ return di;
+ }
+}
+
+irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) {
+ GB_ASSERT(type->kind == Type_Basic && is_type_complex(type));
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ di->CompositeType.name = type->Basic.name;
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(type);
+
+ Type *field_type = nullptr;
+ if (type->Basic.kind == Basic_complex64) {
+ field_type = t_f32;
+ } else if (type->Basic.kind == Basic_complex128) {
+ field_type = t_f64;
+ } else {
+ GB_PANIC("Unreachable");
+ }
+
+ // Field "real"
+ irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type,
+ 0,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(real_di), real_di);
+
+ // Field "imag"
+ irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type,
+ real_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(imag_di), imag_di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2);
+ array_add(&elements_di->DebugInfoArray.elements, real_di);
+ array_add(&elements_di->DebugInfoArray.elements, imag_di);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_proc_type(irModule *module, Type *type) {
+ GB_ASSERT(type->kind == Type_Proc);
+
+ irDebugInfo **existing = map_get(&module->debug_info, hash_type(type));
+ if (existing != nullptr) {
+ GB_ASSERT((*existing)->kind == irDebugInfo_ProcType);
+ return *existing;
+ }
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_ProcType);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ isize result_count = type->Proc.result_count;
+ isize param_count = type->Proc.param_count;
+ // gb_max(result_count, 1) because llvm expects explicit "null" return type
+ di->ProcType.types = ir_add_debug_info_array(module, 0, gb_max(result_count, 1) + param_count);
+
+ // Result/return types
+ if (result_count >= 1) {
+ TypeTuple *results_tuple = &type->Proc.results->Tuple;
+ for_array(i, results_tuple->variables) {
+ Entity *e = results_tuple->variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+
+ irDebugInfo *type_di = ir_add_debug_info_type(module, e->type, e, nullptr, nullptr);
+ GB_ASSERT_NOT_NULL(type_di);
+ array_add(&di->ProcType.types->DebugInfoArray.elements, type_di);
+ }
+ } else {
+ // llvm expects "!{null}" for a function without return type, use nullptr to represent it.
+ array_add(&di->ProcType.types->DebugInfoArray.elements, (irDebugInfo*)nullptr);
+ }
+
+ // Param types
+ if (param_count >= 1) {
+ TypeTuple *params_tuple = &type->Proc.params->Tuple;
+ for_array(i, params_tuple->variables) {
+ Entity *e = params_tuple->variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+
+ irDebugInfo *type_di = ir_add_debug_info_type(module, e->type, e, nullptr, nullptr);
+ GB_ASSERT_NOT_NULL(type_di);
+ array_add(&di->ProcType.types->DebugInfoArray.elements, type_di);
+ }
+ }
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irDebugInfo *scope, irDebugInfo *file) {
+ // NOTE(lachsinc): Special handling for procedure pointers - we hash their types directly into DISubroutineType's
+ // but we need them interpreted as pointers when we use them as variables.
+ if (type->kind == Type_Proc) {
+ if (e->kind == Entity_Variable || e->kind == Entity_TypeName) {
+ // TODO(lachsinc): Wasteful (maybe?). Create a derived type for _every_ different proc ptr type
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ map_set(&module->debug_info, hash_pointer(di), di);
+ di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+ di->DerivedType.size = ir_debug_size_bits(t_rawptr);
+ di->DerivedType.base_type = ir_add_debug_info_proc_type(module, type);
+ return di;
+ } else {
+ GB_PANIC("Proc definitions should have their type created manually (not through this function)");
+ }
+ }
+
+ irDebugInfo **existing = map_get(&module->debug_info, hash_type(type));
+ if (existing != nullptr) {
+ return *existing;
+ }
+
+ // Reset entity/location info, if applicable, for every type we try add.
+ // TODO(lachsinc): Confirm this doesn't mess up field's scopes etc.
+ if (type->kind == Type_Named) {
+ e = type->Named.type_name;
+ if (e) {
+ CheckerInfo *info = module->info;
+ file = ir_add_debug_info_file(module, ast_file_of_filename(info, e->token.pos.file));
+ // TODO(lachsinc): Determine proper scope for type declaration location stuff.
+ scope = file;
+ }
+ }
+
+ // TODO(lachsinc): Reorder if tests, "unique" types, like basic etc. should go last, they are most likely to hit the existing hashed type
+ // and no point checking them for the rest of the types. Or just use a massive switch...
+
+ // NOTE(lachsinc): Types should be inserted into debug_info map as their named, not base_type()'d counterparts.
+ Type *base = base_type(type);
+
+ if (type->kind == Type_Named) {
+ Type *named_base = type->Named.base;
+ // TODO(lachsinc): Better way to determine distinct etc. or just handle structs, enums before we reach here.
+ // ir_is_type_aggregate() except with no call to base_type().
+ if (named_base->kind != Type_Struct &&
+ named_base->kind != Type_Union &&
+ named_base->kind != Type_Enum &&
+ named_base->kind != Type_BitField &&
+ named_base->kind != Type_Tuple) {
+ // distinct / typedef etc.
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ if (type->kind == Type_Named) {
+ di->DerivedType.name = type->Named.name;
+ } else if (named_base->kind == Type_Basic) {
+ di->DerivedType.name = named_base->Basic.name;
+ }
+ di->DerivedType.tag = irDebugBasicEncoding_typedef;
+ map_set(&module->debug_info, hash_type(type), di);
+ // TODO(lachsinc): Do we need to try and resolve a new entity/scope for the base type?
+ // Maybe we also want to pull out type->Named.type_name ?? in the case it is a Named
+ di->DerivedType.base_type = ir_add_debug_info_type(module, named_base, e, scope, file);
+ return di;
+ }
+ }
+
+ if (type->kind == Type_Basic) {
+ switch (type->Basic.kind) {
+ // Composite basic types
+ case Basic_complex64:
+ case Basic_complex128: return ir_add_debug_info_type_complex(module, type);
+ case Basic_string: return ir_add_debug_info_type_string(module, scope, e, type);
+ case Basic_any: return ir_add_debug_info_type_any(module);
+
+ // Derived basic types
+ case Basic_cstring:
+ case Basic_rawptr: {
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ di->DerivedType.name = type->Basic.name;
+ di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+ di->DerivedType.size = ir_debug_size_bits(t_rawptr);
+ di->DerivedType.align = ir_debug_align_bits(t_rawptr); // TODO(lachsinc): Not sure if align is required.
+ map_set(&module->debug_info, hash_type(type), di);
+ if (type->Basic.kind == Basic_cstring) {
+ di->DerivedType.base_type = ir_add_debug_info_type(module, t_i8, e, scope, file);
+ } else {
+ // NOTE(lachsinc): llvm expects "null" for rawptr/voidptr
+ }
+ return di;
+ }
+
+ // Basic basic types
+ default: {
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_BasicType);
+ di->BasicType.encoding = ir_debug_encoding_for_basic(type->Basic.kind);
+ di->BasicType.name = type->Basic.name;
+ di->BasicType.size = ir_debug_size_bits(type);
+ di->BasicType.align = ir_debug_align_bits(type);
+ map_set(&module->debug_info, hash_type(type), di);
+ return di;
+ }
+ }
+ }
+
+ if (is_type_pointer(type)) {
+ // TODO(lachsinc): Ensure this handles pointer-to-pointer of same type etc. correctly.
+ Type *deref = type_deref(base);
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+ di->DerivedType.size = ir_debug_size_bits(type);
+ // NOTE(lachsinc): Map set before creating base_type to avoid circular dependency issues.
+ map_set(&module->debug_info, hash_type(type), di);
+ if (is_type_struct(deref)) {
+ int i = 123;
+ }
+ di->DerivedType.base_type = ir_add_debug_info_type(module, deref, e, scope, file);
+ return di;
+ }
+
+ if (is_type_struct(type) || is_type_union(type) || is_type_enum(type) || is_type_tuple(type)) {
+ if (type->kind == Type_Named) {
+ // NOTE(lachsinc): Named named's should always be handled prior as typedefs.
+ GB_ASSERT(type->Named.base->kind != Type_Named);
+ }
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ // NOTE(lachsinc): Set map value before resolving field types to avoid circular dependencies.
+ map_set(&module->debug_info, hash_type(type), di);
+ if (is_type_named(type)) {
+ di->CompositeType.name = type->kind == Type_Named ? type->Named.name : type->Basic.name;
+ }
+ if (e) {
+ di->CompositeType.file = file;
+ di->CompositeType.scope = scope;
+ di->CompositeType.pos = e->token.pos;
+ }
+ di->CompositeType.size = ir_debug_size_bits(type);
+ // di->CompositeType.align = ir_debug_align_bits(type); // TODO(lachsinc): Necessary?
+
+ if (is_type_struct(type)) {
+ GB_ASSERT(base->kind == Type_Struct);
+ if (!is_type_named(type)) {
+ di->CompositeType.name = str_lit("struct");
+ GB_ASSERT_NOT_NULL(scope);
+ di->CompositeType.scope = scope;
+ }
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ 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->DebugInfoArray.elements,
+ ir_add_debug_info_field(module, di, base->Struct.fields[field_index], type,
+ cast(i32)field_index, base->Struct.fields[field_index]->type, file));
+ }
+ } else if (is_type_union(type)) {
+ GB_ASSERT(base->kind == Type_Union);
+ if (!is_type_named(type)) {
+ di->CompositeType.name = str_lit("union");
+ GB_ASSERT_NOT_NULL(scope);
+ di->CompositeType.scope = scope;
+ }
+ di->CompositeType.tag = irDebugBasicEncoding_union_type;
+ di->CompositeType.elements = ir_add_debug_info_array(module, 0, base->Union.variants.count);
+ // TODO(lachsinc): Cleanup; this should be handled in a more generic manner for all types.
+ file = ir_add_debug_info_file(module, base->Union.node->file);
+ GB_ASSERT_NOT_NULL(file); // Union debug info requires file info
+ di->CompositeType.file = file;
+ di->CompositeType.pos = base->Union.node->UnionType.token.pos;
+ for_array(field_index, base->Union.variants) {
+ array_add(&di->CompositeType.elements->DebugInfoArray.elements,
+ ir_add_debug_info_field(module, di, nullptr, type, cast(i32)field_index,
+ base->Union.variants[field_index], file));
+ }
+ } else if (is_type_enum(type)) {
+ GB_ASSERT(base->kind == Type_Enum);
+ if (!is_type_named(type)) {
+ di->CompositeType.name = str_lit("enum");
+ GB_ASSERT_NOT_NULL(scope);
+ di->CompositeType.scope = scope;
+ }
+ di->CompositeType.tag = irDebugBasicEncoding_enumeration_type;
+ di->CompositeType.base_type = ir_add_debug_info_type(module, base->Enum.base_type, e, scope, file);
+ 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->DebugInfoArray.elements,
+ ir_add_debug_info_enumerator(module, base->Enum.fields[field_index]));
+ }
+
+ // TODO(lachsinc): Do we want to ensure this is an enum in the global scope before
+ // adding it into the modules enum array ??
+ array_add(&module->debug_compile_unit->CompileUnit.enums->DebugInfoArray.elements, di);
+ } else if (is_type_tuple(type)) {
+ GB_ASSERT(base->kind == Type_Tuple);
+ if (!is_type_named(type)) {
+ di->CompositeType.name = str_lit("tuple");
+ GB_ASSERT_NOT_NULL(scope);
+ di->CompositeType.scope = scope;
+ }
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.elements = ir_add_debug_info_array(module, 0, base->Tuple.variables.count);
+ // TODO(lachsinc): Ensure offsets are set properly?
+ for_array(var_index, base->Tuple.variables) {
+ array_add(&di->CompositeType.elements->DebugInfoArray.elements,
+ ir_add_debug_info_field(module, di, base->Tuple.variables[var_index], type,
+ cast(i32)var_index, base->Tuple.variables[var_index]->type, file));
+ }
+ }
+
+ return di;
+ }
+
+ if (is_type_dynamic_array(type)) {
+ return ir_add_debug_info_type_dynamic_array(module, type, e, scope, file);
+ }
+
+ if (is_type_array(type)) {
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.size = ir_debug_size_bits(type);
+ di->CompositeType.align = ir_debug_align_bits(type);
+ di->CompositeType.tag = irDebugBasicEncoding_array_type;
+ di->CompositeType.array_count = (i32)type->Array.count;
+
+ map_set(&module->debug_info, hash_type(type), di);
+ di->CompositeType.base_type = ir_add_debug_info_type(module, type->Array.elem, e, scope, file);
+ GB_ASSERT(base->kind != Type_Named);
+
+ return di;
+ }
+
+ if (is_type_slice(type)) {
+ // NOTE(lachsinc): Every slice type has its own composite type / field debug infos created. This is sorta wasteful.
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType);
+ di->CompositeType.name = str_lit("slice");
+ di->CompositeType.tag = irDebugBasicEncoding_structure_type;
+ di->CompositeType.size = ir_debug_size_bits(type); // TODO(lachsinc): Correct ??
+ di->CompositeType.align = ir_debug_align_bits(type);
+ map_set(&module->debug_info, hash_type(type), di);
+
+ // Data pointer type
+ irDebugInfo *data_ptr_di = ir_alloc_debug_info(irDebugInfo_DerivedType);
+ Type *elem_type = type->Slice.elem;
+ if (is_type_named(elem_type)) {
+ data_ptr_di->DerivedType.name = elem_type->kind == Type_Named ? elem_type->Named.name : elem_type->Basic.name;
+ }
+ data_ptr_di->DerivedType.tag = irDebugBasicEncoding_pointer_type;
+ data_ptr_di->DerivedType.size = ir_debug_size_bits(t_rawptr);
+ map_set(&module->debug_info, hash_pointer(data_ptr_di), data_ptr_di);
+ data_ptr_di->DerivedType.base_type = ir_add_debug_info_type(module, elem_type, e, scope, file);
+
+ irDebugInfo *data_di = ir_add_debug_info_field_internal(module, str_lit("data"), t_rawptr,
+ 0,
+ nullptr,
+ di);
+ data_di->DerivedType.base_type = data_ptr_di;
+ map_set(&module->debug_info, hash_pointer(data_di), data_di);
+
+ irDebugInfo *len_di = ir_add_debug_info_field_internal(module, str_lit("len"), t_int,
+ data_di->DerivedType.size,
+ nullptr,
+ di);
+ map_set(&module->debug_info, hash_pointer(len_di), len_di);
+
+ irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2);
+ array_add(&elements_di->DebugInfoArray.elements, data_di);
+ array_add(&elements_di->DebugInfoArray.elements, len_di);
+ di->CompositeType.elements = elements_di;
+ map_set(&module->debug_info, hash_pointer(elements_di), elements_di);
+
+ return di;
+ }
+
+ if (is_type_map(type)) {
+ // TODO(lachsinc): Looks like "generated_struct_type" map.entries.data is just a u8*, we could
+ // always look at the map header and create the debug info manually (if we
+ // want struct members to be interpreted as the correct type).
+ // Also; are hashes meant to be interpreted as bool*'s ?? or is that simply slot occupied data?
+ return ir_add_debug_info_type(module, type->Map.generated_struct_type, e, scope, file);
+ }
+
+ // NOTE(lachsinc): For now we just interpret all BitFieldValues as i64 inside ir_add_debug_info_type_bit_field().
+ /*
+ if (is_type_bit_field_value(type)) {
+ // NOTE(Lachsinc): Suboptimal; creates a new type for each unique bit field value type
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_BasicType);
+ di->BasicType.encoding = irDebugBasicEncoding_unsigned;
+ // di->BasicType.name = str_lit("todo");
+ di->BasicType.size = base->BitFieldValue.bits;
+ map_set(&module->debug_info, hash_type(type), di);
+ return di;
+ }
+ */
+
+ if (is_type_bit_field(type)) {
+ return ir_add_debug_info_type_bit_field(module, type, e, scope);
+ }
+
+ if (is_type_bit_set(type)) {
+ return ir_add_debug_info_type_bit_set(module, type, e, scope);
+ }
+
+ GB_PANIC("Unreachable");
+ return nullptr;
+}
+
+irDebugInfo *ir_add_debug_info_global(irModule *module, irValue *v) {
+ if (!module->generate_debug_info) {
+ return nullptr;
+ }
+
+ Entity *e = v->Global.entity;
+
+ // NOTE(lachsinc): Just to be safe/robust; globals are likely added once only?
+ irDebugInfo **existing = map_get(&module->debug_info, hash_entity(e));
+ if (existing != nullptr) {
+ return *existing;
+ }
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_GlobalVariableExpression);
+ map_set(&module->debug_info, hash_entity(e), di);
+
+ // Create or fetch file debug info.
+ CheckerInfo *info = module->info;
+ String filename = e->token.pos.file;
+ AstFile *f = ast_file_of_filename(info, filename);
+ GB_ASSERT_NOT_NULL(f);
+ irDebugInfo *scope = ir_add_debug_info_file(module, f);
+
+ irDebugInfo *var_di = ir_alloc_debug_info(irDebugInfo_GlobalVariable);
+ var_di->GlobalVariable.name = e->token.string;
+ var_di->GlobalVariable.scope = scope;
+ var_di->GlobalVariable.file = scope;
+ var_di->GlobalVariable.pos = e->token.pos;
+ var_di->GlobalVariable.variable = v;
+
+ // NOTE(lachsinc): The "DIGlobalVariableExpression" owns us, and is what we refer to from other
+ // locations in the ir source, so we will reserve the "e" hash for it, and use something else
+ // unique for the DIGlobalVariable's hash.
+ map_set(&module->debug_info, hash_pointer(var_di), var_di);
+
+ var_di->GlobalVariable.type = ir_add_debug_info_type(module, e->type, nullptr, nullptr, nullptr);
+ GB_ASSERT_NOT_NULL(var_di->GlobalVariable.type);
+
+ di->GlobalVariableExpression.var = var_di;
+
+ array_add(&module->debug_compile_unit->CompileUnit.globals->DebugInfoArray.elements, di);
+
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_block(irProcedure *proc, Scope *scope) {
+ irModule *module = proc->module;
+
+ irDebugInfo **existing = map_get(&module->debug_info, hash_pointer(scope));
+ if (existing != nullptr) {
+ GB_ASSERT((*existing)->kind == irDebugInfo_LexicalBlock);
+ return *existing;
+ }
+
+ Ast *block = scope->node;
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_LexicalBlock);
+ di->LexicalBlock.file = proc->debug_scope->Proc.file;
+ di->LexicalBlock.scope = proc->debug_scope;
+ di->LexicalBlock.pos = ast_token(block).pos;
+ map_set(&module->debug_info, hash_pointer(scope), di);
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id) {
+ // TODO(lachsinc): Not sure if this handles generated locals properly as they may not have
+ // enough information contained inside "e".
+
+ irModule *module = proc->module;
+ if (!module->generate_debug_info) {
+ return nullptr;
+ }
+
+ irDebugInfo *scope = nullptr;
+ irDebugInfo *file = nullptr;
+ if (e->scope && e->scope->node->kind == Ast_ProcType) {
+ scope = proc->debug_scope;
+ file = proc->debug_scope->Proc.file;
+ } else {
+ scope = ir_add_debug_info_block(proc, e->scope);
+ file = scope->LexicalBlock.file;
+ }
+ GB_ASSERT_NOT_NULL(scope);
+ GB_ASSERT_NOT_NULL(file);
+
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_LocalVariable);
+ di->LocalVariable.name = e->token.string;
+ di->LocalVariable.scope = scope;
+ di->LocalVariable.file = file;
+ di->LocalVariable.pos = e->token.pos;
+ di->LocalVariable.arg = arg_id;
+ di->LocalVariable.type = ir_add_debug_info_type(module, e->type, e, scope, file); // TODO(lachsinc): Is this the correct entity to pass? Or do we want a TypeName ??
+
+ map_set(&module->debug_info, hash_entity(e), di);
+ return di;
+}
+
+irDebugInfo *ir_add_debug_info_proc(irProcedure *proc) {
+ irModule *module = proc->module;
+ if (!module->generate_debug_info) {
+ return nullptr;
+ }
+
+ Entity *entity = proc->entity;
+
+ // Add / retrieve debug info for file.
+ CheckerInfo *info = proc->module->info;
+ String filename = proc->entity->token.pos.file;
+ AstFile *f = ast_file_of_filename(info, filename);
+ irDebugInfo *file = nullptr;
+ if (f) {
+ file = ir_add_debug_info_file(proc->module, f);
+ }
+ // TODO(lachsinc): Should scope be made separate to file?
+ irDebugInfo *scope = file;
- GB_ASSERT(entity != nullptr);
irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Proc);
+ map_set(&proc->module->debug_info, hash_entity(entity), di);
di->Proc.entity = entity;
- di->Proc.name = name;
+ di->Proc.name = proc->name;
di->Proc.file = file;
di->Proc.pos = entity->token.pos;
+ di->Proc.type = ir_add_debug_info_proc_type(proc->module, proc->type);
proc->debug_scope = di;
+ return di;
+}
- map_set(&proc->module->debug_info, hash_entity(entity), di);
+irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *scope) {
+ if (node == nullptr || scope == nullptr) {
+ return nullptr;
+ }
+ // TODO(lachsinc): Should we traverse the node/children until we find one with
+ // valid token/pos and use that instead??
+ irDebugInfo **existing = map_get(&m->debug_info, hash_node(node));
+ if (existing != nullptr) {
+ return *existing;
+ }
+ irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Location);
+ di->Location.pos = ast_token(node).pos;
+ di->Location.scope = scope;
+ map_set(&m->debug_info, hash_node(node), di);
return di;
}
+void ir_push_debug_location(irModule *m, Ast *node, irDebugInfo *scope) {
+ irDebugInfo *debug_location = ir_add_debug_info_location(m, node, scope);
+ array_add(&m->debug_location_stack, debug_location);
+}
+
+void ir_pop_debug_location(irModule *m) {
+ GB_ASSERT_MSG(m->debug_location_stack.count > 0, "Attempt to pop debug location stack too many times");
+ array_pop(&m->debug_location_stack);
+}
+
////////////////////////////////////////////////////////////////
//
// @Emit
//
////////////////////////////////////////////////////////////////
-irValue *ir_emit_runtime_call (irProcedure *proc, char const *name_, Array<irValue *> args, Ast *expr = nullptr);
+irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array<irValue *> args, Ast *expr = nullptr);
irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr = nullptr);
@@ -1677,10 +2643,20 @@ irValue *ir_emit_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) {
return ir_emit(p, ir_instr_select(p, cond, t, f));
}
-void ir_add_debug_location_to_value(irProcedure *proc, irValue *v, Ast *e) {
- if (v != nullptr && e != nullptr) {
- v->loc.debug_scope = proc->debug_scope;
- v->loc.pos = ast_token(e).pos;
+void ir_value_set_debug_location(irProcedure *proc, irValue *v) {
+ GB_ASSERT_NOT_NULL(proc);
+ GB_ASSERT_NOT_NULL(v);
+
+ if (v->loc != nullptr) {
+ return; // Already set
+ }
+
+ irModule *m = proc->module;
+ GB_ASSERT(m->debug_location_stack.count > 0);
+ v->loc = *array_end_ptr(&m->debug_location_stack);
+ if (v->loc == nullptr) {
+ // NOTE(lachsinc): Entry point (main()) and runtime_startup are the only ones where null location is considered valid.
+ GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
}
}
@@ -1836,7 +2812,6 @@ irValue *ir_emit_runtime_call(irProcedure *proc, char const *name_, Array<irValu
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
irValue *gp = *found;
irValue *call = ir_emit_call(proc, gp, args);
- ir_add_debug_location_to_value(proc, call, expr);
return call;
}
irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char const *name_, Array<irValue *> args, Ast *expr) {
@@ -1849,7 +2824,6 @@ irValue *ir_emit_package_call(irProcedure *proc, char const *package_name_, char
GB_ASSERT_MSG(found != nullptr, "%.*s", LIT(name));
irValue *gp = *found;
irValue *call = ir_emit_call(proc, gp, args);
- ir_add_debug_location_to_value(proc, call, expr);
return call;
}
@@ -4892,12 +5866,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr);
irValue *ir_build_expr(irProcedure *proc, Ast *expr) {
irValue *v = ir_build_expr_internal(proc, expr);
- ir_add_debug_location_to_value(proc, v, expr);
return v;
}
irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
expr = unparen_expr(expr);
+ // ir_push_debug_location(proc->module, expr, proc->debug_scope);
+ // defer (ir_pop_debug_location(proc->module));
TypeAndValue tv = type_and_value_of_expr(expr);
GB_ASSERT(tv.mode != Addressing_Invalid);
@@ -6488,7 +7463,9 @@ void ir_build_stmt(irProcedure *proc, Ast *node) {
proc->module->stmt_state_flags = out;
}
+ ir_push_debug_location(proc->module, node, proc->debug_scope);
ir_build_stmt_internal(proc, node);
+ ir_pop_debug_location(proc->module);
proc->module->stmt_state_flags = prev_stmt_state_flags;
}
@@ -7558,6 +8535,19 @@ void ir_begin_procedure_body(irProcedure *proc) {
}
}
+ // NOTE(lachsinc): This is somewhat of a fallback/catch-all; We use the procedure's identifer as a debug location..
+ // Additional debug locations should be pushed for the procedures statements/expressions themselves.
+ if (proc->module->generate_debug_info && proc->entity && proc->entity->identifier) { // TODO(lachsinc): Better way to determine if these procs are main/runtime_startup.
+ // TODO(lachsinc): Passing the file for the scope may not be correct for nested procedures? This should probably be
+ // handled all inside push_debug_location, with just the Ast * we can pull out everything we need to construct scope/file debug info etc.
+ ir_add_debug_info_proc(proc);
+ ir_push_debug_location(proc->module, proc->entity->identifier, proc->debug_scope);
+ GB_ASSERT_NOT_NULL(proc->debug_scope);
+ } else {
+ // GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
+ ir_push_debug_location(proc->module, nullptr, nullptr);
+ }
+
proc->decl_block = ir_new_block(proc, proc->type_expr, "decls");
ir_start_block(proc, proc->decl_block);
proc->entry_block = ir_new_block(proc, proc->type_expr, "entry");
@@ -7603,7 +8593,7 @@ void ir_begin_procedure_body(irProcedure *proc) {
Type *abi_type = proc->type->Proc.abi_compat_params[i];
if (e->token.string != "" && !is_blank_ident(e->token)) {
- irValue *param = ir_add_param(proc, e, name, abi_type);
+ irValue *param = ir_add_param(proc, e, name, abi_type, cast(i32)(i+1));
array_add(&proc->params, param);
}
}
@@ -7620,7 +8610,7 @@ void ir_begin_procedure_body(irProcedure *proc) {
abi_type = abi_types[i];
}
if (e->token.string != "" && !is_blank_ident(e->token)) {
- irValue *param = ir_add_param(proc, e, nullptr, abi_type);
+ irValue *param = ir_add_param(proc, e, nullptr, abi_type, cast(i32)(i+1));
array_add(&proc->params, param);
}
}
@@ -7685,6 +8675,8 @@ void ir_end_procedure_body(irProcedure *proc) {
proc->curr_block = nullptr;
ir_number_proc_registers(proc);
+
+ ir_pop_debug_location(proc->module);
}
@@ -7701,29 +8693,6 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
proc->parent = parent;
- if (proc->entity != nullptr) {
- irModule *m = proc->module;
- CheckerInfo *info = m->info;
- Entity *e = proc->entity;
- String filename = e->token.pos.file;
- AstFile *f = ast_file_of_filename(info, filename);
-
- proc->is_export = e->Procedure.is_export;
- proc->is_foreign = e->Procedure.is_foreign;
-
- irDebugInfo *di_file = nullptr;
-
- irDebugInfo **di_file_found = map_get(&m->debug_info, hash_ast_file(f));
- if (di_file_found) {
- di_file = *di_file_found;
- GB_ASSERT(di_file->kind == irDebugInfo_File);
- } else {
- di_file = ir_add_debug_info_file(proc, f);
- }
-
- ir_add_debug_info_proc(proc, e, proc->name, di_file);
- }
-
if (proc->body != nullptr) {
u64 prev_stmt_state_flags = proc->module->stmt_state_flags;
@@ -7748,6 +8717,8 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
proc->module->stmt_state_flags = prev_stmt_state_flags;
}
+ // NOTE(lachsinc): For now we pop the debug location inside ir_end_procedure_body().
+ // This may result in debug info being missing for below.
if (proc->type->Proc.has_proc_default_values) {
auto *p = &proc->type->Proc;
@@ -7797,6 +8768,12 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
void ir_module_add_value(irModule *m, Entity *e, irValue *v) {
map_set(&m->values, hash_entity(e), v);
+ // TODO(lachsinc): This may not be the most sensible place to do this!
+ // it may be more sensible to look for more specific locations that call ir_value_global and assign it a value? maybe?
+ // ir_value_global itself doesn't have access to module though.
+ if (v->kind == irValue_Global && v->Global.value != nullptr && e->state == EntityState_Resolved) {
+ ir_add_debug_info_global(m, v);
+ }
}
void ir_init_module(irModule *m, Checker *c) {
@@ -7917,6 +8894,18 @@ void ir_init_module(irModule *m, Checker *c) {
map_set(&m->debug_info, hash_pointer(m), di);
m->debug_compile_unit = di;
+
+ irDebugInfo *enums_di = ir_alloc_debug_info(irDebugInfo_DebugInfoArray);
+ array_init(&enums_di->DebugInfoArray.elements, heap_allocator()); // TODO(lachsinc): ir_allocator() ??
+ map_set(&m->debug_info, hash_pointer(enums_di), enums_di);
+ m->debug_compile_unit->CompileUnit.enums = enums_di;
+
+ irDebugInfo *globals_di = ir_alloc_debug_info(irDebugInfo_DebugInfoArray);
+ array_init(&globals_di->DebugInfoArray.elements, heap_allocator()); // TODO(lachsinc): ir_allocator() ??
+ map_set(&m->debug_info, hash_pointer(globals_di), globals_di);
+ m->debug_compile_unit->CompileUnit.globals = globals_di;
+
+ array_init(&m->debug_location_stack, heap_allocator()); // TODO(lachsinc): ir_allocator() ??
}
}
@@ -7930,6 +8919,7 @@ void ir_destroy_module(irModule *m) {
array_free(&m->procs);
array_free(&m->procs_to_generate);
array_free(&m->foreign_library_paths);
+ array_free(&m->debug_location_stack);
gb_arena_free(&m->tmp_arena);
}
@@ -8687,27 +9677,6 @@ void ir_gen_tree(irGen *s) {
irDebugInfo *compile_unit = m->debug_info.entries[0].value;
GB_ASSERT(compile_unit->kind == irDebugInfo_CompileUnit);
- irDebugInfo *all_procs = ir_alloc_debug_info(irDebugInfo_AllProcs);
-
- isize all_proc_max_count = 0;
- for_array(i, m->debug_info.entries) {
- irDebugInfo *di = m->debug_info.entries[i].value;
- if (di->kind == irDebugInfo_Proc) {
- all_proc_max_count++;
- }
- }
-
- array_init(&all_procs->AllProcs.procs, ir_allocator(), 0, all_proc_max_count);
- map_set(&m->debug_info, hash_pointer(all_procs), all_procs); // NOTE(bill): This doesn't need to be mapped
- compile_unit->CompileUnit.all_procs = all_procs;
-
-
- for_array(i, m->debug_info.entries) {
- irDebugInfo *di = m->debug_info.entries[i].value;
- if (di->kind == irDebugInfo_Proc) {
- array_add(&all_procs->AllProcs.procs, di);
- }
- }
#if defined(GB_SYSTEM_WINDOWS)
@@ -9008,6 +9977,8 @@ void ir_gen_tree(irGen *s) {
ir_build_proc(p, p->Proc.parent);
}
+ GB_ASSERT_MSG(m->debug_location_stack.count == 0, "Debug location stack contains unpopped entries.");
+
// Number debug info
for_array(i, m->debug_info.entries) {
auto *entry = &m->debug_info.entries[i];
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index aec43b276..7c01d6a46 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -212,32 +212,25 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
}
-bool ir_print_debug_location(irFileBuffer *f, irModule *m, irValue *v, irProcedure *proc = nullptr) {
-#if 1
- if (m->generate_debug_info && v != nullptr) {
- TokenPos pos = v->loc.pos;
- irDebugInfo *scope = v->loc.debug_scope;
- i32 id = 0;
- if (scope != nullptr) {
- id = scope->id;
- } else if (proc != nullptr) {
- if (proc->debug_scope != nullptr) {
- id = proc->debug_scope->id;
- pos = proc->entity->token.pos;
- }
- }
- if (id > 0 && pos.line > 0) {
- ir_fprintf(f, ", !dbg !DILocation(line: %td, column: %td, scope: !%d)", pos.line, pos.column, id);
- return true;
- }
+bool ir_print_debug_location(irFileBuffer *f, irModule *m, irValue *v) {
+ if (!m->generate_debug_info) {
+ return false;
+ }
+
+ GB_ASSERT_NOT_NULL(v);
+ GB_ASSERT(v->kind == irValue_Instr);
+
+ if (v->loc != nullptr) {
+ GB_ASSERT(v->loc->kind == irDebugInfo_Location);
+ ir_fprintf(f, ", !dbg !%d", v->loc->id);
+ return true;
+ } else {
+ irProcedure *proc = v->Instr.block->proc;
+ GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
}
return false;
-#else
- return true;
-#endif
}
-
void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct = false);
void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint);
@@ -519,6 +512,36 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
}
}
+void ir_print_debug_encoding(irFileBuffer *f, irDebugInfoKind kind, irDebugEncoding encoding) {
+ switch (kind) {
+ case irDebugInfo_BasicType:
+ switch (encoding) {
+ case irDebugBasicEncoding_address: ir_write_str_lit(f, "DW_ATE_address"); return;
+ case irDebugBasicEncoding_boolean: ir_write_str_lit(f, "DW_ATE_boolean"); return;
+ case irDebugBasicEncoding_float: ir_write_str_lit(f, "DW_ATE_float"); return;
+ case irDebugBasicEncoding_signed: ir_write_str_lit(f, "DW_ATE_signed"); return;
+ case irDebugBasicEncoding_signed_char: ir_write_str_lit(f, "DW_ATE_signed_char"); return;
+ case irDebugBasicEncoding_unsigned: ir_write_str_lit(f, "DW_ATE_unsigned"); return;
+ case irDebugBasicEncoding_unsigned_char: ir_write_str_lit(f, "DW_ATE_unsigned_char"); return;
+ }
+ case irDebugInfo_DerivedType:
+ switch (encoding) {
+ case irDebugBasicEncoding_member: ir_write_str_lit(f, "DW_TAG_member"); return;
+ case irDebugBasicEncoding_pointer_type: ir_write_str_lit(f, "DW_TAG_pointer_type"); return;
+ case irDebugBasicEncoding_typedef: ir_write_str_lit(f, "DW_TAG_typedef"); return;
+ }
+ case irDebugInfo_CompositeType:
+ switch (encoding) {
+ case irDebugBasicEncoding_array_type: ir_write_str_lit(f, "DW_TAG_array_type"); return;
+ case irDebugBasicEncoding_enumeration_type: ir_write_str_lit(f, "DW_TAG_enumeration_type"); return;
+ case irDebugBasicEncoding_structure_type: ir_write_str_lit(f, "DW_TAG_structure_type"); return;
+ case irDebugBasicEncoding_union_type: ir_write_str_lit(f, "DW_TAG_union_type"); return;
+ }
+ }
+
+ GB_PANIC("Unreachable");
+}
+
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type);
void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type *elem_type) {
@@ -1755,7 +1778,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case ProcInlining_inline: ir_write_str_lit(f, " alwaysinline"); break;
case ProcInlining_no_inline: ir_write_str_lit(f, " noinline"); break;
}
- ir_print_debug_location(f, m, value, instr->block->proc);
+ ir_print_debug_location(f, m, value);
break;
}
@@ -1782,22 +1805,22 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
irInstrDebugDeclare *dd = &instr->DebugDeclare;
Type *vt = ir_type(dd->value);
- irDebugInfo *di = dd->scope;
Entity *e = dd->entity;
String name = e->token.string;
TokenPos pos = e->token.pos;
+ irDebugInfo **lookup_di = map_get(&m->debug_info, hash_entity(e));
+ GB_ASSERT_NOT_NULL(*lookup_di);
+ irDebugInfo* local_var_di = *lookup_di;
+
ir_write_str_lit(f, "call void @llvm.dbg.declare(");
ir_write_str_lit(f, "metadata ");
ir_print_type(f, m, vt);
ir_write_byte(f, ' ');
ir_print_value(f, m, dd->value, vt);
- ir_write_str_lit(f, ", metadata !DILocalVariable(name: \"");
- ir_print_escape_string(f, name, false, false);
- ir_fprintf(f, "\", scope: !%d, line: %td)", di->id, pos.line);
- ir_write_str_lit(f, ", metadata !DIExpression()");
- ir_write_byte(f, ')');
- ir_fprintf(f, ", !dbg !DILocation(line: %td, column: %td, scope: !%d)", pos.line, pos.column, di->id);
+ ir_fprintf(f, ", metadata !%d", local_var_di->id);
+ ir_write_str_lit(f, ", metadata !DIExpression())");
+ ir_print_debug_location(f, m, value);
break;
}
}
@@ -1913,7 +1936,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
if (di_ != nullptr) {
irDebugInfo *di = *di_;
GB_ASSERT(di->kind == irDebugInfo_Proc);
- ir_fprintf(f, "!dbg !%d ", di->id);
+ ir_fprintf(f, "!dbg !%d ", di->id); // TODO(lachsinc): !dbg
}
}
@@ -2033,7 +2056,7 @@ void print_llvm_ir(irGen *ir) {
ir_print_type(f, m, t_typeid);
ir_write_str_lit(f, "} ; Basic_any\n");
- ir_write_str_lit(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n");
+ ir_write_str_lit(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) #3 \n");
ir_write_byte(f, '\n');
@@ -2140,13 +2163,24 @@ void print_llvm_ir(irGen *ir) {
} else {
ir_write_string(f, str_lit("zeroinitializer"));
}
+ if (m->generate_debug_info) {
+ irDebugInfo **di_lookup = map_get(&m->debug_info, hash_entity(g->entity));
+ if (di_lookup != nullptr) {
+ irDebugInfo *di = *di_lookup;
+ GB_ASSERT(di);
+ GB_ASSERT(di->kind == irDebugInfo_GlobalVariableExpression);
+ ir_fprintf(f, ", !dbg !%d", di->id);
+ }
+ }
}
ir_write_byte(f, '\n');
}
+ // TODO(lachsinc): Attribute map inside ir module?
ir_fprintf(f, "attributes #0 = {nounwind uwtable}\n");
ir_fprintf(f, "attributes #1 = {nounwind alwaysinline uwtable}\n");
ir_fprintf(f, "attributes #2 = {nounwind noinline optnone uwtable}\n");
+ ir_fprintf(f, "attributes #3 = {nounwind readnone}\n");
if (m->generate_debug_info) {
ir_write_byte(f, '\n');
@@ -2178,16 +2212,17 @@ void print_llvm_ir(irGen *ir) {
"language: DW_LANG_C_plus_plus" // Is this good enough?
", file: !%d"
", producer: \"Odin %.*s\""
- ", flags: \"\""
", runtimeVersion: 0"
", isOptimized: false"
", emissionKind: FullDebug"
- ", retainedTypes: !0"
- ", enums: !0"
- ", globals: !0"
+ ", retainedTypes: !0" // TODO(lachsinc)
+ ", enums: !%d"
+ ", globals: !%d"
")",
- file->id, LIT(build_context.ODIN_VERSION));
-
+ file->id,
+ LIT(build_context.ODIN_VERSION),
+ m->debug_compile_unit->CompileUnit.enums->id,
+ m->debug_compile_unit->CompileUnit.globals->id);
break;
}
case irDebugInfo_File:
@@ -2197,29 +2232,205 @@ void print_llvm_ir(irGen *ir) {
ir_fprintf(f, ")");
break;
case irDebugInfo_Proc:
+ // TODO(lachsinc): We need to store scope info inside di, not just file info, for procs.
+ // Should all subprograms have distinct ??
ir_fprintf(f, "distinct !DISubprogram("
"name: \"%.*s\""
", linkageName: \"%.*s\""
+ ", scope: !%d"
", file: !%d"
", line: %td"
+ ", scopeLine: %td"
", isDefinition: true"
- ", isLocal: true"
+ ", isLocal: false" // TODO(lachsinc): Is this fine?
", flags: DIFlagPrototyped"
", isOptimized: false"
", unit: !%d"
- ")",
+ ", type: !%d",
LIT(di->Proc.entity->token.string),
LIT(di->Proc.name),
- di->Proc.file->id, di->Proc.pos.line,
- m->debug_compile_unit->id);
+ 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,
+ di->Proc.type->id);
+ ir_write_byte(f, ')'); // !DISubprogram(
+ break;
+ case irDebugInfo_ProcType:
+ ir_fprintf(f, "!DISubroutineType(types: !%d)",
+ di->ProcType.types->id);
+ break;
+ case irDebugInfo_Location:
+ GB_ASSERT_NOT_NULL(di->Location.scope);
+ ir_fprintf(f, "!DILocation("
+ "line: %td"
+ ", column: %td"
+ ", scope: !%d)",
+ di->Location.pos.line,
+ di->Location.pos.column,
+ di->Location.scope->id);
+ break;
+ case irDebugInfo_LexicalBlock:
+ GB_ASSERT_NOT_NULL(di->LexicalBlock.file);
+ GB_ASSERT_NOT_NULL(di->LexicalBlock.scope);
+ ir_fprintf(f, "distinct !DILexicalBlock("
+ "line: %td"
+ ", column: %td"
+ ", file: !%d"
+ ", scope: !%d)",
+ di->LexicalBlock.pos.line,
+ di->LexicalBlock.pos.column,
+ di->LexicalBlock.file->id,
+ di->LexicalBlock.scope->id);
+ break;
+ case irDebugInfo_GlobalVariableExpression: {
+ ir_fprintf(f, "!DIGlobalVariableExpression("
+ "var: !%d"
+ ", expr: !DIExpression(",
+ di->GlobalVariableExpression.var->id);
+ if (di->GlobalVariableExpression.var->GlobalVariable.variable->Global.is_constant) {
+ ir_write_str_lit(f, "DW_OP_constu, ");
+ ir_print_value(f, m, di->GlobalVariable.variable, ir_type(di->GlobalVariable.variable));
+ ir_write_str_lit(f, ", DW_OP_stack_value");
+ } else {
+ // NOTE(lachsinc): non-const globals expect empty "!DIExpression()"
+ }
+ ir_write_byte(f, ')'); // !DIExpression(
+ ir_write_byte(f, ')'); // !DIGlobalVariableExpression(
+ break;
+ }
+ case irDebugInfo_GlobalVariable: {
+ ir_fprintf(f, "distinct !DIGlobalVariable("
+ "name: \"%.*s\""
+ ", scope: !%d"
+ ", file: !%d"
+ ", line: %d"
+ ", type: !%d"
+ ", isLocal: true" // TODO(lachsinc): Check locality ??
+ ", isDefinition: true)", // TODO(lachsinc): ??
+ LIT(di->GlobalVariable.name),
+ di->GlobalVariable.scope->id,
+ di->GlobalVariable.file->id,
+ di->GlobalVariable.pos.line,
+ di->GlobalVariable.type->id);
+ break;
+ }
+ case irDebugInfo_LocalVariable: {
+ ir_fprintf(f, "!DILocalVariable("
+ "scope: !%d"
+ ", file: !%d"
+ ", line: %d"
+ ", type: !%d",
+ di->LocalVariable.scope->id,
+ di->LocalVariable.file->id,
+ di->LocalVariable.pos.line,
+ di->LocalVariable.type->id);
+ if (di->DerivedType.name.len > 0) {
+ ir_fprintf(f, ", name: \"%.*s\"", LIT(di->LocalVariable.name));
+ }
+ if (di->LocalVariable.arg > 0) {
+ ir_fprintf(f, ", arg: %d", di->LocalVariable.arg);
+ }
+ ir_write_byte(f, ')');
+ break;
+ }
+ case irDebugInfo_BasicType:
+ ir_fprintf(f, "!DIBasicType("
+ "name: \"%.*s\""
+ ", size: %d"
+ ", encoding: ",
+ LIT(di->BasicType.name),
+ di->BasicType.size);
+ ir_print_debug_encoding(f, irDebugInfo_BasicType, di->BasicType.encoding);
+ ir_write_byte(f, ')');
break;
+ case irDebugInfo_DerivedType: {
+ if (di->DerivedType.tag == irDebugBasicEncoding_member) {
+ // NOTE(lachsinc): We crash llvm super hard if we don't specify a name :)
+ GB_ASSERT(di->DerivedType.name.len > 0);
+ }
+ ir_write_str_lit(f, "!DIDerivedType(tag: ");
+ ir_print_debug_encoding(f, irDebugInfo_DerivedType, di->DerivedType.tag);
+ if (di->DerivedType.name.len > 0) {
+ ir_fprintf(f, ", name: \"%.*s\"", LIT(di->DerivedType.name));
+ }
+ if (di->DerivedType.base_type != nullptr) {
+ ir_fprintf(f, ", baseType: !%d", di->DerivedType.base_type->id);
+ } else {
+ ir_write_str_lit(f, ", baseType: null"); // Valid/required for rawptr
+ }
+ if (di->DerivedType.size > 0) ir_fprintf(f, ", size: %d", di->DerivedType.size);
+ if (di->DerivedType.align > 0) ir_fprintf(f, ", align: %d", di->DerivedType.align);
+ if (di->DerivedType.offset > 0) ir_fprintf(f, ", offset: %d", di->DerivedType.offset);
+ if (di->DerivedType.flags > 0) {
+ // TODO(lachsinc): Handle in a more generic manner.
+ if (di->DerivedType.flags & irDebugInfoFlag_Bitfield) ir_write_str_lit(f, ", flags: DIFlagBitField, extraData: i64 0");
+ }
+ ir_write_byte(f, ')');
+ break;
+ }
+ case irDebugInfo_CompositeType: {
+ if (di->CompositeType.tag == irDebugBasicEncoding_array_type) {
+ GB_ASSERT_NOT_NULL(di->CompositeType.base_type);
+ GB_ASSERT(di->CompositeType.array_count > 0);
+ GB_ASSERT(di->CompositeType.name.len == 0);
+ GB_ASSERT(di->CompositeType.size > 0);
+ }
- case irDebugInfo_AllProcs:
+ if (di->CompositeType.tag == irDebugBasicEncoding_union_type) {
+ GB_ASSERT_NOT_NULL(di->CompositeType.file); // Union _requires_ file to be valid.
+ }
+
+ ir_write_str_lit(f, "!DICompositeType(tag: ");
+ ir_print_debug_encoding(f, irDebugInfo_CompositeType, di->CompositeType.tag);
+ if (di->CompositeType.name.len > 0) {
+ ir_fprintf(f, ", name: \"%.*s\"", LIT(di->CompositeType.name));
+ }
+ if (di->CompositeType.scope != nullptr) {
+ ir_fprintf(f, ", scope: !%d", di->CompositeType.scope->id);
+ }
+ if (di->CompositeType.file != nullptr) {
+ ir_fprintf(f, ", file: !%d"
+ ", line: %td",
+ di->CompositeType.file->id,
+ di->CompositeType.pos.line);
+ }
+ if (di->CompositeType.size > 0) ir_fprintf(f, ", size: %d", di->CompositeType.size);
+ if (di->CompositeType.align > 0) ir_fprintf(f, ", align: %d", di->CompositeType.align);
+ if (di->CompositeType.base_type != nullptr) {
+ GB_ASSERT(di->CompositeType.tag != irDebugBasicEncoding_structure_type);
+ GB_ASSERT(di->CompositeType.tag != irDebugBasicEncoding_union_type);
+ ir_fprintf(f, ", baseType: !%d", di->CompositeType.base_type->id);
+ }
+ if (di->CompositeType.tag == irDebugBasicEncoding_array_type) {
+ ir_fprintf(f, ", elements: !{!DISubrange(count: %d)}", di->CompositeType.array_count);
+ } else {
+ if (di->CompositeType.elements != nullptr) {
+ ir_fprintf(f, ", elements: !%d", di->CompositeType.elements->id);
+ }
+ }
+ ir_write_byte(f, ')');
+ break;
+ }
+ case irDebugInfo_Enumerator: {
+ ir_fprintf(f, "!DIEnumerator("
+ "name: \"%.*s\""
+ ", value: %lld)",
+ LIT(di->Enumerator.name),
+ di->Enumerator.value);
+ break;
+ }
+ case irDebugInfo_DebugInfoArray:
ir_fprintf(f, "!{");
- for_array(proc_index, di->AllProcs.procs) {
- irDebugInfo *p = di->AllProcs.procs[proc_index];
- if (proc_index > 0) {ir_fprintf(f, ",");}
- ir_fprintf(f, "!%d", p->id);
+ 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 != nullptr) {
+ 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;
diff --git a/src/main.cpp b/src/main.cpp
index 87c1ad511..ed72b5f0a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -665,9 +665,6 @@ i32 exec_llvm_opt(String output_base) {
// For more passes arguments: http://llvm.org/docs/Passes.html
return system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
- "-mem2reg "
- "-memcpyopt "
- "-die "
"",
LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base),
@@ -677,9 +674,6 @@ i32 exec_llvm_opt(String output_base) {
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
return system_exec_command_line_app("llvm-opt", false,
"opt \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
- "-mem2reg "
- "-memcpyopt "
- "-die "
"",
LIT(output_base), LIT(output_base),
LIT(build_context.opt_flags));