]> git.lizzy.rs Git - rust.git/commitdiff
debuginfo: Added description of algorithm for handling recursive types.
authorMichael Woerister <michaelwoerister@gmail>
Fri, 13 Sep 2013 14:26:35 +0000 (16:26 +0200)
committerMichael Woerister <michaelwoerister@gmail>
Sun, 15 Sep 2013 10:28:25 +0000 (12:28 +0200)
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).

src/librustc/middle/trans/debuginfo.rs
src/rustllvm/RustWrapper.cpp
src/test/debug-info/recursive-struct.rs
src/test/debug-info/trait-pointers.rs

index f26d8365092d5bcd7c513e190b0638d16c9cd027..b10f03729e44c996584d0e6cd81c1fc1d04b778a 100644 (file)
 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: int,
+    tail: Option<@List>,
+}
+```
+
+will generate the following callstack with a naive DFS algorithm:
+
+```
+describe(t = List)
+  describe(t = int)
+  describe(t = Option<@List>)
+    describe(t = @List)
+      describe(t = List) // at the beginning again...
+      ...
+```
+
+To break cycles like these, we use "forward declarations". 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 behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which represents a kind of
+continuation, storing all state needed to continue traversal at the type members after the type has
+been registered with the cache. (This implementation approach might be a tad over-engineered and
+may change in the future)
+
 */
 
 
@@ -561,16 +601,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
             _) => {
             (ident, fn_decl, generics, Some(top_level_block), span)
         }
-        ast_map::node_foreign_item(@ast::foreign_item {
-                ident: ident,
-                node: ast::foreign_item_fn(ref fn_decl, ref generics),
-                span: span,
-                _
-            },
-            _,
-            _,
-            _) => {
-            //(ident, fn_decl, generics, None, span)
+        ast_map::node_foreign_item(@ast::foreign_item { _ }, _, _, _) => {
             return FunctionWithoutDebugInfo;
         }
         ast_map::node_variant(*)     |
@@ -1062,18 +1093,13 @@ fn prepare_struct_metadata(cx: &mut CrateContext,
                            span: Span)
                         -> RecursiveTypeDescription {
     let struct_name = ppaux::ty_to_str(cx.tcx, struct_type);
-    println(fmt!("struct_metadata: %s", struct_name));
-
     let struct_llvm_type = type_of::type_of(cx, struct_type);
+
     let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, def_id, span);
 
     let file_name = span_start(cx, definition_span).file.name;
     let file_metadata = file_metadata(cx, file_name);
 
-    let loc = span_start(cx, definition_span);
-
-    let (composite_size, composite_align) = size_and_align_of(cx, struct_llvm_type);
-
     let struct_metadata_stub = create_struct_stub(cx,
                                                   struct_llvm_type,
                                                   struct_name,
@@ -1142,43 +1168,14 @@ fn finalize(&self, cx: &mut CrateContext) -> DICompositeType {
 
                 // ... then create the member descriptions
                 let member_descriptions = member_description_factory(cx);
-                let member_metadata: ~[DIDescriptor] = member_descriptions
-                    .iter()
-                    .enumerate()
-                    .map(|(i, member_description)| {
-                        let (member_size,
-                             member_align) = size_and_align_of(cx, member_description.llvm_type);
-                        let member_offset = match member_description.offset {
-                            FixedMemberOffset { bytes } => bytes,
-                            ComputedMemberOffset => {
-                                machine::llelement_offset(cx, llvm_type, i)
-                            }
-                        };
-
-                        do member_description.name.with_c_str |member_name| {
-                            unsafe {
-                                llvm::LLVMDIBuilderCreateMemberType(
-                                    DIB(cx),
-                                    metadata_stub,
-                                    member_name,
-                                    file_metadata,
-                                    0 as c_uint,
-                                    bytes_to_bits(member_size),
-                                    bytes_to_bits(member_align),
-                                    bytes_to_bits(member_offset),
-                                    0,
-                                    member_description.type_metadata)
-                            }
-                        }
-                    })
-                    .collect();
-
-                unsafe {
-                    let type_array = create_DIArray(DIB(cx), member_metadata);
-                    llvm::LLVMDICompositeTypeSetTypeArray(metadata_stub, type_array);
-                }
 
-                metadata_stub
+                set_members_of_composite_type(cx,
+                                              metadata_stub,
+                                              llvm_type,
+                                              member_descriptions,
+                                              file_metadata,
+                                              codemap::dummy_sp());
+                return metadata_stub;
             }
         }
     }
@@ -1469,6 +1466,7 @@ fn describe_variant(cx: &mut CrateContext,
 
 enum MemberOffset {
     FixedMemberOffset{ bytes: uint },
+    // For ComputedMemberOffset, the offset is read from the llvm type definition
     ComputedMemberOffset
 }
 
@@ -1490,26 +1488,15 @@ fn composite_type_metadata(cx: &mut CrateContext,
                            file_metadata: DIFile,
                            definition_span: Span)
                         -> DICompositeType {
-    let loc = span_start(cx, definition_span);
-    let (composite_size, composite_align) = size_and_align_of(cx, composite_llvm_type);
-
-    let composite_type_metadata = do composite_type_name.with_c_str |name| {
-        unsafe {
-            llvm::LLVMDIBuilderCreateStructType(
-                DIB(cx),
-                containing_scope,
-                name,
-                file_metadata,
-                loc.line as c_uint,
-                bytes_to_bits(composite_size),
-                bytes_to_bits(composite_align),
-                0,
-                ptr::null(),
-                ptr::null(),
-                0,
-                ptr::null())
-    }};
-
+    // Create the (empty) struct metadata node ...
+    let composite_type_metadata = create_struct_stub(cx,
+                                                     composite_llvm_type,
+                                                     composite_type_name,
+                                                     containing_scope,
+                                                     file_metadata,
+                                                     definition_span);
+
+    // ... and immediately create and add the member descriptions.
     set_members_of_composite_type(cx,
                                   composite_type_metadata,
                                   composite_llvm_type,
@@ -1563,7 +1550,7 @@ fn set_members_of_composite_type(cx: &mut CrateContext,
 }
 
 // A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any caching, does not
-// add any fields to the struct. This can be done later with LLVMDICompositeTypeSetTypeArray().
+// add any fields to the struct. This can be done later with set_members_of_composite_type().
 fn create_struct_stub(cx: &mut CrateContext,
                       struct_llvm_type: Type,
                       struct_type_name: &str,
@@ -1576,6 +1563,10 @@ fn create_struct_stub(cx: &mut CrateContext,
 
     return do struct_type_name.with_c_str |name| {
         unsafe {
+            // LLVMDIBuilderCreateStructType() wants an empty array. A null pointer will lead to
+            // hard to trace and debug LLVM assertions later on in llvm/lib/IR/Value.cpp
+            let empty_array = create_DIArray(DIB(cx), []);
+
             llvm::LLVMDIBuilderCreateStructType(
                 DIB(cx),
                 containing_scope,
@@ -1586,7 +1577,7 @@ fn create_struct_stub(cx: &mut CrateContext,
                 bytes_to_bits(struct_align),
                 0,
                 ptr::null(),
-                ptr::null(),
+                empty_array,
                 0,
                 ptr::null())
     }};
@@ -1864,7 +1855,7 @@ fn trait_metadata(cx: &mut CrateContext,
                   substs: &ty::substs,
                   trait_store: ty::TraitStore,
                   mutability: ast::Mutability,
-                  builtinBounds: &ty::BuiltinBounds,
+                  _: &ty::BuiltinBounds,
                   usage_site_span: Span)
                -> DIType {
     // The implementation provided here is a stub. It makes sure that the trait type is
@@ -2120,14 +2111,6 @@ fn DIB(cx: &CrateContext) -> DIBuilderRef {
     cx.dbg_cx.get_ref().builder
 }
 
-fn assert_fcx_has_span(fcx: &FunctionContext) {
-    if fcx.span.is_none() {
-        fcx.ccx.sess.bug(fmt!("debuginfo: Encountered function %s with invalid source span. \
-            This function should have been ignored by debuginfo generation.",
-            ast_map::path_to_str(fcx.path, fcx.ccx.sess.intr())));
-    }
-}
-
 fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
     match fcx.debug_context {
         FunctionDebugContext(_) => false,
index 9aca0705911125a5b4e016124ea0434805bdaf7c..63d42816207cd79dae5cd0713cd6233e2d0d4882 100644 (file)
@@ -790,29 +790,6 @@ extern "C" LLVMValueRef LLVMDIBuilderCreateNameSpace(
         LineNo));
 }
 
-// extern "C" LLVMValueRef LLVMDIBuilderCreateForwardDecl(
-//     DIBuilderRef Builder,
-//     unsigned Tag,
-//     const char* Name,
-//     LLVMValueRef Scope,
-//     LLVMValueRef File,
-//     unsigned Line,
-//     unsigned RuntimeLang,
-//     uint64_t SizeInBits,
-//     uint64_t AlignInBits)
-// {
-//     return wrap(Builder->createForwardDecl(
-//         Tag,
-//         Name,
-//         unwrapDI<DIDescriptor>(Scope),
-//         unwrapDI<DIFile>(File),
-//         Line,
-//         RuntimeLang,
-//         SizeInBits,
-//         AlignInBits
-//     ));
-// }
-
 extern "C" void LLVMDICompositeTypeSetTypeArray(
     LLVMValueRef CompositeType,
     LLVMValueRef TypeArray)
index 4c602ba383298e9e13384d198f8dc2dc18fba9c1..b8a43d6d16a40a82493afb40fbfc001b0e60e3ff 100644 (file)
@@ -142,14 +142,14 @@ struct LongCycleWithAnonymousTypes {
 
 // This test case makes sure that recursive structs are properly described. The Node structs are
 // generic so that we can have a new type (that newly needs to be described) for the different
-// cases. The potential problem with recursive types is that the DI generation algorithm get trapped
-// in an endless loop. To make sure, we actually test this in the different cases, we have to
-// operate on a new type each time, otherwise we would just hit the DI cache for all but the first
-// case.
+// cases. The potential problem with recursive types is that the DI generation algorithm gets
+// trapped in an endless loop. To make sure, we actually test this in the different cases, we have
+// to operate on a new type each time, otherwise we would just hit the DI cache for all but the
+// first case.
 
 // The different cases below (stack_*, unique_*, box_*, etc) are set up so that the type description
 // algorithm will enter the type reference cycle that is created by a recursive definition from a
-// different context.
+// different context each time.
 
 // The "long cycle" cases are constructed to span a longer, indirect recursion cycle between types.
 // The different locals will cause the DI algorithm to enter the type reference cycle at different
index 1a5e3505511b2cf1fe9161dd439edb3f343e7fe3..4ec9fae1e2f126eb3d2a329ec7b7596b0134268a 100644 (file)
@@ -24,7 +24,7 @@ struct Struct {
 
 impl Trait for Struct {}
 
-// There is no real test here yet. Just make that it compiles without crashing.
+// There is no real test here yet. Just make sure that it compiles without crashing.
 fn main() {
     let stack_struct = Struct { a:0, b: 1.0 };
     let reference: &Trait = &stack_struct as &Trait;