Author: Andy Kaylor
Date: 2025-08-22T13:26:17-07:00
New Revision: ffe3768dfbbb3a598482d1f4a3dce4f02446340d

URL: 
https://github.com/llvm/llvm-project/commit/ffe3768dfbbb3a598482d1f4a3dce4f02446340d
DIFF: 
https://github.com/llvm/llvm-project/commit/ffe3768dfbbb3a598482d1f4a3dce4f02446340d.diff

LOG: [CIR] Add support for emitting vtables (#154808)

This adds a simplified version of the code to emit vtables. It does not
yet handle RTTI or cases that require multiple vtables.

Added: 
    clang/test/CIR/CodeGen/vtable-emission.cpp

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenCXXABI.h
    clang/lib/CIR/CodeGen/CIRGenCall.cpp
    clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/CodeGen/CIRGenTypes.h
    clang/lib/CIR/CodeGen/CIRGenVTables.cpp
    clang/lib/CIR/CodeGen/CIRGenVTables.h
    clang/lib/CIR/CodeGen/CIRGenerator.cpp
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 49c66a40e47b6..e2326b1031765 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -279,6 +279,7 @@ struct MissingFeatures {
   static bool appleKext() { return false; }
   static bool dtorCleanups() { return false; }
   static bool vtableInitialization() { return false; }
+  static bool vtableEmitMetadata() { return false; }
   static bool vtableRelativeLayout() { return false; }
   static bool msvcBuiltins() { return false; }
   static bool vaArgABILowering() { return false; }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h 
b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 3f1cb8363a556..b5f2e1a067274 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -95,6 +95,10 @@ class CIRGenCXXABI {
   isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
                                       CIRGenFunction::VPtr vptr) = 0;
 
+  /// Emits the VTable definitions required for the given record type.
+  virtual void emitVTableDefinitions(CIRGenVTables &cgvt,
+                                     const CXXRecordDecl *rd) = 0;
+
   /// Returns true if the given destructor type should be emitted as a linkonce
   /// delegating thunk, regardless of whether the dtor is defined in this TU or
   /// not.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 6d749940fa128..8a15e5f96aea2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -42,6 +42,11 @@ CIRGenFunctionInfo::create(CanQualType resultType,
   return fi;
 }
 
+cir::FuncType CIRGenTypes::getFunctionType(GlobalDecl gd) {
+  const CIRGenFunctionInfo &fi = arrangeGlobalDeclaration(gd);
+  return getFunctionType(fi);
+}
+
 cir::FuncType CIRGenTypes::getFunctionType(const CIRGenFunctionInfo &info) {
   mlir::Type resultType = convertType(info.getReturnType());
   SmallVector<mlir::Type, 8> argTypes;
@@ -55,6 +60,16 @@ cir::FuncType CIRGenTypes::getFunctionType(const 
CIRGenFunctionInfo &info) {
                             info.isVariadic());
 }
 
+cir::FuncType CIRGenTypes::getFunctionTypeForVTable(GlobalDecl gd) {
+  const CXXMethodDecl *md = cast<CXXMethodDecl>(gd.getDecl());
+  const FunctionProtoType *fpt = md->getType()->getAs<FunctionProtoType>();
+
+  if (!isFuncTypeConvertible(fpt))
+    cgm.errorNYI("getFunctionTypeForVTable: non-convertible function type");
+
+  return getFunctionType(gd);
+}
+
 CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
   if (isVirtual()) {
     const CallExpr *ce = getVirtualCallExpr();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 347656b5f6488..aaf7dc767d888 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -81,6 +81,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
       CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
       clang::BaseSubobject base,
       const clang::CXXRecordDecl *nearestVBase) override;
+  void emitVTableDefinitions(CIRGenVTables &cgvt,
+                             const CXXRecordDecl *rd) override;
 
   bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
     return true;
@@ -270,6 +272,67 @@ bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) 
{
   return false;
 }
 
