瀏覽代碼

Merge pull request #260 from lachsinc/master

[WIP] Provide llvm with more debug info (for Visual Studio debugger support)
gingerBill 6 年之前
父節點
當前提交
2bd85e764e
共有 4 個文件被更改,包括 1302 次插入118 次删除
  1. 15 2
      src/build_settings.cpp
  2. 1029 63
      src/ir.cpp
  3. 258 47
      src/ir_print.cpp
  4. 0 6
      src/main.cpp

+ 15 - 2
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);
 
 

文件差異過大導致無法顯示
+ 1029 - 63
src/ir.cpp


+ 258 - 47
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;

+ 0 - 6
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));

部分文件因文件數量過多而無法顯示