Skip to content

[WPD]Regard unreachable function as a possible devirtualizable target #115668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,28 @@ static cl::list<std::string>
cl::desc("Prevent function(s) from being devirtualized"),
cl::Hidden, cl::CommaSeparated);

/// With Clang, a pure virtual class's deleting destructor is emitted as a
/// `llvm.trap` intrinsic followed by an unreachable IR instruction. In the
/// context of whole program devirtualization, the deleting destructor of a pure
/// virtual class won't be invoked by the source code so safe to skip as a
/// devirtualize target.
///
/// However, not all unreachable functions are safe to skip. In some cases, the
/// program intends to run such functions and terminate, for instance, a unit
/// test may run a death test. A non-test program might (or allowed to) invoke
/// such functions to report failures (whether/when it's a good practice or not
/// is a different topic).
///
/// This option is enabled to keep an unreachable function as a possible
/// devirtualize target to conservatively keep the program behavior.
///
/// TODO: Make a pure virtual class's deleting destructor precisely identifiable
/// in Clang's codegen for more devirtualization in LLVM.
static cl::opt<bool> WholeProgramDevirtKeepUnreachableFunction(
"wholeprogramdevirt-keep-unreachable-function",
cl::desc("Regard unreachable functions as possible devirtualize targets."),
cl::Hidden, cl::init(true));

/// If explicitly specified, the devirt module pass will stop transformation
/// once the total number of devirtualizations reach the cutoff value. Setting
/// this option to 0 explicitly will do 0 devirtualization.
Expand Down Expand Up @@ -386,6 +408,9 @@ template <> struct DenseMapInfo<VTableSlotSummary> {
// 2) All function summaries indicate it's unreachable
// 3) There is no non-function with the same GUID (which is rare)
static bool mustBeUnreachableFunction(ValueInfo TheFnVI) {
if (WholeProgramDevirtKeepUnreachableFunction)
return false;

if ((!TheFnVI) || TheFnVI.getSummaryList().empty()) {
// Returns false if ValueInfo is absent, or the summary list is empty
// (e.g., function declarations).
Expand Down Expand Up @@ -2241,6 +2266,8 @@ DevirtModule::lookUpFunctionValueInfo(Function *TheFn,

bool DevirtModule::mustBeUnreachableFunction(
Function *const F, ModuleSummaryIndex *ExportSummary) {
if (WholeProgramDevirtKeepUnreachableFunction)
return false;
// First, learn unreachability by analyzing function IR.
if (!F->isDeclaration()) {
// A function must be unreachable if its entry block ends with an
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
; Test that static devirtualization doesn't happen because there are two
; devirtualizable targets. Unreachable functions are kept in the devirtualizable
; target set by default.
; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt %s 2>&1 | FileCheck %s --implicit-check-not="single-impl"

; Test that regular LTO will analyze IR, detect unreachable functions and discard unreachable functions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is only done under the -wholeprogramdevirt-keep-unreachable-function=false option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

; when finding virtual call targets.
; when finding virtual call targets under `wholeprogramdevirt-keep-unreachable-function=false` option.
; In this test case, the unreachable function is the virtual deleting destructor of an abstract class.
; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -wholeprogramdevirt-keep-unreachable-function=false %s 2>&1 | FileCheck %s --check-prefix=DEVIRT

; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt %s 2>&1 | FileCheck %s

; CHECK: remark: tmp.cc:21:3: single-impl: devirtualized a call to _ZN7DerivedD0Ev
; CHECK: remark: <unknown>:0:0: devirtualized _ZN7DerivedD0Ev
; DEVIRT: remark: tmp.cc:21:3: single-impl: devirtualized a call to _ZN7DerivedD0Ev
; DEVIRT: remark: <unknown>:0:0: devirtualized _ZN7DerivedD0Ev

source_filename = "tmp.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
Expand Down
Loading