Skip to content

Commit 90e1214

Browse files
authored
Merge pull request #85456 from aidan-hall/pack-opt-fix-weather-swb
PackSpecialization: Fix result & type parameter handling
2 parents dd9543f + 97b7c35 commit 90e1214

File tree

4 files changed

+318
-77
lines changed

4 files changed

+318
-77
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/PackSpecialization.swift

Lines changed: 106 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -353,36 +353,45 @@ private struct CallSiteSpecializer {
353353
var directResults = resultInstruction.results.makeIterator()
354354
var substitutedResultTupleElements = [any Value]()
355355
var mappedResultPacks = self.callee.resultMap.makeIterator()
356+
var indirectResultIterator = self.apply.indirectResultOperands.makeIterator()
356357

357358
for resultInfo in self.apply.functionConvention.results {
358359
// We only need to handle direct and pack results, since indirect results are handled above
359360
if !resultInfo.isSILIndirect {
360361
// Direct results of the original function are mapped to direct results of the specialized function.
361362
substitutedResultTupleElements.append(directResults.next()!)
362363

363-
} else if resultInfo.type.shouldExplode {
364-
// Some elements of pack results may get mapped to direct results of the specialized function.
365-
// We handle those here.
366-
let mapped = mappedResultPacks.next()!
367-
368-
let originalPackArgument = self.apply.arguments[mapped.argumentIndex]
369-
let packIndices = packArgumentIndices[mapped.argumentIndex]!
370-
371-
for (mappedDirectResult, (packIndex, elementType)) in zip(
372-
mapped.expandedElements, zip(packIndices, originalPackArgument.type.packElements)
373-
)
374-
where !mappedDirectResult.isSILIndirect
375-
{
376-
377-
let result = directResults.next()!
378-
let outputResultAddress = builder.createPackElementGet(
379-
packIndex: packIndex, pack: originalPackArgument,
380-
elementType: elementType)
381-
382-
builder.createStore(
383-
source: result, destination: outputResultAddress,
384-
// The callee is responsible for initializing return pack elements
385-
ownership: storeOwnership(for: result, normal: .initialize))
364+
} else {
365+
guard let indirectResult = indirectResultIterator.next()?.value,
366+
indirectResult.type.shouldExplode
367+
else {
368+
continue
369+
}
370+
371+
do {
372+
// Some elements of pack results may get mapped to direct results of the specialized function.
373+
// We handle those here.
374+
let mapped = mappedResultPacks.next()!
375+
376+
377+
let packIndices = packArgumentIndices[mapped.argumentIndex]!
378+
379+
for (mappedDirectResult, (packIndex, elementType)) in zip(
380+
mapped.expandedElements, zip(packIndices, indirectResult.type.packElements)
381+
)
382+
where !mappedDirectResult.isSILIndirect
383+
{
384+
385+
let result = directResults.next()!
386+
let outputResultAddress = builder.createPackElementGet(
387+
packIndex: packIndex, pack: indirectResult,
388+
elementType: elementType)
389+
390+
builder.createStore(
391+
source: result, destination: outputResultAddress,
392+
// The callee is responsible for initializing return pack elements
393+
ownership: storeOwnership(for: result, normal: .initialize))
394+
}
386395
}
387396
}
388397
}
@@ -499,6 +508,14 @@ private struct PackExplodedFunction {
499508
/// Index of this pack in the function's result type tuple.
500509
/// For a non-tuple result, this is 0.
501510
let resultIndex: Int
511+
/// ResultInfo for the results produced by exploding the original result.
512+
///
513+
/// NOTE: The expandedElements members of MappedResult & MappedParameter
514+
/// correspond to slices of the [ResultInfo] and [ParameterInfo] arrays
515+
/// produced at the same time as the ResultMap & ParameterMap respectively.
516+
/// Replacing these members with integer ranges or spans referring to those
517+
/// full arrays could be an easy performance optimization if this pass
518+
/// becomes a bottleneck.
502519
let expandedElements: [ResultInfo]
503520
}
504521

@@ -510,6 +527,7 @@ private struct PackExplodedFunction {
510527
/// order.
511528
struct MappedParameter {
512529
let argumentIndex: Int
530+
/// ParameterInfo for the parameters produced by exploding the original parameter.
513531
let expandedElements: [ParameterInfo]
514532
}
515533

@@ -600,10 +618,9 @@ private struct PackExplodedFunction {
600618

601619
for (argument, parameterInfo) in zip(function.parameters, function.convention.parameters) {
602620
if argument.type.shouldExplode {
603-
604621
let mappedParameterInfos = argument.type.packElements.map { element in
605622
ParameterInfo(
606-
type: element.canonicalType,
623+
type: element.hasArchetype ? element.rawType.mapOutOfEnvironment().canonical : element.canonicalType,
607624
convention: explodedPackElementArgumentConvention(
608625
pack: parameterInfo, type: element, in: function),
609626
options: parameterInfo.options,
@@ -631,36 +648,42 @@ private struct PackExplodedFunction {
631648
var resultMap = ResultMap()
632649
var newResults = [ResultInfo]()
633650

634-
var indirectResultIdx = 0
635-
for (resultIndex, resultInfo) in function.convention.results.enumerated() {
636-
let silType = resultInfo.type.loweredType(in: function)
637-
if silType.shouldExplode {
638-
let mappedResultInfos = silType.packElements.map { elem in
639-
// TODO: Determine correct values for options and hasLoweredAddress
640-
ResultInfo(
641-
type: elem.canonicalType,
642-
convention: explodedPackElementResultConvention(in: function, type: elem),
643-
options: resultInfo.options,
644-
hasLoweredAddresses: resultInfo.hasLoweredAddresses)
645-
}
651+
var indirectResultIterator = function.arguments[0..<function.convention.indirectSILResultCount]
652+
.lazy.enumerated().makeIterator()
646653

647-
resultMap.append(
648-
MappedResult(
649-
argumentIndex: indirectResultIdx, resultIndex: resultIndex,
650-
expandedElements: mappedResultInfos))
651-
newResults += mappedResultInfos
652-
} else {
653-
// Leave the original result unchanged
654+
for (resultIndex, resultInfo) in function.convention.results.enumerated() {
655+
assert(
656+
!resultInfo.isSILIndirect || !indirectResultIterator.isEmpty,
657+
"There must be exactly as many indirect results in the function convention and argument list."
658+
)
659+
660+
guard resultInfo.isSILIndirect,
661+
// There should always be a value here (expressed by the assert above).
662+
let (indirectResultIdx, indirectResult) = indirectResultIterator.next(),
663+
indirectResult.type.shouldExplode
664+
else {
654665
newResults.append(resultInfo)
666+
continue
655667
}
656668

657-
if resultInfo.isSILIndirect {
658-
indirectResultIdx += 1
669+
let mappedResultInfos = indirectResult.type.packElements.map { element in
670+
ResultInfo(
671+
type: element.hasArchetype ? element.rawType.mapOutOfEnvironment().canonical : element.canonicalType,
672+
convention: explodedPackElementResultConvention(in: function, type: element),
673+
options: resultInfo.options,
674+
hasLoweredAddresses: resultInfo.hasLoweredAddresses)
659675
}
676+
677+
resultMap.append(
678+
MappedResult(
679+
argumentIndex: indirectResultIdx, resultIndex: resultIndex,
680+
expandedElements: mappedResultInfos))
681+
newResults += mappedResultInfos
682+
660683
}
661684

662685
assert(
663-
indirectResultIdx == function.argumentConventions.firstParameterIndex,
686+
indirectResultIterator.isEmpty,
664687
"We should have walked through all the indirect results, and no further.")
665688

666689
return (newResults, resultMap)
@@ -726,31 +749,36 @@ private struct PackExplodedFunction {
726749
let originalValue = originalReturn.returnedValue
727750

728751
let originalReturnTupleElements: [Value]
729-
if originalValue.type.isTuple {
752+
if originalValue.type.isVoid {
753+
originalReturnTupleElements = []
754+
} else if originalValue.type.isTuple {
730755
originalReturnTupleElements = [Value](
731756
builder.createDestructureTuple(tuple: originalValue).results)
732757
} else {
733758
originalReturnTupleElements = [originalValue]
734759
}
735760

736-
var returnValues = [any Value]()
737-
738761
// Thread together the original and exploded direct return values.
739-
var resultMapIndex = 0
740-
var originalReturnIndex = 0
741-
for (i, originalResult) in self.original.convention.results.enumerated()
742-
where originalResult.type.shouldExplode
743-
|| !originalResult.isSILIndirect
744-
{
745-
if !resultMap.indices.contains(resultMapIndex) || resultMap[resultMapIndex].resultIndex != i {
746-
returnValues.append(originalReturnTupleElements[originalReturnIndex])
747-
originalReturnIndex += 1
748-
749-
} else {
762+
let theReturnValues: [any Value]
763+
do {
764+
var returnValues = [any Value]()
765+
// The next original result to process.
766+
var resultIndex = 0
767+
var originalDirectResultIterator = originalReturnTupleElements.makeIterator()
768+
769+
for mappedResult in resultMap {
770+
771+
// Collect any direct results before the next mappedResult.
772+
while resultIndex < mappedResult.resultIndex {
773+
if !self.original.convention.results[resultIndex].isSILIndirect {
774+
returnValues.append(originalDirectResultIterator.next()!)
775+
}
776+
resultIndex += 1
777+
}
750778

751-
let mapped = resultMap[resultMapIndex]
779+
assert(resultIndex == mappedResult.resultIndex, "The next pack result is not skipped.")
752780

753-
let argumentMapping = argumentMap[mapped.argumentIndex]!
781+
let argumentMapping = argumentMap[mappedResult.argumentIndex]!
754782
for argument in argumentMapping.arguments {
755783

756784
switch argument.resources {
@@ -769,18 +797,26 @@ private struct PackExplodedFunction {
769797
}
770798
}
771799

772-
resultMapIndex += 1
800+
// We have finished processing mappedResult, so step forward.
801+
resultIndex += 1
773802
}
803+
804+
// Collect any remaining original direct results.
805+
while let directResult = originalDirectResultIterator.next() {
806+
returnValues.append(directResult)
807+
}
808+
809+
theReturnValues = returnValues
774810
}
775811

776-
// Return the single value directly, rather than constructing a single-element tuple for it.
777-
if returnValues.count == 1 {
778-
builder.createReturn(of: returnValues[0])
812+
// Return a single return value directly, rather than constructing a single-element tuple for it.
813+
if theReturnValues.count == 1 {
814+
builder.createReturn(of: theReturnValues[0])
779815
} else {
780-
let tupleElementTypes = returnValues.map { $0.type }
816+
let tupleElementTypes = theReturnValues.map { $0.type }
781817
let tupleType = context.getTupleType(elements: tupleElementTypes).loweredType(
782818
in: specialized)
783-
let tuple = builder.createTuple(type: tupleType, elements: returnValues)
819+
let tuple = builder.createTuple(type: tupleType, elements: theReturnValues)
784820
builder.createReturn(of: tuple)
785821
}
786822

@@ -1026,7 +1062,7 @@ private func loadOwnership(for value: any Value, normal: LoadInst.LoadOwnership)
10261062
}
10271063
}
10281064

1029-
extension TypeProperties {
1065+
extension Type {
10301066
/// A pack argument can explode if it contains no pack expansion types
10311067
fileprivate var shouldExplode: Bool {
10321068
// For now, we only attempt to explode indirect packs, since these are the most common and inefficient.

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ void addFunctionPasses(SILPassPipelinePlan &P,
528528
// of embedded Swift.
529529
if (!P.getOptions().EmbeddedSwift) {
530530
P.addGenericSpecializer();
531-
// P.addPackSpecialization();
531+
P.addPackSpecialization();
532532
// Run devirtualizer after the specializer, because many
533533
// class_method/witness_method instructions may use concrete types now.
534534
P.addDevirtualizer();

0 commit comments

Comments
 (0)