+void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
+                                                const CXXRecordDecl *rd) {
+  cir::GlobalOp vtable = getAddrOfVTable(rd, CharUnits());
+  if (vtable.hasInitializer())
+    return;
+
+  ItaniumVTableContext &vtContext = cgm.getItaniumVTableContext();
+  const VTableLayout &vtLayout = vtContext.getVTableLayout(rd);
+  cir::GlobalLinkageKind linkage = cgm.getVTableLinkage(rd);
+  mlir::Attribute rtti =
+      cgm.getAddrOfRTTIDescriptor(cgm.getLoc(rd->getBeginLoc()),
+                                  cgm.getASTContext().getCanonicalTagType(rd));
+
+  // Classic codegen uses ConstantInitBuilder here, which is a very general
+  // and feature-rich class to generate initializers for global values.
+  // For now, this is using a simpler approach to create the initializer in 
CIR.
+  cgvt.createVTableInitializer(vtable, vtLayout, rtti,
+                               cir::isLocalLinkage(linkage));
+
+  // Set the correct linkage.
+  vtable.setLinkage(linkage);
+
+  if (cgm.supportsCOMDAT() && cir::isWeakForLinker(linkage))
+    vtable.setComdat(true);
+
+  // Set the right visibility.
+  cgm.setGVProperties(vtable, rd);
+
+  // If this is the magic class __cxxabiv1::__fundamental_type_info,
+  // we will emit the typeinfo for the fundamental types. This is the
+  // same behaviour as GCC.
+  const DeclContext *DC = rd->getDeclContext();
+  if (rd->getIdentifier() &&
+      rd->getIdentifier()->isStr("__fundamental_type_info") &&
+      isa<NamespaceDecl>(DC) && cast<NamespaceDecl>(DC)->getIdentifier() &&
+      cast<NamespaceDecl>(DC)->getIdentifier()->isStr("__cxxabiv1") &&
+      DC->getParent()->isTranslationUnit()) {
+    cgm.errorNYI(rd->getSourceRange(),
+                 "emitVTableDefinitions: __fundamental_type_info");
+  }
+
+  auto vtableAsGlobalValue = dyn_cast<cir::CIRGlobalValueInterface>(*vtable);
+  assert(vtableAsGlobalValue && "VTable must support CIRGlobalValueInterface");
+  // Always emit type metadata on non-available_externally definitions, and on
+  // available_externally definitions if we are performing whole program
+  // devirtualization. For WPD we need the type metadata on all vtable
+  // definitions to ensure we associate derived classes with base classes
+  // defined in headers but with a strong definition only in a shared
+  // library.
+  assert(!cir::MissingFeatures::vtableEmitMetadata());
+  if (cgm.getCodeGenOpts().WholeProgramVTables) {
+    cgm.errorNYI(rd->getSourceRange(),
+                 "emitVTableDefinitions: WholeProgramVTables");
+  }
+
+  assert(!cir::MissingFeatures::vtableRelativeLayout());
+  if (vtContext.isRelativeLayout()) {
+    cgm.errorNYI(rd->getSourceRange(), "vtableRelativeLayout");
+  }
+}
+
 void CIRGenItaniumCXXABI::emitDestructorCall(
     CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
     bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index a557d2aae9dd9..46bca51767c02 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -845,7 +845,7 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl 
gd,
         emitGlobalFunctionDefinition(gd, op);
 
       if (method->isVirtual())
-        errorNYI(method->getSourceRange(), "virtual member function");
+        getVTables().emitThunks(gd);
 
       return;
     }
@@ -2151,6 +2151,18 @@ bool CIRGenModule::verifyModule() const {
   return mlir::verify(theModule).succeeded();
 }
 
+mlir::Attribute CIRGenModule::getAddrOfRTTIDescriptor(mlir::Location loc,
+                                                      QualType ty, bool forEh) 
{
+  // Return a bogus pointer if RTTI is disabled, unless it's for EH.
+  // FIXME: should we even be calling this method if RTTI is disabled
+  // and it's not for EH?
+  if (!shouldEmitRTTI(forEh))
+    return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
+
+  errorNYI(loc, "getAddrOfRTTIDescriptor");
+  return mlir::Attribute();
+}
+
 // TODO(cir): this can be shared with LLVM codegen.
 CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
     const CXXRecordDecl *derivedClass,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 128e2af5e1126..d90baa55d0b5c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -190,6 +190,16 @@ class CIRGenModule : public CIRGenTypeCache {
       mlir::Location loc, llvm::StringRef name, mlir::Type ty,
       cir::GlobalLinkageKind linkage, clang::CharUnits alignment);
 
