1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# Debug Info Module
This module serves the purpose of generating debug symbols. We use LLVM's
[source level debugging](https://llvm.org/docs/SourceLevelDebugging.html)
features for generating the debug information. The general principle is
this:
Given the right metadata in the LLVM IR, the LLVM code generator is able to
create DWARF debug symbols for the given code. The
[metadata](https://llvm.org/docs/LangRef.html#metadata-type) is structured
much like DWARF *debugging information entries* (DIE), representing type
information such as datatype layout, function signatures, block layout,
variable location and scope information, etc. It is the purpose of this
module to generate correct metadata and insert it into the LLVM IR.
As the exact format of metadata trees may change between different LLVM
versions, we now use LLVM
[DIBuilder](https://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html)
to create metadata where possible. This will hopefully ease the adaption of
this module to future LLVM versions.
The public API of the module is a set of functions that will insert the
correct metadata into the LLVM IR when called with the right parameters.
The module is thus driven from an outside client with functions like
`debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`.
Internally the module will try to reuse already created metadata by
utilizing a cache. The way to get a shared metadata node when needed is
thus to just call the corresponding function in this module:
```ignore (illustrative)
let file_metadata = file_metadata(cx, file);
```
The function will take care of probing the cache for an existing node for
that exact file path.
All private state used by the module is stored within either the
CodegenUnitDebugContext struct (owned by the CodegenCx) or the
FunctionDebugContext (owned by the FunctionCx).
This file consists of three conceptual sections:
1. The public interface of the module
2. Module-internal metadata creation functions
3. Minor utility functions
## Recursive Types
Some kinds of types, such as structs and enums can be recursive. That means
that the type definition of some type X refers to some other type which in
turn (transitively) refers to X. This introduces cycles into the type
referral graph. A naive algorithm doing an on-demand, depth-first traversal
of this graph when describing types, can get trapped in an endless loop
when it reaches such a cycle.
For example, the following simple type for a singly-linked list...
```
struct List {
value: i32,
tail: Option<Box<List>>,
}
```
will generate the following callstack with a naive DFS algorithm:
```ignore (illustrative)
describe(t = List)
describe(t = i32)
describe(t = Option<Box<List>>)
describe(t = Box<List>)
describe(t = List) // at the beginning again...
...
```
To break cycles like these, we use "stubs". That is, when
the algorithm encounters a possibly recursive type (any struct or enum), it
immediately creates a type description node and inserts it into the cache
*before* describing the members of the type. This type description is just
a stub (as type members are not described and added to it yet) but it
allows the algorithm to already refer to the type. After the stub is
inserted into the cache, the algorithm continues as before. If it now
encounters a recursive reference, it will hit the cache and does not try to
describe the type anew. This behavior is encapsulated in the
`type_map::build_type_with_children()` function.
## Source Locations and Line Information
In addition to data type descriptions the debugging information must also
allow to map machine code locations back to source code locations in order
to be useful. This functionality is also handled in this module. The
following functions allow to control source mappings:
+ `set_source_location()`
+ `clear_source_location()`
+ `start_emitting_source_locations()`
`set_source_location()` allows to set the current source location. All IR
instructions created after a call to this function will be linked to the
given source location, until another location is specified with
`set_source_location()` or the source location is cleared with
`clear_source_location()`. In the later case, subsequent IR instruction
will not be linked to any source location. As you can see, this is a
stateful API (mimicking the one in LLVM), so be careful with source
locations set by previous calls. It's probably best to not rely on any
specific state being present at a given point in code.
One topic that deserves some extra attention is *function prologues*. At
the beginning of a function's machine code there are typically a few
instructions for loading argument values into allocas and checking if
there's enough stack space for the function to execute. This *prologue* is
not visible in the source code and LLVM puts a special PROLOGUE END marker
into the line table at the first non-prologue instruction of the function.
In order to find out where the prologue ends, LLVM looks for the first
instruction in the function body that is linked to a source location. So,
when generating prologue instructions we have to make sure that we don't
emit source location information until the 'real' function body begins. For
this reason, source location emission is disabled by default for any new
function being codegened and is only activated after a call to the third
function from the list above, `start_emitting_source_locations()`. This
function should be called right before regularly starting to codegen the
top-level block of the given function.
There is one exception to the above rule: `llvm.dbg.declare` instruction
must be linked to the source location of the variable being declared. For
function parameters these `llvm.dbg.declare` instructions typically occur
in the middle of the prologue, however, they are ignored by LLVM's prologue
detection. The `create_argument_metadata()` and related functions take care
of linking the `llvm.dbg.declare` instructions to the correct source
locations even while source location emission is still disabled, so there
is no need to do anything special with source location handling here.
|