aboutsummaryrefslogtreecommitdiff
path: root/src/llvm_backend_debug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm_backend_debug.cpp')
-rw-r--r--src/llvm_backend_debug.cpp983
1 files changed, 983 insertions, 0 deletions
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
new file mode 100644
index 000000000..f3640de11
--- /dev/null
+++ b/src/llvm_backend_debug.cpp
@@ -0,0 +1,983 @@
+LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) {
+ if (key == nullptr) {
+ return nullptr;
+ }
+ auto found = map_get(&m->debug_values, hash_pointer(key));
+ if (found) {
+ return *found;
+ }
+ return nullptr;
+}
+void lb_set_llvm_metadata(lbModule *m, void *key, LLVMMetadataRef value) {
+ if (key != nullptr) {
+ map_set(&m->debug_values, hash_pointer(key), value);
+ }
+}
+
+LLVMMetadataRef lb_get_llvm_file_metadata_from_node(lbModule *m, Ast *node) {
+ if (node == nullptr) {
+ return nullptr;
+ }
+ return lb_get_llvm_metadata(m, node->file);
+}
+
+LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
+ GB_ASSERT_MSG(p->debug_info != nullptr, "missing debug information for %.*s", LIT(p->name));
+
+ for (isize i = p->scope_stack.count-1; i >= 0; i--) {
+ Scope *s = p->scope_stack[i];
+ LLVMMetadataRef md = lb_get_llvm_metadata(p->module, s);
+ if (md) {
+ return md;
+ }
+ }
+ return p->debug_info;
+}
+
+LLVMMetadataRef lb_debug_location_from_token_pos(lbProcedure *p, TokenPos pos) {
+ LLVMMetadataRef scope = lb_get_current_debug_scope(p);
+ GB_ASSERT_MSG(scope != nullptr, "%.*s", LIT(p->name));
+ return LLVMDIBuilderCreateDebugLocation(p->module->ctx, cast(unsigned)pos.line, cast(unsigned)pos.column, scope, nullptr);
+}
+LLVMMetadataRef lb_debug_location_from_ast(lbProcedure *p, Ast *node) {
+ GB_ASSERT(node != nullptr);
+ return lb_debug_location_from_token_pos(p, ast_token(node).pos);
+}
+
+LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
+ Type *original_type = type;
+
+ LLVMContextRef ctx = m->ctx;
+ i64 size = type_size_of(type); // Check size
+
+ GB_ASSERT(type != t_invalid);
+
+ unsigned const word_size = cast(unsigned)build_context.word_size;
+ unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
+
+ GB_ASSERT(type->kind == Type_Proc);
+ LLVMTypeRef return_type = LLVMVoidTypeInContext(ctx);
+ unsigned parameter_count = 1;
+ for (i32 i = 0; i < type->Proc.param_count; i++) {
+ Entity *e = type->Proc.params->Tuple.variables[i];
+ if (e->kind == Entity_Variable) {
+ parameter_count += 1;
+ }
+ }
+ LLVMMetadataRef *parameters = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, parameter_count);
+
+ unsigned param_index = 0;
+ if (type->Proc.result_count == 0) {
+ parameters[param_index++] = nullptr;
+ } else {
+ parameters[param_index++] = lb_debug_type(m, type->Proc.results);
+ }
+
+ LLVMMetadataRef parent_scope = nullptr;
+ LLVMMetadataRef scope = nullptr;
+ LLVMMetadataRef file = nullptr;
+
+ for (i32 i = 0; i < type->Proc.param_count; i++) {
+ Entity *e = type->Proc.params->Tuple.variables[i];
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+ parameters[param_index] = lb_debug_type(m, e->type);
+ param_index += 1;
+ }
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ if (type->Proc.diverging) {
+ flags = LLVMDIFlagNoReturn;
+ }
+
+ return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters, parameter_count, flags);
+}
+
+LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
+ unsigned field_line = 1;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+
+ AstPackage *pkg = m->info->runtime_package;
+ GB_ASSERT(pkg->files.count != 0);
+ LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]);
+ LLVMMetadataRef scope = file;
+
+ return LLVMDIBuilderCreateMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
+ 8*cast(u64)type_size_of(type), 8*cast(u32)type_align_of(type), offset_in_bits,
+ field_flags, lb_debug_type(m, type)
+ );
+}
+LLVMMetadataRef lb_debug_basic_struct(lbModule *m, String const &name, u64 size_in_bits, u32 align_in_bits, LLVMMetadataRef *elements, unsigned element_count) {
+ AstPackage *pkg = m->info->runtime_package;
+ GB_ASSERT(pkg->files.count != 0);
+ LLVMMetadataRef file = lb_get_llvm_metadata(m, pkg->files[0]);
+ LLVMMetadataRef scope = file;
+
+ return LLVMDIBuilderCreateStructType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, 1, size_in_bits, align_in_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
+}
+
+
+LLVMMetadataRef lb_debug_type_basic_type(lbModule *m, String const &name, u64 size_in_bits, LLVMDWARFTypeEncoding encoding, LLVMDIFlags flags = LLVMDIFlagZero) {
+ LLVMMetadataRef basic_type = LLVMDIBuilderCreateBasicType(m->debug_builder, cast(char const *)name.text, name.len, size_in_bits, encoding, flags);
+#if 1
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, basic_type, cast(char const *)name.text, name.len, nullptr, 0, nullptr, cast(u32)size_in_bits);
+ return final_decl;
+#else
+ return basic_type;
+#endif
+}
+
+LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
+ Type *original_type = type;
+
+ LLVMContextRef ctx = m->ctx;
+ i64 size = type_size_of(type); // Check size
+
+ GB_ASSERT(type != t_invalid);
+
+ unsigned const word_size = cast(unsigned)build_context.word_size;
+ unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
+
+ switch (type->kind) {
+ case Type_Basic:
+ switch (type->Basic.kind) {
+ case Basic_llvm_bool: return lb_debug_type_basic_type(m, str_lit("llvm bool"), 1, LLVMDWARFTypeEncoding_Boolean);
+ case Basic_bool: return lb_debug_type_basic_type(m, str_lit("bool"), 8, LLVMDWARFTypeEncoding_Boolean);
+ case Basic_b8: return lb_debug_type_basic_type(m, str_lit("b8"), 8, LLVMDWARFTypeEncoding_Boolean);
+ case Basic_b16: return lb_debug_type_basic_type(m, str_lit("b16"), 16, LLVMDWARFTypeEncoding_Boolean);
+ case Basic_b32: return lb_debug_type_basic_type(m, str_lit("b32"), 32, LLVMDWARFTypeEncoding_Boolean);
+ case Basic_b64: return lb_debug_type_basic_type(m, str_lit("b64"), 64, LLVMDWARFTypeEncoding_Boolean);
+
+ case Basic_i8: return lb_debug_type_basic_type(m, str_lit("i8"), 8, LLVMDWARFTypeEncoding_Signed);
+ case Basic_u8: return lb_debug_type_basic_type(m, str_lit("u8"), 8, LLVMDWARFTypeEncoding_Unsigned);
+ case Basic_i16: return lb_debug_type_basic_type(m, str_lit("i16"), 16, LLVMDWARFTypeEncoding_Signed);
+ case Basic_u16: return lb_debug_type_basic_type(m, str_lit("u16"), 16, LLVMDWARFTypeEncoding_Unsigned);
+ case Basic_i32: return lb_debug_type_basic_type(m, str_lit("i32"), 32, LLVMDWARFTypeEncoding_Signed);
+ case Basic_u32: return lb_debug_type_basic_type(m, str_lit("u32"), 32, LLVMDWARFTypeEncoding_Unsigned);
+ case Basic_i64: return lb_debug_type_basic_type(m, str_lit("i64"), 64, LLVMDWARFTypeEncoding_Signed);
+ case Basic_u64: return lb_debug_type_basic_type(m, str_lit("u64"), 64, LLVMDWARFTypeEncoding_Unsigned);
+ case Basic_i128: return lb_debug_type_basic_type(m, str_lit("i128"), 128, LLVMDWARFTypeEncoding_Signed);
+ case Basic_u128: return lb_debug_type_basic_type(m, str_lit("u128"), 128, LLVMDWARFTypeEncoding_Unsigned);
+
+ case Basic_rune: return lb_debug_type_basic_type(m, str_lit("rune"), 32, LLVMDWARFTypeEncoding_Utf);
+
+
+ case Basic_f16: return lb_debug_type_basic_type(m, str_lit("f16"), 16, LLVMDWARFTypeEncoding_Float);
+ case Basic_f32: return lb_debug_type_basic_type(m, str_lit("f32"), 32, LLVMDWARFTypeEncoding_Float);
+ case Basic_f64: return lb_debug_type_basic_type(m, str_lit("f64"), 64, LLVMDWARFTypeEncoding_Float);
+
+ case Basic_int: return lb_debug_type_basic_type(m, str_lit("int"), word_bits, LLVMDWARFTypeEncoding_Signed);
+ case Basic_uint: return lb_debug_type_basic_type(m, str_lit("uint"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
+ case Basic_uintptr: return lb_debug_type_basic_type(m, str_lit("uintptr"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
+
+ case Basic_typeid:
+ return lb_debug_type_basic_type(m, str_lit("typeid"), word_bits, LLVMDWARFTypeEncoding_Unsigned);
+
+ // Endian Specific Types
+ case Basic_i16le: return lb_debug_type_basic_type(m, str_lit("i16le"), 16, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
+ case Basic_u16le: return lb_debug_type_basic_type(m, str_lit("u16le"), 16, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
+ case Basic_i32le: return lb_debug_type_basic_type(m, str_lit("i32le"), 32, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
+ case Basic_u32le: return lb_debug_type_basic_type(m, str_lit("u32le"), 32, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
+ case Basic_i64le: return lb_debug_type_basic_type(m, str_lit("i64le"), 64, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
+ case Basic_u64le: return lb_debug_type_basic_type(m, str_lit("u64le"), 64, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
+ case Basic_i128le: return lb_debug_type_basic_type(m, str_lit("i128le"), 128, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagLittleEndian);
+ case Basic_u128le: return lb_debug_type_basic_type(m, str_lit("u128le"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
+
+ case Basic_f16le: return lb_debug_type_basic_type(m, str_lit("f16le"), 16, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+ case Basic_f32le: return lb_debug_type_basic_type(m, str_lit("f32le"), 32, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+ case Basic_f64le: return lb_debug_type_basic_type(m, str_lit("f64le"), 64, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+
+ case Basic_i16be: return lb_debug_type_basic_type(m, str_lit("i16be"), 16, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
+ case Basic_u16be: return lb_debug_type_basic_type(m, str_lit("u16be"), 16, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
+ case Basic_i32be: return lb_debug_type_basic_type(m, str_lit("i32be"), 32, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
+ case Basic_u32be: return lb_debug_type_basic_type(m, str_lit("u32be"), 32, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
+ case Basic_i64be: return lb_debug_type_basic_type(m, str_lit("i64be"), 64, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
+ case Basic_u64be: return lb_debug_type_basic_type(m, str_lit("u64be"), 64, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
+ case Basic_i128be: return lb_debug_type_basic_type(m, str_lit("i128be"), 128, LLVMDWARFTypeEncoding_Signed, LLVMDIFlagBigEndian);
+ case Basic_u128be: return lb_debug_type_basic_type(m, str_lit("u128be"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
+
+ case Basic_f16be: return lb_debug_type_basic_type(m, str_lit("f16be"), 16, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+ case Basic_f32be: return lb_debug_type_basic_type(m, str_lit("f32be"), 32, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+ case Basic_f64be: return lb_debug_type_basic_type(m, str_lit("f64be"), 64, LLVMDWARFTypeEncoding_Float, LLVMDIFlagLittleEndian);
+
+ case Basic_complex32:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4);
+ return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements));
+ }
+ case Basic_complex64:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4);
+ return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements));
+ }
+ case Basic_complex128:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8);
+ return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements));
+ }
+
+ case Basic_quaternion64:
+ {
+ LLVMMetadataRef elements[4] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12);
+ return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements));
+ }
+ case Basic_quaternion128:
+ {
+ LLVMMetadataRef elements[4] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12);
+ return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements));
+ }
+ case Basic_quaternion256:
+ {
+ LLVMMetadataRef elements[4] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8);
+ elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16);
+ elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24);
+ return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements));
+ }
+
+
+
+ case Basic_rawptr:
+ {
+ LLVMMetadataRef void_type = lb_debug_type_basic_type(m, str_lit("void"), 8, LLVMDWARFTypeEncoding_Unsigned);
+ return LLVMDIBuilderCreatePointerType(m->debug_builder, void_type, word_bits, word_bits, LLVMDWARFTypeEncoding_Address, "rawptr", 6);
+ }
+ case Basic_string:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("data"), t_u8_ptr, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, word_bits);
+ return lb_debug_basic_struct(m, str_lit("string"), 2*word_bits, word_bits, elements, gb_count_of(elements));
+ }
+ case Basic_cstring:
+ {
+ LLVMMetadataRef char_type = lb_debug_type_basic_type(m, str_lit("char"), 8, LLVMDWARFTypeEncoding_Unsigned);
+ return LLVMDIBuilderCreatePointerType(m->debug_builder, char_type, word_bits, word_bits, 0, "cstring", 7);
+ }
+ case Basic_any:
+ {
+ LLVMMetadataRef elements[2] = {};
+ elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("id"), t_typeid, word_bits);
+ return lb_debug_basic_struct(m, str_lit("any"), 2*word_bits, word_bits, elements, gb_count_of(elements));
+ }
+
+ // Untyped types
+ case Basic_UntypedBool: GB_PANIC("Basic_UntypedBool"); break;
+ case Basic_UntypedInteger: GB_PANIC("Basic_UntypedInteger"); break;
+ case Basic_UntypedFloat: GB_PANIC("Basic_UntypedFloat"); break;
+ case Basic_UntypedComplex: GB_PANIC("Basic_UntypedComplex"); break;
+ case Basic_UntypedQuaternion: GB_PANIC("Basic_UntypedQuaternion"); break;
+ case Basic_UntypedString: GB_PANIC("Basic_UntypedString"); break;
+ case Basic_UntypedRune: GB_PANIC("Basic_UntypedRune"); break;
+ case Basic_UntypedNil: GB_PANIC("Basic_UntypedNil"); break;
+ case Basic_UntypedUndef: GB_PANIC("Basic_UntypedUndef"); break;
+
+ default: GB_PANIC("Basic Unhandled"); break;
+ }
+ break;
+
+ case Type_Named:
+ GB_PANIC("Type_Named should be handled in lb_debug_type separately");
+
+ case Type_Pointer:
+ return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0);
+
+ case Type_Array: {
+ LLVMMetadataRef subscripts[1] = {};
+ subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+ 0ll,
+ type->Array.count
+ );
+
+ return LLVMDIBuilderCreateArrayType(m->debug_builder,
+ 8*cast(uint64_t)type_size_of(type),
+ 8*cast(unsigned)type_align_of(type),
+ lb_debug_type(m, type->Array.elem),
+ subscripts, gb_count_of(subscripts));
+ }
+
+ case Type_EnumeratedArray: {
+ LLVMMetadataRef subscripts[1] = {};
+ subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+ 0ll,
+ type->EnumeratedArray.count
+ );
+
+ LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder,
+ 8*cast(uint64_t)type_size_of(type),
+ 8*cast(unsigned)type_align_of(type),
+ lb_debug_type(m, type->EnumeratedArray.elem),
+ subscripts, gb_count_of(subscripts));
+ gbString name = type_to_string(type, temporary_allocator());
+ return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
+ }
+
+
+ case Type_Struct:
+ case Type_Union:
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Map:
+ case Type_BitSet:
+ {
+ unsigned tag = DW_TAG_structure_type;
+ if (is_type_raw_union(type) || is_type_union(type)) {
+ tag = DW_TAG_union_type;
+ }
+ u64 size_in_bits = cast(u64)(8*type_size_of(type));
+ u32 align_in_bits = cast(u32)(8*type_size_of(type));
+ LLVMDIFlags flags = LLVMDIFlagZero;
+
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, tag, "", 0, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
+ );
+ lbIncompleteDebugType idt = {};
+ idt.type = type;
+ idt.metadata = temp_forward_decl;
+
+ array_add(&m->debug_incomplete_types, idt);
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+ return temp_forward_decl;
+ }
+
+ case Type_Enum:
+ {
+ LLVMMetadataRef scope = nullptr;
+ LLVMMetadataRef file = nullptr;
+ unsigned line = 0;
+ unsigned element_count = cast(unsigned)type->Enum.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+ Type *bt = base_enum_type(type);
+ LLVMBool is_unsigned = is_type_unsigned(bt);
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = type->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ String name = f->token.string;
+ i64 value = exact_value_to_i64(f->Constant.value);
+ elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
+ }
+ LLVMMetadataRef class_type = lb_debug_type(m, bt);
+ return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, "", 0, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
+ }
+
+ case Type_Tuple:
+ if (type->Tuple.variables.count == 1) {
+ return lb_debug_type(m, type->Tuple.variables[0]->type);
+ } else {
+ type_set_offsets(type);
+ LLVMMetadataRef parent_scope = nullptr;
+ LLVMMetadataRef scope = nullptr;
+ LLVMMetadataRef file = nullptr;
+ unsigned line = 0;
+ u64 size_in_bits = 8*cast(u64)type_size_of(type);
+ u32 align_in_bits = 8*cast(u32)type_align_of(type);
+ LLVMDIFlags flags = LLVMDIFlagZero;
+
+ unsigned element_count = cast(unsigned)type->Tuple.variables.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = type->Tuple.variables[i];
+ GB_ASSERT(f->kind == Entity_Variable);
+ String name = f->token.string;
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ u64 offset_in_bits = 8*cast(u64)type->Tuple.offsets[i];
+ elements[i] = LLVMDIBuilderCreateMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
+ 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type), offset_in_bits,
+ field_flags, lb_debug_type(m, f->type)
+ );
+ }
+
+
+ return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
+ size_in_bits, align_in_bits, flags,
+ nullptr, elements, element_count, 0, nullptr,
+ "", 0
+ );
+ }
+
+ case Type_Proc:
+ {
+ LLVMMetadataRef proc_underlying_type = lb_debug_type_internal_proc(m, type);
+ LLVMMetadataRef pointer_type = LLVMDIBuilderCreatePointerType(m->debug_builder, proc_underlying_type, word_bits, word_bits, 0, nullptr, 0);
+ gbString name = type_to_string(type, temporary_allocator());
+ return LLVMDIBuilderCreateTypedef(m->debug_builder, pointer_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
+ }
+ break;
+
+ case Type_SimdVector:
+ return LLVMDIBuilderCreateVectorType(m->debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
+
+ case Type_RelativePointer: {
+ LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
+ gbString name = type_to_string(type, temporary_allocator());
+ return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
+ }
+
+ case Type_RelativeSlice:
+ {
+ unsigned element_count = 0;
+ LLVMMetadataRef elements[2] = {};
+ Type *base_integer = type->RelativeSlice.base_integer;
+ elements[0] = lb_debug_struct_field(m, str_lit("data_offset"), base_integer, 0);
+ elements[1] = lb_debug_struct_field(m, str_lit("len"), base_integer, 8*type_size_of(base_integer));
+ gbString name = type_to_string(type, temporary_allocator());
+ return LLVMDIBuilderCreateStructType(m->debug_builder, nullptr, name, gb_string_length(name), nullptr, 0, 2*word_bits, word_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
+ }
+ }
+
+ GB_PANIC("Invalid type %s", type_to_string(type));
+ return nullptr;
+}
+
+LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) {
+ LLVMMetadataRef found = nullptr;
+ for (;;) {
+ if (scope == nullptr) {
+ return nullptr;
+ }
+ if (scope->flags & ScopeFlag_Proc) {
+ found = lb_get_llvm_metadata(m, scope->procedure_entity);
+ if (found) {
+ return found;
+ }
+ }
+ if (scope->flags & ScopeFlag_File) {
+ found = lb_get_llvm_metadata(m, scope->file);
+ if (found) {
+ return found;
+ }
+ }
+ scope = scope->parent;
+ }
+}
+
+LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
+ GB_ASSERT(type != nullptr);
+ LLVMMetadataRef found = lb_get_llvm_metadata(m, type);
+ if (found != nullptr) {
+ return found;
+ }
+
+ if (type->kind == Type_Named) {
+ LLVMMetadataRef file = nullptr;
+ unsigned line = 0;
+ LLVMMetadataRef scope = nullptr;
+
+
+ if (type->Named.type_name != nullptr) {
+ Entity *e = type->Named.type_name;
+ scope = lb_get_base_scope_metadata(m, e->scope);
+ if (scope != nullptr) {
+ file = LLVMDIScopeGetFile(scope);
+ }
+ line = cast(unsigned)e->token.pos.line;
+ }
+ // TODO(bill): location data for Type_Named
+
+ u64 size_in_bits = 8*type_size_of(type);
+ u32 align_in_bits = 8*cast(u32)type_align_of(type);
+ String name = type->Named.name;
+ char const *name_text = cast(char const *)name.text;
+ size_t name_len = cast(size_t)name.len;
+ unsigned tag = DW_TAG_structure_type;
+ if (is_type_raw_union(type) || is_type_union(type)) {
+ tag = DW_TAG_union_type;
+ }
+ LLVMDIFlags flags = LLVMDIFlagZero;
+
+ Type *bt = base_type(type->Named.base);
+
+ lbIncompleteDebugType idt = {};
+ idt.type = type;
+
+ switch (bt->kind) {
+ case Type_Enum:
+ {
+ unsigned line = 0;
+ unsigned element_count = cast(unsigned)bt->Enum.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+ Type *ct = base_enum_type(type);
+ LLVMBool is_unsigned = is_type_unsigned(ct);
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = bt->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ String name = f->token.string;
+ i64 value = exact_value_to_i64(f->Constant.value);
+ elements[i] = LLVMDIBuilderCreateEnumerator(m->debug_builder, cast(char const *)name.text, cast(size_t)name.len, value, is_unsigned);
+ }
+ LLVMMetadataRef class_type = lb_debug_type(m, ct);
+ return LLVMDIBuilderCreateEnumerationType(m->debug_builder, scope, name_text, name_len, file, line, 8*type_size_of(type), 8*cast(unsigned)type_align_of(type), elements, element_count, class_type);
+ }
+
+
+ case Type_Basic:
+ case Type_Pointer:
+ case Type_Array:
+ case Type_EnumeratedArray:
+ case Type_Tuple:
+ case Type_Proc:
+ case Type_SimdVector:
+ case Type_RelativePointer:
+ case Type_RelativeSlice:
+ {
+ LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+ }
+
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Map:
+ case Type_Struct:
+ case Type_Union:
+ case Type_BitSet:
+ LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
+ m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
+ );
+ idt.metadata = temp_forward_decl;
+
+ array_add(&m->debug_incomplete_types, idt);
+ lb_set_llvm_metadata(m, type, temp_forward_decl);
+ return temp_forward_decl;
+ }
+ }
+
+
+ LLVMMetadataRef dt = lb_debug_type_internal(m, type);
+ lb_set_llvm_metadata(m, type, dt);
+ return dt;
+}
+
+void lb_debug_complete_types(lbModule *m) {
+ unsigned const word_size = cast(unsigned)build_context.word_size;
+ unsigned const word_bits = cast(unsigned)(8*build_context.word_size);
+
+ for_array(debug_incomplete_type_index, m->debug_incomplete_types) {
+ auto const &idt = m->debug_incomplete_types[debug_incomplete_type_index];
+ GB_ASSERT(idt.type != nullptr);
+ GB_ASSERT(idt.metadata != nullptr);
+
+ Type *t = idt.type;
+ Type *bt = base_type(t);
+
+ LLVMMetadataRef parent_scope = nullptr;
+ LLVMMetadataRef file = nullptr;
+ unsigned line_number = 0;
+ u64 size_in_bits = 8*type_size_of(t);
+ u32 align_in_bits = cast(u32)(8*type_align_of(t));
+ LLVMDIFlags flags = LLVMDIFlagZero;
+
+ LLVMMetadataRef derived_from = nullptr;
+
+ LLVMMetadataRef *elements = nullptr;
+ unsigned element_count = 0;
+
+
+ unsigned runtime_lang = 0; // Objective-C runtime version
+ char const *unique_id = "";
+ LLVMMetadataRef vtable_holder = nullptr;
+ size_t unique_id_len = 0;
+
+
+ LLVMMetadataRef record_scope = nullptr;
+
+ switch (bt->kind) {
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Map:
+ case Type_Struct:
+ case Type_Union:
+ case Type_BitSet: {
+ bool is_union = is_type_raw_union(bt) || is_type_union(bt);
+
+ String name = str_lit("<anonymous-struct>");
+ if (t->kind == Type_Named) {
+ name = t->Named.name;
+ if (t->Named.type_name && t->Named.type_name->pkg && t->Named.type_name->pkg->name.len != 0) {
+ name = concatenate3_strings(temporary_allocator(), t->Named.type_name->pkg->name, str_lit("."), t->Named.name);
+ }
+
+ LLVMMetadataRef file = nullptr;
+ unsigned line = 0;
+ LLVMMetadataRef file_scope = nullptr;
+
+ if (t->Named.type_name != nullptr) {
+ Entity *e = t->Named.type_name;
+ file_scope = lb_get_llvm_metadata(m, e->scope);
+ if (file_scope != nullptr) {
+ file = LLVMDIScopeGetFile(file_scope);
+ }
+ line = cast(unsigned)e->token.pos.line;
+ }
+ // TODO(bill): location data for Type_Named
+
+ } else {
+ name = make_string_c(type_to_string(t, temporary_allocator()));
+ }
+
+
+
+ switch (bt->kind) {
+ case Type_Slice:
+ element_count = 2;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits);
+ elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
+ break;
+ case Type_DynamicArray:
+ element_count = 4;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits);
+ elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
+ elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*word_bits);
+ elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*word_bits);
+ break;
+
+ case Type_Map:
+ bt = bt->Map.internal_type;
+ /*fallthrough*/
+ case Type_Struct:
+ if (file == nullptr) {
+ if (bt->Struct.node) {
+ file = lb_get_llvm_metadata(m, bt->Struct.node->file);
+ line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
+ }
+ }
+
+ type_set_offsets(bt);
+ {
+ isize element_offset = 0;
+ record_scope = lb_get_llvm_metadata(m, bt->Struct.scope);
+ switch (bt->Struct.soa_kind) {
+ case StructSoa_Slice: element_offset = 1; break;
+ case StructSoa_Dynamic: element_offset = 3; break;
+ }
+ element_count = cast(unsigned)(bt->Struct.fields.count + element_offset);
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ switch (bt->Struct.soa_kind) {
+ case StructSoa_Slice:
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ ".len", 4,
+ file, 0,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ 8*type_size_of(bt)-word_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+ break;
+ case StructSoa_Dynamic:
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ ".len", 4,
+ file, 0,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ 8*type_size_of(bt)-word_bits + 0*word_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+ elements[1] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ ".cap", 4,
+ file, 0,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ 8*type_size_of(bt)-word_bits + 1*word_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_int)
+ );
+ elements[2] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ ".allocator", 12,
+ file, 0,
+ 8*cast(u64)type_size_of(t_int), 8*cast(u32)type_align_of(t_int),
+ 8*type_size_of(bt)-word_bits + 2*word_bits,
+ LLVMDIFlagZero, lb_debug_type(m, t_allocator)
+ );
+ break;
+ }
+
+ for_array(j, bt->Struct.fields) {
+ Entity *f = bt->Struct.fields[j];
+ String fname = f->token.string;
+
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ u64 offset_in_bits = 8*cast(u64)bt->Struct.offsets[j];
+
+ elements[element_offset+j] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ cast(char const *)fname.text, cast(size_t)fname.len,
+ file, field_line,
+ 8*cast(u64)type_size_of(f->type), 8*cast(u32)type_align_of(f->type),
+ offset_in_bits,
+ field_flags, lb_debug_type(m, f->type)
+ );
+ }
+ }
+ break;
+ case Type_Union:
+ {
+ if (file == nullptr) {
+ GB_ASSERT(bt->Union.node != nullptr);
+ file = lb_get_llvm_metadata(m, bt->Union.node->file);
+ line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
+ }
+
+ isize index_offset = 1;
+ if (is_type_union_maybe_pointer(bt)) {
+ index_offset = 0;
+ }
+ record_scope = lb_get_llvm_metadata(m, bt->Union.scope);
+ element_count = cast(unsigned)bt->Union.variants.count;
+ if (index_offset > 0) {
+ element_count += 1;
+ }
+
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ if (index_offset > 0) {
+ Type *tag_type = union_tag_type(bt);
+ unsigned field_line = 0;
+ u64 offset_in_bits = 8*cast(u64)bt->Union.variant_block_size;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+
+ elements[0] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ "tag", 3,
+ file, field_line,
+ 8*cast(u64)type_size_of(tag_type), 8*cast(u32)type_align_of(tag_type),
+ offset_in_bits,
+ field_flags, lb_debug_type(m, tag_type)
+ );
+ }
+
+ for_array(j, bt->Union.variants) {
+ Type *variant = bt->Union.variants[j];
+
+ unsigned field_index = cast(unsigned)(index_offset+j);
+
+ char name[16] = {};
+ gb_snprintf(name, gb_size_of(name), "v%u", field_index);
+ isize name_len = gb_strlen(name);
+
+ unsigned field_line = 0;
+ LLVMDIFlags field_flags = LLVMDIFlagZero;
+ u64 offset_in_bits = 0;
+
+ elements[field_index] = LLVMDIBuilderCreateMemberType(
+ m->debug_builder, record_scope,
+ name, name_len,
+ file, field_line,
+ 8*cast(u64)type_size_of(variant), 8*cast(u32)type_align_of(variant),
+ offset_in_bits,
+ field_flags, lb_debug_type(m, variant)
+ );
+ }
+ }
+ break;
+
+ case Type_BitSet:
+ {
+ if (file == nullptr) {
+ GB_ASSERT(bt->BitSet.node != nullptr);
+ file = lb_get_llvm_metadata(m, bt->BitSet.node->file);
+ line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
+ }
+
+ LLVMMetadataRef bit_set_field_type = lb_debug_type(m, t_bool);
+ LLVMMetadataRef scope = file;
+
+ Type *elem = base_type(bt->BitSet.elem);
+ if (elem->kind == Type_Enum) {
+ element_count = cast(unsigned)elem->Enum.fields.count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ for_array(i, elem->Enum.fields) {
+ Entity *f = elem->Enum.fields[i];
+ GB_ASSERT(f->kind == Entity_Constant);
+ i64 val = exact_value_to_i64(f->Constant.value);
+ String name = f->token.string;
+ u64 offset_in_bits = cast(u64)(val - bt->BitSet.lower);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ cast(char const *)name.text, name.len,
+ file, line_number,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ } else {
+
+ char name[32] = {};
+
+ GB_ASSERT(is_type_integer(elem));
+ i64 count = bt->BitSet.upper - bt->BitSet.lower + 1;
+ GB_ASSERT(0 <= count);
+
+ element_count = cast(unsigned)count;
+ elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
+ for (unsigned i = 0; i < element_count; i++) {
+ u64 offset_in_bits = i;
+ i64 val = bt->BitSet.lower + cast(i64)i;
+ gb_snprintf(name, gb_count_of(name), "%lld", cast(long long)val);
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(
+ m->debug_builder,
+ scope,
+ name, gb_strlen(name),
+ file, line_number,
+ 1,
+ offset_in_bits,
+ 0,
+ LLVMDIFlagZero,
+ bit_set_field_type
+ );
+ }
+ }
+ }
+ }
+
+
+ LLVMMetadataRef final_metadata = nullptr;
+ if (is_union) {
+ final_metadata = LLVMDIBuilderCreateUnionType(
+ m->debug_builder,
+ parent_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line_number,
+ size_in_bits, align_in_bits,
+ flags,
+ elements, element_count,
+ runtime_lang,
+ unique_id, unique_id_len
+ );
+ } else {
+ final_metadata = LLVMDIBuilderCreateStructType(
+ m->debug_builder,
+ parent_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line_number,
+ size_in_bits, align_in_bits,
+ flags,
+ derived_from,
+ elements, element_count,
+ runtime_lang,
+ vtable_holder,
+ unique_id, unique_id_len
+ );
+ }
+
+ LLVMMetadataReplaceAllUsesWith(idt.metadata, final_metadata);
+ lb_set_llvm_metadata(m, idt.type, final_metadata);
+ } break;
+ default:
+ GB_PANIC("invalid incomplete debug type");
+ break;
+ }
+ }
+ array_clear(&m->debug_incomplete_types);
+}
+
+
+
+void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ if (type == nullptr) {
+ return;
+ }
+ if (type == t_invalid) {
+ return;
+ }
+ if (p->body == nullptr) {
+ return;
+ }
+
+ lbModule *m = p->module;
+ String const &name = token.string;
+ if (name == "" || name == "_") {
+ return;
+ }
+
+ if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+ // Already been set
+ return;
+ }
+
+
+ AstFile *file = p->body->file;
+
+ LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ GB_ASSERT(llvm_scope != nullptr);
+ if (llvm_file == nullptr) {
+ llvm_file = LLVMDIScopeGetFile(llvm_scope);
+ }
+
+ if (llvm_file == nullptr) {
+ return;
+ }
+
+ unsigned alignment_in_bits = cast(unsigned)(8*type_align_of(type));
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ LLVMBool always_preserve = build_context.optimization_level == 0;
+
+ LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+ LLVMMetadataRef var_info = LLVMDIBuilderCreateAutoVariable(
+ m->debug_builder, llvm_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ llvm_file, token.pos.line,
+ debug_type,
+ always_preserve, flags, alignment_in_bits
+ );
+
+ LLVMValueRef storage = ptr;
+ LLVMValueRef instr = ptr;
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+ LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+ lb_set_llvm_metadata(m, ptr, llvm_expr);
+ LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+}
+
+void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
+ if (!p->debug_info || !p->body) {
+ return;
+ }
+ LLVMMetadataRef loc = LLVMGetCurrentDebugLocation2(p->builder);
+ if (!loc) {
+ return;
+ }
+ TokenPos pos = {};
+
+ pos.file_id = p->body->file ? p->body->file->id : 0;
+ pos.line = LLVMDILocationGetLine(loc);
+ pos.column = LLVMDILocationGetColumn(loc);
+
+ Token token = {};
+ token.kind = Token_context;
+ token.string = str_lit("context");
+ token.pos = pos;
+
+ lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
+}