From 6b0cba4a6898ff5eaaf692851b2cc73bdcb813b5 Mon Sep 17 00:00:00 2001 From: Tommy McMichen Date: Fri, 18 Jul 2025 17:22:23 -0700 Subject: [PATCH 1/5] [CIR] Implemented Opportunistic VTable Emission Implemented opportunistic vtable emission, which marks vtables as `available_externally` to enable inlining if optimizations are enabled. Added `GlobalOp` verifier support `available_externally` linkage type, all cases are covered now, so I removed the `default` case. Added the `vtable-available-externally` CIRGen test. --- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 23 ++++++++++++++++--- clang/lib/CIR/CodeGen/CIRGenModule.h | 9 ++++++++ clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 7 +++--- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 4 +--- .../CodeGen/vtable-available-externally.cpp | 23 +++++++++++++++++++ 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 clang/test/CIR/CodeGen/vtable-available-externally.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d64adef8c6ab..3be1255b9160 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..b6650d891ea9 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..59972e81281a --- /dev/null +++ b/clang/test/CIR/CodeGen/vtable-available-externally.cpp @@ -0,0 +1,23 @@ +// 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-DAG: cir.global{{.*}} available_externally @_ZTV1A +struct A { + A(); + virtual void f(); + virtual ~A() { } +}; + +A::A() { } + +void f(A* a) { + a->f(); +}; + +void g() { + A a; + f(&a); +} From c54bf30df675922cc6f7f7cbea2fba89bf874399 Mon Sep 17 00:00:00 2001 From: Tommy McMichen Date: Mon, 21 Jul 2025 09:51:47 -0700 Subject: [PATCH 2/5] [CIR][CodeGen] Updated the naming of local variable to be consistent with the file --- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 3be1255b9160..c4289fd2681e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -4253,11 +4253,11 @@ void CIRGenModule::emitVTablesOpportunistically() { (opportunisticVTables.empty() || shouldOpportunisticallyEmitVTables()) && "Only emit opportunistic vtables with optimizations"); - for (const CXXRecordDecl *RD : opportunisticVTables) { - assert(getVTables().isVTableExternal(RD) && + for (const CXXRecordDecl *rd : opportunisticVTables) { + assert(getVTables().isVTableExternal(rd) && "This queue should only contain external vtables"); - if (getCXXABI().canSpeculativelyEmitVTable(RD)) - VTables.GenerateClassData(RD); + if (getCXXABI().canSpeculativelyEmitVTable(rd)) + VTables.GenerateClassData(rd); } opportunisticVTables.clear(); } From c8a73e5334f03b4aa80659f83f7806e054a17127 Mon Sep 17 00:00:00 2001 From: Tommy McMichen Date: Mon, 21 Jul 2025 09:52:30 -0700 Subject: [PATCH 3/5] [CIR][CodeGen] Added clang:: namespace qualifier to be consistent with the file --- clang/lib/CIR/CodeGen/CIRGenModule.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index b6650d891ea9..2249ae75c0a1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -574,7 +574,7 @@ class CIRGenModule : public CIRGenTypeCache { std::vector DeferredVTables; /// A queue of (optional) vtables that may be emitted opportunistically. - std::vector opportunisticVTables; + std::vector opportunisticVTables; mlir::Type getVTableComponentType(); CIRGenVTables &getVTables() { return VTables; } From 2b0a871d3c0aa01b384f8360928e84a485c340ce Mon Sep 17 00:00:00 2001 From: Tommy McMichen Date: Mon, 21 Jul 2025 09:55:34 -0700 Subject: [PATCH 4/5] [CIR][CodeGen] Removed unneeded -DAG specifier in lit check --- clang/test/CIR/CodeGen/vtable-available-externally.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CIR/CodeGen/vtable-available-externally.cpp b/clang/test/CIR/CodeGen/vtable-available-externally.cpp index 59972e81281a..585f1e45f1ed 100644 --- a/clang/test/CIR/CodeGen/vtable-available-externally.cpp +++ b/clang/test/CIR/CodeGen/vtable-available-externally.cpp @@ -4,7 +4,7 @@ // RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-FORCE-EMIT %s < %t.opt // CHECK: cir.global{{.*}} external @_ZTV1A -// CHECK-FORCE-EMIT-DAG: cir.global{{.*}} available_externally @_ZTV1A +// CHECK-FORCE-EMIT: cir.global{{.*}} available_externally @_ZTV1A struct A { A(); virtual void f(); From 147c02bc208574be36ca6d7c6d1520fe114c856d Mon Sep 17 00:00:00 2001 From: Tommy McMichen Date: Mon, 21 Jul 2025 10:10:25 -0700 Subject: [PATCH 5/5] [CIR][CodeGen] Added check on extra function to match OG test --- clang/test/CIR/CodeGen/vtable-available-externally.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/test/CIR/CodeGen/vtable-available-externally.cpp b/clang/test/CIR/CodeGen/vtable-available-externally.cpp index 585f1e45f1ed..7a75c92bb7a1 100644 --- a/clang/test/CIR/CodeGen/vtable-available-externally.cpp +++ b/clang/test/CIR/CodeGen/vtable-available-externally.cpp @@ -17,6 +17,8 @@ void f(A* a) { a->f(); }; +// CHECK-LABEL: cir.func{{.*}} @_Z1gv +// CHECK: cir.call @_Z1fP1A void g() { A a; f(&a);