Skip to content

Commit ccb721a

Browse files
debuginfo: Added description of algorithm for handling recursive types.
Also fixed nasty bug caused by calling LLVMDIBuilderCreateStructType() with a null pointer where an empty array was expected (which would trigger an unintelligable assertion somewhere down the line).
1 parent 1ce02e7 commit ccb721a

File tree

4 files changed

+72
-112
lines changed

4 files changed

+72
-112
lines changed

src/librustc/middle/trans/debuginfo.rs

+66-83
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,46 @@ This file consists of three conceptual sections:
4545
2. Module-internal metadata creation functions
4646
3. Minor utility functions
4747
48+
49+
## Recursive Types
50+
Some kinds of types, such as structs and enums can be recursive. That means that the type definition
51+
of some type X refers to some other type which in turn (transitively) refers to X. This introduces
52+
cycles into the type referral graph. A naive algorithm doing an on-demand, depth-first traversal of
53+
this graph when describing types, can get trapped in an endless loop when it reaches such a cycle.
54+
55+
For example, the following simple type for a singly-linked list...
56+
57+
```
58+
struct List {
59+
value: int,
60+
tail: Option<@List>,
61+
}
62+
```
63+
64+
will generate the following callstack with a naive DFS algorithm:
65+
66+
```
67+
describe(t = List)
68+
describe(t = int)
69+
describe(t = Option<@List>)
70+
describe(t = @List)
71+
describe(t = List) // at the beginning again...
72+
...
73+
```
74+
75+
To break cycles like these, we use "forward declarations". That is, when the algorithm encounters a
76+
possibly recursive type (any struct or enum), it immediately creates a type description node and
77+
inserts it into the cache *before* describing the members of the type. This type description is just
78+
a stub (as type members are not described and added to it yet) but it allows the algorithm to
79+
already refer to the type. After the stub is inserted into the cache, the algorithm continues as
80+
before. If it now encounters a recursive reference, it will hit the cache and does not try to
81+
describe the type anew.
82+
83+
This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which represents a kind of
84+
continuation, storing all state needed to continue traversal at the type members after the type has
85+
been registered with the cache. (This implementation approach might be a tad over-engineered and
86+
may change in the future)
87+
4888
*/
4989

5090

