pcc created this revision.
pcc added reviewers: rsmith, eugenis, aaron.ballman, cfe-commits.
pcc added subscribers: joker.eph, pete.

Bitsets, and the compiler features they rely on (vtable opt, CFI),
only have scope within the linkage unit. Therefore, only enable these
features for classes with linkage unit scope or internal linkage. A new flag,
-fdefault-class-scope=, controls which classes we infer linkage unit scope for.
By default, we infer scope from visibility or dllimport/dllexport attributes.

If the cross-DSO CFI mode is enabled, bitset checks are emitted even for
classes without linkage unit scope, as that mode uses a separate mechanism
to cause bitsets to be exported.

We also provide the [[clang::linkage_unit_scope]] and [[clang::global_scope]]
attributes, which allow users to override the compiler's scope inferences
on a per-class or per-namespace basis.

These attributes replace the whole-program-vtables blacklist, so remove the
-fwhole-program-vtables-blacklist flag.

Because __declspec(uuid()) now implies [[clang::no_linkage_unit_scope]], the
support for the special attr:uuid blacklist entry is removed.

http://reviews.llvm.org/D18635

Files:
  docs/ClassScope.rst
  docs/ControlFlowIntegrity.rst
  docs/UsersManual.rst
  docs/index.rst
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  include/clang/Frontend/CodeGenOptions.h
  include/clang/Sema/AttributeList.h
  include/clang/Sema/Sema.h
  lib/CodeGen/CGClass.cpp
  lib/CodeGen/CGVTables.cpp
  lib/CodeGen/CodeGenModule.cpp
  lib/CodeGen/CodeGenModule.h
  lib/CodeGen/MicrosoftCXXABI.cpp
  lib/Driver/SanitizerArgs.cpp
  lib/Driver/Tools.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclCXX.cpp
  runtime/CMakeLists.txt
  runtime/vtables_blacklist.txt
  test/CodeGenCXX/bitset-blacklist.cpp
  test/CodeGenCXX/bitset-inference.cpp
  test/CodeGenCXX/bitsets.cpp
  test/CodeGenCXX/cfi-blacklist.cpp
  test/CodeGenCXX/cfi-cast.cpp
  test/CodeGenCXX/cfi-nvcall.cpp
  test/CodeGenCXX/cfi-stats.cpp
  test/Driver/default-class-scope.c
  test/Driver/fsanitize.c
  test/Driver/whole-program-vtables.c
  test/Frontend/default-class-scope.c
  test/SemaCXX/attr-linkage-unit-scope.cpp
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -2527,6 +2527,7 @@
     case ObjCProtocol | ObjCInterface:
       return "ExpectedObjectiveCInterfaceOrProtocol";
     case Field | Var: return "ExpectedFieldOrGlobalVar";
+    case GenericRecord | Namespace: return "ExpectedRecordOrNamespace";
   }
 
   PrintFatalError(S.getLoc(),
Index: test/SemaCXX/attr-linkage-unit-scope.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/attr-linkage-unit-scope.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -DATTR=linkage_unit_scope %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -DATTR=global_scope %s
+
+int i [[clang::ATTR]]; // expected-warning {{attribute only applies to struct, union, class or namespace}}
+typedef int t [[clang::ATTR]]; // expected-warning {{attribute only applies to struct, union, class or namespace}}
+[[clang::ATTR]] void f(); // expected-warning {{attribute only applies to struct, union, class or namespace}}
+void f() [[clang::ATTR]]; // expected-error {{attribute cannot be applied to types}}
+
+struct [[clang::ATTR]] s1 {
+  int i [[clang::ATTR]]; // expected-warning {{attribute only applies to struct, union, class or namespace}}
+  [[clang::ATTR]] void f(); // expected-warning {{attribute only applies to struct, union, class or namespace}}
+};
+
+struct [[clang::ATTR(1)]] s2 { // expected-error {{attribute takes no arguments}}
+  virtual void f();
+};
+
+struct
+[[clang::linkage_unit_scope]] // expected-error{{'linkage_unit_scope' and 'global_scope' attributes are not compatible}}
+[[clang::global_scope]] // expected-note{{conflicting attribute is here}}
+s3 {};
+
+struct
+[[clang::global_scope]] // expected-error{{'global_scope' and 'linkage_unit_scope' attributes are not compatible}}
+[[clang::linkage_unit_scope]] // expected-note{{conflicting attribute is here}}
+s4 {};
+
+struct [[clang::ATTR]] s5;
+struct s5 {};
+
+struct s6;
+struct [[clang::ATTR]] s6 {};
+
+struct [[clang::ATTR]] s7;
+struct s7;
+struct s7 {};
+
+struct [[clang::ATTR]] s8;
+struct s8;
+struct [[clang::ATTR]] s8 {};
+
+struct s9;
+struct [[clang::ATTR]] s9;
+
+struct [[clang::linkage_unit_scope]] s10; //expected-error{{attributes are not compatible}}
+struct [[clang::global_scope]] s10 {}; // expected-note{{conflicting attribute is here}}
+
+namespace [[clang::ATTR]] ns1 {}
+
+namespace [[clang::linkage_unit_scope]] ns2 {} // expected-error{{attributes are not compatible}}
+namespace [[clang::global_scope]] ns2 {} // expected-note{{conflicting attribute is here}}
Index: test/Frontend/default-class-scope.c
===================================================================
--- /dev/null
+++ test/Frontend/default-class-scope.c
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -fdefault-class-scope=global %s
+// RUN: %clang_cc1 -fdefault-class-scope=attrs %s
+// RUN: %clang_cc1 -fdefault-class-scope=linkage-unit %s
+
+// RUN: not %clang_cc1 -fdefault-class-scope=foo %s 2>&1 | FileCheck %s
+// CHECK: error: invalid value 'foo' in '-fdefault-class-scope=foo'
Index: test/Driver/whole-program-vtables.c
===================================================================
--- test/Driver/whole-program-vtables.c
+++ test/Driver/whole-program-vtables.c
@@ -1,11 +1,2 @@
 // 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"
Index: test/Driver/fsanitize.c
===================================================================
--- test/Driver/fsanitize.c
+++ test/Driver/fsanitize.c
@@ -274,6 +274,10 @@
 // RUN: %clang -target mips-unknown-linux -fsanitize=cfi-icall %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-ICALL-MIPS
 // CHECK-CFI-ICALL-MIPS: unsupported option '-fsanitize=cfi-icall' for target 'mips-unknown-linux'
 
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -flto -c %s -fdefault-class-scope=attrs -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-CLASS-SCOPE
+// CHECK-CFI-CLASS-SCOPE: -fdefault-class-scope=linkage-unit
+// CHECK-CFI-CLASS-SCOPE: -fdefault-class-scope=attrs
+
 // RUN: %clang -target x86_64-linux-gnu -fsanitize-trap=address -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TRAP
 // CHECK-ASAN-TRAP: error: unsupported argument 'address' to option '-fsanitize-trap'
 
Index: test/Driver/default-class-scope.c
===================================================================
--- /dev/null
+++ test/Driver/default-class-scope.c
@@ -0,0 +1,2 @@
+// RUN: %clang -target x86_64-unknown-linux -fdefault-class-scope=attrs -### %s 2>&1 | FileCheck --check-prefix=ATTRS %s
+// ATTRS: "-fdefault-class-scope=attrs"
Index: test/CodeGenCXX/cfi-stats.cpp
===================================================================
--- test/CodeGenCXX/cfi-stats.cpp
+++ test/CodeGenCXX/cfi-stats.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck --check-prefix=CHECK %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck --check-prefix=CHECK %s
 
 // CHECK: [[STATS:@[^ ]*]] = internal global { i8*, i32, [5 x [2 x i8*]] } { i8* null, i32 5, [5 x [2 x i8*]]
 // CHECK: {{\[\[}}2 x i8*] zeroinitializer,
Index: test/CodeGenCXX/cfi-nvcall.cpp
===================================================================
--- test/CodeGenCXX/cfi-nvcall.cpp
+++ test/CodeGenCXX/cfi-nvcall.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall,cfi-cast-strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -fsanitize=cfi-nvcall -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -fsanitize=cfi-nvcall,cfi-cast-strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s
 
 struct A {
   virtual void f();
Index: test/CodeGenCXX/cfi-cast.cpp
===================================================================
--- test/CodeGenCXX/cfi-cast.cpp
+++ test/CodeGenCXX/cfi-cast.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-derived-cast -fsanitize-trap=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-unrelated-cast -fsanitize-trap=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-unrelated-cast,cfi-cast-strict -fsanitize-trap=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -std=c++11 -fsanitize=cfi-derived-cast -fsanitize-trap=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -std=c++11 -fsanitize=cfi-unrelated-cast -fsanitize-trap=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -std=c++11 -fsanitize=cfi-unrelated-cast,cfi-cast-strict -fsanitize-trap=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s
 
 // In this test the main thing we are searching for is something like
 // 'metadata !"1B"' where "1B" is the mangled name of the class we are
Index: test/CodeGenCXX/cfi-blacklist.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/cfi-blacklist.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOBL %s
+// RUN: echo "type:std::*" > %t.txt
+// RUN: %clang_cc1 -fvisibility hidden -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s
+
+struct S1 {
+  virtual void f();
+};
+
+namespace std {
+
+struct S2 {
+  virtual void f();
+};
+
+}
+
+// CHECK: define{{.*}}s1f
+// NOBL: llvm.bitset.test
+// NOSTD: llvm.bitset.test
+void s1f(S1 *s1) {
+  s1->f();
+}
+
+// CHECK: define{{.*}}s2f
+// NOBL: llvm.bitset.test
+// NOSTD-NOT: llvm.bitset.test
+void s2f(std::S2 *s2) {
+  s2->f();
+}
Index: test/CodeGenCXX/bitsets.cpp
===================================================================
--- test/CodeGenCXX/bitsets.cpp
+++ test/CodeGenCXX/bitsets.cpp
@@ -1,12 +1,12 @@
 // 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
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -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 -fdefault-class-scope=linkage-unit -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 -fdefault-class-scope=linkage-unit -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 -fdefault-class-scope=linkage-unit -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
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fdefault-class-scope=linkage-unit -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 -fdefault-class-scope=linkage-unit -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@@")
Index: test/CodeGenCXX/bitset-inference.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/bitset-inference.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fms-extensions -fvisibility hidden -fwhole-program-vtables -fdefault-class-scope=attrs -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -std=c++11 -fms-extensions -fwhole-program-vtables -fdefault-class-scope=attrs -emit-llvm -o - %s | FileCheck --check-prefix=MS %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fdefault-class-scope=global -std=c++11 -fms-extensions -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=MS-INFER-GLOBAL %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fdefault-class-scope=linkage-unit -std=c++11 -fms-extensions -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=MS-INFER-LU %s
+
+struct C1 {
+  virtual void f();
+};
+
+struct __attribute__((visibility("default"))) C2 {
+  virtual void f();
+};
+
+struct __declspec(dllexport) C3 {
+  virtual void f();
+};
+
+struct __declspec(dllimport) C4 {
+  virtual void f();
+};
+
+struct __attribute__((visibility("default"))) [[clang::linkage_unit_scope]] C5 {
+  virtual void f();
+};
+
+struct [[clang::global_scope]] C6 {
+  virtual void f();
+};
+
+struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) C7 {
+  virtual void f();
+};
+
+namespace [[clang::global_scope]] ns {}
+
+namespace ns {
+
+struct C8 {
+  virtual void f();
+  struct C9 {
+    virtual void f();
+  };
+  struct [[clang::linkage_unit_scope]] C10 {
+    virtual void f();
+  };
+};
+
+struct [[clang::linkage_unit_scope]] C11;
+struct C11 {
+  virtual void f();
+};
+
+}
+
+void f(C1 *c1, C2 *c2, C3 *c3, C4 *c4, C5 *c5, C6 *c6, C7 *c7, ns::C8 *c8,
+       ns::C8::C9 *c9, ns::C8::C10 *c10, ns::C11 *c11) {
+  // ITANIUM: bitset.test{{.*}}!"_ZTS2C1"
+  // MS: bitset.test{{.*}}!"?AUC1@@"
+  // MS-INFER-GLOBAL-NOT: bitset.test{{.*}}!"?AUC1@@"
+  // MS-INFER-LU: bitset.test{{.*}}!"?AUC1@@"
+  c1->f();
+  // ITANIUM-NOT: bitset.test{{.*}}!"_ZTS2C2"
+  // MS: bitset.test{{.*}}!"?AUC2@@"
+  c2->f();
+  // ITANIUM: bitset.test{{.*}}!"_ZTS2C3"
+  // MS-NOT: bitset.test{{.*}}!"?AUC3@@"
+  // MS-INFER-LU: bitset.test{{.*}}!"?AUC3@@"
+  c3->f();
+  // ITANIUM: bitset.test{{.*}}!"_ZTS2C4"
+  // MS-NOT: bitset.test{{.*}}!"?AUC4@@"
+  c4->f();
+  // ITANIUM: bitset.test{{.*}}!"_ZTS2C5"
+  // MS: bitset.test{{.*}}!"?AUC5@@"
+  // MS-INFER-GLOBAL: bitset.test{{.*}}!"?AUC5@@"
+  // MS-INFER-LU: bitset.test{{.*}}!"?AUC5@@"
+  c5->f();
+  // ITANIUM-NOT: bitset.test{{.*}}!"_ZTS2C6"
+  // MS-NOT: bitset.test{{.*}}!"?AUC6@@"
+  // MS-INFER-GLOBAL-NOT: bitset.test{{.*}}!"?AUC6@@"
+  c6->f();
+  // ITANIUM-NOT: bitset.test{{.*}}!"_ZTS2C7"
+  // MS-NOT: bitset.test{{.*}}!"?AUC7@@"
+  // MS-INFER-LU-NOT: bitset.test{{.*}}!"?AUC7@@"
+  c7->f();
+  // ITANIUM-NOT: bitset.test{{.*}}!"_ZTSN2ns2C8E"
+  // MS-NOT: bitset.test{{.*}}!"?AUC8@ns@@"
+  // MS-INFER-LU-NOT: bitset.test{{.*}}!"?AUC8@ns@@"
+  c8->f();
+  // ITANIUM-NOT: bitset.test{{.*}}!"_ZTSN2ns2C82C9E"
+  // MS-NOT: bitset.test{{.*}}!"?AUC9@C8@ns@@"
+  c9->f();
+  // ITANIUM: bitset.test{{.*}}!"_ZTSN2ns2C83C10E"
+  // MS: bitset.test{{.*}}!"?AUC10@C8@ns@@"
+  c10->f();
+  // ITANIUM: bitset.test{{.*}}!"_ZTSN2ns3C11E"
+  // MS: bitset.test{{.*}}!"?AUC11@ns@@"
+  c11->f();
+}
Index: test/CodeGenCXX/bitset-blacklist.cpp
===================================================================
--- test/CodeGenCXX/bitset-blacklist.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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();
-};
-
-namespace std {
-
-struct S2 {
-  virtual void f();
-};
-
-}
-
-// CHECK: define{{.*}}s1f
-// NOSTD: llvm.bitset.test
-// NOUUID-NOT: llvm.bitset.test
-void s1f(S1 *s1) {
-  s1->f();
-}
-
-// CHECK: define{{.*}}s2f
-// NOSTD-NOT: llvm.bitset.test
-// NOUUID: llvm.bitset.test
-void s2f(std::S2 *s2) {
-  s2->f();
-}
Index: runtime/vtables_blacklist.txt
===================================================================
--- runtime/vtables_blacklist.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# 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
Index: runtime/CMakeLists.txt
===================================================================
--- runtime/CMakeLists.txt
+++ runtime/CMakeLists.txt
@@ -148,15 +148,3 @@
       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})
