diff --git a/spec/compiler/codegen/debug_spec.cr b/spec/compiler/codegen/debug_spec.cr index fcf1c7d71ab9..30046b87b773 100644 --- a/spec/compiler/codegen/debug_spec.cr +++ b/spec/compiler/codegen/debug_spec.cr @@ -188,6 +188,37 @@ describe "Code gen: debug" do @@x end end - ), debug: Crystal::Debug::All) + ), debug: Crystal::Debug::All) + end + + it "stores and restores debug location after jumping to main (2)" do + codegen(%( + module Foo + @@x : Int32 = begin + y = 1 + end + + def self.x + @@x + end + end + + Foo.x + ), debug: Crystal::Debug::All) + end + + it "stores and restores debug location after jumping to main (3)" do + codegen(%( + def raise(exception) + x = uninitialized NoReturn + x + end + + lib LibFoo + $foo : -> + end + + LibFoo.foo = ->{ } + ), debug: Crystal::Debug::All) end end diff --git a/src/compiler/crystal/codegen/class_var.cr b/src/compiler/crystal/codegen/class_var.cr index b9573b9d54af..d2979dcf161d 100644 --- a/src/compiler/crystal/codegen/class_var.cr +++ b/src/compiler/crystal/codegen/class_var.cr @@ -92,6 +92,7 @@ class Crystal::CodeGenVisitor global = declare_class_var(class_var) global = ensure_class_var_in_this_module(global, class_var) if init_func + set_current_debug_location initializer.node if @debug.line_numbers? call init_func end return global @@ -121,6 +122,8 @@ class Crystal::CodeGenVisitor discard = false new_func = in_main do define_main_function(init_function_name, ([] of LLVM::Type), llvm_context.void, needs_alloca: true) do |func| + set_internal_fun_debug_location(func, init_function_name, node.location) + with_cloned_context do # "self" in a constant is the class_var owner context.type = class_var.owner @@ -223,6 +226,8 @@ class Crystal::CodeGenVisitor def create_read_virtual_class_var_ptr_function(fun_name, class_var, owner) in_main do define_main_function(fun_name, [llvm_context.int32], llvm_type(class_var.type).pointer) do |func| + set_internal_fun_debug_location(func, fun_name) + self_type_id = func.params[0] cmp = equal?(self_type_id, type_id(owner.base_type)) @@ -268,6 +273,8 @@ class Crystal::CodeGenVisitor def create_read_virtual_metaclass_var_ptr_function(fun_name, class_var, owner) in_main do define_main_function(fun_name, [llvm_context.int32], llvm_type(class_var.type).pointer) do |func| + set_internal_fun_debug_location(func, fun_name) + self_type_id = func.params[0] cmp = equal?(self_type_id, type_id(owner.base_type.metaclass)) @@ -313,6 +320,7 @@ class Crystal::CodeGenVisitor in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_type(class_var.type).pointer) do |func| + set_internal_fun_debug_location(func, fun_name, initializer.node.location) init_func = check_main_fun init_func.name, init_func ret lazy_initialize_class_var(initializer.node, init_func, global, initialized_flag) end diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index d08b99b3dfab..bcd0932f31b1 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -189,8 +189,6 @@ module Crystal @personality_name = "__crystal_personality" end - emit_main_def_debug_metadata(@main, "??") unless @debug.none? - @context = Context.new @main, @program @context.return_type = @main_ret_type @@ -207,6 +205,8 @@ module Crystal @modules = {"" => @main_module_info} of String => ModuleInfo @types_to_modules = {} of Type => ModuleInfo + set_internal_fun_debug_location(@main, MAIN_NAME, nil) + @alloca_block, @entry_block = new_entry_block_chain "alloca", "entry" @in_lib = false @@ -574,6 +574,7 @@ module Crystal the_fun = codegen_fun fun_literal_name, node.def, context.type, fun_module_info: @main_module_info, is_fun_literal: true, is_closure: is_closure the_fun = check_main_fun fun_literal_name, the_fun + set_current_debug_location(node) if @debug.line_numbers? fun_ptr = bit_cast(the_fun, llvm_context.void_pointer) if is_closure ctx_ptr = bit_cast(context.closure_ptr.not_nil!, llvm_context.void_pointer) @@ -1119,6 +1120,7 @@ module Crystal unless thread_local_fun thread_local_fun = in_main do define_main_function(fun_name, [llvm_type(type).pointer.pointer], llvm_context.void) do |func| + set_internal_fun_debug_location(func, fun_name, real_var.location) builder.store get_global_var(name, type, real_var), func.params[0] builder.ret end @@ -1595,6 +1597,8 @@ module Crystal def create_check_proc_is_not_closure_fun(fun_name) in_main do define_main_function(fun_name, [llvm_typer.proc_type], llvm_context.void_pointer) do |func| + set_internal_fun_debug_location(func, fun_name) + param = func.params.first fun_ptr = extract_value param, 0 @@ -1693,6 +1697,16 @@ module Crystal end end + # used for generated internal functions like `~metaclass` and `~match` + def set_internal_fun_debug_location(func, name, location = nil) + return if @debug.none? + location ||= UNKNOWN_LOCATION + emit_fun_debug_metadata(func, name, location) + set_current_debug_location(location) if @debug.line_numbers? + end + + private UNKNOWN_LOCATION = Location.new("??", 0, 0) + def llvm_self(type = context.type) self_var = context.vars["self"]? if self_var diff --git a/src/compiler/crystal/codegen/const.cr b/src/compiler/crystal/codegen/const.cr index 48a053619a60..80b91f2b7170 100644 --- a/src/compiler/crystal/codegen/const.cr +++ b/src/compiler/crystal/codegen/const.cr @@ -72,6 +72,8 @@ class Crystal::CodeGenVisitor end def initialize_simple_const(const) + set_current_debug_location const.locations.try &.first? if @debug.line_numbers? + global = declare_const(const) request_value do accept const.value @@ -100,6 +102,8 @@ class Crystal::CodeGenVisitor # Start with fresh variables context.vars = LLVMVars.new + set_current_debug_location const.locations.try &.first? if @debug.line_numbers? + alloca_vars const.fake_def.try(&.vars), const.fake_def request_value do accept const.value @@ -146,6 +150,8 @@ class Crystal::CodeGenVisitor in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_context.void, needs_alloca: true) do |func| + set_internal_fun_debug_location(func, fun_name, const.locations.try &.first?) + with_cloned_context do # "self" in a constant is the constant's namespace context.type = const.namespace @@ -228,6 +234,7 @@ class Crystal::CodeGenVisitor def create_read_const_function(fun_name, const) in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_type(const.value.type).pointer) do |func| + set_internal_fun_debug_location(func, fun_name, const.locations.try &.first?) global = initialize_const(const) ret global end diff --git a/src/compiler/crystal/codegen/debug.cr b/src/compiler/crystal/codegen/debug.cr index fb80af027025..8181f40ea7ee 100644 --- a/src/compiler/crystal/codegen/debug.cr +++ b/src/compiler/crystal/codegen/debug.cr @@ -451,25 +451,25 @@ module Crystal builder.set_current_debug_location(0, 0, nil) end - def emit_main_def_debug_metadata(main_fun, filename) + def emit_fun_debug_metadata(func, fun_name, location, *, debug_types = [] of LibLLVMExt::Metadata, is_optimized = false) + filename = location.try(&.original_filename) || "??" + line_number = location.try(&.line_number) || 0 + file, dir = file_and_dir(filename) scope = di_builder.create_file(file, dir) - fn_metadata = di_builder.create_function(scope, MAIN_NAME, MAIN_NAME, scope, - 0, fun_metadata_type, true, true, 0, LLVM::DIFlags::Zero, false, main_fun) - fun_metadatas[main_fun] = [FunMetadata.new(filename || "??", fn_metadata)] + fn_metadata = di_builder.create_function(scope, fun_name, fun_name, scope, + line_number, fun_metadata_type(debug_types), true, true, + line_number, LLVM::DIFlags::Zero, is_optimized, func) + fun_metadatas[func] = [FunMetadata.new(filename, fn_metadata)] end def emit_def_debug_metadata(target_def) location = target_def.location.try &.expanded_location return unless location - file, dir = file_and_dir(location.filename) - scope = di_builder.create_file(file, dir) - is_optimised = !@debug.variables? - fn_metadata = di_builder.create_function(scope, target_def.name, target_def.name, scope, - location.line_number, fun_metadata_type(context.fun_debug_params), true, true, - location.line_number, LLVM::DIFlags::Zero, is_optimised, context.fun) - fun_metadatas[context.fun] = [FunMetadata.new(location.original_filename || "??", fn_metadata)] + emit_fun_debug_metadata(context.fun, target_def.name, location, + debug_types: context.fun_debug_params, + is_optimized: !@debug.variables?) end def declare_debug_for_function_argument(arg_name, arg_type, arg_no, alloca, location) diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr index deb168395ade..dbcc0964bd05 100644 --- a/src/compiler/crystal/codegen/fun.cr +++ b/src/compiler/crystal/codegen/fun.cr @@ -84,6 +84,7 @@ class Crystal::CodeGenVisitor needs_body = !target_def.is_a?(External) || is_exported_fun if needs_body emit_def_debug_metadata target_def unless @debug.none? + set_current_debug_location target_def if @debug.line_numbers? context.fun.add_attribute LLVM::Attribute::UWTable if @program.has_flag?("darwin") diff --git a/src/compiler/crystal/codegen/match.cr b/src/compiler/crystal/codegen/match.cr index 8e0cc646c0e3..67bf74dd8c9b 100644 --- a/src/compiler/crystal/codegen/match.cr +++ b/src/compiler/crystal/codegen/match.cr @@ -47,6 +47,7 @@ class Crystal::CodeGenVisitor private def create_match_fun(name, type) in_main do define_main_function(name, ([llvm_context.int32]), llvm_context.int1) do |func| + set_internal_fun_debug_location(func, name) type_id = func.params.first create_match_fun_body(type, type_id) end diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index b23082a8cbd3..281b1374f813 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -916,6 +916,8 @@ class Crystal::CodeGenVisitor in_main do define_main_function(name, ([llvm_context.int32]), llvm_context.int32) do |func| + set_internal_fun_debug_location(func, name) + arg = func.params.first current_block = insert_block