modimo updated this revision to Diff 533326.
modimo added a comment.

Address feedback. Allow empty string to unset the flag


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D152741/new/

https://reviews.llvm.org/D152741

Files:
  clang/include/clang/Basic/CodeGenOptions.h
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/CGVTables.cpp
  clang/lib/CodeGen/CodeGenModule.h
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/CodeGenCXX/type-metadata-unknown-vtable-visibility-filepaths.cpp
  clang/test/Driver/funknown-vtable-visibility-filepaths.c
  llvm/include/llvm/IR/GlobalObject.h
  llvm/lib/IR/Metadata.cpp
  llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
  llvm/test/ThinLTO/X86/devirt.ll

Index: llvm/test/ThinLTO/X86/devirt.ll
===================================================================
--- llvm/test/ThinLTO/X86/devirt.ll
+++ llvm/test/ThinLTO/X86/devirt.ll
@@ -42,9 +42,11 @@
 ; RUN:   -r=%t2.o,_ZN1B1fEi,p \
 ; RUN:   -r=%t2.o,_ZN1C1fEi,p \
 ; RUN:   -r=%t2.o,_ZN1D1mEi,p \
+; RUN:   -r=%t2.o,_ZN1E1nEi,p \
 ; RUN:   -r=%t2.o,_ZTV1B,px \
 ; RUN:   -r=%t2.o,_ZTV1C,px \
-; RUN:   -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK
+; RUN:   -r=%t2.o,_ZTV1D,px \
+; RUN:   -r=%t2.o,_ZTV1F,px 2>&1 | FileCheck %s --check-prefix=REMARK
 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
 
 ; Check that we're able to prevent specific function from being
@@ -58,9 +60,11 @@
 ; RUN:   -r=%t2.o,_ZN1B1fEi,p \
 ; RUN:   -r=%t2.o,_ZN1C1fEi,p \
 ; RUN:   -r=%t2.o,_ZN1D1mEi,p \
+; RUN:   -r=%t2.o,_ZN1E1nEi,p \
 ; RUN:   -r=%t2.o,_ZTV1B,px \
 ; RUN:   -r=%t2.o,_ZTV1C,px \
-; RUN:   -r=%t2.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=SKIP
+; RUN:   -r=%t2.o,_ZTV1D,px \
+; RUN:   -r=%t2.o,_ZTV1F,px 2>&1 | FileCheck %s --check-prefix=SKIP
 
 ; RUN: llvm-lto2 run %t.o -save-temps -pass-remarks=. \
 ; RUN:   -whole-program-visibility \
@@ -70,16 +74,20 @@
 ; RUN:   -r=%t.o,_ZN1B1fEi,p \
 ; RUN:   -r=%t.o,_ZN1C1fEi,p \
 ; RUN:   -r=%t.o,_ZN1D1mEi,p \
+; RUN:   -r=%t.o,_ZN1E1nEi,p \
 ; RUN:   -r=%t.o,_ZTV1B, \
 ; RUN:   -r=%t.o,_ZTV1C, \
 ; RUN:   -r=%t.o,_ZTV1D, \
+; RUN:   -r=%t.o,_ZTV1F, \
 ; RUN:   -r=%t.o,_ZN1A1nEi, \
 ; RUN:   -r=%t.o,_ZN1B1fEi, \
 ; RUN:   -r=%t.o,_ZN1C1fEi, \
 ; RUN:   -r=%t.o,_ZN1D1mEi, \
+; RUN:   -r=%t.o,_ZN1E1nEi, \
 ; RUN:   -r=%t.o,_ZTV1B,px \
 ; RUN:   -r=%t.o,_ZTV1C,px \
-; RUN:   -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK --dump-input=fail
+; RUN:   -r=%t.o,_ZTV1D,px \
+; RUN:   -r=%t.o,_ZTV1F,px 2>&1 | FileCheck %s --check-prefix=REMARK --dump-input=fail
 ; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
 
 ; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
@@ -95,13 +103,18 @@
 %struct.C = type { %struct.A }
 %struct.D = type { ptr }
 
