Skip to content

Missing symbol in generated dylib #117137

Closed
Closed

Description

@istankovic

I tried compiling this code with rustc 1.75.0-nightly (249624b 2023-10-20):

pub fn foo1(x: Option<bool>) -> bool {
    x.is_some()                       
}                                     
                                      
pub fn foo2(x: Option<bool>) -> bool {
    x.unwrap()                        
}                                     

using rustc a.rs -O --crate-type dylib and expected that both foo1 and foo2 are present in the generated dylib.
However, only foo2 is:

$ nm --defined-only -C liba.so | grep foo
00000000000532b0 T a::foo2

MIR contains foo1:

fn foo1(_1: Option<bool>) -> bool {             
    debug x => _1;                              
    let mut _0: bool;                           
    let mut _2: &std::option::Option<bool>;     
    scope 1 (inlined Option::<bool>::is_some) { 
        debug self => _2;                       
        let mut _3: isize;                      
    }                                           
                                                
    bb0: {                                      
        StorageLive(_2);                        
        _2 = &_1;                               
        StorageLive(_3);                        
        _3 = discriminant(_1);                  
        _0 = Eq(_3, const 1_isize);             
        StorageDead(_3);                        
        StorageDead(_2);                        
        return;                                 
    }                                           
}                                              

however, it's missing from LLVM IR.

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 24, 2023
  2. saethlin commented on Oct 24, 2023

    @saethlin
    Member

    The symbol is not missing. Only symbols with #[no_mangle] are guaranteed to be in the generated code for a library.

  3. removed
    C-bugCategory: This is a bug.
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 24, 2023
  4. istankovic commented on Oct 24, 2023

    @istankovic
    ContributorAuthor

    The symbol is not missing.

    Well, without #[no_mangle] I was not able to find it in the generated library.
    Do you see foo1 in the generated library without using #[no_mangle]?

    Only symbols with #[no_mangle] are guaranteed to be in the generated code for a library.

    But without using #[no_mangle], I can get foo2, but not foo1. It looks like it is not the lack of #[no_mangle] on foo1 that is causing it to be ignored when building the library, but rather the fact that it uses is_some(). Somehow using is_some() causes foo1 to be lost somewhere in the MIR -> LLVM IR process.

  5. saethlin commented on Oct 24, 2023

    @saethlin
    Member

    I wrote the PR that caused this change. The function is not lost. It is in the not-yet-lowered part of the rlib because the compiler has deduced that the function should be made eligible for cross-crate inlining, and rustc implements cross-crate-inlining by delaying codegen.

  6. istankovic commented on Oct 25, 2023

    @istankovic
    ContributorAuthor

    I wrote the PR that caused this change.

    Do you maybe have a link to the PR? I'm curious.

    The function is not lost. It is in the not-yet-lowered part of the rlib because the compiler has deduced that the function should be made eligible for cross-crate inlining, and rustc implements cross-crate-inlining by delaying codegen.

    Hmm... ok, that certainly explains what I'm seeing, but surely that's a bug, no? In the sense, foo1 should end up in the dylib, regardless of whether it is a candidate for cross-crate inlining.

  7. bjorn3 commented on Oct 25, 2023

    @bjorn3
    Member

    Do you maybe have a link to the PR? I'm curious.

    #116505

    In the sense, foo1 should end up in the dylib, regardless of whether it is a candidate for cross-crate inlining.

    It only needs to end up in the dylib if there is a possibility that a crate will depend on it without locally codegening it. All candidates for cross-crate inlining are codegened in the crate that calls them even if LLVM decides not to inline them.

  8. istankovic commented on Oct 25, 2023

    @istankovic
    ContributorAuthor

    Do you maybe have a link to the PR? I'm curious.

    #116505

    Thanks!

    In the sense, foo1 should end up in the dylib, regardless of whether it is a candidate for cross-crate inlining.

    It only needs to end up in the dylib if there is a possibility that a crate will depend on it without locally codegening it. All candidates for cross-crate inlining are codegened in the crate that calls them even if LLVM decides not to inline them.

    I understand that all items need #[no_mangle] if one wants to use them externally.

    What I'm having trouble understanding is, if all you have is a dylib (e.g. the above liba.so that lacks foo1), how are you going to "codegen in the crate that calls them"? Where does the source (or MIR) come from?

    Also it seems the behaviour is not really consistent when comparing different crate types.
    Suppose we have this:

    #[inline(never)]                      
    pub fn foo1(x: Option<bool>) -> bool {
        x.is_some()                       
    }                                     

    Then it seems the inline hint is ignored in case of staticlib:

    $ rustc a.rs -O  --crate-type lib      
    $ nm liba.rlib | grep foo              
    nm: lib.rmeta: no symbols                               
    0000000000000000 T _ZN1a4foo117h7bb26724037c3560E       
    $ rustc a.rs -O  --crate-type dylib    
    $ nm liba.so | grep foo                
    00000000000532b0 T _ZN1a4foo117h7bb26724037c3560E       
    $ rustc a.rs -O  --crate-type staticlib
    $ nm liba.a | grep foo                 
    $
  9. bjorn3 commented on Oct 25, 2023

    @bjorn3
    Member

    Where does the source (or MIR) come from?

    The crate metadata stored in the dylib.

    Then it seems the inline hint is ignored in case of staticlib:

    For staticlib and cdylib any function not callable by C will be omitted as they are meant for exporting an api to C code, unlike the dylib crate type which is mostly meant for use by rust code. Only functions with #[no_mangle] can be called by C.

  10. istankovic commented on Oct 26, 2023

    @istankovic
    ContributorAuthor

    Where does the source (or MIR) come from?

    The crate metadata stored in the dylib.

    Ah... 👍

    Then it seems the inline hint is ignored in case of staticlib:

    For staticlib and cdylib any function not callable by C will be omitted as they are meant for exporting an api to C code, unlike the dylib crate type which is mostly meant for use by rust code. Only functions with #[no_mangle] can be called by C.

    Sounds reasonable. Thanks for clarifying!
    Am going to close this as things seem to be working as intended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions