Author: Akira Hatanaka
Date: 2026-07-09T09:12:47-07:00
New Revision: 494697f79f5f7f6b40b78c8f49a98588ee87f1bd

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

LOG: [CodeGen] Disable exact dynamic_cast for duplicable vtables (#205929)

Add `TargetInfo::getVTableUniqueness()`, which returns a
`VTableUniquenessKind` that describes whether a target's ABI guarantees
a class's vtable has a single address program-wide. Apple Mach-O targets
return `UniqueIfStrongLinkage`, since a vague-linkage (weak) vtable may
be autohidden and duplicated per image, while a vtable with strong
linkage (e.g., a class with a key function) still has a unique address.
Every other target keeps the default, `AlwaysUnique`.

Rename the `CodeGenOpts` flag backing `-f[no-]assume-unique-vtables`
from `AssumeUniqueVTables` to `DisableExactDynamicCast`, defaulting to
false so the optimization stays on unless `-fno-assume-unique-vtables`
is given. Use it together with `getVTableUniqueness()` in
`ItaniumCXXABI::hasUniqueVTablePointer()` so a weak vtable on a target
that may duplicate vtables no longer qualifies for the exact
`dynamic_cast` optimization; a vtable with a key function, or any vtable
on a target with `AlwaysUnique` semantics, is unaffected.

Fixes https://github.com/llvm/llvm-project/issues/120129

rdar://129380573

Added: 
    

Modified: 
    clang/include/clang/Basic/CodeGenOptions.def
    clang/include/clang/Basic/CodeGenOptions.h
    clang/include/clang/Basic/TargetInfo.h
    clang/include/clang/Options/Options.td
    clang/lib/Basic/TargetInfo.cpp
    clang/lib/Basic/Targets/OSTargets.h
    clang/lib/CodeGen/ItaniumCXXABI.cpp
    clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
    clang/test/CodeGenCXX/dynamic-cast-exact.cpp
    clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 49374efbeb28b..bcb47169403d8 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -39,7 +39,7 @@ CODEGENOPT(ImplicitMapSyms, 1, 0, Benign) ///< 
-Wa,-mmapsyms=implicit
 CODEGENOPT(AsmVerbose        , 1, 0, Benign) ///< -dA, -fverbose-asm.
 CODEGENOPT(PreserveAsmComments, 1, 1, Benign) ///< -dA, 
-fno-preserve-as-comments.
 CODEGENOPT(AssumeSaneOperatorNew , 1, 1, Benign) ///< implicit 
__attribute__((malloc)) operator new
-CODEGENOPT(AssumeUniqueVTables , 1, 1, Benign) ///< Assume a class has only 
one vtable.
+CODEGENOPT(DisableExactDynamicCast , 1, 0, Benign) ///< 
-fno-assume-unique-vtables: disable the exact dynamic_cast optimization.
 CODEGENOPT(Autolink          , 1, 1, Benign) ///< -fno-autolink
 CODEGENOPT(AutoImport        , 1, 1, Benign) ///< -fno-auto-import
 CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0, Benign) ///< Whether ARC should 
be EH-safe.

