Skip to content

Commit

Permalink
Attach debug locations to generated internal LLVM functions (#10934)
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil authored Aug 4, 2021
1 parent 42ce885 commit 606ea3b
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 14 deletions.
33 changes: 32 additions & 1 deletion spec/compiler/codegen/debug_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/compiler/crystal/codegen/class_var.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
18 changes: 16 additions & 2 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/crystal/codegen/const.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 11 additions & 11 deletions src/compiler/crystal/codegen/debug.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/compiler/crystal/codegen/fun.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions src/compiler/crystal/codegen/match.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/codegen/primitives.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 606ea3b

Please sign in to comment.