Author: Andy Kaylor
Date: 2026-04-10T11:12:29-07:00
New Revision: 05ffd0dc29bccc839b453b94918ba8e088f4397c

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

LOG: [CIR] Handle globals with vptr init (#191291)

When a class contains virtual functions but no data members and has a
trivial constructor, global variables of that type are initialized with
a vptr. CIR was incorrectly creating the global variable with the type
of the vtable (an anonymous record) rather than the class type.

When replacing structors with aliases, we were calling a function to
update argument types at the call sites, but this was only necessary
because we initially generated the call using the same incorrect type
that we used for the global. The type correction wasn't implemented
because we hadn't encountered a case where it was needed. Having found
such a case led me to diagnose the problem as above, and I verified that
the same test case compiled without -mconstructor-aliases just failed in
the verifier because we never hit the replacement code. I'm now
convinced that this argument type fixup isn't necessary, so I replaced
the fixup function with an assert.

Assisted-by: Cursor / claude-4.6-opus-high

Added: 
    clang/test/CIR/CodeGen/global-ptr-init.cpp

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRTypes.td
    clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 778f9bfdfd0c1..b177eab08ccad 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -362,6 +362,7 @@ def CIR_DataMemberType : CIR_Type<"DataMember", 
"data_member", [
 
//===----------------------------------------------------------------------===//
 
 def CIR_VPtrType : CIR_Type<"VPtr", "vptr", [
+    DeclareTypeInterfaceMethods<CIR_SizedTypeInterface>,
     DeclareTypeInterfaceMethods<DataLayoutTypeInterface>
 ]> {
   let summary = "CIR type that is used for the vptr member of C++ objects";

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index fe9a8ee1957c4..330e51c214dfc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -751,8 +751,10 @@ bool ConstRecordBuilder::build(const APValue &val, const 
RecordDecl *rd,
           builder.getI32IntegerAttr(addressPoint.VTableIndex),
           builder.getI32IntegerAttr(addressPoint.AddressPointIndex),
       });
+      auto vptrTy = cir::VPtrType::get(cgm.getBuilder().getContext());
+      auto symbol = mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr());
       cir::GlobalViewAttr vtableInit =
-          cgm.getBuilder().getGlobalViewAttr(vtable, indices);
+          cir::GlobalViewAttr::get(vptrTy, symbol, indices);
       if (!appendBytes(offset, vtableInit))
         return false;
     }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 2037b92e2b5d1..3f0f493f5a50b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1441,30 +1441,29 @@ void CIRGenModule::addReplacement(StringRef name, 
mlir::Operation *op) {
   replacements[name] = op;
 }
 
-void CIRGenModule::replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF) {
+#ifndef NDEBUG
+static bool verifyPointerTypeArgs(mlir::ModuleOp modOp, cir::FuncOp oldF,
+                                  cir::FuncOp newF) {
   std::optional<mlir::SymbolTable::UseRange> optionalUseRange =
-      oldF.getSymbolUses(theModule);
+      oldF.getSymbolUses(modOp);
   if (!optionalUseRange)
-    return;
+    return true;
 
   for (const mlir::SymbolTable::SymbolUse &u : *optionalUseRange) {
-    // CallTryOp only shows up after FlattenCFG.
     auto call = mlir::dyn_cast<cir::CallOp>(u.getUser());
     if (!call)
       continue;
 
-    for (const auto [argOp, fnArgType] :
+    for (auto [argOp, fnArgType] :
          llvm::zip(call.getArgs(), newF.getFunctionType().getInputs())) {
-      if (argOp.getType() == fnArgType)
-        continue;
-
-      // The purpose of this entire function is to insert bitcasts in the case
-      // where these types don't match, but I haven't seen a case where that
-      // happens.
-      errorNYI(call.getLoc(), "replace call with mismatched types");
+      if (argOp.getType() != fnArgType)
+        return false;
     }
   }
+
+  return true;
 }
+#endif // NDEBUG
 
 void CIRGenModule::applyReplacements() {
   for (auto &i : replacements) {
@@ -1482,9 +1481,8 @@ void CIRGenModule::applyReplacements() {
       continue;
     }
 
-    // LLVM has opaque pointer but CIR not. So we may have to handle these
-    // 
diff erent pointer types when performing replacement.
-    replacePointerTypeArgs(oldF, newF);
+    assert(verifyPointerTypeArgs(theModule, oldF, newF) &&
+           "call argument types do not match replacement function");
 
     // Replace old with new, but keep the old order.
     if (oldF.replaceAllSymbolUses(newF.getSymNameAttr(), theModule).failed())

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 266510de84fd0..0bb07f0815d43 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -799,11 +799,6 @@ class CIRGenModule : public CIRGenTypeCache {
   /// Call replaceAllUsesWith on all pairs in replacements.
   void applyReplacements();
 
-  /// A helper function to replace all uses of OldF to NewF that replace
-  /// the type of pointer arguments. This is not needed to tradtional
-  /// pipeline since LLVM has opaque pointers but CIR not.
-  void replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF);
-
   void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op);
 
   /// Map source language used to a CIR attribute.

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 488fc100f981b..c352ef91d1ee2 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -618,6 +618,9 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
                                          llvmDstTy, addrOp);
   }
 
+  if (mlir::isa<cir::VPtrType>(globalAttr.getType()))
+    return addrOp;
+
   llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr");
 }
 

diff  --git a/clang/test/CIR/CodeGen/global-ptr-init.cpp 
b/clang/test/CIR/CodeGen/global-ptr-init.cpp
new file mode 100644
index 0000000000000..1520485cf3597
--- /dev/null
+++ b/clang/test/CIR/CodeGen/global-ptr-init.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1  -triple=x86_64-linux-gnu -std=gnu++14 -fclangir -emit-cir 
-o %t.cir %s
+// RUN: FileCheck -check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -std=gnu++14 -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-linux-gnu -std=gnu++14 -emit-llvm -o %t.ll %s
+// RUN: FileCheck -check-prefix=OGCG --input-file=%t.ll %s
+
+struct B {
+  virtual void f() {}
+  virtual ~B() {}
+};
+B x;
+
+// CIR: cir.global external @x = #cir.const_record<{#cir.global_view<@_ZTV1B, 
[0 : i32, 2 : i32]> : !cir.vptr}> : !rec_B
+// LLVM: @x = global %struct.B { ptr getelementptr inbounds nuw (i8, ptr 
@_ZTV1B, i64 16) }, align 8
+// OGCG: @x = global %struct.B { ptr getelementptr inbounds inrange(-16, 24) 
({ [5 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2) }, align 8


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to