-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Perform ExistentialSpecializer when SoleConformingType is known #20977
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
#include "ExistentialTransform.h" | ||
#include "swift/SIL/SILFunction.h" | ||
#include "swift/SIL/SILInstruction.h" | ||
#include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h" | ||
#include "swift/SILOptimizer/PassManager/Transforms.h" | ||
#include "swift/SILOptimizer/Utils/Existential.h" | ||
#include "swift/SILOptimizer/Utils/Local.h" | ||
|
@@ -48,9 +49,18 @@ class ExistentialSpecializer : public SILFunctionTransform { | |
/// Specialize existential args in function F. | ||
void specializeExistentialArgsInAppliesWithinFunction(SILFunction &F); | ||
|
||
/// Find concrete type using protocolconformance analysis. | ||
bool findConcreteTypeFromSoleConformingType( | ||
SILFunctionArgument *Arg, CanType &ConcreteType); | ||
|
||
/// CallerAnalysis information. | ||
CallerAnalysis *CA; | ||
|
||
// Determine the set of types a protocol conforms to in whole-module | ||
// compilation mode. | ||
ProtocolConformanceAnalysis *PCA; | ||
|
||
ClassHierarchyAnalysis *CHA; | ||
public: | ||
void run() override { | ||
|
||
|
@@ -64,12 +74,39 @@ class ExistentialSpecializer : public SILFunctionTransform { | |
/// Get CallerAnalysis information handy. | ||
CA = PM->getAnalysis<CallerAnalysis>(); | ||
|
||
/// Get ProtocolConformanceAnalysis. | ||
PCA = PM->getAnalysis<ProtocolConformanceAnalysis>(); | ||
|
||
/// Get ClassHierarchyAnalysis. | ||
CHA = PM->getAnalysis<ClassHierarchyAnalysis>(); | ||
/// Perform specialization. | ||
specializeExistentialArgsInAppliesWithinFunction(*F); | ||
} | ||
}; | ||
} // namespace | ||
|
||
/// Find concrete type from Sole Conforming Type. | ||
bool ExistentialSpecializer::findConcreteTypeFromSoleConformingType( | ||
SILFunctionArgument *Arg, CanType &ConcreteType) { | ||
auto ArgType = Arg->getType(); | ||
auto SwiftArgType = ArgType.getASTType(); | ||
|
||
/// Do not handle composition types yet. | ||
if (isa<ProtocolCompositionType>(SwiftArgType)) | ||
return false; | ||
assert(ArgType.isExistentialType()); | ||
/// Find the protocol decl. | ||
auto *PD = dyn_cast<ProtocolDecl>(SwiftArgType->getAnyNominal()); | ||
if (!PD) | ||
return false; | ||
|
||
// Get SoleConformingType in ConcreteType using ProtocolConformanceAnalysis | ||
// and ClassHierarchyAnalysis. | ||
if (!PCA->getSoleConformingType(PD, CHA, ConcreteType)) | ||
return false; | ||
return true; | ||
} | ||
|
||
/// Check if the argument Arg is used in a destroy_use instruction. | ||
static void | ||
findIfCalleeUsesArgInDestroyUse(SILValue Arg, | ||
|
@@ -83,6 +120,14 @@ findIfCalleeUsesArgInDestroyUse(SILValue Arg, | |
} | ||
} | ||
|
||
/// Helper function to ensure that the argument is not InOut or InOut_Aliasable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added to filter out inout args, which otherwise would be fed to openExistentialRef as non-objects! |
||
static bool isNonInoutIndirectArgument(SILValue Arg, | ||
SILArgumentConvention ArgConvention) { | ||
return !Arg->getType().isObject() && ArgConvention.isIndirectConvention() && | ||
ArgConvention != SILArgumentConvention::Indirect_Inout && | ||
ArgConvention != SILArgumentConvention::Indirect_InoutAliasable; | ||
} | ||
|
||
/// Check if any apply argument meets the criteria for existential | ||
/// specialization. | ||
bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction( | ||
|
@@ -122,7 +167,14 @@ bool ExistentialSpecializer::canSpecializeExistentialArgsInFunction( | |
Operand &ArgOper = Apply.getArgumentRef(Idx); | ||
CanType ConcreteType = | ||
ConcreteExistentialInfo(ArgOper.get(), ArgOper.getUser()).ConcreteType; | ||
if (!ConcreteType) { | ||
auto ArgConvention = F->getConventions().getSILArgumentConvention(Idx); | ||
/// Find the concrete type, either via sole type or via | ||
/// findInitExistential.. | ||
CanType SoleConcreteType; | ||
if (!((F->getModule().isWholeModule() && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First tries to check if there is a SoleConformingType, if not, then tries findConcreteTypeUsingInitExistential. |
||
isNonInoutIndirectArgument(CalleeArg, ArgConvention) && | ||
findConcreteTypeFromSoleConformingType(CalleeArg, SoleConcreteType)) || | ||
ConcreteType)) { | ||
LLVM_DEBUG( | ||
llvm::dbgs() | ||
<< "ExistentialSpecializer Pass: Bail! cannot find ConcreteType " | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -401,6 +401,8 @@ void ExistentialTransform::populateThunkBody() { | |
break; | ||
} | ||
case ExistentialRepresentation::Class: { | ||
/// If the operand is not object type, we would need an explicit load. | ||
assert(OrigOperand->getType().isObject()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extra check for accidental non-object operand type being passed to OEI. |
||
archetypeValue = | ||
Builder.createOpenExistentialRef(Loc, OrigOperand, OpenedSILType); | ||
ApplyArgs.push_back(archetypeValue); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// RUN: %target-swift-frontend -O -wmo -sil-existential-specializer -Xllvm -sil-disable-pass=GenericSpecializer -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xllvm -sil-disable-pass=SILCombine -emit-sil -sil-verify-all %s | %FileCheck %s | ||
|
||
internal protocol SPP { | ||
func bar() -> Int32 | ||
} | ||
internal class SCC: SPP { | ||
@inline(never) func bar() -> Int32 { | ||
return 5 | ||
} | ||
} | ||
|
||
@inline(never) internal func opt2(b:SPP) -> Int32{ | ||
return b.bar() | ||
} | ||
|
||
@inline(never) internal func opt1(b:SPP) -> Int32{ | ||
return opt2(b:b) | ||
} | ||
|
||
// CHECK-LABEL: sil hidden [noinline] @$s30existential_transform_soletype4opt11bs5Int32VAA3SPP_p_tF : $@convention(thin) (@in_guaranteed SPP) -> Int32 { | ||
// CHECK: bb0(%0 : $*SPP): | ||
// CHECK: debug_value_addr | ||
// CHECK: function_ref @$s30existential_transform_soletype4opt21bs5Int32VAA3SPP_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SPP> (@in_guaranteed τ_0_0) -> Int32 // user: %4 | ||
// CHECK: open_existential_addr | ||
// CHECK: apply | ||
// CHECK: return | ||
// CHECK-LABEL: } // end sil function '$s30existential_transform_soletype4opt11bs5Int32VAA3SPP_p_tF' | ||
|
||
// CHECK-LABEL: sil shared [noinline] @$s30existential_transform_soletype4opt21bs5Int32VAA3SPP_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SPP> (@in_guaranteed τ_0_0) -> Int32 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opt1 does not get optimized since the caller foo is under @_optimize(none). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's generally better to use |
||
// CHECK: bb0(%0 : $*τ_0_0): | ||
// CHECK: alloc_stack | ||
// CHECK: init_existential_addr | ||
// CHECK: copy_addr | ||
// CHECK: debug_value_addr | ||
// CHECK: open_existential_addr | ||
// CHECK: witness_method | ||
// CHECK: apply | ||
// CHECK: dealloc_stack | ||
// CHECK: return | ||
// CHECK-LABEL: } // end sil function '$s30existential_transform_soletype4opt21bs5Int32VAA3SPP_p_tFTf4e_n' | ||
|
||
|
||
@_optimize(none) func foo(number:Int32)->Int32 { | ||
var b:SPP | ||
if number < 5 { | ||
b = SCC() | ||
} else { | ||
b = SCC() | ||
} | ||
let x = opt1(b:b) | ||
return x | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method checks if a SoleConformingType can be determined for the argument. Some parts of code (5-6 lines) overlap with what was done in SILCombiner, but there are lot of disparities to make it a new APi in Existential.h altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atrick You had a similar concern from SILCombiner, but the overlap is minor to warrant a new API?