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
