Skip to content

Handle global_addr for concrete type propagation #16116

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 1 commit into from
May 2, 2018
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
49 changes: 49 additions & 0 deletions lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,51 @@ SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) {
return tryToConcatenateStrings(AI, Builder);
}

/// Determine the pattern for global_addr.
/// %3 = global_addr @$P : $*SomeP
/// %4 = init_existential_addr %3 : $*SomeP, $SomeC
/// %5 = alloc_ref $SomeC
/// store %5 to %4 : $*SomeC
/// %8 = alloc_stack $SomeP
/// copy_addr %3 to [initialization] %8 : $*SomeP
/// %9 = open_existential_addr immutable_access %8 : $*SomeP to $*@opened SomeP
static SILValue findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI,
CopyAddrInst *CAI) {
assert(CAI->getSrc() == SILValue(GAI) &&
"Broken Assumption! Global Addr is not the source of the passed in "
"copy_addr?!");

/// Check for a single InitExistential usage for GAI and
/// a simple dominance check: both InitExistential and CAI are in
/// the same basic block and only one InitExistential
/// occurs between GAI and CAI.
llvm::SmallPtrSet<SILInstruction *, 8> IEUses;
for (auto *Use : GAI->getUses()) {
if (auto *InitExistential =
dyn_cast<InitExistentialAddrInst>(Use->getUser())) {
IEUses.insert(InitExistential);
}
}

/// No InitExistential found in the basic block.
if (IEUses.empty())
return SILValue();

/// Walk backwards from CAI instruction till the begining of the basic block
/// looking for InitExistential.
SILValue SingleIE;
for (auto II = CAI->getIterator().getReverse(), IE = CAI->getParent()->rend();
II != IE; ++II) {
if (!IEUses.count(&*II))
continue;
if (SingleIE)
return SILValue();

SingleIE = cast<InitExistentialAddrInst>(&*II);
}
return SingleIE;
}

/// Returns the address of an object with which the stack location \p ASI is
/// initialized. This is either a init_existential_addr or the destination of a
/// copy_addr. Returns a null value if the address does not dominate the
Expand Down Expand Up @@ -666,6 +711,10 @@ static SILValue getAddressOfStackInit(AllocStackInst *ASI,
SILValue CAISrc = CAI->getSrc();
if (auto *ASI = dyn_cast<AllocStackInst>(CAISrc))
return getAddressOfStackInit(ASI, CAI, isCopied);
// Check if the CAISrc is a global_addr.
if (auto *GAI = dyn_cast<GlobalAddrInst>(CAISrc)) {
return findInitExistentialFromGlobalAddr(GAI, CAI);
}
return CAISrc;
}
return cast<InitExistentialAddrInst>(SingleWrite);
Expand Down
143 changes: 143 additions & 0 deletions test/SILOptimizer/sil_combine_global_addr.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine -verify-skip-unreachable-must-be-last -devirtualizer | %FileCheck %s

// Test to see if concrete type can be propagated from
// global_addr in sil_combiner.
sil_stage canonical

import Builtin
import Swift
protocol SomeProtocol {
func foo() -> Int
}
class SomeClass : SomeProtocol {
init()
func foo() -> Int
}
sil hidden [thunk] [always_inline] @foo_ : $@convention(witness_method:SomeProtocol) (@in_guaranteed SomeClass) -> Int {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please provide a failing test case where we have multiple init_existential_addr. I would just do this

BB1 (global) -> (BB2, BB3) where init existential_addr is -> BB4 the rest.

bb0(%0 : $*SomeClass):
%1 = load %0 : $*SomeClass
%2 = class_method %1 : $SomeClass, #SomeClass.foo!1 : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int
%3 = apply %2(%1) : $@convention(method) (@guaranteed SomeClass) -> Int
return %3 : $Int
}
sil hidden [thunk] [always_inline] @foo : $@convention(method) (@guaranteed SomeClass) -> Int {
bb0(%0: $SomeClass):
%1 = integer_literal $Builtin.Int64, 10
%2 = struct $Int (%1 : $Builtin.Int64)
return %2 : $Int
}
sil_global hidden [let] @$global_var : $SomeProtocol

// CHECK-LABEL: sil @witness_global_addr
// CHECK: bb0
// CHECK: alloc_global
// CHECK: global_addr
// CHECK: init_existential_addr
// CHECK: alloc_ref
// CHECK: store
// CHECK: function_ref
// CHECK: apply
// CHECK: return
Copy link
Contributor

Choose a reason for hiding this comment

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

Quick suggestion. Rather than checking for return to signal the end of the function, can you check for:

} // end sil function '$INSERT_FUNCTION_NAME_HERE'

This prevents patterns from matching in the wrong function in an absolute way. Can you change the patterns here to use that?

// CHECK: } // end sil function 'witness_global_addr'
sil @witness_global_addr : $@convention(thin) () -> Int {
bb0:
alloc_global @$global_var
%3 = global_addr @$global_var : $*SomeProtocol
%4 = init_existential_addr %3 : $*SomeProtocol, $SomeClass
%5 = alloc_ref $SomeClass
store %5 to %4 : $*SomeClass
%8 = alloc_stack $SomeProtocol
copy_addr %3 to [initialization] %8 : $*SomeProtocol
%9 = open_existential_addr immutable_access %8 : $*SomeProtocol to $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol
%10 = witness_method $@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol, #SomeProtocol.foo!1 : <Self where Self : SomeProtocol> (Self) -> () -> Int, %9 : $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
%11 = apply %10<@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol>(%9) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
destroy_addr %8 : $*SomeProtocol
dealloc_stack %8 : $*SomeProtocol
return %11 : $Int
}

// CHECK-LABEL: sil @witness_global_addr_fail_1
// CHECK: bb0
// CHECK: alloc_global
// CHECK: global_addr
// CHECK: bb1
// CHECK: init_existential_addr
// CHECK: bb2
// CHECK: init_existential_addr
// CHECK: bb3
// CHECK: alloc_ref
// CHECK: store
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: witness_method
// CHECK: apply
// CHECK: destroy_addr
// CHECK: dealloc_stack
// CHECK: return
// CHECK: } // end sil function 'witness_global_addr_fail_1'
sil @witness_global_addr_fail_1: $@convention(thin) (Builtin.Int1) -> Int {
bb0(%0 : $Builtin.Int1):
alloc_global @$global_var
%3 = global_addr @$global_var : $*SomeProtocol
cond_br %0, bb1, bb2
bb1:
%4 = init_existential_addr %3 : $*SomeProtocol, $SomeClass
br bb3(%4 : $*SomeClass)
bb2:
%5 = init_existential_addr %3 : $*SomeProtocol, $SomeClass
br bb3(%5 : $*SomeClass)
bb3(%6 : $*SomeClass):
%7 = alloc_ref $SomeClass
store %7 to %6 : $*SomeClass
%8 = alloc_stack $SomeProtocol
copy_addr %3 to [initialization] %8 : $*SomeProtocol
%9 = open_existential_addr immutable_access %8 : $*SomeProtocol to $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol
%10 = witness_method $@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol, #SomeProtocol.foo!1 : <Self where Self : SomeProtocol> (Self) -> () -> Int, %9 : $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
%11 = apply %10<@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol>(%9) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
destroy_addr %8 : $*SomeProtocol
dealloc_stack %8 : $*SomeProtocol
return %11 : $Int
}

// CHECK-LABEL: sil @witness_global_addr_fail_2
// CHECK: bb0
// CHECK: alloc_global
// CHECK: global_addr
// CHECK: init_existential_addr
// CHECK: alloc_ref
// CHECK: store
// CHECK: init_existential_addr
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: open_existential_addr
// CHECK: witness_method
// CHECK: apply
// CHECK: destroy_addr
// CHECK: dealloc_stack
// CHECK: return
// CHECK: } // end sil function 'witness_global_addr_fail_2'
sil @witness_global_addr_fail_2 : $@convention(thin) () -> Int {
bb0:
alloc_global @$global_var
%3 = global_addr @$global_var : $*SomeProtocol
%4 = init_existential_addr %3 : $*SomeProtocol, $SomeClass
%5 = alloc_ref $SomeClass
store %5 to %4 : $*SomeClass
%6 = init_existential_addr %3 : $*SomeProtocol, $SomeClass
%8 = alloc_stack $SomeProtocol
copy_addr %3 to [initialization] %8 : $*SomeProtocol
%9 = open_existential_addr immutable_access %8 : $*SomeProtocol to $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol
%10 = witness_method $@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol, #SomeProtocol.foo!1 : <Self where Self : SomeProtocol> (Self) -> () -> Int, %9 : $*@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
%11 = apply %10<@opened("1B0A5B84-3441-11E8-AC03-DCA9048B1C6D") SomeProtocol>(%9) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> Int
destroy_addr %8 : $*SomeProtocol
dealloc_stack %8 : $*SomeProtocol
return %11 : $Int
}

sil_vtable SomeClass {
#SomeClass.foo!1: (SomeClass) -> () -> Int : @foo
}

sil_witness_table hidden SomeClass: SomeProtocol module test {
method #SomeProtocol.foo!1: <Self where Self : SomeProtocol> (Self) -> () -> Int : @foo_
}