Skip to content

Commit 9d25009

Browse files
authored
Merge pull request #80025 from eeckstein/optimize-array-cast
Simplification: remove redundant array casts
2 parents bac5232 + 04ede10 commit 9d25009

File tree

4 files changed

+85
-1
lines changed

4 files changed

+85
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift

+44
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ extension ApplyInst : OnoneSimplifiable, SILCombineSimplifiable {
2525
if context.tryDevirtualize(apply: self, isMandatory: false) != nil {
2626
return
2727
}
28+
if tryRemoveArrayCast(apply: self, context) {
29+
return
30+
}
2831
if !context.preserveDebugInfo {
2932
_ = tryReplaceExistentialArchetype(of: self, context)
3033
}
@@ -73,6 +76,40 @@ private func tryTransformThickToThinCallee(of apply: ApplyInst, _ context: Simpl
7376
return false
7477
}
7578

79+
/// Removes casts between arrays of the same type.
80+
///
81+
/// %1 = function_ref @_arrayConditionalCast : (@guaranteed Array<Int>) -> @owned Optional<Array<Int>>
82+
/// %2 = apply %1(%0) : (@guaranteed Array<Int>) -> @owned Optional<Array<Int>>
83+
/// ->
84+
/// %1 = copy_value %0
85+
/// %2 = enum $Optional<Array<Int>>, #Optional.some!enumelt, %1
86+
///
87+
private func tryRemoveArrayCast(apply: ApplyInst, _ context: SimplifyContext) -> Bool {
88+
guard let callee = apply.referencedFunction,
89+
callee.hasSemanticsAttribute("array.conditional_cast"),
90+
apply.parentFunction.hasOwnership,
91+
92+
// Check if the cast function has the expected calling convention
93+
apply.arguments.count == 1,
94+
apply.convention(of: apply.argumentOperands[0]) == .directGuaranteed,
95+
apply.functionConvention.results[0].convention == .owned,
96+
apply.type.isOptional,
97+
98+
// Check if the source and target type of the cast is identical.
99+
// Note that we are checking the _formal_ element types and not the lowered types, because
100+
// the element types are replacement type in the Array's substitution map and this is a formal type.
101+
apply.arguments[0].type == apply.type.optionalPayloadType(in: apply.parentFunction)
102+
else {
103+
return false
104+
}
105+
106+
let builder = Builder(after: apply, context)
107+
let copiedArray = builder.createCopyValue(operand: apply.arguments[0])
108+
let optional = builder.createEnum(caseIndex: 1, payload: copiedArray, enumType: apply.type)
109+
apply.replace(with: optional, context)
110+
return true
111+
}
112+
76113
/// If the apply uses an existential archetype (`@opened("...")`) and the concrete type is known,
77114
/// replace the existential archetype with the concrete type
78115
/// 1. in the apply's substitution map
@@ -199,3 +236,10 @@ private extension FullApplySite {
199236
return SubstitutionMap(genericSignature: genSig, replacementTypes: newReplacementTypes)
200237
}
201238
}
239+
240+
private extension Type {
241+
func optionalPayloadType(in function: Function) -> Type {
242+
let subs = contextSubstitutionMap
243+
return subs.replacementTypes[0].loweredType(in: function)
244+
}
245+
}

stdlib/public/core/ArrayCast.swift

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ internal func _arrayDownCastConditionalIndirect<SourceValue, TargetValue>(
7171
///
7272
/// - Complexity: O(n), because each element must be checked.
7373
@inlinable //for performance reasons
74+
@_semantics("array.conditional_cast")
7475
public func _arrayConditionalCast<SourceElement, TargetElement>(
7576
_ source: [SourceElement]
7677
) -> [TargetElement]? {

test/SILOptimizer/cast_folding.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -O -Xllvm -sil-print-types -emit-sil %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -O -Xllvm -sil-print-types -Xllvm -sil-disable-pass=function-signature-opts -emit-sil %s | %FileCheck %s
22

33
// We want to check two things here:
44
// - Correctness
@@ -723,6 +723,19 @@ func test33() -> Bool {
723723
return cast33(A())
724724
}
725725

726+
func castArray<T>(from: [T]) -> [Int]? {
727+
return from as? [Int]
728+
}
729+
730+
// CHECK-LABEL: sil @$s12cast_folding13callCastArray1aSaySiGSgAD_tF :
731+
// CHECK-NOT: apply
732+
// CHECK: [[E:%.*]] = enum $Optional<Array<Int>>, #Optional.some!enumelt, %0
733+
// CHECK-NOT: apply
734+
// CHECK: return [[E]]
735+
// CHECK: } // end sil function '$s12cast_folding13callCastArray1aSaySiGSgAD_tF'
736+
public func callCastArray(a: [Int]) -> [Int]? {
737+
return castArray(from: a)
738+
}
726739

727740
protocol PP {
728741
func foo() -> Int

test/SILOptimizer/simplify_apply.sil

+26
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,29 @@ bb0(%0 : $Int):
139139
return %11
140140
}
141141

142+
sil [_semantics "array.conditional_cast"] @cast_int_array : $@convention(thin) (@guaranteed Array<Int>) -> @owned Optional<Array<Int>>
143+
sil [_semantics "array.conditional_cast"] @cast_any_int_array : $@convention(thin) (@guaranteed Array<Any>) -> @owned Optional<Array<Int>>
144+
145+
// CHECK-LABEL: sil [ossa] @remove_array_cast :
146+
// CHECK: %1 = copy_value %0
147+
// CHECK: %2 = enum $Optional<Array<Int>>, #Optional.some!enumelt, %1
148+
// CHECK: return %2
149+
// CHECK: } // end sil function 'remove_array_cast'
150+
sil [ossa] @remove_array_cast : $@convention(thin) (@guaranteed Array<Int>) -> @owned Optional<Array<Int>> {
151+
bb0(%0 : @guaranteed $Array<Int>):
152+
%1 = function_ref @cast_int_array : $@convention(thin) (@guaranteed Array<Int>) -> @owned Optional<Array<Int>>
153+
%2 = apply %1(%0) : $@convention(thin) (@guaranteed Array<Int>) -> @owned Optional<Array<Int>>
154+
return %2
155+
}
156+
157+
// CHECK-LABEL: sil [ossa] @dont_remove_array_cast :
158+
// CHECK: %2 = apply
159+
// CHECK: return %2
160+
// CHECK: } // end sil function 'dont_remove_array_cast'
161+
sil [ossa] @dont_remove_array_cast : $@convention(thin) (@guaranteed Array<Any>) -> @owned Optional<Array<Int>> {
162+
bb0(%0 : @guaranteed $Array<Any>):
163+
%1 = function_ref @cast_any_int_array : $@convention(thin) (@guaranteed Array<Any>) -> @owned Optional<Array<Int>>
164+
%2 = apply %1(%0) : $@convention(thin) (@guaranteed Array<Any>) -> @owned Optional<Array<Int>>
165+
return %2
166+
}
167+

0 commit comments

Comments
 (0)