Browse Source

Add debug info for labels (#4385)

* Emit label debug info w/o location

* Insert debug label call

* Slight refactor for later fix

* Improve debug labels for block statements

* Improve debug info with for loops

* Generate label lbBlocks w/ debug

* Lightly refactor lb_add_debug_label

* Revise comments, add null check assertion

* Use LLVM-C API for debug labels

* Prefer C DILabel API for POSIX, fallback to CPP

* Use version check for LLVM-C DILabel
tf2spi 5 months ago
parent
commit
2f636886a5
2 changed files with 123 additions and 5 deletions
  1. 56 0
      src/llvm_backend_debug.cpp
  2. 67 5
      src/llvm_backend_stmt.cpp

+ 56 - 0
src/llvm_backend_debug.cpp

@@ -1295,3 +1295,59 @@ gb_internal void add_debug_info_for_global_constant_from_entity(lbGenerator *gen
 		}
 		}
 	}
 	}
 }
 }
+
+gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) {
+// NOTE(tf2spi): LLVM-C DILabel API used only existed for major versions 20+
+#if LLVM_VERSION_MAJOR >= 20
+	if (p == nullptr || p->debug_info == nullptr) {
+		return;
+	}
+	if (target == nullptr || label == nullptr || label->kind != Ast_Label) {
+		return;
+	}
+	Token label_token = label->Label.token;
+	if (is_blank_ident(label_token.string)) {
+		return;
+	}
+	lbModule *m = p->module;
+	if (m == nullptr) {
+		return;
+	}
+
+	AstFile *file = label->file();
+	LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+	if (llvm_file == nullptr) {
+		debugf("llvm file not found for label\n");
+		return;
+	}
+	LLVMMetadataRef llvm_scope = p->debug_info;
+	if(llvm_scope == nullptr) {
+		debugf("llvm scope not found for label\n");
+		return;
+	}
+	LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, label_token.pos);
+	LLVMBasicBlockRef llvm_block = target->block;
+	if (llvm_block == nullptr || llvm_debug_loc == nullptr) {
+		return;
+	}
+	LLVMMetadataRef llvm_label = LLVMDIBuilderCreateLabel(
+		m->debug_builder,
+		llvm_scope,
+		(const char *)label_token.string.text,
+		(size_t)label_token.string.len,
+		llvm_file,
+		label_token.pos.line,
+
+		// NOTE(tf2spi): Defaults to false in LLVM API, but I'd rather not take chances
+		//               Always preserve the label no matter what when debugging
+		true
+	);
+	GB_ASSERT(llvm_label != nullptr);
+	(void)LLVMDIBuilderInsertLabelAtEnd(
+		m->debug_builder,
+		llvm_label,
+		llvm_debug_loc,
+		llvm_block
+	);
+#endif
+}

+ 67 - 5
src/llvm_backend_stmt.cpp

@@ -136,7 +136,6 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
 	return empty;
 	return empty;
 }
 }
 
 
-
 gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
 gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
 	lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
 	lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
 	tl->prev = p->target_list;
 	tl->prev = p->target_list;
@@ -688,6 +687,18 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
 	lbBlock *body = lb_create_block(p, "for.interval.body");
 	lbBlock *body = lb_create_block(p, "for.interval.body");
 	lbBlock *done = lb_create_block(p, "for.interval.done");
 	lbBlock *done = lb_create_block(p, "for.interval.done");
 
 
+	// TODO(tf2spi): This is inlined in more than several places.
+	//               Putting this in a function might be preferred.
+	//               LLVMSetCurrentDebugLocation2 has side effects,
+	//               so I didn't want to hide that before it got reviewed.
+	if (rs->label != nullptr && p->debug_info != nullptr) {
+		lbBlock *label = lb_create_block(p, "for.interval.label");
+		lb_emit_jump(p, label);
+		lb_start_block(p, label);
+
+		LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+		lb_add_debug_label(p, rs->label, label);
+	}
 	lb_emit_jump(p, loop);
 	lb_emit_jump(p, loop);
 	lb_start_block(p, loop);
 	lb_start_block(p, loop);
 
 
@@ -893,6 +904,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
 
 
 	lbAddr index = lb_add_local_generated(p, t_int, false);
 	lbAddr index = lb_add_local_generated(p, t_int, false);
 
 