+%struct.E = type { ptr }
+%struct.F = type { %struct.E }
+
 @_ZTV1B = constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr undef, ptr @_ZN1B1fEi, ptr @_ZN1A1nEi] }, !type !0, !type !1
 @_ZTV1C = constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr undef, ptr @_ZN1C1fEi, ptr @_ZN1A1nEi] }, !type !0, !type !2
 @_ZTV1D = constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr undef, ptr @_ZN1D1mEi] }, !type !3
 
+@_ZTV1F = constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr undef, ptr @_ZN1E1nEi] }, !type !5, !vcall_visibility !6
+
 
 ; CHECK-IR-LABEL: define i32 @test
-define i32 @test(ptr %obj, ptr %obj2, i32 %a) {
+define i32 @test(ptr %obj, ptr %obj2, ptr %obj3, i32 %a) {
 entry:
   %vtable = load ptr, ptr %obj
   %p = call i1 @llvm.type.test(ptr %vtable, metadata !"_ZTS1A")
@@ -114,7 +127,7 @@
   ; Ensure !prof and !callees metadata for indirect call promotion removed.
   ; CHECK-IR-NOT: prof
   ; CHECK-IR-NOT: callees
-  %call = tail call i32 %fptr1(ptr nonnull %obj, i32 %a), !prof !5, !callees !6
+  %call = tail call i32 %fptr1(ptr nonnull %obj, i32 %a), !prof !7, !callees !8
 
   %fptr22 = load ptr, ptr %vtable, align 8
 
@@ -131,7 +144,18 @@
   ; Check that the call was devirtualized.
   ; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi
   %call4 = tail call i32 %fptr33(ptr nonnull %obj2, i32 %call3)
-  ret i32 %call4
+
+  %vtable1 = load ptr, ptr %obj3
+  %p3 = call i1 @llvm.type.test(ptr %vtable1, metadata !"_ZTS1E")
+  call void @llvm.assume(i1 %p3)
+  %fptrptr1 = getelementptr ptr, ptr %vtable1, i32 0
+  %fptr44 = load ptr, ptr %fptrptr1, align 8
+
+  ; Check that the call was not devirtualized because of "unknown"
+  ; vcall_visibility.
+  ; CHECK-IR: %call5 = tail call i32 %fptr44
+  %call5 = tail call i32 %fptr44(ptr nonnull %obj, i32 %call4)
+  ret i32 %call5
 }
 ; CHECK-IR-LABEL: ret i32
 ; CHECK-IR-LABEL: }
@@ -155,6 +179,10 @@
    ret i32 0;
 }
 
+define i32 @_ZN1E1nEi(ptr %this, i32 %a) #0 {
+   ret i32 0;
+}
+
 ; Make sure we don't inline or otherwise optimize out the direct calls.
 attributes #0 = { noinline optnone }
 
@@ -163,5 +191,7 @@
 !2 = !{i64 16, !"_ZTS1C"}
 !3 = !{i64 16, !4}
 !4 = distinct !{}
-!5 = !{!"VP", i32 0, i64 1, i64 1621563287929432257, i64 1}
-!6 = !{ptr @_ZN1A1nEi}
+!5 = !{i64 16, !"_ZTS1E"}
+!6 = !{i64 3}
+!7 = !{!"VP", i32 0, i64 1, i64 1621563287929432257, i64 1}
+!8 = !{ptr @_ZN1A1nEi}
Index: llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
===================================================================
--- llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
+++ llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
@@ -996,9 +996,11 @@
       return false;
 
     // We cannot perform whole program devirtualization analysis on a vtable
-    // with public LTO visibility.
+    // with public or unknown LTO visibility.
     if (TM.Bits->GV->getVCallVisibility() ==
-        GlobalObject::VCallVisibilityPublic)
+            GlobalObject::VCallVisibilityPublic ||
+        TM.Bits->GV->getVCallVisibility() ==
+            GlobalObject::VCallVisibilityUnknown)
       return false;
 
     Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(),
@@ -1075,7 +1077,8 @@
         VS = CurVS;
         // We cannot perform whole program devirtualization analysis on a vtable
         // with public LTO visibility.
-        if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic)
+        if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic ||
+            VS->getVCallVisibility() == GlobalObject::VCallVisibilityUnknown)
           return false;
       }
     }