-if(TARGET clang)
-  add_dependencies(clang vtables_blacklist)
-endif()
-install(FILES ${src} DESTINATION lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION})
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -7253,7 +7253,9 @@
   
   ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
 
-  // FIXME: Should we be merging attributes?
+  if (PrevNS)
+    mergeDeclAttributes(Namespc, PrevNS);
+
   if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
     PushNamespaceVisibilityAttr(Attr, Loc);
 
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -246,9 +246,9 @@
 
 /// \brief Diagnose mutually exclusive attributes when present on a given
 /// declaration. Returns true if diagnosed.
-template <typename AttrTy>
+template <typename AttrTy, typename NameTy>
 static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range,
-                                     IdentifierInfo *Ident) {
+                                     NameTy Ident) {
   if (AttrTy *A = D->getAttr<AttrTy>()) {
     S.Diag(Range.getBegin(), diag::err_attributes_are_not_compatible) << Ident
                                                                       << A;
@@ -5214,6 +5214,30 @@
     D->addAttr(Internal);
 }
 
+InheritableAttr *Sema::mergeClassScopeAttr(Decl *D, SourceRange Range,
+                                           int SpellingIndex,
+                                           bool IsLinkageUnitScope) {
+  if (IsLinkageUnitScope) {
+    if (checkAttrMutualExclusion<GlobalScopeAttr>(
+            *this, D, Range, "'linkage_unit_scope'"))
+      return nullptr;
+    return ::new (Context) LinkageUnitScopeAttr(Range, Context, SpellingIndex);
+  } else {
+    if (checkAttrMutualExclusion<LinkageUnitScopeAttr>(
+            *this, D, Range, "'global_scope'"))
+      return nullptr;
+    return ::new (Context)
+        GlobalScopeAttr(Range, Context, SpellingIndex);
+  }
+}
+
+static void handleClassScopeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (InheritableAttr *A = S.mergeClassScopeAttr(
+          D, Attr.getRange(), Attr.getAttributeSpellingListIndex(),
+          Attr.getKind() == AttributeList::AT_LinkageUnitScope))
+    D->addAttr(A);
+}
+
 /// Handles semantic checking for features that are common to all attributes,
 /// such as checking whether a parameter was properly specified, or the correct
 /// number of arguments were passed, etc.
