Skip to content

Update global init optimization to work with vars #29122

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
Feb 12, 2020
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
33 changes: 24 additions & 9 deletions lib/SILOptimizer/IPO/GlobalOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ class SILGlobalOpt {
bool run();

protected:
/// Checks if a given global variable is assigned only once.
bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG,
SILFunction *globalAddrF);

/// Reset all the maps of global variables.
void reset();

Expand Down Expand Up @@ -637,15 +641,26 @@ static SILFunction *genGetterFromInit(SILOptFunctionBuilder &FunctionBuilder,
return GetterF;
}

/// Checks if a given global variable is assigned only once.
static bool isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG) {
bool SILGlobalOpt::isAssignedOnlyOnceInInitializer(SILGlobalVariable *SILG,
SILFunction *globalAddrF) {
if (SILG->isLet())
return true;
// TODO: If we can prove that a given global variable
// is assigned only once, during initialization, then
// we can treat it as if it is a let.
// If this global is internal or private, it should be
return false;

// If we should skip this, it is probably because there are multiple stores.
// Return false if there are multiple stores or no stores.
if (GlobalVarSkipProcessing.count(SILG) || !GlobalVarStore.count(SILG) ||
// Check if there is more than one use the global addr function. If there
// is only one use, it must be the use that we are trying to optimize, so
// that is OK. If there is more than one use, one of the other uses may
// have a store attached to it which means there may be more than one
// assignment, so return false.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added this additional check to prevent an edge case where global uses were improperly optimized away. In the future, GlobalInitCallMap applies could theoretically be traced to a store instruction that could be added to GlobalVarStore which would be beneficial for a few reasons (including that we could get rid of this extra check and optimize more code correctly).

(GlobalInitCallMap.count(globalAddrF) &&
GlobalInitCallMap[globalAddrF].size() != 1))
return false;

// Otherwise, return true if this can't be used externally (false, otherwise).
return !isPossiblyUsedExternally(SILG->getLinkage(),
SILG->getModule().isWholeModule());
}

/// Replace load sequence which may contain
Expand Down Expand Up @@ -691,7 +706,7 @@ replaceLoadsByKnownValue(BuiltinInst *CallToOnce, SILFunction *AddrF,
LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: replacing loads with known value for "
<< SILG->getName() << '\n');

assert(isAssignedOnlyOnceInInitializer(SILG) &&
assert(isAssignedOnlyOnceInInitializer(SILG, AddrF) &&
"The value of the initializer should be known at compile-time");
assert(SILG->getDecl() &&
"Decl corresponding to the global variable should be known");
Expand Down Expand Up @@ -804,7 +819,7 @@ void SILGlobalOpt::optimizeInitializer(SILFunction *AddrF,
<< SILG->getName() << '\n');

// Remove "once" call from the addressor.
if (!isAssignedOnlyOnceInInitializer(SILG) || !SILG->getDecl()) {
if (!isAssignedOnlyOnceInInitializer(SILG, AddrF) || !SILG->getDecl()) {
LLVM_DEBUG(llvm::dbgs() << "GlobalOpt: building static initializer for "
<< SILG->getName() << '\n');

Expand Down
22 changes: 22 additions & 0 deletions test/SILOptimizer/globalopt_global_propagation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,25 @@ let IT2 = (100, 200, 300)
public func test_let_tuple_wrapped_ints() -> Int {
return IT1.0.0 + IT2.1
}

class Foo {
fileprivate static var x: Int = 0
}

// CHECK-LABEL: sil @$s28globalopt_global_propagation25test_optimize_init_staticSiyF
// CHECK: bb0:
// CHECK-NOT: global_addr
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK-NEXT: return

// CHECK-WMO-LABEL: sil @$s28globalopt_global_propagation25test_optimize_init_staticSiyF
// CHECK-WMO: bb0:
// CHECK-WMO-NOT: global_addr
// CHECK-WMO-NEXT: integer_literal
// CHECK-WMO-NEXT: struct
// CHECK-WMO-NEXT: return
public func test_optimize_init_static() -> Int {
return Foo.x
}