Author: prazek Date: Wed Jun 13 06:55:42 2018 New Revision: 334600 URL: http://llvm.org/viewvc/llvm-project?rev=334600&view=rev Log: 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 <krzysztof.pszenic...@gmail.com> Modified: cfe/trunk/docs/ClangCommandLineReference.rst cfe/trunk/docs/ReleaseNotes.rst cfe/trunk/docs/UsersManual.rst cfe/trunk/include/clang/Basic/LangOptions.def cfe/trunk/include/clang/Driver/Options.td cfe/trunk/include/clang/Frontend/CodeGenOptions.def cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp cfe/trunk/lib/Driver/ToolChains/Clang.cpp cfe/trunk/lib/Frontend/CompilerInvocation.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp Modified: cfe/trunk/docs/ClangCommandLineReference.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ClangCommandLineReference.rst?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/docs/ClangCommandLineReference.rst (original) +++ cfe/trunk/docs/ClangCommandLineReference.rst Wed Jun 13 06:55:42 2018 @@ -1934,6 +1934,12 @@ Set the default symbol visibility for al 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 Modified: cfe/trunk/docs/ReleaseNotes.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ReleaseNotes.rst?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/docs/ReleaseNotes.rst (original) +++ cfe/trunk/docs/ReleaseNotes.rst Wed Jun 13 06:55:42 2018 @@ -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 Modified: cfe/trunk/docs/UsersManual.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/UsersManual.rst?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/docs/UsersManual.rst (original) +++ cfe/trunk/docs/UsersManual.rst Wed Jun 13 06:55:42 2018 @@ -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. Modified: cfe/trunk/include/clang/Basic/LangOptions.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/LangOptions.def?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/LangOptions.def (original) +++ cfe/trunk/include/clang/Basic/LangOptions.def Wed Jun 13 06:55:42 2018 @@ -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") Modified: cfe/trunk/include/clang/Driver/Options.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/include/clang/Driver/Options.td (original) +++ cfe/trunk/include/clang/Driver/Options.td Wed Jun 13 06:55:42 2018 @@ -1688,6 +1688,11 @@ def fwhole_program_vtables : Flag<["-"], 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]>, Modified: cfe/trunk/include/clang/Frontend/CodeGenOptions.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CodeGenOptions.def?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/include/clang/Frontend/CodeGenOptions.def (original) +++ cfe/trunk/include/clang/Frontend/CodeGenOptions.def Wed Jun 13 06:55:42 2018 @@ -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 Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original) +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Wed Jun 13 06:55:42 2018 @@ -1707,11 +1707,19 @@ bool ItaniumCXXABI::canSpeculativelyEmit 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, Modified: cfe/trunk/lib/Driver/ToolChains/Clang.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains/Clang.cpp?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/lib/Driver/ToolChains/Clang.cpp (original) +++ cfe/trunk/lib/Driver/ToolChains/Clang.cpp Wed Jun 13 06:55:42 2018 @@ -3472,6 +3472,10 @@ void Clang::ConstructJob(Compilation &C, 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"); Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original) +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Wed Jun 13 06:55:42 2018 @@ -719,6 +719,7 @@ static bool ParseCodeGenArgs(CodeGenOpti 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 &O 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); Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Jun 13 06:55:42 2018 @@ -6125,6 +6125,12 @@ void Sema::CheckCompletedCXXClass(CXXRec 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 Modified: cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp?rev=334600&r1=334599&r2=334600&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp (original) +++ cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp Wed Jun 13 06:55:42 2018 @@ -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 <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>(); +} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits