Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang] ICE with dynamic_cast and multiple inheritance: Shouldn't query vtable linkage without key function #64088

Closed
BertalanD opened this issue Jul 24, 2023 · 22 comments · Fixed by llvm/llvm-project-release-prs#486

Comments

@BertalanD
Copy link
Member

BertalanD commented Jul 24, 2023

Starting with 9d525bf ("Optimize emission of dynamic_cast to final classes", @zygoloid) the following code triggers an assertion at -O0 -g0:

struct Foo {
    virtual void foo();
};
struct Bar {
    virtual void bar();
};
struct Baz final : Foo, Bar {
    void bar() override;
};

bool test(Foo *f) {
    return dynamic_cast<Baz*>(f);
}
clang++: /root/llvm-project/clang/lib/CodeGen/CGVTables.cpp:1060: llvm::GlobalValue::LinkageTypes clang::CodeGen::CodeGenModule::getVTableLinkage(const clang::CXXRecordDecl*): Assertion `(def || CodeGenOpts.OptimizationLevel > 0 || CodeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) && "Shouldn't query vtable linkage without key function, " "optimizations, or debug info"' failed.
Full backtrace
0.	Program arguments: /opt/compiler-explorer/clang-assertions-trunk/bin/clang++ -gdwarf-4 -g -o /app/output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot -fcolor-diagnostics -fno-crash-diagnostics -g0 <source>
1.	<eof> parser at end of file
2.	<source>:11:6: LLVM IR generation of declaration 'test'
3.	<source>:11:6: Generating code for declaration 'test'
 #0 0x000000000367ff18 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x367ff18)
 #1 0x000000000367dd9c llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x367dd9c)
 #2 0x00000000035cb398 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #3 0x00007f4275ecb420 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x14420)
 #4 0x00007f427598e00b raise (/lib/x86_64-linux-gnu/libc.so.6+0x4300b)
 #5 0x00007f427596d859 abort (/lib/x86_64-linux-gnu/libc.so.6+0x22859)
 #6 0x00007f427596d729 (/lib/x86_64-linux-gnu/libc.so.6+0x22729)
 #7 0x00007f427597efd6 (/lib/x86_64-linux-gnu/libc.so.6+0x33fd6)
 #8 0x00000000039f1d00 clang::CodeGen::CodeGenModule::getVTableLinkage(clang::CXXRecordDecl const*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x39f1d00)
 #9 0x0000000003acdff4 (anonymous namespace)::ItaniumCXXABI::shouldEmitExactDynamicCast(clang::QualType) ItaniumCXXABI.cpp:0:0
#10 0x0000000003d99f5f clang::CodeGen::CodeGenFunction::EmitDynamicCast(clang::CodeGen::Address, clang::CXXDynamicCastExpr const*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3d99f5f)
#11 0x0000000003dc8c2b (anonymous namespace)::ScalarExprEmitter::VisitCastExpr(clang::CastExpr*) CGExprScalar.cpp:0:0
#12 0x0000000003dc355c clang::StmtVisitorBase<std::add_pointer, (anonymous namespace)::ScalarExprEmitter, llvm::Value*>::Visit(clang::Stmt*) CGExprScalar.cpp:0:0
#13 0x0000000003dc531b (anonymous namespace)::ScalarExprEmitter::Visit(clang::Expr*) CGExprScalar.cpp:0:0
#14 0x0000000003dc7d98 (anonymous namespace)::ScalarExprEmitter::VisitCastExpr(clang::CastExpr*) CGExprScalar.cpp:0:0
#15 0x0000000003dc38e7 clang::StmtVisitorBase<std::add_pointer, (anonymous namespace)::ScalarExprEmitter, llvm::Value*>::Visit(clang::Stmt*) CGExprScalar.cpp:0:0
#16 0x0000000003dcb67c clang::CodeGen::CodeGenFunction::EmitScalarExpr(clang::Expr const*, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3dcb67c)
#17 0x00000000039913c4 clang::CodeGen::CodeGenFunction::EmitReturnStmt(clang::ReturnStmt const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x39913c4)
#18 0x000000000399888b clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt const*, llvm::ArrayRef<clang::Attr const*>) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x399888b)
#19 0x000000000399eb9c clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope(clang::CompoundStmt const&, bool, clang::CodeGen::AggValueSlot) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x399eb9c)
#20 0x00000000039fc686 clang::CodeGen::CodeGenFunction::EmitFunctionBody(clang::Stmt const*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x39fc686)
#21 0x0000000003a0f8e8 clang::CodeGen::CodeGenFunction::GenerateCode(clang::GlobalDecl, llvm::Function*, clang::CodeGen::CGFunctionInfo const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3a0f8e8)
#22 0x0000000003a70fad clang::CodeGen::CodeGenModule::EmitGlobalFunctionDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3a70fad)
#23 0x0000000003a6c185 clang::CodeGen::CodeGenModule::EmitGlobalDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3a6c185)
#24 0x0000000003a6c743 clang::CodeGen::CodeGenModule::EmitGlobal(clang::GlobalDecl) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3a6c743)
#25 0x0000000003a758f3 clang::CodeGen::CodeGenModule::EmitTopLevelDecl(clang::Decl*) (.part.0) CodeGenModule.cpp:0:0
#26 0x00000000048d05a6 (anonymous namespace)::CodeGeneratorImpl::HandleTopLevelDecl(clang::DeclGroupRef) ModuleBuilder.cpp:0:0
#27 0x00000000048c2d48 clang::BackendConsumer::HandleTopLevelDecl(clang::DeclGroupRef) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x48c2d48)
#28 0x0000000005d7ae14 clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x5d7ae14)
#29 0x00000000048cd618 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x48cd618)
#30 0x0000000004133f09 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4133f09)
#31 0x00000000040b9c1e clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x40b9c1e)
#32 0x0000000004213e66 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x4213e66)
#33 0x0000000000bc9242 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0xbc9242)
#34 0x0000000000bc1a2a ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#35 0x0000000003f1adb9 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#36 0x00000000035cb844 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x35cb844)
#37 0x0000000003f1b3af clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const (.part.0) Job.cpp:0:0
#38 0x0000000003ee30a5 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3ee30a5)
#39 0x0000000003ee3b0d clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3ee3b0d)
#40 0x0000000003eeb63d clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0x3eeb63d)
#41 0x0000000000bc74f7 clang_main(int, char**, llvm::ToolContext const&) (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0xbc74f7)
#42 0x0000000000ac1ff1 main (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0xac1ff1)
#43 0x00007f427596f083 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24083)
#44 0x0000000000bc150e _start (/opt/compiler-explorer/clang-assertions-trunk/bin/clang+++0xbc150e)

(Godbolt link)

This was reduced from a larger program where this commit caused the emission of the derived type's vtable even though the key function was not defined in that TU. This caused a linker error, as some of the non-inline virtual functions were defined in a different shared object and had hidden visibility.

@BertalanD BertalanD added clang Clang issues not falling into any other category c++ regression labels Jul 24, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Jul 24, 2023

@llvm/issue-subscribers-c-1

@EugeneZelenko EugeneZelenko added clang:codegen crash Prefer [crash-on-valid] or [crash-on-invalid] and removed clang Clang issues not falling into any other category labels Jul 24, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Jul 24, 2023

@llvm/issue-subscribers-clang-codegen

@zygoloid
Copy link
Collaborator

The assert looks strange, and should probably just be removed, but that doesn't explain the other issue (the unexpected emission of the derived type's vtable from the wrong TU).

zygoloid added a commit that referenced this issue Jul 25, 2023
It seems preferable to avoid this optimization under -O0, and we're not
set up to emit speculative references to vtables at -O0 in general
anyway.

For #64088.
@zygoloid
Copy link
Collaborator

The assertion failure should be fixed in 6cf8179, by disabling this optimization at -O0. I don't fully understand the unexpected additional vtable emission yet; can you let me know if you're still seeing issues after that change?

@BertalanD
Copy link
Member Author

BertalanD commented Jul 25, 2023

Thank you for the very quick response and fix!

This is the simplest case I could find that demonstrates new undefined symbols:

// test.h
struct Foo {
    virtual ~Foo();
};
struct Bar final : Foo {
    virtual ~Bar() = default;
};

// dylib.cpp
#include "test.h"
Foo::~Foo() {}

// test.cpp
#include "test.h"
bool test(Foo *o) {
    return dynamic_cast<Bar*>(o);
}

int main() {}

Compile dylib.cpp to a shared object and create an executable from test.cpp that links against it:

clang++ -O1 -std=c++20 -shared dylib.cpp -o libdylib.dylib # libdylib.so on ELF
clang++ -O1 test.cpp -L. -ldylib

This results in the following error:

$ clang++ -O1 test.cpp -L. -ldylib
ld: Undefined symbols:
  Bar::~Bar(), referenced from:
      vtable for Bar in test-25e355.o
  Bar::~Bar(), referenced from:
      vtable for Bar in test-25e355.o

nm shows that the dylib only contains definitions for Foo's destructor and vtable, but not Bar's. This matches GCC's behavior as well as Clang 16, so I don't think that's the problem. The main executable contains a definition for Bar's vtable, but no code is emitted for ~Bar().

This might still be a different issue than what's causing problems in the SerenityOS codebase. This is because for us, the derived class's destructor is defined in the dylib – although with hidden visibility. I'm working on a better reproducer.

Edit: I think this is actually the same issue; the reason we have a definition for the dtor is that the vtable is referenced somewhere else within the DSO, and the visibility is hidden because it's an inline virtual function.

@zygoloid
Copy link
Collaborator

Thanks, that's also now fixed.

@BertalanD
Copy link
Member Author

Awesome, I can confirm that this fixes our original build failure.

Should the fix be backported to the release/17.x branch?

@zygoloid
Copy link
Collaborator

Awesome, I can confirm that this fixes our original build failure.

Should the fix be backported to the release/17.x branch?

Yeah, either the fix should be backported or the patch should be removed from the branch. I just realized that I forgot to add release notes for it too.

@tru How would you like us to proceed here?

@AaronBallman
Copy link
Collaborator

FWIW, I think the changes are sufficiently small and correct to be cherry-picked into the 17.x branch.

@tru
Copy link
Collaborator

tru commented Jul 26, 2023

Yeah. Since we are early in the release lifecycle I think it's just fine to backport these changes.

@zygoloid zygoloid added this to the LLVM 17.0.X Release milestone Jul 26, 2023
@zygoloid

This comment was marked as outdated.

@llvmbot

This comment was marked as outdated.

@zygoloid
Copy link
Collaborator

/cherry-pick b6847ed

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 26, 2023

/branch llvm/llvm-project-release-prs/issue64088

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 26, 2023

/pull-request llvm/llvm-project-release-prs#486

@zygoloid
Copy link
Collaborator

@tru This change was also missing release notes; should I just commit a release notes update on the release branch, or is there a better process for that?

@tru
Copy link
Collaborator

tru commented Jul 27, 2023

@tru This change was also missing release notes; should I just commit a release notes update on the release branch, or is there a better process for that?

There is no great process for it. the problem with direct pushes is that I need to sync the repos manually. I wrote up the full process for merging notes here: https://discourse.llvm.org/t/llvm-17-x-release-notes-update/72292

It's not great since it's a bit heavy, but it makes it merged the "right way" with the current setup.

tru pushed a commit that referenced this issue Jul 27, 2023
steelannelida added a commit that referenced this issue Jul 28, 2023
eymay pushed a commit to eymay/llvm-project that referenced this issue Jul 28, 2023
eymay pushed a commit to eymay/llvm-project that referenced this issue Jul 28, 2023
@BertalanD
Copy link
Member Author

@zygoloid FYI, it looks like b6847ed was reverted in main (but not in the release branch!) via 3b34d69.

@zhuhan0
Copy link
Member

zhuhan0 commented Aug 1, 2023

The fix b6847ed was necessary to fix a build break at Meta caused by the initial feature diff. But the fix has been reverted as mentioned above, and no reason for the revert was provided. Shall we re-open this issue?

@zygoloid
Copy link
Collaborator

zygoloid commented Aug 2, 2023

I'm seeing if we can undo the revert.

steelannelida added a commit that referenced this issue Aug 2, 2023
@zygoloid
Copy link
Collaborator

zygoloid commented Aug 2, 2023

@BertalanD @zhuhan0 The fix was relanded in 9a370a1.

doru1004 pushed a commit to doru1004/llvm-project that referenced this issue Aug 3, 2023
@zhuhan0
Copy link
Member

zhuhan0 commented Aug 3, 2023

@BertalanD @zhuhan0 The fix was relanded in 9a370a1.

Thank you. Seems to have fixed the builds on our side.

razmser pushed a commit to razmser/llvm-project that referenced this issue Sep 8, 2023
razmser pushed a commit to razmser/llvm-project that referenced this issue Sep 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging a pull request may close this issue.

7 participants