@@ -561,16 +601,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
561601
_) => {
562602
(ident, fn_decl, generics, Some(top_level_block), span)
563603
}
564-
ast_map::node_foreign_item(@ast::foreign_item {
565-
ident: ident,
566-
node: ast::foreign_item_fn(ref fn_decl, ref generics),
567-
span: span,
568-
_
569-
},
570-
_,
571-
_,
572-
_) => {
573-
//(ident, fn_decl, generics, None, span)
604+
ast_map::node_foreign_item(@ast::foreign_item { _ }, _, _, _) => {
574605
return FunctionWithoutDebugInfo;
575606
}
576607
ast_map::node_variant(*) |
@@ -1062,18 +1093,13 @@ fn prepare_struct_metadata(cx: &mut CrateContext,
10621093
span: Span)
10631094
-> RecursiveTypeDescription {
10641095
let struct_name = ppaux::ty_to_str(cx.tcx, struct_type);
1065-
println(fmt!("struct_metadata: %s", struct_name));
1066-
10671096
let struct_llvm_type = type_of::type_of(cx, struct_type);
1097+
10681098
let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, def_id, span);
10691099

10701100
let file_name = span_start(cx, definition_span).file.name;
10711101
let file_metadata = file_metadata(cx, file_name);
10721102

1073-
let loc = span_start(cx, definition_span);
1074-
1075-
let (composite_size, composite_align) = size_and_align_of(cx, struct_llvm_type);
1076-
10771103
let struct_metadata_stub = create_struct_stub(cx,
10781104
struct_llvm_type,
10791105
struct_name,
@@ -1142,43 +1168,14 @@ impl RecursiveTypeDescription {
11421168

11431169
// ... then create the member descriptions
11441170
let member_descriptions = member_description_factory(cx);
1145-
let member_metadata: ~[DIDescriptor] = member_descriptions
1146-
.iter()
1147-
.enumerate()
1148-
.map(|(i, member_description)| {
1149-
let (member_size,
1150-
member_align) = size_and_align_of(cx, member_description.llvm_type);
1151-
let member_offset = match member_description.offset {
1152-
FixedMemberOffset { bytes } => bytes,
1153-
ComputedMemberOffset => {
1154-
machine::llelement_offset(cx, llvm_type, i)
1155-
}
1156-
};
1157-
1158-
do member_description.name.with_c_str |member_name| {
1159-
unsafe {
1160-
llvm::LLVMDIBuilderCreateMemberType(
1161-
DIB(cx),
1162-
metadata_stub,
1163-
member_name,
1164-
file_metadata,
1165-
0 as c_uint,
1166-
bytes_to_bits(member_size),
1167-
bytes_to_bits(member_align),
1168-
bytes_to_bits(member_offset),
1169-
0,
1170-
member_description.type_metadata)
1171-
}
1172-
}
1173-
})
1174-
.collect();
1175-
1176-
unsafe {
1177-
let type_array = create_DIArray(DIB(cx), member_metadata);
1178-
llvm::LLVMDICompositeTypeSetTypeArray(metadata_stub, type_array);
1179-
}
11801171

1181-
metadata_stub
1172+
set_members_of_composite_type(cx,
1173+
metadata_stub,
1174+
llvm_type,
1175+
member_descriptions,
1176+
file_metadata,
1177+
codemap::dummy_sp());
1178+
return metadata_stub;
11821179
}
11831180
}
11841181
}
@@ -1469,6 +1466,7 @@ fn prepare_enum_metadata(cx: &mut CrateContext,
14691466

14701467
enum MemberOffset {
14711468
FixedMemberOffset{ bytes: uint },
1469+
// For ComputedMemberOffset, the offset is read from the llvm type definition
14721470
ComputedMemberOffset
14731471
}
14741472

@@ -1490,26 +1488,15 @@ fn composite_type_metadata(cx: &mut CrateContext,
14901488
file_metadata: DIFile,
14911489
definition_span: Span)
14921490
-> DICompositeType {
1493-
let loc = span_start(cx, definition_span);
1494-
let (composite_size, composite_align) = size_and_align_of(cx, composite_llvm_type);
1495-
1496-
let composite_type_metadata = do composite_type_name.with_c_str |name| {
1497-
unsafe {
1498-
llvm::LLVMDIBuilderCreateStructType(
1499-
DIB(cx),
1500-
containing_scope,
1501-
name,
1502-
file_metadata,
1503-
loc.line as c_uint,
1504-
bytes_to_bits(composite_size),
1505-
bytes_to_bits(composite_align),
1506-
0,
1507-
ptr::null(),
1508-
ptr::null(),
1509-
0,
1510-
ptr::null())
1511-
}};
1512-
1491+
// Create the (empty) struct metadata node ...
1492+
let composite_type_metadata = create_struct_stub(cx,
1493+
composite_llvm_type,
1494+
composite_type_name,
1495+
containing_scope,
1496+
file_metadata,
1497+
definition_span);
1498+
1499+
// ... and immediately create and add the member descriptions.
15131500
set_members_of_composite_type(cx,
15141501
composite_type_metadata,
15151502
composite_llvm_type,
@@ -1563,7 +1550,7 @@ fn set_members_of_composite_type(cx: &mut CrateContext,
15631550
}
15641551

15651552
// A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any caching, does not
1566-
// add any fields to the struct. This can be done later with LLVMDICompositeTypeSetTypeArray().
1553+
// add any fields to the struct. This can be done later with set_members_of_composite_type().
15671554
fn create_struct_stub(cx: &mut CrateContext,
15681555
struct_llvm_type: Type,
15691556
struct_type_name: &str,
@@ -1576,6 +1563,10 @@ fn create_struct_stub(cx: &mut CrateContext,
15761563

15771564
return do struct_type_name.with_c_str |name| {
15781565
unsafe {
1566+
// LLVMDIBuilderCreateStructType() wants an empty array. A null pointer will lead to
1567+
// hard to trace and debug LLVM assertions later on in llvm/lib/IR/Value.cpp
1568+
let empty_array = create_DIArray(DIB(cx), []);
1569+
15791570
llvm::LLVMDIBuilderCreateStructType(
15801571
DIB(cx),
15811572
containing_scope,
@@ -1586,7 +1577,7 @@ fn create_struct_stub(cx: &mut CrateContext,
15861577
bytes_to_bits(struct_align),
15871578
0,
15881579
ptr::null(),
1589-
ptr::null(),
1580+
empty_array,
15901581
0,
15911582
ptr::null())
15921583
}};
@@ -1864,7 +1855,7 @@ fn trait_metadata(cx: &mut CrateContext,
18641855
substs: &ty::substs,
18651856
trait_store: ty::TraitStore,
18661857
mutability: ast::Mutability,
1867-
builtinBounds: &ty::BuiltinBounds,
1858+
_: &ty::BuiltinBounds,
18681859
usage_site_span: Span)
18691860
-> DIType {
18701861
// The implementation provided here is a stub. It makes sure that the trait type is
@@ -2120,14 +2111,6 @@ fn DIB(cx: &CrateContext) -> DIBuilderRef {
21202111
cx.dbg_cx.get_ref().builder
21212112
}
21222113

2123-
fn assert_fcx_has_span(fcx: &FunctionContext) {
2124-
if fcx.span.is_none() {
2125-
fcx.ccx.sess.bug(fmt!("debuginfo: Encountered function %s with invalid source span. \
2126-
This function should have been ignored by debuginfo generation.",
2127-
ast_map::path_to_str(fcx.path, fcx.ccx.sess.intr())));
2128-
}
2129-
}
2130-
21312114
fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
21322115
match fcx.debug_context {
21332116
FunctionDebugContext(_) => false,

src/rustllvm/RustWrapper.cpp

-23
Original file line numberDiff line numberDiff line change
@@ -790,29 +790,6 @@ extern "C" LLVMValueRef LLVMDIBuilderCreateNameSpace(
790790
LineNo));
791791
}
792792

793-
// extern "C" LLVMValueRef LLVMDIBuilderCreateForwardDecl(
794-
// DIBuilderRef Builder,
795-
// unsigned Tag,
796-
// const char* Name,
797-
// LLVMValueRef Scope,
798-
// LLVMValueRef File,
799-
// unsigned Line,
800-
// unsigned RuntimeLang,
801-
// uint64_t SizeInBits,
802-
// uint64_t AlignInBits)
803-
// {
804-
// return wrap(Builder->createForwardDecl(
805-
// Tag,
806-
// Name,
807-
// unwrapDI<DIDescriptor>(Scope),
808-
// unwrapDI<DIFile>(File),
809-
// Line,
810-
// RuntimeLang,
811-
// SizeInBits,
812-
// AlignInBits
813-
// ));
814-
// }
815-
816793
extern "C" void LLVMDICompositeTypeSetTypeArray(
817794
LLVMValueRef CompositeType,
818795
LLVMValueRef TypeArray)

src/test/debug-info/recursive-struct.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ struct LongCycleWithAnonymousTypes {
142142

143143
// This test case makes sure that recursive structs are properly described. The Node structs are
144144
// generic so that we can have a new type (that newly needs to be described) for the different
145-
// cases. The potential problem with recursive types is that the DI generation algorithm get trapped
146-
// in an endless loop. To make sure, we actually test this in the different cases, we have to
147-
// operate on a new type each time, otherwise we would just hit the DI cache for all but the first
148-
// case.
145+
// cases. The potential problem with recursive types is that the DI generation algorithm gets
146+
// trapped in an endless loop. To make sure, we actually test this in the different cases, we have
147+
// to operate on a new type each time, otherwise we would just hit the DI cache for all but the
148+
// first case.
149149

150150
// The different cases below (stack_*, unique_*, box_*, etc) are set up so that the type description
151151
// algorithm will enter the type reference cycle that is created by a recursive definition from a
152-
// different context.
152+
// different context each time.
153153

154154
// The "long cycle" cases are constructed to span a longer, indirect recursion cycle between types.
155155
// The different locals will cause the DI algorithm to enter the type reference cycle at different

src/test/debug-info/trait-pointers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct Struct {
2424

2525
impl Trait for Struct {}
2626

27-
// There is no real test here yet. Just make that it compiles without crashing.
27+
// There is no real test here yet. Just make sure that it compiles without crashing.
2828
fn main() {
2929
let stack_struct = Struct { a:0, b: 1.0 };
3030
let reference: &Trait = &stack_struct as &Trait;

0 commit comments

Comments
 (0)