+  void emitVTable(const CXXRecordDecl *rd);
+
+  /// Return the appropriate linkage for the vtable, VTT, and type information
+  /// of the given class.
+  cir::GlobalLinkageKind getVTableLinkage(const CXXRecordDecl *rd);
+
+  /// Get the address of the RTTI descriptor for the given type.
+  mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty,
+                                          bool forEH = false);
+
   /// Return a constant array for the given string.
   mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
 
@@ -290,6 +300,13 @@ class CIRGenModule : public CIRGenTypeCache {
   getAddrOfGlobal(clang::GlobalDecl gd,
                   ForDefinition_t isForDefinition = NotForDefinition);
 
+  // Return whether RTTI information should be emitted for this target.
+  bool shouldEmitRTTI(bool forEH = false) {
+    return (forEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice &&
+           !(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice &&
+             getTriple().isNVPTX());
+  }
+
   /// Emit type info if type of an expression is a variably modified
   /// type. Also emit proper debug info for cast types.
   void emitExplicitCastExprType(const ExplicitCastExpr *e,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h 
b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index c2813d79bf63b..7af0d956e7d56 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -130,6 +130,13 @@ class CIRGenTypes {
   /// Get the CIR function type for \arg Info.
   cir::FuncType getFunctionType(const CIRGenFunctionInfo &info);
 
+  cir::FuncType getFunctionType(clang::GlobalDecl gd);
+
+  /// Get the CIR function type for use in a vtable, given a CXXMethodDecl. If
+  /// the method has an incomplete return type, and/or incomplete argument
+  /// types, this will return the opaque type.
+  cir::FuncType getFunctionTypeForVTable(clang::GlobalDecl gd);
+
   // The arrangement methods are split into three families:
   //   - those meant to drive the signature and prologue/epilogue
   //     of a function declaration or definition,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp 
b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index fdd1a6e3f57ef..438b483ccb9c2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -11,6 +11,8 @@
 
//===----------------------------------------------------------------------===//
 
 #include "CIRGenVTables.h"
+
+#include "CIRGenCXXABI.h"
 #include "CIRGenModule.h"
 #include "mlir/IR/Types.h"
 #include "clang/AST/VTableBuilder.h"
@@ -33,9 +35,9 @@ mlir::Type CIRGenVTables::getVTableComponentType() {
   return cgm.getVTableComponentType();
 }
 
-mlir::Type CIRGenVTables::getVTableType(const VTableLayout &layout) {
+cir::RecordType CIRGenVTables::getVTableType(const VTableLayout &layout) {
   SmallVector<mlir::Type, 4> tys;
-  auto componentType = getVTableComponentType();
+  mlir::Type componentType = getVTableComponentType();
   for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i)
     tys.push_back(cir::ArrayType::get(componentType, layout.getVTableSize(i)));
 
@@ -43,3 +45,205 @@ mlir::Type CIRGenVTables::getVTableType(const VTableLayout 
&layout) {
   // AST nodes?
   return cgm.getBuilder().getAnonRecordTy(tys, /*incomplete=*/false);
 }
+
+/// This is a callback from Sema to tell us that a particular vtable is
+/// required to be emitted in this translation unit.
+///
+/// This is only called for vtables that _must_ be emitted (mainly due to key
+/// functions).  For weak vtables, CodeGen tracks when they are needed and
+/// emits them as-needed.
+void CIRGenModule::emitVTable(const CXXRecordDecl *rd) {
+  vtables.generateClassData(rd);
+}
+
+void CIRGenVTables::generateClassData(const CXXRecordDecl *rd) {
+  assert(!cir::MissingFeatures::generateDebugInfo());
+
+  if (rd->getNumVBases())
+    cgm.errorNYI(rd->getSourceRange(), "emitVirtualInheritanceTables");
+
+  cgm.getCXXABI().emitVTableDefinitions(*this, rd);
+}
+
+mlir::Attribute CIRGenVTables::getVTableComponent(
+    const VTableLayout &layout, unsigned componentIndex, mlir::Attribute rtti,
+    unsigned &nextVTableThunkIndex, unsigned vtableAddressPoint,
+    bool vtableHasLocalLinkage) {
+  const VTableComponent &component = 
layout.vtable_components()[componentIndex];
+
+  CIRGenBuilderTy builder = cgm.getBuilder();
+
+  assert(!cir::MissingFeatures::vtableRelativeLayout());
+
+  switch (component.getKind()) {
+  case VTableComponent::CK_VCallOffset:
+    cgm.errorNYI("getVTableComponent: VCallOffset");
+    return mlir::Attribute();
+  case VTableComponent::CK_VBaseOffset:
+    cgm.errorNYI("getVTableComponent: VBaseOffset");
+    return mlir::Attribute();
+  case VTableComponent::CK_CompleteDtorPointer:
+    cgm.errorNYI("getVTableComponent: CompleteDtorPointer");
+    return mlir::Attribute();
+  case VTableComponent::CK_DeletingDtorPointer:
+    cgm.errorNYI("getVTableComponent: DeletingDtorPointer");
+    return mlir::Attribute();
+  case VTableComponent::CK_UnusedFunctionPointer:
+    cgm.errorNYI("getVTableComponent: UnusedFunctionPointer");
+    return mlir::Attribute();
+
+  case VTableComponent::CK_OffsetToTop:
+    return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
+                                   component.getOffsetToTop().getQuantity());
+
+  case VTableComponent::CK_RTTI:
+    assert((mlir::isa<cir::GlobalViewAttr>(rtti) ||
+            mlir::isa<cir::ConstPtrAttr>(rtti)) &&
+           "expected GlobalViewAttr or ConstPtrAttr");
+    return rtti;
+
+  case VTableComponent::CK_FunctionPointer: {
+    GlobalDecl gd = component.getGlobalDecl();
+
+    assert(!cir::MissingFeatures::cudaSupport());
+
+    cir::FuncOp fnPtr;
+    if (cast<CXXMethodDecl>(gd.getDecl())->isPureVirtual()) {
+      cgm.errorNYI("getVTableComponent: CK_FunctionPointer: pure virtual");
+      return mlir::Attribute();
+    } else if (cast<CXXMethodDecl>(gd.getDecl())->isDeleted()) {
+      cgm.errorNYI("getVTableComponent: CK_FunctionPointer: deleted virtual");
+      return mlir::Attribute();
+    } else if (nextVTableThunkIndex < layout.vtable_thunks().size() &&
+               layout.vtable_thunks()[nextVTableThunkIndex].first ==
+                   componentIndex) {
+      cgm.errorNYI("getVTableComponent: CK_FunctionPointer: thunk");
+      return mlir::Attribute();
+    } else {
+      // Otherwise we can use the method definition directly.
+      cir::FuncType fnTy = cgm.getTypes().getFunctionTypeForVTable(gd);
+      fnPtr = cgm.getAddrOfFunction(gd, fnTy, /*ForVTable=*/true);
+    }
+
+    return cir::GlobalViewAttr::get(
+        builder.getUInt8PtrTy(),
+        mlir::FlatSymbolRefAttr::get(fnPtr.getSymNameAttr()));
+  }
+  }
+
+  llvm_unreachable("Unexpected vtable component kind");
+}
+
+void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
+                                            const clang::VTableLayout &layout,
+                                            mlir::Attribute rtti,
+                                            bool vtableHasLocalLinkage) {
+  mlir::Type componentType = getVTableComponentType();
+
+  const llvm::SmallVectorImpl<unsigned> &addressPoints =
+      layout.getAddressPointIndices();
+  unsigned nextVTableThunkIndex = 0;
+
+  if (layout.getNumVTables() > 1)
+    cgm.errorNYI("emitVTableDefinitions: multiple vtables");
+
+  // We'll need a loop here to handle multiple vtables, but for now we only
+  // support one.
+  unsigned vtableIndex = 0;
+  size_t vtableStart = layout.getVTableOffset(vtableIndex);
+  size_t vtableEnd = vtableStart + layout.getVTableSize(vtableIndex);
+
+  // Build a ConstArrayAttr of the vtable components.
+  llvm::SmallVector<mlir::Attribute> components;
+  for (size_t componentIndex = vtableStart; componentIndex < vtableEnd;
+       ++componentIndex) {
+    components.push_back(
+        getVTableComponent(layout, componentIndex, rtti, nextVTableThunkIndex,
+                           addressPoints[vtableIndex], vtableHasLocalLinkage));
+  }
+
+  mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();
+
+  // Create a ConstArrayAttr to hold the components.
+  auto arr = cir::ConstArrayAttr::get(
+      cir::ArrayType::get(componentType, components.size()),
+      mlir::ArrayAttr::get(mlirContext, components));
+
+  // Create a ConstRecordAttr to hold the component array.
+  const auto members = mlir::ArrayAttr::get(mlirContext, {arr});
+  cir::ConstRecordAttr record = cgm.getBuilder().getAnonConstRecord(members);
+
+  // Create a VTableAttr
+  auto vtableAttr = cir::VTableAttr::get(record.getType(), 
record.getMembers());
+
+  // Add the vtable initializer to the vtable global op.
+  cgm.setInitializer(vtableOp, vtableAttr);
+}
+
+/// Compute the required linkage of the vtable for the given class.
+///
+/// Note that we only call this at the end of the translation unit.
+cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) 
{
+  if (!rd->isExternallyVisible())
+    return cir::GlobalLinkageKind::InternalLinkage;
+
+  // We're at the end of the translation unit, so the current key
+  // function is fully correct.
+  const CXXMethodDecl *keyFunction = astContext.getCurrentKeyFunction(rd);
+  if (keyFunction && !rd->hasAttr<DLLImportAttr>()) {
+    // If this class has a key function, use that to determine the
+    // linkage of the vtable.
+    const FunctionDecl *def = nullptr;
+    if (keyFunction->hasBody(def))
+      keyFunction = cast<CXXMethodDecl>(def);
+
+    // All of the cases below do something 
diff erent with AppleKext enabled.
+    assert(!cir::MissingFeatures::appleKext());
+    switch (keyFunction->getTemplateSpecializationKind()) {
+    case TSK_Undeclared:
+    case TSK_ExplicitSpecialization:
+      assert(
+          (def || codeGenOpts.OptimizationLevel > 0 ||
+           codeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&
+          "Shouldn't query vtable linkage without key function, "
+          "optimizations, or debug info");
+      if (!def && codeGenOpts.OptimizationLevel > 0)
+        return cir::GlobalLinkageKind::AvailableExternallyLinkage;
+
+      if (keyFunction->isInlined())
+        return !astContext.getLangOpts().AppleKext
+                   ? cir::GlobalLinkageKind::LinkOnceODRLinkage
+                   : cir::GlobalLinkageKind::InternalLinkage;
+      return cir::GlobalLinkageKind::ExternalLinkage;
+
+    case TSK_ImplicitInstantiation:
+      return cir::GlobalLinkageKind::LinkOnceODRLinkage;
+
+    case TSK_ExplicitInstantiationDefinition:
+      return cir::GlobalLinkageKind::WeakODRLinkage;
+
+    case TSK_ExplicitInstantiationDeclaration:
+      llvm_unreachable("Should not have been asked to emit this");
+    }
+  }
+
+  errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function");
+  return cir::GlobalLinkageKind::ExternalLinkage;
+}
+
+void CIRGenVTables::emitThunks(GlobalDecl gd) {
+  const CXXMethodDecl *md =
+      cast<CXXMethodDecl>(gd.getDecl())->getCanonicalDecl();
+
+  // We don't need to generate thunks for the base destructor.
+  if (isa<CXXDestructorDecl>(md) && gd.getDtorType() == Dtor_Base)
+    return;
+
+  const VTableContextBase::ThunkInfoVectorTy *thunkInfoVector =
+      vtContext->getThunkInfo(gd);
+
+  if (!thunkInfoVector)
+    return;
+
+  cgm.errorNYI(md->getSourceRange(), "emitThunks");
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenVTables.h 
b/clang/lib/CIR/CodeGen/CIRGenVTables.h
index 66318c5f2393a..518d7d78f1737 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.h
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.h
@@ -16,6 +16,7 @@
 #include "mlir/IR/Types.h"
 #include "clang/AST/GlobalDecl.h"
 #include "clang/AST/VTableBuilder.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 namespace clang {
 class CXXRecordDecl;
@@ -29,11 +30,23 @@ class CIRGenVTables {
 
   clang::VTableContextBase *vtContext;
 
+  mlir::Attribute
+  getVTableComponent(const VTableLayout &layout, unsigned componentIndex,
+                     mlir::Attribute rtti, unsigned &nextVTableThunkIndex,
+                     unsigned vtableAddressPoint, bool vtableHasLocalLinkage);
+
   mlir::Type getVTableComponentType();
 
 public:
   CIRGenVTables(CIRGenModule &cgm);
 
+  /// Add vtable components for the given vtable layout to the given
+  /// global initializer.
+  void createVTableInitializer(cir::GlobalOp &vtable,
+                               const clang::VTableLayout &layout,
+                               mlir::Attribute rtti,
+                               bool vtableHasLocalLinkage);
+
   clang::ItaniumVTableContext &getItaniumVTableContext() {
     return *llvm::cast<clang::ItaniumVTableContext>(vtContext);
   }
@@ -42,10 +55,18 @@ class CIRGenVTables {
     return *llvm::cast<clang::ItaniumVTableContext>(vtContext);
   }
 
+  /// Emit the associated thunks for the given global decl.
+  void emitThunks(GlobalDecl gd);
+
+  /// Generate all the class data required to be generated upon definition of a
+  /// KeyFunction. This includes the vtable, the RTTI data structure (if RTTI
+  /// is enabled) and the VTT (if the class has virtual bases).
+  void generateClassData(const CXXRecordDecl *rd);
+
   /// Returns the type of a vtable with the given layout. Normally a struct of
   /// arrays of pointers, with one struct element for each vtable in the vtable
   /// group.
-  mlir::Type getVTableType(const clang::VTableLayout &layout);
+  cir::RecordType getVTableType(const clang::VTableLayout &layout);
 };
 
 } // namespace clang::CIRGen

diff  --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp 
b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index fb013d1532689..aa4d9eba35c04 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -177,5 +177,5 @@ void CIRGenerator::HandleVTable(CXXRecordDecl *rd) {
   if (diags.hasErrorOccurred())
     return;
 
-  cgm->errorNYI(rd->getSourceRange(), "HandleVTable");
+  cgm->emitVTable(rd);
 }

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 9972d7612105d..9f0e4e6ecb8ce 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -202,8 +202,8 @@ class CIRAttrToValue {
     return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr)
         .Case<cir::IntAttr, cir::FPAttr, cir::ConstComplexAttr,
               cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
-              cir::ConstPtrAttr, cir::GlobalViewAttr, cir::ZeroAttr>(
-            [&](auto attrT) { return visitCirAttr(attrT); })
+              cir::ConstPtrAttr, cir::GlobalViewAttr, cir::VTableAttr,
+              cir::ZeroAttr>([&](auto attrT) { return visitCirAttr(attrT); })
         .Default([&](auto attrT) { return mlir::Value(); });
   }
 
@@ -215,6 +215,7 @@ class CIRAttrToValue {
   mlir::Value visitCirAttr(cir::ConstRecordAttr attr);
   mlir::Value visitCirAttr(cir::ConstVectorAttr attr);
   mlir::Value visitCirAttr(cir::GlobalViewAttr attr);
+  mlir::Value visitCirAttr(cir::VTableAttr attr);
   mlir::Value visitCirAttr(cir::ZeroAttr attr);
 
 private:
@@ -500,6 +501,21 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
   llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr");
 }
 
+// VTableAttr visitor.
+mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
+  mlir::Type llvmTy = converter->convertType(vtableArr.getType());
+  mlir::Location loc = parentOp->getLoc();
+  mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
+
+  for (auto [idx, elt] : llvm::enumerate(vtableArr.getData())) {
+    mlir::Value init = visit(elt);
+    result =
+        mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
+  }
+
+  return result;
+}
+
 /// ZeroAttr visitor.
 mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
   mlir::Location loc = parentOp->getLoc();
@@ -1569,7 +1585,7 @@ 
CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
   // TODO: Generalize this handling when more types are needed here.
   assert((isa<cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
               cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
-              cir::ZeroAttr>(init)));
+              cir::VTableAttr, cir::ZeroAttr>(init)));
 
   // TODO(cir): once LLVM's dialect has proper equivalent attributes this
   // should be updated. For now, we use a custom op to initialize globals
