diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d64adef8c6ab..c4289fd2681e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3373,7 +3373,7 @@ void CIRGenModule::Release() { assert(!MissingFeatures::emitModuleInitializers()); emitDeferred(getCodeGenOpts().ClangIRBuildDeferredThreshold); assert(!MissingFeatures::emittedDeferredDecls()); - assert(!MissingFeatures::emitVTablesOpportunistically()); + emitVTablesOpportunistically(); assert(!MissingFeatures::applyGlobalValReplacements()); applyReplacements(); assert(!MissingFeatures::emitMultiVersionFunctions()); @@ -4013,8 +4013,6 @@ cir::GlobalOp CIRGenModule::createOrReplaceCXXRuntimeVariable( } bool CIRGenModule::shouldOpportunisticallyEmitVTables() { - if (codeGenOpts.OptimizationLevel != 0) - llvm_unreachable("NYI"); return codeGenOpts.OptimizationLevel > 0; } @@ -4245,6 +4243,25 @@ void CIRGenModule::addGlobalAnnotations(const ValueDecl *d, func.setAnnotationsAttr(builder.getArrayAttr(annotations)); } +void CIRGenModule::emitVTablesOpportunistically() { + // Try to emit external vtables as available_externally if they have emitted + // all inlined virtual functions. It runs after EmitDeferred() and therefore + // is not allowed to create new references to things that need to be emitted + // lazily. Note that it also uses fact that we eagerly emitting RTTI. + + assert( + (opportunisticVTables.empty() || shouldOpportunisticallyEmitVTables()) && + "Only emit opportunistic vtables with optimizations"); + + for (const CXXRecordDecl *rd : opportunisticVTables) { + assert(getVTables().isVTableExternal(rd) && + "This queue should only contain external vtables"); + if (getCXXABI().canSpeculativelyEmitVTable(rd)) + VTables.GenerateClassData(rd); + } + opportunisticVTables.clear(); +} + void CIRGenModule::emitGlobalAnnotations() { for (const auto &[mangledName, vd] : deferredAnnotations) { mlir::Operation *gv = getGlobalValue(mangledName); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 9dc7a74548fe..2249ae75c0a1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -573,6 +573,9 @@ class CIRGenModule : public CIRGenTypeCache { /// A queue of (optional) vtables to consider emitting. std::vector DeferredVTables; + /// A queue of (optional) vtables that may be emitted opportunistically. + std::vector opportunisticVTables; + mlir::Type getVTableComponentType(); CIRGenVTables &getVTables() { return VTables; } @@ -783,6 +786,12 @@ class CIRGenModule : public CIRGenTypeCache { /// Emit any needed decls for which code generation was deferred. void emitDeferred(unsigned recursionLimit); + /// Try to emit external vtables as available_externally if they have emitted + /// all inlined virtual functions. It runs after EmitDeferred() and therefore + /// is not allowed to create new references to things that need to be emitted + /// lazily. + void emitVTablesOpportunistically(); + /// Helper for `emitDeferred` to apply actual codegen. void emitGlobalDecl(clang::GlobalDecl &D); diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp index 9b5c45a80a36..3619fcaf7a1b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -137,11 +137,10 @@ void CIRGenModule::emitDeferredVTables() { #endif for (const CXXRecordDecl *RD : DeferredVTables) - if (shouldEmitVTableAtEndOfTranslationUnit(*this, RD)) { + if (shouldEmitVTableAtEndOfTranslationUnit(*this, RD)) VTables.GenerateClassData(RD); - } else if (shouldOpportunisticallyEmitVTables()) { - llvm_unreachable("NYI"); - } + else if (shouldOpportunisticallyEmitVTables()) + opportunisticVTables.push_back(RD); assert(savedSize == DeferredVTables.size() && "deferred extra vtables during vtable emission?"); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 18ef47095e6e..ba5f6e104b90 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2257,6 +2257,7 @@ LogicalResult cir::GlobalOp::verify() { case GlobalLinkageKind::CommonLinkage: case GlobalLinkageKind::WeakAnyLinkage: case GlobalLinkageKind::WeakODRLinkage: + case GlobalLinkageKind::AvailableExternallyLinkage: // FIXME: mlir's concept of visibility gets tricky with LLVM ones, // for instance, symbol declarations cannot be "public", so we // have to mark them "private" to workaround the symbol verifier. @@ -2265,9 +2266,6 @@ LogicalResult cir::GlobalOp::verify() { << stringifyGlobalLinkageKind(getLinkage()) << "' linkage"; break; - default: - return emitError() << stringifyGlobalLinkageKind(getLinkage()) - << ": verifier not implemented\n"; } // TODO: verify visibility for declarations? diff --git a/clang/test/CIR/CodeGen/vtable-available-externally.cpp b/clang/test/CIR/CodeGen/vtable-available-externally.cpp new file mode 100644 index 000000000000..7a75c92bb7a1 --- /dev/null +++ b/clang/test/CIR/CodeGen/vtable-available-externally.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 %s -I%S -triple x86_64-unknown-linux-gnu -std=c++98 -O0 -disable-llvm-passes -emit-cir -o %t +// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK %s < %t +// RUN: %clang_cc1 %s -I%S -triple x86_64-unknown-linux-gnu -std=c++98 -O2 -disable-llvm-passes -emit-cir -o %t.opt +// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-FORCE-EMIT %s < %t.opt + +// CHECK: cir.global{{.*}} external @_ZTV1A +// CHECK-FORCE-EMIT: cir.global{{.*}} available_externally @_ZTV1A +struct A { + A(); + virtual void f(); + virtual ~A() { } +}; + +A::A() { } + +void f(A* a) { + a->f(); +}; + +// CHECK-LABEL: cir.func{{.*}} @_Z1gv +// CHECK: cir.call @_Z1fP1A +void g() { + A a; + f(&a); +}