@@ -5709,6 +5733,10 @@
   case AttributeList::AT_InternalLinkage:
     handleInternalLinkageAttr(S, D, Attr);
     break;
+  case AttributeList::AT_LinkageUnitScope:
+  case AttributeList::AT_GlobalScope:
+    handleClassScopeAttr(S, D, Attr);
+    break;
 
   // Microsoft attributes:
   case AttributeList::AT_MSNoVTable:
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -2192,7 +2192,12 @@
                                Sema::AvailabilityMergeKind AMK) {
   InheritableAttr *NewAttr = nullptr;
   unsigned AttrSpellingListIndex = Attr->getSpellingListIndex();
-  if (const auto *AA = dyn_cast<AvailabilityAttr>(Attr))
+  if (isa<LinkageUnitScopeAttr>(Attr) || isa<GlobalScopeAttr>(Attr))
+    NewAttr = S.mergeClassScopeAttr(D, Attr->getRange(), AttrSpellingListIndex,
+                                    isa<LinkageUnitScopeAttr>(Attr));
+  else if (isa<NamespaceDecl>(D))
+    return false;
+  else if (const auto *AA = dyn_cast<AvailabilityAttr>(Attr))
     NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(),
                                       AA->getIntroduced(), AA->getDeprecated(),
                                       AA->getObsoleted(), AA->getUnavailable(),
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -482,8 +482,21 @@
   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);
