Merge pull request #9931 from CodaFi/what-does-brian-eno
diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp
index 626cb05..2bf4152 100644
--- a/lib/SIL/DynamicCasts.cpp
+++ b/lib/SIL/DynamicCasts.cpp
@@ -89,15 +89,24 @@
if (source == target)
return DynamicCastFeasibility::WillSucceed;
- auto *SourceNominalTy = source.getAnyNominal();
-
- if (!SourceNominalTy)
- return DynamicCastFeasibility::MaySucceed;
-
auto *TargetProtocol = target.getAnyNominal();
if (!TargetProtocol)
return DynamicCastFeasibility::MaySucceed;
+ auto *SourceNominalTy = source.getAnyNominal();
+
+ if (!SourceNominalTy) {
+ if (auto Archetype = dyn_cast<ArchetypeType>(source)) {
+ auto SourceProtocols = Archetype->getConformsTo();
+ // Check all protocols implemented by the archetype.
+ for (auto *Protocol : SourceProtocols) {
+ if (Protocol == TargetProtocol)
+ return DynamicCastFeasibility::WillSucceed;
+ }
+ }
+ return DynamicCastFeasibility::MaySucceed;
+ }
+
auto SourceProtocols = SourceNominalTy->getAllProtocols();
// Check all protocols implemented by the type.
@@ -330,11 +339,8 @@
if (source->hasArchetype() || source.isExistentialType() ||
target->hasArchetype() || target.isExistentialType()) {
- auto *SourceNominalTy = source.getAnyNominal();
-
// Check conversions from non-protocol types into protocol types.
if (!source.isExistentialType() &&
- SourceNominalTy &&
target.isExistentialType())
return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);
diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp
index e4c9d7d..638a3e5 100644
--- a/lib/SILOptimizer/Utils/Local.cpp
+++ b/lib/SILOptimizer/Utils/Local.cpp
@@ -2458,6 +2458,104 @@
}
}
+/// TODO: Move to emitSuccessfulIndirectUnconditionalCast?
+///
+/// Peephole to avoid runtime calls:
+/// unconditional_checked_cast_addr T in %0 : $*T to P in %1 : $*P
+/// ->
+/// %addr = init_existential_addr %1 : $*P, T
+/// copy_addr %0 to %addr
+///
+/// where T is a type statically known to conform to P.
+///
+/// In caase P is a class existential type, it generates:
+/// %val = load %0 : $*T
+/// %existential = init_existential_ref %val : $T, $T, P
+/// store %existential to %1 : $*P
+///
+/// Returns true if the optimization was possible and false otherwise.
+static bool optimizeStaticallyKnownProtocolConformance(
+ UnconditionalCheckedCastAddrInst *Inst) {
+ auto Loc = Inst->getLoc();
+ auto Src = Inst->getSrc();
+ auto Dest = Inst->getDest();
+ auto SourceType = Inst->getSourceType();
+ auto TargetType = Inst->getTargetType();
+ auto &Mod = Inst->getModule();
+
+ if (TargetType->isAnyExistentialType() &&
+ !SourceType->isAnyExistentialType()) {
+ auto &Ctx = Mod.getASTContext();
+ auto *SM = Mod.getSwiftModule();
+
+ auto Proto = dyn_cast<ProtocolDecl>(TargetType->getAnyNominal());
+ if (Proto) {
+ auto Conformance = SM->lookupConformance(SourceType, Proto, nullptr);
+ if (Conformance.hasValue()) {
+ // SourceType is a non-existential type conforming to a
+ // protocol represented by the TargetType.
+ SILBuilder B(Inst);
+ SmallVector<ProtocolConformanceRef, 1> NewConformances;
+ NewConformances.push_back(Conformance.getValue());
+ ArrayRef<ProtocolConformanceRef> Conformances =
+ Ctx.AllocateCopy(NewConformances);
+
+ auto ExistentialRepr =
+ Dest->getType().getPreferredExistentialRepresentation(Mod,
+ SourceType);
+
+ switch (ExistentialRepr) {
+ default:
+ return false;
+ case ExistentialRepresentation::Opaque: {
+ auto ExistentialAddr = B.createInitExistentialAddr(
+ Loc, Dest, SourceType, Src->getType().getObjectType(),
+ Conformances);
+ B.createCopyAddr(
+ Loc, Src, ExistentialAddr,
+ (Inst->getConsumptionKind() != CastConsumptionKind::CopyOnSuccess)
+ ? IsTake_t::IsTake
+ : IsTake_t::IsNotTake,
+ IsInitialization_t::IsInitialization);
+ break;
+ }
+ case ExistentialRepresentation::Class: {
+ auto Value = B.createLoad(Loc, Src,
+ swift::LoadOwnershipQualifier::Unqualified);
+ if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
+ B.createRetainValue(Loc, Value, B.getDefaultAtomicity());
+ auto Existential =
+ B.createInitExistentialRef(Loc, Dest->getType().getObjectType(),
+ SourceType, Value, Conformances);
+ B.createStore(Loc, Existential, Dest,
+ swift::StoreOwnershipQualifier::Unqualified);
+ break;
+ }
+ case ExistentialRepresentation::Boxed: {
+ auto AllocBox = B.createAllocExistentialBox(Loc, Dest->getType(),
+ SourceType, Conformances);
+ auto Projection =
+ B.createProjectExistentialBox(Loc, Src->getType(), AllocBox);
+ auto Value = B.createLoad(Loc, Src,
+ swift::LoadOwnershipQualifier::Unqualified);
+ if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
+ B.createRetainValue(Loc, Value, B.getDefaultAtomicity());
+ B.createStore(Loc, Value, Projection,
+ swift::StoreOwnershipQualifier::Unqualified);
+ B.createStore(Loc, AllocBox, Dest,
+ swift::StoreOwnershipQualifier::Unqualified);
+ break;
+ }
+ };
+
+ Inst->replaceAllUsesWithUndef();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
SILInstruction *
CastOptimizer::
optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst) {
@@ -2512,18 +2610,24 @@
if (Feasibility == DynamicCastFeasibility::WillSucceed ||
Feasibility == DynamicCastFeasibility::MaySucceed) {
+ // Check if a result of a cast is unused. If this is the case, the cast can
+ // be removed even if the cast may fail at runtime.
+ // Swift optimizer does not claim to be crash-preserving.
bool ResultNotUsed = isa<AllocStackInst>(Dest);
DestroyAddrInst *DestroyDestInst = nullptr;
- for (auto Use : Dest->getUses()) {
- auto *User = Use->getUser();
- if (isa<DeallocStackInst>(User) || User == Inst)
- continue;
- if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
- DestroyDestInst = cast<DestroyAddrInst>(User);
- continue;
+ if (ResultNotUsed) {
+ for (auto Use : Dest->getUses()) {
+ auto *User = Use->getUser();
+ if (isa<DeallocStackInst>(User) || User == Inst)
+ continue;
+ if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
+ DestroyDestInst = cast<DestroyAddrInst>(User);
+ continue;
+ }
+ ResultNotUsed = false;
+ DestroyDestInst = nullptr;
+ break;
}
- ResultNotUsed = false;
- break;
}
if (ResultNotUsed) {
@@ -2544,23 +2648,33 @@
return nullptr;
}
- // Try to apply the bridged casts optimizations
+ // Try to apply the bridged casts optimizations.
auto NewI = optimizeBridgedCasts(Inst, Inst->getConsumptionKind(),
false, Src, Dest, SourceType,
TargetType, nullptr, nullptr);
if (NewI) {
- WillSucceedAction();
- return nullptr;
+ WillSucceedAction();
+ return nullptr;
+ }
+
+ if (Feasibility == DynamicCastFeasibility::MaySucceed)
+ return nullptr;
+
+ assert(Feasibility == DynamicCastFeasibility::WillSucceed);
+
+ if (optimizeStaticallyKnownProtocolConformance(Inst)) {
+ EraseInstAction(Inst);
+ WillSucceedAction();
+ return nullptr;
}
if (isBridgingCast(SourceType, TargetType))
return nullptr;
SILBuilderWithScope Builder(Inst);
- if (!emitSuccessfulIndirectUnconditionalCast(Builder, Mod.getSwiftModule(),
- Loc, Inst->getConsumptionKind(),
- Src, SourceType,
- Dest, TargetType, Inst)) {
+ if (!emitSuccessfulIndirectUnconditionalCast(
+ Builder, Mod.getSwiftModule(), Loc, Inst->getConsumptionKind(), Src,
+ SourceType, Dest, TargetType, Inst)) {
// No optimization was possible.
return nullptr;
}
diff --git a/stdlib/public/SwiftShims/LibcShims.h b/stdlib/public/SwiftShims/LibcShims.h
index 636c587..e631905 100644
--- a/stdlib/public/SwiftShims/LibcShims.h
+++ b/stdlib/public/SwiftShims/LibcShims.h
@@ -33,7 +33,7 @@
// This declaration is not universally correct. We verify its correctness for
// the current platform in the runtime code.
-#if defined(__linux__) && defined (__arm__) && !defined(__android__)
+#if defined(__linux__) && defined (__arm__) && !defined(__ANDROID__)
typedef int __swift_ssize_t;
#elif defined(_WIN32)
#if defined(_M_ARM) || defined(_M_IX86)
@@ -113,7 +113,9 @@
// TLS - thread local storage
-#if defined(__linux__)
+#if defined(__ANDROID__)
+typedef int __swift_pthread_key_t;
+#elif defined(__linux__)
typedef unsigned int __swift_pthread_key_t;
#else
typedef unsigned long __swift_pthread_key_t;
diff --git a/test/SILOptimizer/constant_propagation.sil b/test/SILOptimizer/constant_propagation.sil
index 90d483a..48e9e17 100644
--- a/test/SILOptimizer/constant_propagation.sil
+++ b/test/SILOptimizer/constant_propagation.sil
@@ -1,6 +1,7 @@
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -performance-constant-propagation | %FileCheck %s
+import Swift
import Builtin
struct UInt {
@@ -692,3 +693,254 @@
dealloc_stack %31 : $*AnObject
br bb1
}
+
+public protocol P {
+}
+
+public protocol PP {
+}
+
+struct X : P {
+}
+
+struct Y<T>: P {
+ var x : T
+}
+
+class Z: P {
+ init()
+}
+
+// Do not optimize casts to unrelated protocols.
+// CHECK-LABEL: sil @dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential
+// CHECK: unconditional_checked_cast_addr
+// CHECK: end sil function 'dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential'
+sil @dont_replace_unconditional_check_cast_addr_for_type_to_unrelated_existential : $@convention(thin) (@in X) -> (@out PP) {
+bb0(%0 : $*PP, %1 : $*X):
+ unconditional_checked_cast_addr take_always X in %1 : $*X to PP in %0 : $*PP
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// Do not optimize casts between existentials.
+// CHECK-LABEL: sil @dont_replace_unconditional_check_cast_addr_for_existential_to_existential
+// CHECK: unconditional_checked_cast_addr
+// CHECK: end sil function 'dont_replace_unconditional_check_cast_addr_for_existential_to_existential'
+sil @dont_replace_unconditional_check_cast_addr_for_existential_to_existential : $@convention(thin) (@in PP) -> (@out P) {
+bb0(%0 : $*P, %1 : $*PP):
+ unconditional_checked_cast_addr take_always PP in %1 : $*PP to P in %0 : $*P
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// Check that an unconditional_checked_cast_addr from a non-existential loadable type to a protocol
+// can be replaced by a more efficient code sequence if it is statitcally known that this
+// type conforms to this protocol.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential
+// CHECK-NOT: store
+// CHECK: copy_addr
+// CHECK-NOT: destroy_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_type_to_existential : $@convention(thin) (@in X) -> (@out P) {
+bb0(%0 : $*P, %1 : $*X):
+ unconditional_checked_cast_addr take_always X in %1 : $*X to P in %0 : $*P
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_class_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_class_to_existential : $@convention(thin) (@in Z) -> (@out P) {
+bb0(%0 : $*P, %1 : $*Z):
+ unconditional_checked_cast_addr take_always Z in %1 : $*Z to P in %0 : $*P
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_archetype_to_existential : $@convention(thin) <X:P> (@in X) -> (@out P) {
+bb0(%0 : $*P, %1 : $*X):
+ unconditional_checked_cast_addr take_always X in %1 : $*X to P in %0 : $*P
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_generic_type_to_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+sil @replace_unconditional_check_cast_addr_for_generic_type_to_existential : $@convention(thin) <X:P> (@in Y<X>) -> (@out P) {
+bb0(%0 : $*P, %1 : $*Y<X>):
+ unconditional_checked_cast_addr take_always Y<X> in %1 : $*Y<X> to P in %0 : $*P
+ %2 = tuple ()
+ return %2 : $()
+}
+
+protocol Q : class {
+}
+
+class V : Q {
+ init()
+}
+
+class W<T>: Q {
+ var x : T
+ init()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_class_existential : $@convention(thin) (@in V) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*V):
+ unconditional_checked_cast_addr take_always V in %1 : $*V to Q in %0 : $*Q
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_class_existential_copy_on_success
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_class_existential_copy_on_success : $@convention(thin) (@in V) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*V):
+ unconditional_checked_cast_addr copy_on_success V in %1 : $*V to Q in %0 : $*Q
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_archetype_to_class_existential : $@convention(thin) <X:Q> (@in X) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*X):
+ unconditional_checked_cast_addr take_always X in %1 : $*X to Q in %0 : $*Q
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_to_class_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK-NOT: retain_value
+// CHECK: init_existential_ref
+// CHECK-NOT: retain_value
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_to_class_existential : $@convention(thin) <X:Q> (@in W<X>) -> (@out Q) {
+bb0(%0 : $*Q, %1 : $*W<X>):
+ unconditional_checked_cast_addr take_always W<X> in %1 : $*W<X> to Q in %0 : $*Q
+ %2 = tuple ()
+ return %2 : $()
+}
+
+public protocol MyError : Error {
+}
+
+public class E1 : MyError {
+ init()
+}
+
+public class E2<T> : MyError {
+ init()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential : $@convention(thin) (@in E1) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E1):
+ unconditional_checked_cast_addr take_always E1 in %1 : $*E1 to MyError in %0 : $*MyError
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential_copy_on_success
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// There should be no [take] in copy_addr!
+// CHECK: copy_addr %{{.}} to [initialization]
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_myerror_existential_copy_on_success : $@convention(thin) (@in E1) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E1):
+ unconditional_checked_cast_addr copy_on_success E1 in %1 : $*E1 to MyError in %0 : $*MyError
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_archetype_to_myerror_existentia
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_archetype_to_myerror_existential : $@convention(thin) <X:MyError> (@in X) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*X):
+ unconditional_checked_cast_addr take_always X in %1 : $*X to MyError in %0 : $*MyError
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_to_myerror_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_to_myerror_existential : $@convention(thin) <X:MyError> (@in E2<X>) -> (@out MyError) {
+bb0(%0 : $*MyError, %1 : $*E2<X>):
+ unconditional_checked_cast_addr take_always E2<X> in %1 : $*E2<X> to MyError in %0 : $*MyError
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// Check casts to Error.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_error_existential
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: [[ALLOC_BOX:%.*]] = alloc_existential_box $Error, $E1
+// CHECK: [[PROJ:%.*]] = project_existential_box $E1 in [[ALLOC_BOX]] : $Error
+// CHECK: [[VAL:%.*]] = load %1 : $*E1
+// CHECK: store [[VAL]] to [[PROJ]]
+// CHECK: store [[ALLOC_BOX]] to %0 : $*Error
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_error_existential : $@convention(thin) (@in E1) -> (@out Error) {
+bb0(%0 : $*Error, %1 : $*E1):
+ unconditional_checked_cast_addr take_always E1 in %1 : $*E1 to Error in %0 : $*Error
+ %2 = tuple ()
+ return %2 : $()
+}
+
+// Check casts to Error.
+// CHECK-LABEL: sil @replace_unconditional_check_cast_addr_for_type_to_error_existential_copy_on_success
+// CHECK-NOT: unconditional_checked_cast_addr
+// CHECK: [[ALLOC_BOX:%.*]] = alloc_existential_box $Error, $E1
+// CHECK: [[PROJ:%.*]] = project_existential_box $E1 in [[ALLOC_BOX]] : $Error
+// CHECK: [[VAL:%.*]] = load %1 : $*E1
+// CHECK: retain_value [[VAL]]
+// CHECK: store [[VAL]] to [[PROJ]]
+// CHECK: store [[ALLOC_BOX]] to %0 : $*Error
+// CHECK: return
+sil @replace_unconditional_check_cast_addr_for_type_to_error_existential_copy_on_success : $@convention(thin) (@in E1) -> (@out Error) {
+bb0(%0 : $*Error, %1 : $*E1):
+ unconditional_checked_cast_addr copy_on_success E1 in %1 : $*E1 to Error in %0 : $*Error
+ %2 = tuple ()
+ return %2 : $()
+}
diff --git a/test/SILOptimizer/sil_combine_enum_addr.sil b/test/SILOptimizer/sil_combine_enum_addr.sil
index 8493ee9..a525fbf 100644
--- a/test/SILOptimizer/sil_combine_enum_addr.sil
+++ b/test/SILOptimizer/sil_combine_enum_addr.sil
@@ -6,7 +6,7 @@
import Swift
// CHECK-LABEL: sil @convert_inject_enum_addr_select_enum_addr_into_cond_br : $@convention(thin) (@in Int, @inout _Stdout) -> ()
-// CHECK: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
// CHECK: inject_enum_addr
// CHECK-NOT: select_enum_addr
// CHECK-NOT: bb1
@@ -42,7 +42,7 @@
// CHECK-LABEL: sil @convert_inject_enum_addr_switch_enum_addr_into_cond_br : $@convention(thin) (@in Int, @inout _Stdout) -> ()
-// CHECK: unconditional_checked_cast_addr
+// CHECK: init_existential_addr
// CHECK: inject_enum_addr
// CHECK-NOT: switch_enum_addr
// CHECK-NOT: bb1