Index: llvm/lib/IR/Metadata.cpp
===================================================================
--- llvm/lib/IR/Metadata.cpp
+++ llvm/lib/IR/Metadata.cpp
@@ -1712,7 +1712,7 @@
     uint64_t Val = cast<ConstantInt>(
                        cast<ConstantAsMetadata>(MD->getOperand(0))->getValue())
                        ->getZExtValue();
-    assert(Val <= 2 && "unknown vcall visibility!");
+    assert(Val <= 3 && "unknown vcall visibility!");
     return (VCallVisibility)Val;
   }
   return VCallVisibility::VCallVisibilityPublic;
Index: llvm/include/llvm/IR/GlobalObject.h
===================================================================
--- llvm/include/llvm/IR/GlobalObject.h
+++ llvm/include/llvm/IR/GlobalObject.h
@@ -37,6 +37,8 @@
     VCallVisibilityLinkageUnit = 1,
     // Type is only visible to code in the current Module.
     VCallVisibilityTranslationUnit = 2,
+    // Type is unknown and should always be treated conservatively.
+    VCallVisibilityUnknown = 3,
   };
 
 protected:
Index: clang/test/Driver/funknown-vtable-visibility-filepaths.c
===================================================================
--- /dev/null
+++ clang/test/Driver/funknown-vtable-visibility-filepaths.c
@@ -0,0 +1,9 @@
+// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin 2>&1 | FileCheck --check-prefix=NOSKIP %s
+// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fwhole-program-vtables -funknown-vtable-visibility-filepaths=abc 2>&1 | FileCheck --check-prefix=SKIP %s
+// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fwhole-program-vtables -funknown-vtable-visibility-filepaths=abc -funknown-vtable-visibility-filepaths= 2>&1 | FileCheck --check-prefix=NOSKIP %s
+// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -funknown-vtable-visibility-filepaths=abc 2>&1 | FileCheck --check-prefix=ERROR1 %s
+// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -funknown-vtable-visibility-filepaths=abc -funknown-vtable-visibility-filepaths= 2>&1 | FileCheck --check-prefix=NOSKIP %s
+
+// SKIP: "-funknown-vtable-visibility-filepaths=abc"
+// NOSKIP-NOT: "-funknown-vtable-visibility-filepaths=abc"
+// ERROR1: error: invalid argument '-funknown-vtable-visibility-filepaths' only allowed with '-fwhole-program-vtables'
Index: clang/test/CodeGenCXX/type-metadata-unknown-vtable-visibility-filepaths.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/type-metadata-unknown-vtable-visibility-filepaths.cpp
@@ -0,0 +1,35 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -flto=thin -flto-unit -fwhole-program-vtables -triple x86_64-unknown-linux -fvisibility=hidden -emit-llvm -o - a.cpp | FileCheck %s
+// RUN: %clang_cc1 -flto=thin -flto-unit -fwhole-program-vtables -triple x86_64-unknown-linux -funknown-vtable-visibility-filepaths=h$ -fvisibility=hidden -emit-llvm -o - a.cpp | FileCheck -check-prefix=SKIP-PATH %s
+
+// CHECK-DAG: gv: (name: "_ZTV1A", {{.*}} vcall_visibility: 1
+// CHECK-DAG: gv: (name: "_ZTV1B", {{.*}} vcall_visibility: 1
+
+// Check that unknown visibility only applies to type from header file.
+// SKIP-PATH-DAG: gv: (name: "_ZTV1A", {{.*}} vcall_visibility: 3
+// SKIP-PATH-DAG: gv: (name: "_ZTV1B", {{.*}} vcall_visibility: 1
+
+//--- a.h
+struct A {
+  virtual int f() { return 1; }
+};
+
+//--- a.cpp
+#include "a.h"
+
+struct B {
+  virtual int f() { return 1; }
+};
+
+int f() {
+  auto a = new A();
+  return a->f();
+}
+
+int f1() {
+  auto b = new B();
+  return b->f();
+}
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1578,6 +1578,10 @@
   GenerateOptimizationRemark(Args, SA, OPT_Rpass_analysis_EQ, "pass-analysis",
                              Opts.OptimizationRemarkAnalysis);
 