+  if (Arg *A = Args.getLastArg(OPT_fdefault_class_scope_EQ)) {
+    unsigned Val =
+        llvm::StringSwitch<unsigned>(A->getValue())
+            .Case("global", unsigned(CodeGenOptions::ClassScopeGlobal))
+            .Case("attrs", unsigned(CodeGenOptions::ClassScopeAttrs))
+            .Case("linkage-unit",
+                  unsigned(CodeGenOptions::ClassScopeLinkageUnit))
+            .Default(~0U);
+    if (Val == ~0U)
+      Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args)
+                                                << A->getValue();
+    else
+      Opts.setDefaultClassScope(
+          static_cast<CodeGenOptions::ClassScopeKind>(Val));
+  }
   Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file);
   Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs);
   Opts.DebugExplicitImport = Triple.isPS4CPU(); 
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -4390,32 +4390,6 @@
     CmdArgs.push_back("-ffunction-sections");
   }
 
-  if (Args.hasFlag(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");
@@ -5751,6 +5725,18 @@
       CmdArgs.push_back(I->getFilename());
     }
 
+  bool WholeProgramVTables =
+      Args.hasFlag(options::OPT_fwhole_program_vtables,
+                   options::OPT_fno_whole_program_vtables, false);
+  if (WholeProgramVTables) {
+    if (!D.isUsingLTO())
+      D.Diag(diag::err_drv_argument_only_allowed_with)
+          << "-fwhole-program-vtables"
+          << "-flto";
+    CmdArgs.push_back("-fwhole-program-vtables");
+  }
+  Args.AddLastArg(CmdArgs, options::OPT_fdefault_class_scope_EQ);
+
   // Finally add the compile command to the compilation.
   if (Args.hasArg(options::OPT__SLASH_fallback) &&
       Output.getType() == types::TY_Object &&
Index: lib/Driver/SanitizerArgs.cpp
===================================================================
--- lib/Driver/SanitizerArgs.cpp
+++ lib/Driver/SanitizerArgs.cpp
@@ -39,6 +39,7 @@
   TrappingSupported =
       (Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds | CFI,
   TrappingDefault = CFI,
+  CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast,
 };
 
 enum CoverageFeature {
@@ -674,6 +675,9 @@
                                          TC.getCompilerRT(Args, "stats")));
     addIncludeLinkerOption(TC, Args, CmdArgs, "__sanitizer_stats_register");
   }
+
+  if (Sanitizers.hasOneOf(CFIClasses))
+    CmdArgs.push_back(Args.MakeArgString("-fdefault-class-scope=linkage-unit"));
 }
 
 SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1519,15 +1519,13 @@
           : CharUnits::Zero();
 
   if (Info->PathToBaseWithVPtr.empty()) {
-    if (!CGM.IsBitSetBlacklistedRecord(RD))
-      CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD);
+    CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD);
     return;
   }
 
   // Add a bitset entry for the least derived base belonging to this vftable.
-  if (!CGM.IsBitSetBlacklistedRecord(Info->PathToBaseWithVPtr.back()))
-    CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint,
-                                Info->PathToBaseWithVPtr.back());
+  CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint,
+                              Info->PathToBaseWithVPtr.back());
 
   // Add a bitset entry for each derived class that is laid out at the same
   // offset as the least derived base.
@@ -1545,12 +1543,11 @@
       Offset = VBI->second.VBaseOffset;
     if (!Offset.isZero())
       return;
-    if (!CGM.IsBitSetBlacklistedRecord(DerivedRD))
-      CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, DerivedRD);
+    CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, DerivedRD);
   }
 
   // Finally do the same for the most derived class.
-  if (Info->FullOffsetInMDC.isZero() && !CGM.IsBitSetBlacklistedRecord(RD))
+  if (Info->FullOffsetInMDC.isZero())
     CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD);
 }
 
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -490,8 +490,6 @@
   /// MDNodes.
   llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap;
 
-  SanitizerBlacklist WholeProgramVTablesBlacklist;
-
 public:
   CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts,
                 const PreprocessorOptions &ppopts,
@@ -1118,9 +1116,9 @@
   /// 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);
+  /// Returns whether the given record has linkage-unit scope and therefore may
+  /// participate in (single-module) CFI and whole-program vtable optimization.
+  bool HasLinkageUnitScope(const CXXRecordDecl *RD);
 
   /// Emit bit set entries for the given vtable using the given layout if
   /// vptr CFI is enabled.
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -97,9 +97,7 @@
       NSConcreteStackBlock(nullptr), BlockObjectAssign(nullptr),
       BlockObjectDispose(nullptr), BlockDescriptorType(nullptr),
       GenericBlockLiteralType(nullptr), LifetimeStartFn(nullptr),
