]> granicus.if.org Git - clang/commitdiff
Add -fforce-emit-vtables
authorPiotr Padlewski <piotr.padlewski@gmail.com>
Wed, 13 Jun 2018 13:55:42 +0000 (13:55 +0000)
committerPiotr Padlewski <piotr.padlewski@gmail.com>
Wed, 13 Jun 2018 13:55:42 +0000 (13:55 +0000)
Summary:
 In many cases we can't devirtualize
 because definition of vtable is not present. Most of the
 time it is caused by inline virtual function not beeing
 emitted. Forcing emitting of vtable adds a reference of these
 inline virtual functions.
 Note that GCC was always doing it.

Reviewers: rjmccall, rsmith, amharc, kuhar

Subscribers: llvm-commits, cfe-commits

Differential Revision: https://reviews.llvm.org/D47108

Co-authored-by: Krzysztof Pszeniczny <krzysztof.pszeniczny@gmail.com>
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@334600 91177308-0d34-0410-b5e6-96231b3b80d8

docs/ClangCommandLineReference.rst
docs/ReleaseNotes.rst
docs/UsersManual.rst
include/clang/Basic/LangOptions.def
include/clang/Driver/Options.td
include/clang/Frontend/CodeGenOptions.def
lib/CodeGen/ItaniumCXXABI.cpp
lib/Driver/ToolChains/Clang.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Sema/SemaDeclCXX.cpp
test/CodeGenCXX/vtable-available-externally.cpp

index 264729dd0f141548472bf5b6f504c1c011a373bb..513128119e25fde3e979c357b49d31c02c526416 100644 (file)
@@ -1934,6 +1934,12 @@ Set the default symbol visibility for all global declarations
 
 Enables whole-program vtable optimization. Requires -flto
 
+.. option:: -fforce-emit-vtables, -fno-force-emit-vtables
+
+In order to improve devirtualization, forces emitting of vtables even in
+modules where it isn't necessary. It causes more inline virtual functions
+to be emitted.
+
 .. option:: -fwrapv, -fno-wrapv
 
 Treat signed integer overflow as two's complement
index 84145554647327edfa5086473794cfebb1349450..15ae73e859e753198bac6e3b60102856c7e2dac8 100644 (file)
@@ -112,6 +112,12 @@ New Compiler Flags
   'no-strict' option, Clang attempts to match the overflowing behavior of the
   target's native float-to-int conversion instructions.
 
+- :option: `-fforce-emit-vtables` and `-fno-force-emit-vtables`.
+
+   In order to improve devirtualization, forces emitting of vtables even in
+   modules where it isn't necessary. It causes more inline virtual functions
+   to be emitted.
+
 - ...
 
 Deprecated Compiler Flags
index 9778b1654de9e285e3566683320b7468fcd73c65..b1c286193cf96c5705148522d9db85d16d736adc 100644 (file)
@@ -1269,6 +1269,12 @@ are listed below.
    devirtualization and virtual constant propagation, for classes with
    :doc:`hidden LTO visibility <LTOVisibility>`. Requires ``-flto``.
 
+.. option:: -fforce-emit-vtables
+
+   In order to improve devirtualization, forces emitting of vtables even in
+   modules where it isn't necessary. It causes more inline virtual functions
+   to be emitted.
+
 .. option:: -fno-assume-sane-operator-new
 
    Don't assume that the C++'s new operator is sane.
index 6bd8958e36db41bf4b5c5359cce814f884cb047c..f9246bd8935b2de9d43333343c9de97412604278 100644 (file)
@@ -293,6 +293,8 @@ LANGOPT(XRayAlwaysEmitTypedEvents, 1, 0,
         "controls whether to always emit intrinsic calls to "
         "__xray_typedevent(...) builtin.")
 
+LANGOPT(ForceEmitVTables, 1, 0, "whether to emit all vtables")
+
 BENIGN_LANGOPT(AllowEditorPlaceholders, 1, 0,
                "allow editor placeholders in source")
 
index 4c04d33466d8b66608a9df7d8870c6102088c17c..cd02a5ab5bd6654bb148dc1b6e44442d93e99380 100644 (file)
@@ -1688,6 +1688,11 @@ def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group<f_Grou
   HelpText<"Enables whole-program vtable optimization. Requires -flto">;
 def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group<f_Group>,
   Flags<[CoreOption]>;