+  if (Opts.SkipVtableFilepaths.hasValidPattern())
+    GenerateArg(Args, OPT_funknown_vtable_visibility_filepaths_EQ,
+                Opts.SkipVtableFilepaths.Pattern, SA);
+
   GenerateArg(Args, OPT_fdiagnostics_hotness_threshold_EQ,
               Opts.DiagnosticsHotnessThreshold
                   ? Twine(*Opts.DiagnosticsHotnessThreshold)
@@ -1989,6 +1993,19 @@
                      Opts.OptimizationRemarkMissed.hasValidPattern() ||
                      Opts.OptimizationRemarkAnalysis.hasValidPattern();
 
+  if (Arg *A = Args.getLastArg(OPT_funknown_vtable_visibility_filepaths_EQ)) {
+    StringRef Val = A->getValue();
+    std::string RegexError;
+    std::shared_ptr<llvm::Regex> Pattern = std::make_shared<llvm::Regex>(Val);
+    if (!Pattern->isValid(RegexError)) {
+      Diags.Report(diag::err_drv_optimization_remark_pattern)
+          << RegexError << A->getAsString(Args);
+      Pattern.reset();
+    }
+    Opts.SkipVtableFilepaths =
+        CodeGenOptions::RegexWithPattern(std::string(Val), Pattern);
+  }
+
   bool UsingSampleProfile = !Opts.SampleProfileFile.empty();
   bool UsingProfile =
       UsingSampleProfile || !Opts.ProfileInstrumentUsePath.empty();
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -7245,6 +7245,20 @@
   if (SplitLTOUnit)
     CmdArgs.push_back("-fsplit-lto-unit");
 
+  if (const Arg *A =
+       Args.getLastArg(options::OPT_funknown_vtable_visibility_filepaths_EQ)) {
+    StringRef Path = A->getValue();
+    if (!Path.empty()) {
+      if (!WholeProgramVTables)
+        D.Diag(diag::err_drv_argument_only_allowed_with)
+            << "-funknown-vtable-visibility-filepaths"
+            << "-fwhole-program-vtables";
+      CmdArgs.push_back(
+          Args.MakeArgString("-funknown-vtable-visibility-filepaths=" + Path));
+    }
+    A->claim();
+  }
+
   if (Arg *A = Args.getLastArg(options::OPT_fglobal_isel,
                                options::OPT_fno_global_isel)) {
     CmdArgs.push_back("-mllvm");
Index: clang/lib/CodeGen/CodeGenModule.h
===================================================================
--- clang/lib/CodeGen/CodeGenModule.h
+++ clang/lib/CodeGen/CodeGenModule.h
@@ -1431,6 +1431,11 @@
   /// optimization.
   bool HasHiddenLTOVisibility(const CXXRecordDecl *RD);
 
+  /// Returns whether the given record has unknown LTO visibility and therefore
+  /// should never participate in (single-module) CFI and whole-program vtable
+  /// optimization.
+  bool HasUnknownLTOVisibility(const CXXRecordDecl *RD);
+
   /// Returns whether the given record has public LTO visibility (regardless of
   /// -lto-whole-program-visibility) and therefore may not participate in
   /// (single-module) CFI and whole-program vtable optimization.
Index: clang/lib/CodeGen/CGVTables.cpp
===================================================================
--- clang/lib/CodeGen/CGVTables.cpp
+++ clang/lib/CodeGen/CGVTables.cpp
@@ -1247,6 +1247,29 @@
   return !AlwaysHasLTOVisibilityPublic(RD);
 }
 
+bool CodeGenModule::HasUnknownLTOVisibility(const CXXRecordDecl *RD) {
+  if (CodeGenOpts.SkipVtableFilepaths.hasValidPattern()) {
+    const PresumedLoc &PLoc =
+        Context.getFullLoc(RD->getBeginLoc()).getPresumedLoc(true);
+    assert(PLoc.isValid() && "Source location is expected to be always valid.");
+    std::string FilePath = PLoc.getFilename();
+
+    if (CodeGenOpts.SkipVtableFilepaths.patternMatches(FilePath))
+      return true;
+  }
+  return false;
+}
+
+static llvm::GlobalObject::VCallVisibility
+getMinVCallVisibility(llvm::GlobalObject::VCallVisibility TypeVisA,
+                      llvm::GlobalObject::VCallVisibility TypeVisB) {
+  if (TypeVisA == llvm::GlobalObject::VCallVisibilityUnknown ||
+      TypeVisB == llvm::GlobalObject::VCallVisibilityUnknown)
+    return llvm::GlobalObject::VCallVisibilityUnknown;
+
+  return std::min(TypeVisA, TypeVisB);
+}
+
 llvm::GlobalObject::VCallVisibility CodeGenModule::GetVCallVisibilityLevel(
     const CXXRecordDecl *RD, llvm::DenseSet<const CXXRecordDecl *> &Visited) {
   // If we have already visited this RD (which means this is a recursive call
@@ -1259,7 +1282,9 @@
 
   LinkageInfo LV = RD->getLinkageAndVisibility();
   llvm::GlobalObject::VCallVisibility TypeVis;
-  if (!isExternallyVisible(LV.getLinkage()))
+  if (HasUnknownLTOVisibility(RD))
+    TypeVis = llvm::GlobalObject::VCallVisibilityUnknown;
+  else if (!isExternallyVisible(LV.getLinkage()))
     TypeVis = llvm::GlobalObject::VCallVisibilityTranslationUnit;
   else if (HasHiddenLTOVisibility(RD))
     TypeVis = llvm::GlobalObject::VCallVisibilityLinkageUnit;
@@ -1268,13 +1293,13 @@
 
   for (const auto &B : RD->bases())
     if (B.getType()->getAsCXXRecordDecl()->isDynamicClass())
-      TypeVis = std::min(
+      TypeVis = getMinVCallVisibility(
           TypeVis,
           GetVCallVisibilityLevel(B.getType()->getAsCXXRecordDecl(), Visited));
 
   for (const auto &B : RD->vbases())
     if (B.getType()->getAsCXXRecordDecl()->isDynamicClass())
-      TypeVis = std::min(
+      TypeVis = getMinVCallVisibility(
           TypeVis,
           GetVCallVisibilityLevel(B.getType()->getAsCXXRecordDecl(), Visited));
 
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -3157,6 +3157,9 @@
   CodeGenOpts<"WholeProgramVTables">, DefaultFalse,
   PosFlag<SetTrue, [CC1Option], "Enables whole-program vtable optimization. Requires -flto">,
   NegFlag<SetFalse>, BothFlags<[CoreOption]>>;
+def funknown_vtable_visibility_filepaths_EQ : Joined<["-"], "funknown-vtable-visibility-filepaths=">, Group<f_Group>,
+  HelpText<"Skip types from participating in whole-program vtable optimization defined under these filepaths. Pass empty string to unset">,
+  Flags<[CC1Option]>;
 defm split_lto_unit : BoolFOption<"split-lto-unit",
   CodeGenOpts<"EnableSplitLTOUnit">, DefaultFalse,
   PosFlag<SetTrue, [CC1Option], "Enables splitting of the LTO unit">,
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -186,6 +186,29 @@
   /// Regexes separated by a semi-colon to filter the files to not instrument.
   std::string ProfileExcludeFiles;
 
+  /// Regular expression with its original pattern string
+  struct RegexWithPattern {
+    std::string Pattern;
+    std::shared_ptr<llvm::Regex> Regex;
+
+    RegexWithPattern() = default;
+    RegexWithPattern(std::string Pattern, std::shared_ptr<llvm::Regex> Regex)
+        : Pattern(Pattern), Regex(Regex) {}
+
+    /// Returns true iff the optimization remark holds a valid regular
+    /// expression.
+    bool hasValidPattern() const { return Regex != nullptr; }
+
+    /// Matches the given string against the regex, if there is some.
+    bool patternMatches(StringRef String) const {
+      return hasValidPattern() && Regex->match(String);
+    }
+  };
+
+  /// Skip types defined in these paths when performing whole-program vtable
+  /// optimization.
+  RegexWithPattern SkipVtableFilepaths;
+
   /// The version string to put into coverage files.
   char CoverageVersion[4];
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to