-      LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)),
-      WholeProgramVTablesBlacklist(CGO.WholeProgramVTablesBlacklistFiles,
-                                   C.getSourceManager()) {
+      LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)) {
 
   // Initialize the type cache.
   llvm::LLVMContext &LLVMContext = M.getContext();
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -910,21 +910,36 @@
          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;
+bool CodeGenModule::HasLinkageUnitScope(const CXXRecordDecl *RD) {
+  LinkageInfo LV = RD->getLinkageAndVisibility();
+  if (!isExternallyVisible(LV.getLinkage()))
+    return true;
 
-    return BL.isBlacklistedType(TypeName);
-  };
+  // These attributes override our checks below.
+  const DeclContext *DC = RD;
+  while (DC) {
+    const Decl *D = cast<Decl>(DC);
+    if (D->hasAttr<LinkageUnitScopeAttr>()) {
+      return true;
+    } else if (D->hasAttr<GlobalScopeAttr>() || D->hasAttr<UuidAttr>()) {
+      return false;
+    }
+    DC = DC->getParent();
+  }
 
-  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()));
+  switch (getCodeGenOpts().getDefaultClassScope()) {
+  case CodeGenOptions::ClassScopeGlobal:
+    return false;
+  case CodeGenOptions::ClassScopeLinkageUnit:
+    return true;
+  case CodeGenOptions::ClassScopeAttrs:
+    // Use object format-specific attributes to see if the record is visible
+    // outside the linkage unit.
+    if (getTriple().isOSBinFormatCOFF())
+      return !RD->hasAttr<DLLExportAttr>() && !RD->hasAttr<DLLImportAttr>();
+    else
+      return LV.getVisibility() == HiddenVisibility;
+  }
 }
 
 void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
@@ -938,12 +953,8 @@
   typedef std::pair<const CXXRecordDecl *, unsigned> BSEntry;
   std::vector<BSEntry> BitsetEntries;
   // Create a bit set entry for each address point.
-  for (auto &&AP : VTLayout.getAddressPoints()) {
-    if (IsBitSetBlacklistedRecord(AP.first.getBase()))
-      continue;
-
+  for (auto &&AP : VTLayout.getAddressPoints())
     BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second));
-  }
 
   // Sort the bit set entries for determinism.
   std::sort(BitsetEntries.begin(), BitsetEntries.end(),
Index: lib/CodeGen/CGClass.cpp
===================================================================
--- lib/CodeGen/CGClass.cpp
+++ lib/CodeGen/CGClass.cpp
@@ -2489,7 +2489,7 @@
                                              llvm::Value *VTable,
                                              SourceLocation Loc) {
   if (CGM.getCodeGenOpts().WholeProgramVTables &&
-      !CGM.IsBitSetBlacklistedRecord(RD)) {
+      CGM.HasLinkageUnitScope(RD)) {
     llvm::Metadata *MD =
         CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
     llvm::Value *BitSetName =
@@ -2565,7 +2565,12 @@
                                          llvm::Value *VTable,
                                          CFITypeCheckKind TCK,
                                          SourceLocation Loc) {
-  if (CGM.IsBitSetBlacklistedRecord(RD))
+  if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso &&
+      !CGM.HasLinkageUnitScope(RD))
+    return;
+
+  std::string TypeName = RD->getQualifiedNameAsString();
+  if (getContext().getSanitizerBlacklist().isBlacklistedType(TypeName))
     return;
 
   SanitizerScope SanScope(this);
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2145,6 +2145,9 @@
                                                 unsigned AttrSpellingListIndex);
   CommonAttr *mergeCommonAttr(Decl *D, SourceRange Range, IdentifierInfo *Ident,
                               unsigned AttrSpellingListIndex);
+  InheritableAttr *mergeClassScopeAttr(Decl *D, SourceRange Range,
+                                       int SpellingIndex,
+                                       bool IsLinkageUnitScope);
 
   void mergeDeclAttributes(NamedDecl *New, Decl *Old,
                            AvailabilityMergeKind AMK = AMK_Redeclaration);
Index: include/clang/Sema/AttributeList.h
===================================================================
--- include/clang/Sema/AttributeList.h
+++ include/clang/Sema/AttributeList.h
@@ -904,7 +904,8 @@
   ExpectedVariableEnumFieldOrTypedef,
   ExpectedFunctionMethodEnumOrClass,
   ExpectedStructClassVariableFunctionOrInlineNamespace,
-  ExpectedForMaybeUnused
+  ExpectedForMaybeUnused,
+  ExpectedRecordOrNamespace,
 };
 
 }  // end namespace clang
Index: include/clang/Frontend/CodeGenOptions.h
===================================================================
--- include/clang/Frontend/CodeGenOptions.h
+++ include/clang/Frontend/CodeGenOptions.h
@@ -86,6 +86,13 @@
     ProfileIRInstr,    // IR level PGO instrumentation in LLVM.
   };
 