diff  --git a/clang/include/clang/Basic/CodeGenOptions.h 
b/clang/include/clang/Basic/CodeGenOptions.h
index 97d68877467fd..c12434135a198 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -36,6 +36,19 @@ class PassBuilder;
 }
 namespace clang {
 
+/// A target's ABI policy for whether a class's vtable can be assumed to have
+/// a unique address program-wide. This is the basis for the exact dynamic_cast
+/// optimization (and, where a vtable is not assumed unique, for whether it may
+/// be marked unnamed_addr so the platform can duplicate it).
+enum class VTableUniquenessKind {
+  /// Every vtable has a single address program-wide.
+  AlwaysUnique,
+  /// A vtable with strong linkage (e.g., a class with a key function) is
+  /// unique, but a vague-linkage (weak) vtable may be duplicated by the
+  /// platform and so has no unique address.
+  UniqueIfStrongLinkage,
+};
+
 /// Bitfields of CodeGenOptions, split out from CodeGenOptions to ensure
 /// that this large collection of bitfields is a trivial class type.
 class CodeGenOptionsBase {

diff  --git a/clang/include/clang/Basic/TargetInfo.h 
b/clang/include/clang/Basic/TargetInfo.h
index b7c0428dc2b2b..23b2b5e650c84 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1796,6 +1796,10 @@ class TargetInfo : public TransferrableTargetInfo,
   /// Clang backwards compatibility rather than GCC/Itanium ABI compatibility.
   virtual bool areDefaultedSMFStillPOD(const LangOptions&) const;
 
+  /// Returns whether the target's ABI guarantees that a class's vtable has a
+  /// unique address program-wide.
+  virtual VTableUniquenessKind getVTableUniqueness() const;
+
   /// Controls whether global operator delete is called by the deleting
   /// destructor or at the point where ::delete was called. Historically Clang
   /// called global operator delete outside of the deleting destructor for both

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index c498b4e6acc44..677aaf00b5083 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1664,9 +1664,9 @@ def : Flag<["-"], "static-libasan">, Alias<static_libsan>;
 def fasm : Flag<["-"], "fasm">, Group<f_Group>;
 
 defm assume_unique_vtables : BoolFOption<"assume-unique-vtables",
-  CodeGenOpts<"AssumeUniqueVTables">, DefaultTrue,
-  PosFlag<SetTrue>,
-  NegFlag<SetFalse, [], [ClangOption, CC1Option],
+  CodeGenOpts<"DisableExactDynamicCast">, DefaultFalse,
+  PosFlag<SetFalse>,
+  NegFlag<SetTrue, [], [ClangOption, CC1Option],
           "Disable optimizations based on vtable pointer identity">,
   BothFlags<[], [ClangOption, CLOption]>>;
 

diff  --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index 9a25384347073..df7b238c950ed 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -632,6 +632,10 @@ TargetInfo::getCallingConvKind(bool ClangABICompat4) const 
{
   return CCK_Default;
 }
 
+VTableUniquenessKind TargetInfo::getVTableUniqueness() const {
+  return VTableUniquenessKind::AlwaysUnique;
+}
+
 bool TargetInfo::callGlobalDeleteInDeletingDtor(
     const LangOptions &LangOpts) const {
   if (getCXXABI() == TargetCXXABI::Microsoft &&

diff  --git a/clang/lib/Basic/Targets/OSTargets.h 
b/clang/lib/Basic/Targets/OSTargets.h
index 9461680df8bdb..7c4666606d89f 100644
--- a/clang/lib/Basic/Targets/OSTargets.h
+++ b/clang/lib/Basic/Targets/OSTargets.h
@@ -62,6 +62,13 @@ class LLVM_LIBRARY_VISIBILITY AppleMachOTargetInfo
   /// similar to ELF's "protected";  Apple Mach-O requires a "weak" attribute 
on
   /// declarations that can be dynamically replaced.
   bool hasProtectedVisibility() const override { return false; }
+
+  /// Apple Mach-O can autohide a vague-linkage vtable so that each image gets
+  /// its own copy, so such a vtable may have more than one address in a
+  /// program. A strong (key-function) vtable still has a unique address.
+  VTableUniquenessKind getVTableUniqueness() const override {
+    return VTableUniquenessKind::UniqueIfStrongLinkage;
+  }
 };
 
 template <typename Target>

diff  --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index b4b3284f752ae..e5799d7f17959 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -202,9 +202,10 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
   bool hasUniqueVTablePointer(QualType RecordTy) {
     const CXXRecordDecl *RD = RecordTy->getAsCXXRecordDecl();
 
-    // Under -fapple-kext, multiple definitions of the same vtable may be
-    // emitted.
-    if (!CGM.getCodeGenOpts().AssumeUniqueVTables ||
+    // The exact dynamic_cast optimization relies on the vtable having a unique
+    // address. -fno-assume-unique-vtables disables it, and under -fapple-kext
+    // multiple definitions of the same vtable may be emitted.
+    if (CGM.getCodeGenOpts().DisableExactDynamicCast ||
         getContext().getLangOpts().AppleKext)
       return false;
 
@@ -227,7 +228,11 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
         llvm::GlobalValue::DefaultVisibility)
       return false;
 
-    return true;
+    // A vague-linkage (weak) vtable on a target whose ABI may duplicate it can
+    // be emitted with a distinct address in more than one image, so its 
address
+    // cannot be assumed unique.
+    return CGM.getTarget().getVTableUniqueness() !=
+           VTableUniquenessKind::UniqueIfStrongLinkage;
   }
 
   bool shouldEmitExactDynamicCast(QualType DestRecordTy) override {

diff  --git a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp 
b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
index bf202d14c3398..0fe32cac77773 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
@@ -1,16 +1,33 @@
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,EXACT
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O0 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,INEXACT
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 
-fvisibility=hidden -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,INEXACT
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 -fapple-kext 
-emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,INEXACT
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 
-fno-assume-unique-vtables -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,INEXACT
+// The ENABLED/DISABLED prefixes track the weak vtable (class B);
+// KEY-ENABLED/KEY-DISABLED track the key-function class (WithKey), whose
+// vtable is external with a unique address. The weak vtable gets the exact
+// dynamic_cast optimization only on targets with unique vtables; the
+// key-function vtable keeps it even on targets that may duplicate weak
+// vtables, and loses it only when the optimization is turned off entirely
+// (-O0, -fapple-kext, -fno-assume-unique-vtables).
+//
+// Baseline, unique vtables:
+// RUN: %clang_cc1 -I%S %s -triple x86_64-unknown-linux-gnu -O1 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,ENABLED,KEY-ENABLED
+// Disabled without optimization:
+// RUN: %clang_cc1 -I%S %s -triple x86_64-unknown-linux-gnu -O0 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,DISABLED,KEY-DISABLED
+// Disabled for a weak vtable with non-default visibility, but kept for the
+// key-function class (its vtable is external):
+// RUN: %clang_cc1 -I%S %s -triple x86_64-unknown-linux-gnu -O1 
-fvisibility=hidden -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,DISABLED,KEY-ENABLED
+// Disabled under -fapple-kext:
+// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 -fapple-kext 
-emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,DISABLED,KEY-DISABLED
+// Disabled by -fno-assume-unique-vtables:
+// RUN: %clang_cc1 -I%S %s -triple x86_64-unknown-linux-gnu -O1 
-fno-assume-unique-vtables -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,DISABLED,KEY-DISABLED
+// Disabled for a weak vtable on a target that may duplicate vtables (Apple
+// Mach-O), but kept for the key-function class:
+// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 -emit-llvm 
-std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,DISABLED,KEY-ENABLED
 
 struct A { virtual ~A(); };
 struct B final : A { };
 
 // CHECK-LABEL: @_Z5exactP1A
 B *exact(A *a) {
-  // INEXACT: call {{.*}} @__dynamic_cast
-  // EXACT-NOT: call {{.*}} @__dynamic_cast
+  // DISABLED: call {{.*}} @__dynamic_cast
+  // ENABLED-NOT: call {{.*}} @__dynamic_cast
   return dynamic_cast<B*>(a);
 }
 
@@ -24,8 +41,20 @@ struct D final : private C {
 
 // CHECK-LABEL: @_Z5exactP1C
 D *exact(C *a) {
-  // INEXACT: call {{.*}} @__dynamic_cast
-  // EXACT: entry:
-  // EXACT-NEXT: ret ptr null
+  // DISABLED: call {{.*}} @__dynamic_cast
+  // ENABLED: entry:
+  // ENABLED-NEXT: ret ptr null
   return dynamic_cast<D*>(a);
 }
+
+// WithKey has a key function (the out-of-line g()), so its vtable has external
+// linkage and a unique address even on a target that may duplicate vtables.
+struct WithKey final : A { virtual void g(); };
+
+// CHECK-LABEL: @_Z12cast_withkeyP1A
+WithKey *cast_withkey(A *a) {
+  // KEY-DISABLED: call {{.*}} @__dynamic_cast
+  // KEY-ENABLED-NOT: call {{.*}} @__dynamic_cast
+  // KEY-ENABLED: icmp eq ptr {{.*}}, {{.*}}@_ZTV7WithKey
+  return dynamic_cast<WithKey*>(a);
+}

diff  --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp 
b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
index 7bf701206f5a4..e751f3c6dd5c2 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -emit-llvm 
-fcxx-exceptions -fexceptions -std=c++11 -o - -O1 -disable-llvm-passes | 
FileCheck %s --implicit-check-not='call {{.*}} @__dynamic_cast'
+// RUN: %clang_cc1 -I%S %s -triple x86_64-unknown-linux-gnu -emit-llvm 
-fcxx-exceptions -fexceptions -std=c++11 -o - -O1 -disable-llvm-passes | 
FileCheck %s --implicit-check-not='call {{.*}} @__dynamic_cast'
 struct Offset { virtual ~Offset(); };
 struct A { virtual ~A(); };
 struct B final : Offset, A { };

diff  --git a/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp 
b/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
index 1710ca5563380..c16f9a38adeb5 100644
--- a/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
+++ b/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -I%S %s -triple arm64e-apple-darwin10 -O1 -fptrauth-calls 
-fptrauth-vtable-pointer-address-discrimination  
-fptrauth-vtable-pointer-type-discrimination -emit-llvm -std=c++11 -o - | 
FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_cc1 -I%S %s -triple aarch64-linux-gnu -O1 -fptrauth-calls 
-fptrauth-vtable-pointer-address-discrimination  
-fptrauth-vtable-pointer-type-discrimination -emit-llvm -std=c++11 -o - | 
FileCheck %s --check-prefixes=CHECK
 
 struct A {
   virtual ~A();


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

Reply via email to