Skip to content

Commit fb532b9

Browse files
committed
Add whole-program vtable optimization feature to Clang.
This patch introduces the -fwhole-program-vtables flag, which enables the whole-program vtable optimization feature (D16795) in Clang. Differential Revision: http://reviews.llvm.org/D16821 llvm-svn: 261767
1 parent 40afcb5 commit fb532b9

File tree

20 files changed

+185
-49
lines changed

20 files changed

+185
-49
lines changed

clang/docs/UsersManual.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,21 @@ are listed below.
10531053
the behavior of sanitizers in the ``cfi`` group to allow checking
10541054
of cross-DSO virtual and indirect calls.
10551055

1056+
.. option:: -fwhole-program-vtables
1057+
1058+
Enable whole-program vtable optimizations, such as single-implementation
1059+
devirtualization and virtual constant propagation. Requires ``-flto``.
1060+
1061+
By default, the compiler will assume that all type hierarchies are
1062+
closed except those in the ``std`` namespace, the ``stdext`` namespace
1063+
and classes with the ``__declspec(uuid())`` attribute.
1064+
1065+
.. option:: -fwhole-program-vtables-blacklist=path
1066+
1067+
Allows the user to specify the path to a list of additional classes to
1068+
blacklist from whole-program vtable optimizations. This list is in the
1069+
:ref:`CFI blacklist <cfi-blacklist>` format.
1070+
10561071
.. option:: -fno-assume-sane-operator-new
10571072

10581073
Don't assume that the C++'s new operator is sane.

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,13 @@ def fvisibility_inlines_hidden : Flag<["-"], "fvisibility-inlines-hidden">, Grou
11241124
def fvisibility_ms_compat : Flag<["-"], "fvisibility-ms-compat">, Group<f_Group>,
11251125
HelpText<"Give global types 'default' visibility and global functions and "
11261126
"variables 'hidden' visibility by default">;
1127+
def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group<f_Group>,
1128+
Flags<[CC1Option]>,
1129+
HelpText<"Enables whole-program vtable optimization. Requires -flto">;
1130+
def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group<f_Group>;
1131+
def fwhole_program_vtables_blacklist_EQ : Joined<["-"], "fwhole-program-vtables-blacklist=">,
1132+
Group<f_Group>, Flags<[CC1Option]>,
1133+
HelpText<"Path to a blacklist file for whole-program vtable optimization">;
11271134
def fwrapv : Flag<["-"], "fwrapv">, Group<f_Group>, Flags<[CC1Option]>,
11281135
HelpText<"Treat signed integer overflow as two's complement">;
11291136
def fwritable_strings : Flag<["-"], "fwritable-strings">, Group<f_Group>, Flags<[CC1Option]>,

clang/include/clang/Frontend/CodeGenOptions.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ CODEGENOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should
179179

180180
CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists.
181181

182+
CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
183+
/// vtable optimization.
184+
182185
/// The user specified number of registers to be used for integral arguments,
183186
/// or 0 if unspecified.
184187
VALUE_CODEGENOPT(NumRegisterParameters, 32, 0)

clang/include/clang/Frontend/CodeGenOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
201201
/// \brief A list of all -fno-builtin-* function names (e.g., memset).
202202
std::vector<std::string> NoBuiltinFuncs;
203203

204+
/// List of blacklist files for the whole-program vtable optimization feature.
205+
std::vector<std::string> WholeProgramVTablesBlacklistFiles;
206+
204207
public:
205208
// Define accessors/mutators for code generation options of enumeration type.
206209
#define CODEGENOPT(Name, Bits, Default)

clang/lib/CodeGen/CGClass.cpp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,15 +2485,35 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
24852485
RD->bases_begin()->getType()->getAsCXXRecordDecl());
24862486
}
24872487

2488-
void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
2488+
void CodeGenFunction::EmitBitSetCodeForVCall(const CXXRecordDecl *RD,
2489+
llvm::Value *VTable,
2490+
SourceLocation Loc) {
2491+
if (CGM.getCodeGenOpts().WholeProgramVTables &&
2492+
!CGM.IsBitSetBlacklistedRecord(RD)) {
2493+
llvm::Metadata *MD =
2494+
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
2495+
llvm::Value *BitSetName =
2496+
llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD);
2497+
2498+
llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
2499+
llvm::Value *BitSetTest =
2500+
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
2501+
{CastedVTable, BitSetName});
2502+
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), BitSetTest);
2503+
}
2504+
2505+
if (SanOpts.has(SanitizerKind::CFIVCall))
2506+
EmitVTablePtrCheckForCall(RD, VTable, CodeGenFunction::CFITCK_VCall, Loc);
2507+
}
2508+
2509+
void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD,
24892510
llvm::Value *VTable,
24902511
CFITypeCheckKind TCK,
24912512
SourceLocation Loc) {
2492-
const CXXRecordDecl *ClassDecl = MD->getParent();
24932513
if (!SanOpts.has(SanitizerKind::CFICastStrict))
2494-
ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);
2514+
RD = LeastDerivedClassWithSameLayout(RD);
24952515

2496-
EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc);
2516+
EmitVTablePtrCheck(RD, VTable, TCK, Loc);
24972517
}
24982518