@@ -1624,7 +1640,7 @@ mlir::LogicalResult 
CIRToLLVMGlobalOpLowering::matchAndRewrite(
     } else if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
                          cir::ConstRecordAttr, cir::ConstPtrAttr,
                          cir::ConstComplexAttr, cir::GlobalViewAttr,
-                         cir::ZeroAttr>(init.value())) {
+                         cir::VTableAttr, cir::ZeroAttr>(init.value())) {
       // TODO(cir): once LLVM's dialect has proper equivalent attributes this
       // should be updated. For now, we use a custom op to initialize globals
       // to the appropriate value.
@@ -2256,6 +2272,9 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter 
&converter,
       }
       break;
     }
+    converter.addConversion([&](cir::VoidType type) -> mlir::Type {
+      return mlir::LLVM::LLVMVoidType::get(type.getContext());
+    });
 
     // Record has a name: lower as an identified record.
     mlir::LLVM::LLVMStructType llvmStruct;

diff  --git a/clang/test/CIR/CodeGen/vtable-emission.cpp 
b/clang/test/CIR/CodeGen/vtable-emission.cpp
new file mode 100644
index 0000000000000..9a34573b475c3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/vtable-emission.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-rtti -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-rtti -fclangir  
-emit-llvm -o %t-cir.ll %s
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-rtti -emit-llvm -o 
%t.ll %s
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// Note: This test is using -fno-rtti so that we can delay implemntation of 
that handling.
+//       When rtti handling for vtables is implemented, that option should be 
removed.
+
+struct S {
+  virtual void key();
+  virtual void nonKey() {}
+};
+
+void S::key() {}
+
+// CHECK-DAG: !rec_anon_struct = !cir.record<struct  
{!cir.array<!cir.ptr<!u8i> x 4>}>
+
+// The definition of the key function should result in the vtable being 
emitted.
+// CHECK:      cir.global "private" external @_ZTV1S = #cir.vtable<{
+// CHECK-SAME:     #cir.const_array<[
+// CHECK-SAME:         #cir.ptr<null> : !cir.ptr<!u8i>,
+// CHECK-SAME:         #cir.ptr<null> : !cir.ptr<!u8i>,
+// CHECK-SAME:         #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>,
+// CHECK-SAME:         #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]>
+// CHECK-SAME:     : !cir.array<!cir.ptr<!u8i> x 4>}> : !rec_anon_struct
+
+// LLVM:      @_ZTV1S = global { [4 x ptr] } { [4 x ptr]
+// LLVM-SAME:      [ptr null, ptr null, ptr @_ZN1S3keyEv, ptr @_ZN1S6nonKeyEv] 
}
+
+// OGCG:      @_ZTV1S = unnamed_addr constant { [4 x ptr] } { [4 x ptr]
+// OGCG-SAME:      [ptr null, ptr null, ptr @_ZN1S3keyEv, ptr @_ZN1S6nonKeyEv] 
}
+
+// CHECK: cir.func dso_local @_ZN1S3keyEv
+
+// The reference from the vtable should result in nonKey being emitted.
+// CHECK: cir.func comdat linkonce_odr @_ZN1S6nonKeyEv


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to