Skip to content

[ThinLTO]Supports declaration import for global variables in distributed ThinLTO #117616

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 5 commits into from
Dec 3, 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
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,11 @@ class ModuleSummaryIndex {

/// Checks if we can import global variable from another module.
bool canImportGlobalVar(const GlobalValueSummary *S, bool AnalyzeRefs) const;

/// Same as above but checks whether the global var is importable as a
/// declaration.
bool canImportGlobalVar(const GlobalValueSummary *S, bool AnalyzeRefs,
bool &CanImportDecl) const;
};

/// GraphTraits definition to build SCC for the index
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4764,7 +4764,8 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() {
NameVals.push_back(*ValueId);
assert(ModuleIdMap.count(VS->modulePath()));
NameVals.push_back(ModuleIdMap[VS->modulePath()]);
NameVals.push_back(getEncodedGVSummaryFlags(VS->flags()));
NameVals.push_back(
getEncodedGVSummaryFlags(VS->flags(), shouldImportValueAsDecl(VS)));
NameVals.push_back(getEncodedGVarFlags(VS->varflags()));
for (auto &RI : VS->refs()) {
auto RefValueId = getValueId(RI.getGUID());
Expand Down
18 changes: 16 additions & 2 deletions llvm/lib/IR/ModuleSummaryIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,13 @@ void ModuleSummaryIndex::propagateAttributes(

bool ModuleSummaryIndex::canImportGlobalVar(const GlobalValueSummary *S,
bool AnalyzeRefs) const {
bool CanImportDecl;
return canImportGlobalVar(S, AnalyzeRefs, CanImportDecl);
}

bool ModuleSummaryIndex::canImportGlobalVar(const GlobalValueSummary *S,
bool AnalyzeRefs,
bool &CanImportDecl) const {
auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {
// We don't analyze GV references during attribute propagation, so
// GV with non-trivial initializer can be marked either read or
Expand All @@ -348,13 +355,20 @@ bool ModuleSummaryIndex::canImportGlobalVar(const GlobalValueSummary *S,
};
auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());

const bool nonInterposable =
!GlobalValue::isInterposableLinkage(S->linkage());
const bool eligibleToImport = !S->notEligibleToImport();

// It's correct to import a global variable only when it is not interposable
// and eligible to import.
CanImportDecl = (nonInterposable && eligibleToImport);

// Global variable with non-trivial initializer can be imported
// if it's readonly. This gives us extra opportunities for constant
// folding and converting indirect calls to direct calls. We don't
// analyze GV references during attribute propagation, because we
// don't know yet if it is readonly or not.
return !GlobalValue::isInterposableLinkage(S->linkage()) &&
!S->notEligibleToImport() &&
return nonInterposable && eligibleToImport &&
(!AnalyzeRefs || !HasRefsPreventingImport(GVS));
}

Expand Down
12 changes: 10 additions & 2 deletions llvm/lib/Transforms/IPO/FunctionImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,10 +430,18 @@ class GlobalsImporter final {
// than as part of the logic deciding which functions to import (i.e.
// based on profile information). Should we decide to handle them here,
// we can refactor accordingly at that time.
if (!GVS || !Index.canImportGlobalVar(GVS, /* AnalyzeRefs */ true) ||
bool CanImportDecl = false;
if (!GVS ||
shouldSkipLocalInAnotherModule(GVS, VI.getSummaryList().size(),
Summary.modulePath()))
Summary.modulePath()) ||
!Index.canImportGlobalVar(GVS, /* AnalyzeRefs */ true,
CanImportDecl)) {
if (ImportDeclaration && CanImportDecl)
ImportList.maybeAddDeclaration(RefSummary->modulePath(),
VI.getGUID());

continue;
}

// If there isn't an entry for GUID, insert <GUID, Definition> pair.
// Otherwise, definition should take precedence over declaration.
Expand Down
30 changes: 29 additions & 1 deletion llvm/test/ThinLTO/X86/import_callee_declaration.ll
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,14 @@
; RUN: -r=main.bc,main,px \
; RUN: -r=main.bc,small_func, \
; RUN: -r=main.bc,large_func, \
; RUN: -r=main.bc,read_write_global_vars, \
; RUN: -r=main.bc,external_func, \
; RUN: -r=lib.bc,callee,pl \
; RUN: -r=lib.bc,large_indirect_callee,px \
; RUN: -r=lib.bc,large_indirect_bar,px \
; RUN: -r=lib.bc,small_func,px \
; RUN: -r=lib.bc,large_func,px \
; RUN: -r=lib.bc,read_write_global_vars,px \
; RUN: -r=lib.bc,large_indirect_callee_alias,px \
; RUN: -r=lib.bc,large_indirect_bar_alias,px \
; RUN: -r=lib.bc,calleeAddrs,px -r=lib.bc,calleeAddrs2,px -o summary main.bc lib.bc 2>&1 | FileCheck %s --check-prefix=DUMP
Expand Down Expand Up @@ -71,13 +74,22 @@
; MAIN-DIS: [[LARGEINDIRECT:\^[0-9]+]] = gv: (guid: 14343440786664691134, summaries: (function: (module: [[LIBMOD]], flags: ({{.*}} importType: declaration), insts: 8, {{.*}})))
; MAIN-DIS: gv: (guid: 16730173943625350469, summaries: (alias: (module: [[LIBMOD]], flags: ({{.*}} importType: declaration), aliasee: [[LARGEINDIRECT]])))

; RUN: opt -passes=function-import -import-all-index -summary-file=main.bc.thinlto.bc main.bc -o main-after-import.bc
; RUN: llvm-dis -o - main-after-import.bc | FileCheck %s --check-prefix=MAIN-IMPORT

; Tests that dso_local attribute is applied on a global var from its summary.
MAIN-IMPORT: @read_write_global_vars = external dso_local global [1 x ptr]

; Run in-process ThinLTO and tests that
; 1. `callee` remains internalized even if the symbols of its callers
; (large_func, large_indirect_callee, large_indirect_bar) are exported as
; declarations and visible to main module.
; 2. the debugging logs from `function-import` pass are expected.
; Set relocation model to static so the dso_local attribute from a summary is
; applied on the global variable declaration.

; RUN: llvm-lto2 run \
; RUN: -relocation-model=static \
; RUN: -debug-only=function-import \
; RUN: -save-temps \
; RUN: -thinlto-threads=1 \
Expand All @@ -87,11 +99,14 @@
; RUN: -r=main.bc,main,px \
; RUN: -r=main.bc,small_func, \
; RUN: -r=main.bc,large_func, \
; RUN: -r=main.bc,read_write_global_vars, \
; RUN: -r=main.bc,external_func, \
; RUN: -r=lib.bc,callee,pl \
; RUN: -r=lib.bc,large_indirect_callee,px \
; RUN: -r=lib.bc,large_indirect_bar,px \
; RUN: -r=lib.bc,small_func,px \
; RUN: -r=lib.bc,large_func,px \
; RUN: -r=lib.bc,read_write_global_vars,px \
; RUN: -r=lib.bc,large_indirect_callee_alias,px \
; RUN: -r=lib.bc,large_indirect_bar_alias,px \
; RUN: -r=lib.bc,calleeAddrs,px -r=lib.bc,calleeAddrs2,px -o in-process main.bc lib.bc 2>&1 | FileCheck %s --check-prefix=IMPORTDUMP
Expand All @@ -103,7 +118,7 @@
; IMPORTDUMP-DAG: Is importing function definition 13568239288960714650 small_indirect_callee from lib.cc
; IMPORTDUMP-DAG: Is importing function definition 6976996067367342685 small_func from lib.cc
; IMPORTDUMP-DAG: Is importing function declaration 2418497564662708935 large_func from lib.cc
; IMPORTDUMP-DAG: Not importing global 7680325410415171624 calleeAddrs from lib.cc
; IMPORTDUMP-DAG: Is importing global declaration 7680325410415171624 calleeAddrs from lib.cc
; IMPORTDUMP-DAG: Is importing alias declaration 16730173943625350469 large_indirect_callee_alias from lib.cc
; IMPORTDUMP-DAG: Is importing alias declaration 13590951773474913315 large_indirect_bar_alias from lib.cc
; IMPORTDUMP-DAG: Not importing function 13770917885399536773 large_indirect_bar
Expand All @@ -115,6 +130,8 @@
; IMPORT-DAG: define available_externally void @small_func
; IMPORT-DAG: define available_externally hidden void @small_indirect_callee
; IMPORT-DAG: declare void @large_func
; Tests that dso_local attribute is applied on a global var from its summary.
; IMPORT-DAG: @read_write_global_vars = external dso_local global [1 x ptr]
; IMPORT-NOT: large_indirect_callee
; IMPORT-NOT: large_indirect_callee_alias
; IMPORT-NOT: large_indirect_bar
Expand All @@ -126,9 +143,14 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@read_write_global_vars = external global [1 x ptr]

define i32 @main() {
call void @small_func()
call void @large_func()
%num = call ptr @external_func(ptr @read_write_global_vars)
store ptr %num, ptr getelementptr inbounds ([1 x ptr], ptr @read_write_global_vars, i64 0, i64 0)
%res1 = call i32 @external_func(ptr @read_write_global_vars)
ret i32 0
}

Expand All @@ -137,6 +159,8 @@ declare void @small_func()
; large_func without attributes
declare void @large_func()

declare ptr @external_func(ptr)

;--- lib.ll
source_filename = "lib.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
Expand All @@ -149,6 +173,10 @@ target triple = "x86_64-unknown-linux-gnu"
; large_indirect_bar_alias is visible to main.ll but its aliasee isn't.
@calleeAddrs2 = global [1 x ptr] [ptr @large_indirect_bar_alias]

; @read_write_global_vars is not read-only nor write-only (in main.ll). It's not
; a constant global var and has references, so it's not importable as a definition.
@read_write_global_vars = dso_local global [1 x ptr] [ptr @large_indirect_callee]

define void @callee() #1 {
ret void
}
Expand Down
Loading