+  enum ClassScopeKind {
+    ClassScopeGlobal,      // Infer global scope for all classes.
+    ClassScopeAttrs,       // Infer scope from visibility/dllimport/dllexport
+                           // attributes.
+    ClassScopeLinkageUnit, // Infer linkage unit scope for all classes.
+  };
+
   /// The code model to use (-mcmodel).
   std::string CodeModel;
 
@@ -202,9 +209,6 @@
   /// \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)
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -184,6 +184,9 @@
 
 CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
                                       ///  vtable optimization.
+ENUM_CODEGENOPT(DefaultClassScope, ClassScopeKind, 2,
+                ClassScopeAttrs) ///< How to derive a class's scope for
+                                 ///  vtable opt and CFI.
 
 /// The user specified number of registers to be used for integral arguments,
 /// or 0 if unspecified.
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -1134,9 +1134,9 @@
   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 fdefault_class_scope_EQ : Joined<["-"], "fdefault-class-scope=">, Group<f_Group>,
+  Flags<[CC1Option]>,
+  HelpText<"Default class scope for vtable opt and CFI">;
 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]>,
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2481,7 +2481,8 @@
   "interface or protocol declarations|kernel functions|non-K&R-style functions|"
   "variables, enums, fields and typedefs|functions, methods, enums, and classes|"
   "structs, classes, variables, functions, and inline namespaces|"
-  "variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members}1">,
+  "variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members|"
+  "struct, union, class or namespace}1">,
   InGroup<IgnoredAttributes>;
 def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Text>;
 def warn_type_attribute_wrong_type : Warning<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -2323,3 +2323,10 @@
 string argument which is the message to display when emitting the warning.
   }];
 }
+
+def ClassScopeDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+See :ref:`Overriding Class Scope Inferences with Attributes <class-scope-attrs>`.
+  }];
+}
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1589,6 +1589,18 @@
   let Documentation = [Undocumented];
 }
 
