-
In many languages, the local variables in functions can have their lifetime
-or scope limited to a subset of a function. In the C family of languages, for
-example, variables are only live (readable and writable) within the source block
-that they are defined in. In functional languages, values are only readable
-after they have been defined. Though this is a very obvious concept, it is also
-non-trivial to model in LLVM, because it has no notion of scoping in this sense,
-and does not want to be tied to a language's scoping rules.
+
Here !7 is metadata providing location information. It has four
+ fields: line number, column number, scope, and original scope. The original
+ scope represents inline location if this instruction is inlined inside a
+ caller, and is null otherwise. In this example, scope is encoded by
+ !1. !1 represents a lexical block inside the scope
+ !2, where !2 is a
+ subprogram descriptor. This way the
+ location information attached to the intrinsics indicates that the
+ variable X is declared at line number 2 at a function level scope in
+ function foo.
-
In order to handle this, the LLVM debug format uses the notion of "regions"
-of a function, delineated by calls to intrinsic functions. These intrinsic
-functions define new regions of the program and indicate when the region
-lifetime expires. Consider the following C fragment, for example:
+
Now lets take another example.
+
-1. void foo() {
-2. int X = ...;
-3. int Y = ...;
-4. {
-5. int Z = ...;
-6. ...
-7. }
-8. ...
-9. }
+call void @llvm.dbg.declare(metadata, metadata !12), !dbg !14
+
-
Compiled to LLVM, this function would be represented like this:
+
The second intrinsic
+ %llvm.dbg.declare
+ encodes debugging information for variable Z. The metadata
+ !dbg !14 attached to the intrinsic provides scope information for
+ the variable Z.
+
-void %foo() {
-entry:
- %X = alloca int
- %Y = alloca int
- %Z = alloca int
-
- ...
-
- call void %llvm.dbg.func.start( %llvm.dbg.subprogram.type* %llvm.dbg.subprogram )
-
- call void %llvm.dbg.stoppoint( uint 2, uint 2, %llvm.dbg.compile_unit* %llvm.dbg.compile_unit )
-
- call void %llvm.dbg.declare({}* %X, ...)
- call void %llvm.dbg.declare({}* %Y, ...)
-
- ;; Evaluate expression on line 2, assigning to X.
-
- call void %llvm.dbg.stoppoint( uint 3, uint 2, %llvm.dbg.compile_unit* %llvm.dbg.compile_unit )
-
- ;; Evaluate expression on line 3, assigning to Y.
-
- call void %llvm.region.start()
- call void %llvm.dbg.stoppoint( uint 5, uint 4, %llvm.dbg.compile_unit* %llvm.dbg.compile_unit )
- call void %llvm.dbg.declare({}* %X, ...)
-
- ;; Evaluate expression on line 5, assigning to Z.
-
- call void %llvm.dbg.stoppoint( uint 7, uint 2, %llvm.dbg.compile_unit* %llvm.dbg.compile_unit )
- call void %llvm.region.end()
-
- call void %llvm.dbg.stoppoint( uint 9, uint 2, %llvm.dbg.compile_unit* %llvm.dbg.compile_unit )
-
- call void %llvm.region.end()
-
- ret void
-}
+!13 = metadata !{i32 458763, metadata !1}; [DW_TAG_lexical_block ]
+!14 = metadata !{i32 5, i32 9, metadata !13, null}
+
-
This example illustrates a few important details about the LLVM debugging
-information. In particular, it shows how the various intrinsics are applied
-together to allow a debugger to analyze the relationship between statements,
-variable definitions, and the code used to implement the function.
-
-
The first intrinsic %llvm.dbg.func.start provides
-a link with the subprogram descriptor
-containing the details of this function. This call also defines the beginning
-of the function region, bounded by the %llvm.region.end at the end of
-the function. This region is used to bracket the lifetime of variables declared
-within. For a function, this outer region defines a new stack frame whose
-lifetime ends when the region is ended.
-
-
It is possible to define inner regions for short term variables by using the
-%llvm.region.start and %llvm.region.end to bound a
-region. The inner region in this example would be for the block containing the
-declaration of Z.
-
-
Using regions to represent the boundaries of source-level functions allow
-LLVM interprocedural optimizations to arbitrarily modify LLVM functions without
-having to worry about breaking mapping information between the LLVM code and the
-and source-level program. In particular, the inliner requires no modification
-to support inlining with debugging information: there is no explicit correlation
-drawn between LLVM functions and their source-level counterparts (note however,
-that if the inliner inlines all instances of a non-strong-linkage function into
-its caller that it will not be possible for the user to manually invoke the
-inlined function from a debugger).
-
-
Once the function has been defined, the stopping point corresponding to
-line #2 (column #2) of the function is encountered. At this point in the
-function, no local variables are live. As lines 2 and 3 of the example
-are executed, their variable definitions are introduced into the program using
-%llvm.dbg.declare, without the
-need to specify a new region. These variables do not require new regions to be
-introduced because they go out of scope at the same point in the program: line
-9.
-
-
In contrast, the Z variable goes out of scope at a different time,
-on line 7. For this reason, it is defined within the inner region, which kills
-the availability of Z before the code for line 8 is executed. In this
-way, regions can support arbitrary source-language scoping rules, as long as
-they can only be nested (ie, one scope cannot partially overlap with a part of
-another scope).
-
-
It is worth noting that this scoping mechanism is used to control scoping of
-all declarations, not just variable declarations. For example, the scope of a
-C++ using declaration is controlled with this and could change how name lookup is
-performed.
+
Here !14 indicates that Z is declared at line number 5 and
+ column number 9 inside of lexical scope !13. The lexical scope
+ itself resides inside of lexical scope !1 described above.
-
The scope information attached with each instruction provides a
+ straightforward way to find instructions covered by a scope.