+def fforce_emit_vtables : Flag<["-"], "fforce-emit-vtables">, Group<f_Group>,
+    Flags<[CC1Option]>,
+    HelpText<"Emits more virtual tables to improve devirtualization">;
+def fno_force_emit_vtables : Flag<["-"], "fno-force-emit-vtables">, Group<f_Group>,
+  Flags<[CoreOption]>;
 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 c2dd7e4358834a53fdbb03e06d147af165744870..019425d1946afba4f13c284d4d3e196337933526 100644 (file)
@@ -332,6 +332,10 @@ CODEGENOPT(NoPLT, 1, 0)
 /// Whether to embed source in DWARF debug line section.
 CODEGENOPT(EmbedSource, 1, 0)
 
+/// Whether to emit all vtables
+CODEGENOPT(ForceEmitVTables, 1, 0)
+
+
 #undef CODEGENOPT
 #undef ENUM_CODEGENOPT
 #undef VALUE_CODEGENOPT
index d84a10c6ba23c96af6f4e39ba45432a3acd21ea0..6ac7d29752d835e5478b9688effacdaf988c6aa0 100644 (file)
@@ -1707,11 +1707,19 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const {
   if (CGM.getLangOpts().AppleKext)
     return false;
 
-  // If we don't have any not emitted inline virtual function, and if vtable is
-  // not hidden, then we are safe to emit available_externally copy of vtable.
+  // If the vtable is hidden then it is not safe to emit an available_externally
+  // copy of vtable.
+  if (isVTableHidden(RD))
+    return false;
+
+  if (CGM.getCodeGenOpts().ForceEmitVTables)
+    return true;
+
+  // If we don't have any not emitted inline virtual function then we are safe
+  // to emit an available_externally copy of vtable.
   // FIXME we can still emit a copy of the vtable if we
   // can emit definition of the inline functions.
-  return !hasAnyUnusedVirtualInlineFunction(RD) && !isVTableHidden(RD);
+  return !hasAnyUnusedVirtualInlineFunction(RD);
 }
 static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
                                           Address InitialPtr,
index 3eee463bb131b26d206c5cc8c6819d926459ecb6..d392f68897ca4b23430b5a622799fa53e7059e2a 100644 (file)
@@ -3472,6 +3472,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                    options::OPT_fno_strict_vtable_pointers,
                    false))
     CmdArgs.push_back("-fstrict-vtable-pointers");
+  if (Args.hasFlag(options::OPT_fforce_emit_vtables,
+                   options::OPT_fno_force_emit_vtables,
+                   false))
+    CmdArgs.push_back("-fforce-emit-vtables");
   if (!Args.hasFlag(options::OPT_foptimize_sibling_calls,
                     options::OPT_fno_optimize_sibling_calls))
     CmdArgs.push_back("-mdisable-tail-calls");
index a959cdb2347ad3dac3f50041c4502524972807a2..2aa74c38e2bac309aabaa121d483b3b134872419 100644 (file)
@@ -719,6 +719,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
   Opts.StrictEnums = Args.hasArg(OPT_fstrict_enums);
   Opts.StrictReturn = !Args.hasArg(OPT_fno_strict_return);
   Opts.StrictVTablePointers = Args.hasArg(OPT_fstrict_vtable_pointers);
+  Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables);
   Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) ||
                       Args.hasArg(OPT_cl_unsafe_math_optimizations) ||
                       Args.hasArg(OPT_cl_fast_relaxed_math);
@@ -2735,6 +2736,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
       Args.getAllArgValues(OPT_fxray_never_instrument);
   Opts.XRayAttrListFiles = Args.getAllArgValues(OPT_fxray_attr_list);
 
+  // -fforce-emit-vtables
+  Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables);
+
   // -fallow-editor-placeholders
   Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders);
 
index 55517fc3f66ddcc5eacc4328cc14faaf3080eaa4..f5e48210924d5e7de398994aff1eb333775b1359 100644 (file)
@@ -6125,6 +6125,12 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
     Record->setParamDestroyedInCallee(true);
   else if (Record->hasNonTrivialDestructor())
     Record->setParamDestroyedInCallee(CanPass);
+
+  if (getLangOpts().ForceEmitVTables) {
+    // If we want to emit all the vtables, we need to mark it as used.  This
+    // is especially required for cases like vtable assumption loads.
+    MarkVTableUsed(Record->getInnerLocStart(), Record);
+  }
 }
 
 /// Look up the special member function that would be called by a special
