summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/docs/UsersManual.rst15
-rw-r--r--clang/include/clang/Driver/Options.td7
-rw-r--r--clang/include/clang/Frontend/CodeGenOptions.def3
-rw-r--r--clang/include/clang/Frontend/CodeGenOptions.h3
-rw-r--r--clang/lib/CodeGen/CGClass.cpp30
-rw-r--r--clang/lib/CodeGen/CGExprCXX.cpp3
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp35
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h8
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp4
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h11
-rw-r--r--clang/lib/CodeGen/ItaniumCXXABI.cpp4
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp19
-rw-r--r--clang/lib/Driver/Tools.cpp26
-rw-r--r--clang/lib/Frontend/CompilerInvocation.cpp3
-rw-r--r--clang/runtime/CMakeLists.txt10
-rw-r--r--clang/runtime/vtables_blacklist.txt8
-rw-r--r--clang/test/CodeGenCXX/bitset-blacklist.cpp (renamed from clang/test/CodeGenCXX/cfi-blacklist.cpp)2
-rw-r--r--clang/test/CodeGenCXX/bitsets.cpp (renamed from clang/test/CodeGenCXX/cfi-vcall.cpp)32
-rw-r--r--clang/test/Driver/Inputs/resource_dir/vtables_blacklist.txt0
-rw-r--r--clang/test/Driver/whole-program-vtables.c11
20 files changed, 185 insertions, 49 deletions
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index e1bb9cbdbd8..8462a7d065c 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1053,6 +1053,21 @@ are listed below.
the behavior of sanitizers in the ``cfi`` group to allow checking
of cross-DSO virtual and indirect calls.
+.. option:: -fwhole-program-vtables
+
+ Enable whole-program vtable optimizations, such as single-implementation
+ devirtualization and virtual constant propagation. Requires ``-flto``.
+
+ By default, the compiler will assume that all type hierarchies are
+ closed except those in the ``std`` namespace, the ``stdext`` namespace
+ and classes with the ``__declspec(uuid())`` attribute.
+
+.. option:: -fwhole-program-vtables-blacklist=path
+
+ Allows the user to specify the path to a list of additional classes to
+ blacklist from whole-program vtable optimizations. This list is in the
+ :ref:`CFI blacklist <cfi-blacklist>` format.
+
.. option:: -fno-assume-sane-operator-new
Don't assume that the C++'s new operator is sane.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 1a974304e76..40e38a6aeaa 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1124,6 +1124,13 @@ def fvisibility_inlines_hidden : Flag<["-"], "fvisibility-inlines-hidden">, Grou
def fvisibility_ms_compat : Flag<["-"], "fvisibility-ms-compat">, Group<f_Group>,
HelpText<"Give global types 'default' visibility and global functions and "
"variables 'hidden' visibility by default">;
+def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group<f_Group>,
+ Flags<[CC1Option]>,
+ HelpText<"Enables whole-program vtable optimization. Requires -flto">;
+def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group<f_Group>;
+def fwhole_program_vtables_blacklist_EQ : Joined<["-"], "fwhole-program-vtables-blacklist=">,
+ Group<f_Group>, Flags<[CC1Option]>,
+ HelpText<"Path to a blacklist file for whole-program vtable optimization">;
def fwrapv : Flag<["-"], "fwrapv">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Treat signed integer overflow as two's complement">;
def fwritable_strings : Flag<["-"], "fwritable-strings">, Group<f_Group>, Flags<[CC1Option]>,
diff --git a/clang/include/clang/Frontend/CodeGenOptions.def b/clang/include/clang/Frontend/CodeGenOptions.def
index bffe85bdbda..9e8d4d12d72 100644
--- a/clang/include/clang/Frontend/CodeGenOptions.def
+++ b/clang/include/clang/Frontend/CodeGenOptions.def
@@ -179,6 +179,9 @@ CODEGENOPT(DebugExplicitImport, 1, 0) ///< Whether or not debug info should
CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists.
+CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
+ /// vtable optimization.
+
/// The user specified number of registers to be used for integral arguments,
/// or 0 if unspecified.
VALUE_CODEGENOPT(NumRegisterParameters, 32, 0)
diff --git a/clang/include/clang/Frontend/CodeGenOptions.h b/clang/include/clang/Frontend/CodeGenOptions.h
index 245b6e9177e..0974288cc83 100644
--- a/clang/include/clang/Frontend/CodeGenOptions.h
+++ b/clang/include/clang/Frontend/CodeGenOptions.h
@@ -201,6 +201,9 @@ public:
/// \brief A list of all -fno-builtin-* function names (e.g., memset).
std::vector<std::string> NoBuiltinFuncs;
+ /// List of blacklist files for the whole-program vtable optimization feature.
+ std::vector<std::string> WholeProgramVTablesBlacklistFiles;
+
public:
// Define accessors/mutators for code generation options of enumeration type.
#define CODEGENOPT(Name, Bits, Default)
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 1ec700204ab..8a82be9e5f2 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2485,15 +2485,35 @@ LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
RD->bases_begin()->getType()->getAsCXXRecordDecl());
}
-void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
+void CodeGenFunction::EmitBitSetCodeForVCall(const CXXRecordDecl *RD,
+ llvm::Value *VTable,
+ SourceLocation Loc) {
+ if (CGM.getCodeGenOpts().WholeProgramVTables &&
+ !CGM.IsBitSetBlacklistedRecord(RD)) {
+ llvm::Metadata *MD =
+ CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
+ llvm::Value *BitSetName =
+ llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD);
+
+ llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
+ llvm::Value *BitSetTest =
+ Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
+ {CastedVTable, BitSetName});
+ Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), BitSetTest);
+ }
+
+ if (SanOpts.has(SanitizerKind::CFIVCall))
+ EmitVTablePtrCheckForCall(RD, VTable, CodeGenFunction::CFITCK_VCall, Loc);
+}
+
+void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD,
llvm::Value *VTable,
CFITypeCheckKind TCK,
SourceLocation Loc) {
- const CXXRecordDecl *ClassDecl = MD->getParent();
if (!SanOpts.has(SanitizerKind::CFICastStrict))
- ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);
+ RD = LeastDerivedClassWithSameLayout(RD);
- EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc);
+ EmitVTablePtrCheck(RD, VTable, TCK, Loc);
}
void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
@@ -2545,7 +2565,7 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
llvm::Value *VTable,
CFITypeCheckKind TCK,
SourceLocation Loc) {
- if (CGM.IsCFIBlacklistedRecord(RD))
+ if (CGM.IsBitSetBlacklistedRecord(RD))
return;
SanitizerScope SanScope(this);
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 604cde76a7b..614b0769231 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -259,7 +259,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
if (SanOpts.has(SanitizerKind::CFINVCall) &&
MD->getParent()->isDynamicClass()) {
llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy, MD->getParent());
- EmitVTablePtrCheckForCall(MD, VTable, CFITCK_NVCall, CE->getLocStart());
+ EmitVTablePtrCheckForCall(MD->getParent(), VTable, CFITCK_NVCall,
+ CE->getLocStart());
}
if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier)
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index e96a8120207..953053bd1c3 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -898,21 +898,34 @@ void CodeGenModule::EmitDeferredVTables() {
DeferredVTables.clear();
}
-bool CodeGenModule::IsCFIBlacklistedRecord(const CXXRecordDecl *RD) {
- if (RD->hasAttr<UuidAttr>() &&
- getContext().getSanitizerBlacklist().isBlacklistedType("attr:uuid"))
- return true;
+bool CodeGenModule::NeedVTableBitSets() {
+ return getCodeGenOpts().WholeProgramVTables ||
+ getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) ||
+ getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) ||
+ getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) ||
+ getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast);
+}
+
+bool CodeGenModule::IsBitSetBlacklistedRecord(const CXXRecordDecl *RD) {
+ std::string TypeName = RD->getQualifiedNameAsString();
+ auto isInBlacklist = [&](const SanitizerBlacklist &BL) {
+ if (RD->hasAttr<UuidAttr>() && BL.isBlacklistedType("attr:uuid"))
+ return true;
+
+ return BL.isBlacklistedType(TypeName);
+ };
- return getContext().getSanitizerBlacklist().isBlacklistedType(
- RD->getQualifiedNameAsString());
+ return isInBlacklist(WholeProgramVTablesBlacklist) ||
+ ((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) ||
+ LangOpts.Sanitize.has(SanitizerKind::CFINVCall) ||
+ LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) ||
+ LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) &&
+ isInBlacklist(getContext().getSanitizerBlacklist()));
}
void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
const VTableLayout &VTLayout) {
- if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
- !LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
- !LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) &&
- !LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast))
+ if (!NeedVTableBitSets())
return;
CharUnits PointerWidth =
@@ -922,7 +935,7 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
std::vector<BSEntry> BitsetEntries;
// Create a bit set entry for each address point.
for (auto &&AP : VTLayout.getAddressPoints()) {
- if (IsCFIBlacklistedRecord(AP.first.getBase()))
+ if (IsBitSetBlacklistedRecord(AP.first.getBase()))
continue;
BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second));
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1254a0f93fc..9a31c8f78b5 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1401,7 +1401,7 @@ public:
/// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable.
/// If vptr CFI is enabled, emit a check that VTable is valid.
- void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable,
+ void EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, llvm::Value *VTable,
CFITypeCheckKind TCK, SourceLocation Loc);
/// EmitVTablePtrCheck - Emit a check that VTable is a valid virtual table for
@@ -1409,6 +1409,12 @@ public:
void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable,
CFITypeCheckKind TCK, SourceLocation Loc);
+ /// If whole-program virtual table optimization is enabled, emit an assumption
+ /// that VTable is a member of the type's bitset. Or, if vptr CFI is enabled,
+ /// emit a check that VTable is a member of the type's bitset.
+ void EmitBitSetCodeForVCall(const CXXRecordDecl *RD, llvm::Value *VTable,
+ SourceLocation Loc);
+
/// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
/// expr can be devirtualized.
bool CanDevirtualizeMemberFunctionCall(const Expr *Base,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index aabcc524c93..b98094f6656 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -97,7 +97,9 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
NSConcreteStackBlock(nullptr), BlockObjectAssign(nullptr),
BlockObjectDispose(nullptr), BlockDescriptorType(nullptr),
GenericBlockLiteralType(nullptr), LifetimeStartFn(nullptr),
- LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)) {
+ LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)),
+ WholeProgramVTablesBlacklist(CGO.WholeProgramVTablesBlacklistFiles,
+ C.getSourceManager()) {
// Initialize the type cache.
llvm::LLVMContext &LLVMContext = M.getContext();
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index f8084ef43d9..db2df793dbc 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -489,6 +489,8 @@ private:
/// MDNodes.
llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap;
+ SanitizerBlacklist WholeProgramVTablesBlacklist;
+
public:
CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
const PreprocessorOptions &ppopts,
@@ -1108,9 +1110,12 @@ public:
/// \param D Threadprivate declaration.
void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D);
- /// Returns whether the given record is blacklisted from control flow
- /// integrity checks.
- bool IsCFIBlacklistedRecord(const CXXRecordDecl *RD);
+ /// Returns whether we need bit sets attached to vtables.
+ bool NeedVTableBitSets();
+
+ /// Returns whether the given record is blacklisted from whole-program
+ /// transformations (i.e. CFI or whole-program vtable optimization).
+ bool IsBitSetBlacklistedRecord(const CXXRecordDecl *RD);
/// Emit bit set entries for the given vtable using the given layout if
/// vptr CFI is enabled.
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 9d7b4da2c53..42ee9d7772c 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1602,9 +1602,7 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
auto *MethodDecl = cast<CXXMethodDecl>(GD.getDecl());
llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent());
- if (CGF.SanOpts.has(SanitizerKind::CFIVCall))
- CGF.EmitVTablePtrCheckForCall(MethodDecl, VTable,
- CodeGenFunction::CFITCK_VCall, Loc);
+ CGF.EmitBitSetCodeForVCall(MethodDecl->getParent(), VTable, Loc);
uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD);
llvm::Value *VFuncPtr =
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index eecb12c6595..ebf5497f0ec 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1503,10 +1503,7 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info,
const CXXRecordDecl *RD,
llvm::GlobalVariable *VTable) {
- if (!getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) &&
- !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) &&
- !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) &&
- !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast))
+ if (!CGM.NeedVTableBitSets())
return;
llvm::NamedMDNode *BitsetsMD =
@@ -1522,13 +1519,13 @@ void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info,
: CharUnits::Zero();
if (Info->PathToBaseWithVPtr.empty()) {
- if (!CGM.IsCFIBlacklistedRecord(RD))
+ if (!CGM.IsBitSetBlacklistedRecord(RD))
CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD);
return;
}
// Add a bitset entry for the least derived base belonging to this vftable.
- if (!CGM.IsCFIBlacklistedRecord(Info->PathToBaseWithVPtr.back()))
+ if (!CGM.IsBitSetBlacklistedRecord(Info->PathToBaseWithVPtr.back()))
CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint,
Info->PathToBaseWithVPtr.back());
@@ -1548,12 +1545,12 @@ void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info,
Offset = VBI->second.VBaseOffset;
if (!Offset.isZero())
return;
- if (!CGM.IsCFIBlacklistedRecord(DerivedRD))
+ if (!CGM.IsBitSetBlacklistedRecord(DerivedRD))
CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, DerivedRD);
}
// Finally do the same for the most derived class.
- if (Info->FullOffsetInMDC.isZero() && !CGM.IsCFIBlacklistedRecord(RD))
+ if (Info->FullOffsetInMDC.isZero() && !CGM.IsBitSetBlacklistedRecord(RD))
CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD);
}
@@ -1822,9 +1819,9 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
MicrosoftVTableContext::MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
- if (CGF.SanOpts.has(SanitizerKind::CFIVCall))
- CGF.EmitVTablePtrCheck(getClassAtVTableLocation(getContext(), GD, ML),
- VTable, CodeGenFunction::CFITCK_VCall, Loc);
+ if (CGM.NeedVTableBitSets())
+ CGF.EmitBitSetCodeForVCall(getClassAtVTableLocation(getContext(), GD, ML),
+ VTable, Loc);
llvm::Value *VFuncPtr =
Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn");
diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp
index 00128dc7a32..0206281ae29 100644
--- a/clang/lib/Driver/Tools.cpp
+++ b/clang/lib/Driver/Tools.cpp
@@ -4270,6 +4270,32 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-ffunction-sections");
}
+ if (Args.hasArg(options::OPT_fwhole_program_vtables,
+ options::OPT_fno_whole_program_vtables, false)) {
+ if (!D.isUsingLTO())
+ D.Diag(diag::err_drv_argument_only_allowed_with)
+ << "-fwhole-program-vtables"
+ << "-flto";
+ CmdArgs.push_back("-fwhole-program-vtables");
+
+ clang::SmallString<64> Path(D.ResourceDir);
+ llvm::sys::path::append(Path, "vtables_blacklist.txt");
+ if (llvm::sys::fs::exists(Path)) {
+ SmallString<64> BlacklistOpt("-fwhole-program-vtables-blacklist=");
+ BlacklistOpt += Path.str();
+ CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
+ }
+
+ for (const Arg *A :
+ Args.filtered(options::OPT_fwhole_program_vtables_blacklist_EQ)) {
+ A->claim();
+ if (!llvm::sys::fs::exists(A->getValue()))
+ D.Diag(clang::diag::err_drv_no_such_file) << A->getValue();
+ }
+
+ Args.AddAllArgs(CmdArgs, options::OPT_fwhole_program_vtables_blacklist_EQ);
+ }
+
if (Args.hasFlag(options::OPT_fdata_sections, options::OPT_fno_data_sections,
UseSeparateSections)) {
CmdArgs.push_back("-fdata-sections");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index cf4b5cf20a1..953d2a8479d 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -441,6 +441,9 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 0, Diags);
Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info);
Opts.EmitCodeView = Args.hasArg(OPT_gcodeview);
+ Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables);
+ Opts.WholeProgramVTablesBlacklistFiles =
+ Args.getAllArgValues(OPT_fwhole_program_vtables_blacklist_EQ);
Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file);
Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs);
Opts.DebugExplicitImport = Triple.isPS4CPU();
diff --git a/clang/runtime/CMakeLists.txt b/clang/runtime/CMakeLists.txt
index 4775b0d25da..3215d8f6286 100644
--- a/clang/runtime/CMakeLists.txt
+++ b/clang/runtime/CMakeLists.txt
@@ -134,3 +134,13 @@ if(LLVM_BUILD_EXTERNAL_COMPILER_RT AND EXISTS ${COMPILER_RT_SRC_ROOT}/)
VERBATIM)
endif()
endif()
+
+set(src "${CMAKE_CURRENT_SOURCE_DIR}/vtables_blacklist.txt")
+set(dst "${LLVM_LIBRARY_OUTPUT_INTDIR}/clang/${CLANG_VERSION}/vtables_blacklist.txt")
+add_custom_command(OUTPUT ${dst}
+ DEPENDS ${src}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}
+ COMMENT "Copying vtables blacklist")
+add_custom_target(vtables_blacklist DEPENDS ${dst})
+add_dependencies(clang vtables_blacklist)
+install(FILES ${src} DESTINATION lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION})
diff --git a/clang/runtime/vtables_blacklist.txt b/clang/runtime/vtables_blacklist.txt
new file mode 100644
index 00000000000..f2016428d80
--- /dev/null
+++ b/clang/runtime/vtables_blacklist.txt
@@ -0,0 +1,8 @@
+# Standard library types.
+type:std::*
+
+# The stdext namespace contains Microsoft standard library extensions.
+type:stdext::*
+
+# Types with a uuid attribute, i.e. COM types.
+type:attr:uuid
diff --git a/clang/test/CodeGenCXX/cfi-blacklist.cpp b/clang/test/CodeGenCXX/bitset-blacklist.cpp
index 32ed05bcc52..ed15e43ce93 100644
--- a/clang/test/CodeGenCXX/cfi-blacklist.cpp
+++ b/clang/test/CodeGenCXX/bitset-blacklist.cpp
@@ -1,7 +1,9 @@
// RUN: echo "type:attr:uuid" > %t.txt
// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s
+// RUN: %clang_cc1 -fms-extensions -fwhole-program-vtables -fwhole-program-vtables-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s
// RUN: echo "type:std::*" > %t.txt
// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s
+// RUN: %clang_cc1 -fms-extensions -fwhole-program-vtables -fwhole-program-vtables-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s
struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) S1 {
virtual void f();
diff --git a/clang/test/CodeGenCXX/cfi-vcall.cpp b/clang/test/CodeGenCXX/bitsets.cpp
index 92409232cfa..dba399cc96f 100644
--- a/clang/test/CodeGenCXX/cfi-vcall.cpp
+++ b/clang/test/CodeGenCXX/bitsets.cpp
@@ -1,7 +1,12 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
-// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s
+// Tests for the cfi-vcall feature:
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=MS --check-prefix=NDIAG %s
+
+// Tests for the whole-program-vtables feature:
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS %s
// MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@")
// MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@")
@@ -53,9 +58,9 @@ void D::f() {
void D::h() {
}
-// DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}cfi-vcall.cpp\00", align 1
+// DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}bitsets.cpp\00", align 1
// DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" }
-// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+23]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] }
+// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+24]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] }
// ITANIUM: define void @_Z2afP1A
// MS: define void @"\01?af@@YAXPEAUA@@@Z"
@@ -63,10 +68,11 @@ void af(A *a) {
// ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
// MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
// DIAG-NEXT: [[VTVALID0:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT]], metadata !"all-vtables")
- // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
- // CHECK-NEXT: {{^$}}
+ // VTABLE-OPT: call void @llvm.assume(i1 [[P]])
+ // CFI-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
+ // CFI-NEXT: {{^$}}
- // CHECK: [[TRAPBB]]
+ // CFI: [[TRAPBB]]
// NDIAG-NEXT: call void @llvm.trap()
// NDIAG-NEXT: unreachable
// DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64
@@ -76,8 +82,8 @@ void af(A *a) {
// DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_check_fail(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]])
// DIAG-RECOVER-NEXT: br label %[[CONTBB]]
- // CHECK: [[CONTBB]]
- // CHECK: call void %
+ // CFI: [[CONTBB]]
+ // CFI: call void %
a->f();
}
@@ -109,7 +115,7 @@ void dh1(D *d) {
// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z"
__attribute__((no_sanitize("cfi")))
void df2(D *d) {
- // CHECK-NOT: call i1 @llvm.bitset.test
+ // CFI-NOT: call i1 @llvm.bitset.test
d->f();
}
@@ -117,7 +123,7 @@ void df2(D *d) {
// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z"
__attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall")))
void df3(D *d) {
- // CHECK-NOT: call i1 @llvm.bitset.test
+ // CFI-NOT: call i1 @llvm.bitset.test
d->f();
}
diff --git a/clang/test/Driver/Inputs/resource_dir/vtables_blacklist.txt b/clang/test/Driver/Inputs/resource_dir/vtables_blacklist.txt
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/clang/test/Driver/Inputs/resource_dir/vtables_blacklist.txt
diff --git a/clang/test/Driver/whole-program-vtables.c b/clang/test/Driver/whole-program-vtables.c
new file mode 100644
index 00000000000..99f6f4c8b13
--- /dev/null
+++ b/clang/test/Driver/whole-program-vtables.c
@@ -0,0 +1,11 @@
+// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables -### %s 2>&1 | FileCheck --check-prefix=NO-LTO %s
+// NO-LTO: invalid argument '-fwhole-program-vtables' only allowed with '-flto'
+
+// RUN: %clang -target x86_64-unknown-linux -resource-dir=%S/Inputs/resource_dir -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=BLACKLIST %s
+// BLACKLIST: "-fwhole-program-vtables-blacklist={{.*}}vtables_blacklist.txt"
+
+// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables-blacklist=nonexistent.txt -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=NON-EXISTENT-BLACKLIST %s
+// NON-EXISTENT-BLACKLIST: no such file or directory: 'nonexistent.txt'
+
+// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables-blacklist=%S/Inputs/resource_dir/vtables_blacklist.txt -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=CUSTOM-BLACKLIST %s
+// CUSTOM-BLACKLIST: "-fwhole-program-vtables-blacklist={{.*}}Inputs/resource_dir/vtables_blacklist.txt"
OpenPOWER on IntegriCloud