+def LinkageUnitScope : InheritableAttr {
+  let Spellings = [CXX11<"clang", "linkage_unit_scope">];
+  let Subjects = SubjectList<[Record, Namespace]>;
+  let Documentation = [ClassScopeDocs];
+}
+
+def GlobalScope : InheritableAttr {
+  let Spellings = [CXX11<"clang", "global_scope">];
+  let Subjects = SubjectList<[Record, Namespace]>;
+  let Documentation = [ClassScopeDocs];
+}
+
 def AnyX86Interrupt : InheritableAttr, TargetSpecificAttr<TargetAnyX86> {
   // NOTE: If you add any additional spellings, ARMInterrupt's,
   // MSP430Interrupt's and MipsInterrupt's spellings must match.
Index: docs/index.rst
===================================================================
--- docs/index.rst
+++ docs/index.rst
@@ -31,6 +31,7 @@
    SanitizerStats
    SanitizerSpecialCaseList
    ControlFlowIntegrity
+   ClassScope
    SafeStack
    Modules
    MSVCCompatibility
Index: docs/UsersManual.rst
===================================================================
--- docs/UsersManual.rst
+++ docs/UsersManual.rst
@@ -1056,17 +1056,16 @@
 .. option:: -fwhole-program-vtables
 
    Enable whole-program vtable optimizations, such as single-implementation
-   devirtualization and virtual constant propagation. Requires ``-flto``.
+   devirtualization and virtual constant propagation, for classes with internal
+   linkage or :doc:`linkage-unit scope <ClassScope>`. 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.
+.. _opt_fdefault-class-scope:
 
-.. option:: -fwhole-program-vtables-blacklist=path
+.. option:: -fdefault-class-scope=VAL
 
-   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.
+   This flag controls how the compiler infers class scope for class
+   declarations when using the flags ``-fwhole-program-vtables`` or
+   ``-fsanitize=cfi*``. See :doc:`ClassScope` for more details.
 
 .. option:: -fno-assume-sane-operator-new
 
Index: docs/ControlFlowIntegrity.rst
===================================================================
--- docs/ControlFlowIntegrity.rst
+++ docs/ControlFlowIntegrity.rst
@@ -25,13 +25,26 @@
 so it is required to specify ``-flto``, and the linker used must support LTO,
 for example via the `gold plugin`_.
 
-To allow the checks to be implemented efficiently, the program must be
-structured such that certain object files are compiled with CFI
+To allow the checks to be implemented efficiently, the program must
+be structured such that certain object files are compiled with CFI
 enabled, and are statically linked into the program. This may preclude
-the use of shared libraries in some cases. Experimental support for
-:ref:`cross-DSO control flow integrity <cfi-cross-dso>` exists that
-does not have these requirements. This cross-DSO support has unstable
-ABI at this time.
+the use of shared libraries in some cases.
+
+By default, the compiler will only produce CFI checks for a class if it can
+infer linkage-unit scope for that class. Linkage-unit scope is a property
+of a class that is inferred from flags and attributes. For more details,
+see the documentation for :doc:`class scope <ClassScope>`.
+
+If your program uses a mixture of unexported classes and exported classes
+marked up with visibility or dllexport attributes, it is recommended to
+use the flags ``-fdefault-class-scope=attrs``, and on non-Windows platforms
+``-fvisibility=hidden -fvisibility-inlines-hidden`` when enabling CFI. This
+will cause the compiler to enable CFI by default for any classes declared in
+your program, except for those that need to be exported to other linkage units.
+
+Experimental support for :ref:`cross-DSO control flow integrity
+<cfi-cross-dso>` exists that does not require classes to have linkage-unit
+scope. This cross-DSO support has unstable ABI at this time.
 
 .. _gold plugin: http://llvm.org/docs/GoldPlugin.html
 
@@ -233,11 +246,6 @@
 source files, functions and types using the ``src``, ``fun`` and ``type``
 entity types.
 
-In addition, if a type has a ``uuid`` attribute and the blacklist contains
-the type entry ``attr:uuid``, CFI checks are suppressed for that type. This
-allows all COM types to be easily blacklisted, which is useful as COM types
-are typically defined outside of the linked program.
-
 .. code-block:: bash
 
     # Suppress checking for code in a file.
@@ -247,8 +255,6 @@
     fun:*MyFooBar*
     # Ignore all types in the standard library.
     type:std::*
-    # Ignore all types with a uuid attribute.
-    type:attr:uuid
 
 .. _cfi-cross-dso:
 
@@ -260,6 +266,11 @@
 apply across DSO boundaries. As in the regular CFI, each DSO must be
 built with ``-flto``.
 
+Normally, the compiler will disable CFI checks for classes that do not have
+linkage-unit scope. With this flag enabled, the compiler will emit cross-DSO
+CFI checks for all classes, except for those which appear in the CFI blacklist
+or which use a ``no_sanitize`` attribute.
+
 Design
 ======
 
Index: docs/ClassScope.rst
===================================================================
--- /dev/null
+++ docs/ClassScope.rst
@@ -0,0 +1,59 @@
+===========
+Class Scope
+===========
+
+Class scope is a concept used by the compiler to determine which classes
+the virtual function call optimization and control flow integrity features
+apply to. These features use whole-program information, so they require
+visibility of entire class hierarchies to work correctly.
+
+A class with external linkage has one of two scopes: linkage-unit scope or
+global scope. Linkage-unit scope means that the scope of the class is limited
+to a single linkage unit (i.e. the executable or DSO). It is effectively an
+ODR violation to declare classes with linkage-unit scope in multiple linkage
+units.  Classes with global scope may be defined in multiple linkage units,
+but the tradeoff is that the virtual function call optimization and control
+flow integrity features can only be applied to classes with internal linkage
+(e.g. classes declared in unnamed namespaces) or classes with linkage-unit
+scope.
+
+The compiler infers class scope based on the value of the
+``-fdefault-class-scope`` flag.
+
+  -  ``-fdefault-class-scope=attrs`` indicates that the compiler will infer
+     class scope based on platform-specific attributes that control the class's
+     visibility outside the linkage unit. When targeting non-Windows platforms,
+     classes with hidden visibility (``__attribute__((visibility("hidden")))``
+     or the ``-fvisibility=hidden -fvisibility-inlines-hidden`` flags)
+     receive global scope, and all others receive linkage-unit scope. When
+     targeting Windows, classes with the ``__declspec(dllexport)`` or
+     ``__declspec(dllimport)`` attributes receive linkage-unit scope, and
+     all others receive global scope.
+
+  -  ``-fdefault-class-scope=global`` indicates default inference of global
+     scope.
+
+  -  ``-fdefault-class-scope=linkage-unit`` indicates default inference of
+     linkage-unit scope.
+
+The default behavior is ``-fdefault-class-scope=attrs``,
+or ``-fdefault-class-scope=linkage-unit`` if one of
+``-fsanitize=cfi-{vcall,nvcall,derived-cast,unrelated-cast}`` is enabled.
+This is to ensure that control flow integrity checks are enabled for classes
+by default.
+
+.. _class-scope-attrs:
+
+Overriding Class Scope Inferences with Attributes
+=================================================
+
+The ``[[clang::linkage_unit_scope]]`` and ``[[clang::global_scope]]``
+attributes override Clang's inferences of class scope on a per-class basis.
+
+If this attribute is applied to a namespace or class declaration, it will
+also apply to all classes with external linkage defined in that namespace
+or class, including nested classes.
+
+The ``__declspec(uuid())`` attribute implies ``[[clang::global_scope]]``,
+as this attribute is normally used on Windows for COM classes which are
+defined in a separate linkage unit.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to