24992519
void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
@@ -2545,7 +2565,7 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
25452565
llvm::Value *VTable,
25462566
CFITypeCheckKind TCK,
25472567
SourceLocation Loc) {
2548-
if (CGM.IsCFIBlacklistedRecord(RD))
2568+
if (CGM.IsBitSetBlacklistedRecord(RD))
25492569
return;
25502570

25512571
SanitizerScope SanScope(this);

clang/lib/CodeGen/CGExprCXX.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
259259
if (SanOpts.has(SanitizerKind::CFINVCall) &&
260260
MD->getParent()->isDynamicClass()) {
261261
llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy, MD->getParent());
262-
EmitVTablePtrCheckForCall(MD, VTable, CFITCK_NVCall, CE->getLocStart());
262+
EmitVTablePtrCheckForCall(MD->getParent(), VTable, CFITCK_NVCall,
263+
CE->getLocStart());
263264
}
264265

265266
if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier)

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -898,21 +898,34 @@ void CodeGenModule::EmitDeferredVTables() {
898898
DeferredVTables.clear();
899899
}
900900

901-
bool CodeGenModule::IsCFIBlacklistedRecord(const CXXRecordDecl *RD) {
902-
if (RD->hasAttr<UuidAttr>() &&
903-
getContext().getSanitizerBlacklist().isBlacklistedType("attr:uuid"))
904-
return true;
901+
bool CodeGenModule::NeedVTableBitSets() {
902+
return getCodeGenOpts().WholeProgramVTables ||
903+
getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) ||
904+
getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) ||
905+
getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) ||
906+
getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast);
907+
}
908+
909+
bool CodeGenModule::IsBitSetBlacklistedRecord(const CXXRecordDecl *RD) {
910+
std::string TypeName = RD->getQualifiedNameAsString();
911+
auto isInBlacklist = [&](const SanitizerBlacklist &BL) {
912+
if (RD->hasAttr<UuidAttr>() && BL.isBlacklistedType("attr:uuid"))
913+
return true;
914+
915+
return BL.isBlacklistedType(TypeName);
916+
};
905917

906-
return getContext().getSanitizerBlacklist().isBlacklistedType(
907-
RD->getQualifiedNameAsString());
918+
return isInBlacklist(WholeProgramVTablesBlacklist) ||
919+
((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) ||
920+
LangOpts.Sanitize.has(SanitizerKind::CFINVCall) ||
921+
LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) ||
922+
LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) &&
923+
isInBlacklist(getContext().getSanitizerBlacklist()));
908924
}
909925

910926
void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
911927
const VTableLayout &VTLayout) {
912-
if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
913-
!LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
914-
!LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) &&
915-
!LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast))
928+
if (!NeedVTableBitSets())
916929
return;
917930

918931
CharUnits PointerWidth =
@@ -922,7 +935,7 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
922935
std::vector<BSEntry> BitsetEntries;
923936
// Create a bit set entry for each address point.
924937
for (auto &&AP : VTLayout.getAddressPoints()) {
925-
if (IsCFIBlacklistedRecord(AP.first.getBase()))
938+
if (IsBitSetBlacklistedRecord(AP.first.getBase()))
926939
continue;
927940

928941
BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second));

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1401,14 +1401,20 @@ class CodeGenFunction : public CodeGenTypeCache {
14011401

14021402
/// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable.
14031403
/// If vptr CFI is enabled, emit a check that VTable is valid.
1404-
void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable,
1404+
void EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, llvm::Value *VTable,
14051405
CFITypeCheckKind TCK, SourceLocation Loc);
14061406

14071407
/// EmitVTablePtrCheck - Emit a check that VTable is a valid virtual table for
14081408
/// RD using llvm.bitset.test.
14091409
void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable,
14101410
CFITypeCheckKind TCK, SourceLocation Loc);
14111411

1412+
/// If whole-program virtual table optimization is enabled, emit an assumption
1413+
/// that VTable is a member of the type's bitset. Or, if vptr CFI is enabled,
1414+
/// emit a check that VTable is a member of the type's bitset.
1415+
void EmitBitSetCodeForVCall(const CXXRecordDecl *RD, llvm::Value *VTable,
1416+
SourceLocation Loc);
1417+
14121418
/// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
14131419
/// expr can be devirtualized.
14141420
bool CanDevirtualizeMemberFunctionCall(const Expr *Base,

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
9797
NSConcreteStackBlock(nullptr), BlockObjectAssign(nullptr),
9898
BlockObjectDispose(nullptr), BlockDescriptorType(nullptr),
9999
GenericBlockLiteralType(nullptr), LifetimeStartFn(nullptr),
100-
LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)) {
100+
LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)),
101+
WholeProgramVTablesBlacklist(CGO.WholeProgramVTablesBlacklistFiles,
102+
C.getSourceManager()) {
101103

102104
// Initialize the type cache.
103105
llvm::LLVMContext &LLVMContext = M.getContext();

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,8 @@ class CodeGenModule : public CodeGenTypeCache {
489489
/// MDNodes.
490490
llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap;
491491

492+
SanitizerBlacklist WholeProgramVTablesBlacklist;
493+
492494
public:
493495
CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
494496
const PreprocessorOptions &ppopts,
@@ -1108,9 +1110,12 @@ class CodeGenModule : public CodeGenTypeCache {
11081110
/// \param D Threadprivate declaration.
11091111
void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D);
11101112

1111-
/// Returns whether the given record is blacklisted from control flow
1112-
/// integrity checks.
1113-
bool IsCFIBlacklistedRecord(const CXXRecordDecl *RD);
1113+
/// Returns whether we need bit sets attached to vtables.
1114+
bool NeedVTableBitSets();
1115+
1116+
/// Returns whether the given record is blacklisted from whole-program
1117+
/// transformations (i.e. CFI or whole-program vtable optimization).
1118+
bool IsBitSetBlacklistedRecord(const CXXRecordDecl *RD);
11141119

11151120
/// Emit bit set entries for the given vtable using the given layout if
11161121
/// vptr CFI is enabled.

0 commit comments

Comments
 (0)