From: Piotr Padlewski Date: Wed, 13 Jun 2018 13:55:42 +0000 (+0000) Subject: Add -fforce-emit-vtables X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1452597f76a7028d9782dc8cff4d08fc5eb3c945;p=clang Add -fforce-emit-vtables 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@334600 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/ClangCommandLineReference.rst b/docs/ClangCommandLineReference.rst index 264729dd0f..513128119e 100644 --- a/docs/ClangCommandLineReference.rst +++ b/docs/ClangCommandLineReference.rst @@ -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 diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 8414555464..15ae73e859 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -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 diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index 9778b1654d..b1c286193c 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -1269,6 +1269,12 @@ are listed below. devirtualization and virtual constant propagation, for classes with :doc:`hidden LTO visibility `. 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. diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 6bd8958e36..f9246bd893 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -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") diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 4c04d33466..cd02a5ab5b 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1688,6 +1688,11 @@ def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group; def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group, Flags<[CoreOption]>; +def fforce_emit_vtables : Flag<["-"], "fforce-emit-vtables">, Group, + Flags<[CC1Option]>, + HelpText<"Emits more virtual tables to improve devirtualization">; +def fno_force_emit_vtables : Flag<["-"], "fno-force-emit-vtables">, Group, + Flags<[CoreOption]>; def fwrapv : Flag<["-"], "fwrapv">, Group, Flags<[CC1Option]>, HelpText<"Treat signed integer overflow as two's complement">; def fwritable_strings : Flag<["-"], "fwritable-strings">, Group, Flags<[CC1Option]>, diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index c2dd7e4358..019425d194 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -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 diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index d84a10c6ba..6ac7d29752 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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, diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 3eee463bb1..d392f68897 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -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"); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a959cdb234..2aa74c38e2 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 55517fc3f6..f5e4821092 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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 diff --git a/test/CodeGenCXX/vtable-available-externally.cpp b/test/CodeGenCXX/vtable-available-externally.cpp index 0e7e8b4a32..b52527d36c 100644 --- a/test/CodeGenCXX/vtable-available-externally.cpp +++ b/test/CodeGenCXX/vtable-available-externally.cpp @@ -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 @@ -13,10 +14,13 @@ // 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 // 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 +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 + 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 +struct OuterTemplate { + template + 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 +int use() { + T *ptr = new T(); + return ptr->foo(42); +} + +void test() { + use >(); + use::NestedTemplateInTemplate >(); + use >(); + + use(); + use::NestedNonTemplateInTemplate>(); + use(); +} +}