+	if (rs->label != nullptr && p->debug_info != nullptr) {
+		lbBlock *label = lb_create_block(p, "for.soa.label");
+		lb_emit_jump(p, label);
+		lb_start_block(p, label);
+
+		LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+		lb_add_debug_label(p, rs->label, label);
+	}
 	if (!is_reverse) {
 	if (!is_reverse) {
 		/*
 		/*
 			for x, i in array {
 			for x, i in array {
@@ -970,7 +989,6 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
 		lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
 		lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
 	}
 	}
 
 
-
 	lb_push_target_list(p, rs->label, done, loop, nullptr);
 	lb_push_target_list(p, rs->label, done, loop, nullptr);
 
 
 	lb_build_stmt(p, rs->body);
 	lb_build_stmt(p, rs->body);
@@ -1029,6 +1047,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
 	lbBlock *done = nullptr;
 	lbBlock *done = nullptr;
 	bool is_map = false;
 	bool is_map = false;
 
 
+	if (rs->label != nullptr && p->debug_info != nullptr) {
+		lbBlock *label = lb_create_block(p, "for.range.label");
+		lb_emit_jump(p, label);
+		lb_start_block(p, label);
+
+		LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
+		lb_add_debug_label(p, rs->label, label);
+	}
+
 	if (tav.mode == Addressing_Type) {
 	if (tav.mode == Addressing_Type) {
 		lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
 		lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
 	} else {
 	} else {
@@ -1530,6 +1557,14 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
 gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
 	lb_open_scope(p, scope);
 	lb_open_scope(p, scope);
 
 
+	if (ss->label != nullptr && p->debug_info != nullptr) {
+		lbBlock *label = lb_create_block(p, "switch.label");
+		lb_emit_jump(p, label);
+		lb_start_block(p, label);
+
+		LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, ss->label));
+		lb_add_debug_label(p, ss->label, label);
+	}
 	if (ss->init != nullptr) {
 	if (ss->init != nullptr) {
 		lb_build_stmt(p, ss->init);
 		lb_build_stmt(p, ss->init);
 	}
 	}
@@ -1736,6 +1771,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
 gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
 gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
 	ast_node(cc, CaseClause, clause);
 	ast_node(cc, CaseClause, clause);
 
 
+	// NOTE(tf2spi): Debug info for label not generated here on purpose
 	lb_push_target_list(p, label, done, nullptr, nullptr);
 	lb_push_target_list(p, label, done, nullptr, nullptr);
 	lb_build_stmt_list(p, cc->stmts);
 	lb_build_stmt_list(p, cc->stmts);
 	lb_close_scope(p, lbDeferExit_Default, body, clause);
 	lb_close_scope(p, lbDeferExit_Default, body, clause);
@@ -2307,6 +2343,14 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 		else_ = lb_create_block(p, "if.else");
 		else_ = lb_create_block(p, "if.else");
 	}
 	}
 	if (is->label != nullptr) {
 	if (is->label != nullptr) {
+		if (p->debug_info != nullptr) {
+			lbBlock *label = lb_create_block(p, "if.label");
+			lb_emit_jump(p, label);
+			lb_start_block(p, label);
+
+			LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, is->label));
+			lb_add_debug_label(p, is->label, label);
+		}
 		lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
 		lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
 		tl->is_block = true;
 		tl->is_block = true;
 	}
 	}
@@ -2399,12 +2443,19 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 
 
 	lb_push_target_list(p, fs->label, done, post, nullptr);
 	lb_push_target_list(p, fs->label, done, post, nullptr);
 
 
+	if (fs->label != nullptr && p->debug_info != nullptr) {
+		lbBlock *label = lb_create_block(p, "for.label");
+		lb_emit_jump(p, label);
+		lb_start_block(p, label);
+
+		LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->label));
+		lb_add_debug_label(p, fs->label, label);
+	}
 	if (fs->init != nullptr) {
 	if (fs->init != nullptr) {
-	#if 1
 		lbBlock *init = lb_create_block(p, "for.init");
 		lbBlock *init = lb_create_block(p, "for.init");
 		lb_emit_jump(p, init);
 		lb_emit_jump(p, init);
 		lb_start_block(p, init);
 		lb_start_block(p, init);
-	#endif
+
 		lb_build_stmt(p, fs->init);
 		lb_build_stmt(p, fs->init);
 	}
 	}
 
 
@@ -2420,7 +2471,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 		lb_start_block(p, body);
 		lb_start_block(p, body);
 	}
 	}
 
 
-
 	lb_build_stmt(p, fs->body);
 	lb_build_stmt(p, fs->body);
 
 
 	lb_pop_target_list(p);
 	lb_pop_target_list(p);
@@ -2694,9 +2744,21 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
 
 
 
 
 	case_ast_node(bs, BlockStmt, node);
 	case_ast_node(bs, BlockStmt, node);
+		lbBlock *body = nullptr;
 		lbBlock *done = nullptr;
 		lbBlock *done = nullptr;
 		if (bs->label != nullptr) {
 		if (bs->label != nullptr) {
+			if (p->debug_info != nullptr) {
+				lbBlock *label = lb_create_block(p, "block.label");
+				lb_emit_jump(p, label);
+				lb_start_block(p, label);
+
+				LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, bs->label));
+				lb_add_debug_label(p, bs->label, label);
+			}
+			body = lb_create_block(p, "block.body");
 			done = lb_create_block(p, "block.done");
 			done = lb_create_block(p, "block.done");
+			lb_emit_jump(p, body);
+			lb_start_block(p, body);
 			lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
 			lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
 			tl->is_block = true;
 			tl->is_block = true;
 		}
 		}