index 0e7e8b4a3226be9f3f8a60a5f23b14167bdb7481..b52527d36c6b4f202d19eed3e16c538e7a5d9e4b 100644 (file)
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -emit-llvm -o %t
 // RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -O2 -disable-llvm-passes -emit-llvm -o %t.opt
+// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -O2 -disable-llvm-passes -emit-llvm -o %t.vtable -fforce-emit-vtables -fstrict-vtable-pointers -mconstructor-aliases
 // RUN: FileCheck --check-prefix=CHECK-TEST1 %s < %t
 // RUN: FileCheck --check-prefix=CHECK-TEST2 %s < %t
 // RUN: FileCheck --check-prefix=CHECK-TEST5 %s < %t
 // RUN: FileCheck --check-prefix=CHECK-TEST15 %s < %t.opt
 // RUN: FileCheck --check-prefix=CHECK-TEST16 %s < %t.opt
 // RUN: FileCheck --check-prefix=CHECK-TEST17 %s < %t.opt
+// RUN: FileCheck --check-prefix=CHECK-FORCE-EMIT %s < %t.vtable
+
 
 #include <typeinfo>
 
 // CHECK-TEST1: @_ZTVN5Test11AE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN5Test11AE = available_externally unnamed_addr constant
 namespace Test1 {
 
 struct A {
@@ -213,6 +217,7 @@ namespace Test10 {
 
 // because A's key function is defined here, vtable is generated in this TU
 // CHECK-TEST10-DAG: @_ZTVN6Test101AE = unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101AE = unnamed_addr constant
 struct A {
   virtual void foo();
   virtual void bar();
@@ -221,6 +226,7 @@ void A::foo() {}
 
 // Because key function is inline we will generate vtable as linkonce_odr.
 // CHECK-TEST10-DAG: @_ZTVN6Test101DE = linkonce_odr unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101DE = linkonce_odr unnamed_addr constant
 struct D : A {
   void bar();
 };
@@ -237,6 +243,7 @@ struct B : A {
 // can't guarantee that we will be able to refer to bar from name
 // so (at the moment) we can't emit vtable available_externally.
 // CHECK-TEST10-DAG: @_ZTVN6Test101CE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101CE = available_externally unnamed_addr constant
 struct C : A {
   void bar() {}               // defined in body - not key function
   virtual inline void gar();  // inline in body - not key function
@@ -245,6 +252,8 @@ struct C : A {
 
 // no key function, vtable will be generated everywhere it will be used
 // CHECK-TEST10-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
+
 struct E : A {};
 
 void g(A& a) {
@@ -298,11 +307,13 @@ void g() {
 namespace Test12 {
 
 // CHECK-TEST12: @_ZTVN6Test121AE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test121AE = available_externally unnamed_addr constant
 struct A {
   virtual void foo();
   virtual ~A() {}
 };
 // CHECK-TEST12: @_ZTVN6Test121BE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test121BE = available_externally unnamed_addr constant
 struct B : A {
   void foo();
 };
@@ -319,6 +330,9 @@ namespace Test13 {
 
 // CHECK-TEST13-DAG: @_ZTVN6Test131AE = available_externally unnamed_addr constant
 // CHECK-TEST13-DAG: @_ZTVN6Test131BE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test131AE = available_externally unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test131BE = available_externally unnamed_addr constant
+
 struct A {
   virtual ~A();
 };
@@ -371,6 +385,8 @@ namespace Test16 {
 // generate available_externally vtable for it.
 // CHECK-TEST16-DAG: @_ZTVN6Test161SE = external unnamed_addr constant
 // CHECK-TEST16-DAG: @_ZTVN6Test162S2E = available_externally
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test161SE = external unnamed_addr constant
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test162S2E = available_externally
 
 struct S {
   __attribute__((visibility("hidden"))) virtual void doStuff();
@@ -395,6 +411,10 @@ namespace Test17 {
 // This test checks if we emit vtables opportunistically.
 // CHECK-TEST17-DAG: @_ZTVN6Test171AE = available_externally
 // CHECK-TEST17-DAG: @_ZTVN6Test171BE = external
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test171AE = available_externally
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test171BE = available_externally
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test171BD2Ev(
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test171BD0Ev(
 
 struct A {
   virtual void key();
@@ -418,3 +438,111 @@ void testcaseB() {
 }
 
 } // namespace Test17
+
+namespace Test18 {
+// Here vtable will be only emitted because it is referenced by assume-load
+// after the Derived construction.
+// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test187DerivedE = linkonce_odr unnamed_addr constant {{.*}} @_ZTIN6Test187DerivedE {{.*}} @_ZN6Test184Base3funEv {{.*}} @_ZN6Test184BaseD2Ev {{.*}} @_ZN6Test187DerivedD0Ev
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test187DerivedD0Ev
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test184BaseD2Ev
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN6Test184Base3funEv
+// CHECK-FORCE-EMIT-DAG: @_ZTIN6Test187DerivedE = linkonce_odr constant
+
+struct Base {
+  virtual int fun() { return 42; }
+  virtual ~Base() { }
+};
+
+struct Derived : Base {
+  Derived();
+};
+
+int foo() {
+  Derived *der = new Derived();
+  return der->fun();
+}
+}
+
+namespace TestTemplates {
+
+// CHECK-FORCE-EMIT-DAG: @_ZTVN13TestTemplates8TemplateIiEE = linkonce_odr unnamed_addr constant {{.*}} @_ZTIN13TestTemplates8TemplateIiEE {{.*}} @_ZN13TestTemplates8TemplateIiE3fooEi {{.*}}@_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi {{.*}}@_ZN13TestTemplates8TemplateIiED1Ev {{.*}}@_ZN13TestTemplates8TemplateIiED0Ev
+// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi
+
+template<class T>
+struct Template {
+  Template();
+  virtual T foo(T val);
+  // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi
+  virtual T thisShouldBeEmittedToo(T val) { return val; }
+  virtual ~Template();
+};
+
+
+struct NonTemplate {
+  typedef int T;
+  NonTemplate();
+  virtual T foo(T val);
+  // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates11NonTemplate22thisShouldBeEmittedTooEi
+  virtual T thisShouldBeEmittedToo(T val) { return val; }
+  virtual ~NonTemplate();
+};
+
+// CHECK-FORCE-EMIT-DAG: @_ZTVN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiEE = linkonce_odr {{.*}} @_ZTIN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiEE {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE3fooEi {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE22thisShouldBeEmittedTooEi {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiED1Ev {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiED0Ev
+
+struct OuterNonTemplate {
+  template<class T>
+  struct NestedTemplateInNonTemplate {
+    NestedTemplateInNonTemplate();
+    virtual T foo(T val);
+    // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE22thisShouldBeEmittedTooEi
+    virtual T thisShouldBeEmittedToo(T val) { return val; }
+    virtual ~NestedTemplateInNonTemplate();
+  };
+
+  struct NestedNonTemplateInNonTemplate {
+    typedef int T;
+    NestedNonTemplateInNonTemplate();
+    virtual T foo(T val);
+    // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates16OuterNonTemplate30NestedNonTemplateInNonTemplate22thisShouldBeEmittedTooEi
+    virtual T thisShouldBeEmittedToo(T val) { return val; }
+    virtual ~NestedNonTemplateInNonTemplate();
+  };
+};
+
+template<class>
+struct OuterTemplate {
+  template<class T>
+  struct NestedTemplateInTemplate {
+    NestedTemplateInTemplate();
+    virtual T foo(T val);
+    // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates13OuterTemplateIlE24NestedTemplateInTemplateIiE22thisShouldBeEmittedTooEi
+    virtual T thisShouldBeEmittedToo(T val) { return val; }
+    virtual ~NestedTemplateInTemplate();
+  };
+
+  struct NestedNonTemplateInTemplate {
+    typedef int T;
+    NestedNonTemplateInTemplate();
+    virtual T foo(T val);
+    // CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates13OuterTemplateIlE27NestedNonTemplateInTemplate22thisShouldBeEmittedTooEi
+    virtual T thisShouldBeEmittedToo(T val) { return val; }
+    virtual ~NestedNonTemplateInTemplate();
+  };
+};
+
+template<class T>
+int use() {
+  T *ptr = new T();
+  return ptr->foo(42);
+}
+
+void test() {
+  use<Template<int> >();
+  use<OuterTemplate<long>::NestedTemplateInTemplate<int> >();
+  use<OuterNonTemplate::NestedTemplateInNonTemplate<int> >();
+
+  use<NonTemplate>();
+  use<OuterTemplate<long>::NestedNonTemplateInTemplate>();
+  use<OuterNonTemplate::NestedNonTemplateInNonTemplate>();
+}
+}