mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
PackSpecialization: Fix result & type parameter handling
Refactor certain functions to make them simpler. and avoid calling AST.Type.loweredType, which can fail. Instead, access the types of the function's (SIL) arguments directly. Correctly handle exploding packs that contain generic or opaque types by using AST.Type.mapOutOfEnvironment(). @substituted types cause the shouldExplode predicate to be unreliable for AST types, so restrict it to just SIL.Type. Add test cases for functions that have @substituted types. Re-enable PackSpecialization in FunctionPass pipeline. Add a check to avoid emitting a destructure_tuple of the original function's return tuple when it is void/().
This commit is contained in:
@@ -353,6 +353,7 @@ private struct CallSiteSpecializer {
|
|||||||
var directResults = resultInstruction.results.makeIterator()
|
var directResults = resultInstruction.results.makeIterator()
|
||||||
var substitutedResultTupleElements = [any Value]()
|
var substitutedResultTupleElements = [any Value]()
|
||||||
var mappedResultPacks = self.callee.resultMap.makeIterator()
|
var mappedResultPacks = self.callee.resultMap.makeIterator()
|
||||||
|
var indirectResultIterator = self.apply.indirectResultOperands.makeIterator()
|
||||||
|
|
||||||
for resultInfo in self.apply.functionConvention.results {
|
for resultInfo in self.apply.functionConvention.results {
|
||||||
// We only need to handle direct and pack results, since indirect results are handled above
|
// We only need to handle direct and pack results, since indirect results are handled above
|
||||||
@@ -360,29 +361,37 @@ private struct CallSiteSpecializer {
|
|||||||
// Direct results of the original function are mapped to direct results of the specialized function.
|
// Direct results of the original function are mapped to direct results of the specialized function.
|
||||||
substitutedResultTupleElements.append(directResults.next()!)
|
substitutedResultTupleElements.append(directResults.next()!)
|
||||||
|
|
||||||
} else if resultInfo.type.shouldExplode {
|
} else {
|
||||||
// Some elements of pack results may get mapped to direct results of the specialized function.
|
guard let indirectResult = indirectResultIterator.next()?.value,
|
||||||
// We handle those here.
|
indirectResult.type.shouldExplode
|
||||||
let mapped = mappedResultPacks.next()!
|
else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
let originalPackArgument = self.apply.arguments[mapped.argumentIndex]
|
do {
|
||||||
let packIndices = packArgumentIndices[mapped.argumentIndex]!
|
// Some elements of pack results may get mapped to direct results of the specialized function.
|
||||||
|
// We handle those here.
|
||||||
|
let mapped = mappedResultPacks.next()!
|
||||||
|
|
||||||
for (mappedDirectResult, (packIndex, elementType)) in zip(
|
|
||||||
mapped.expandedElements, zip(packIndices, originalPackArgument.type.packElements)
|
|
||||||
)
|
|
||||||
where !mappedDirectResult.isSILIndirect
|
|
||||||
{
|
|
||||||
|
|
||||||
let result = directResults.next()!
|
let packIndices = packArgumentIndices[mapped.argumentIndex]!
|
||||||
let outputResultAddress = builder.createPackElementGet(
|
|
||||||
packIndex: packIndex, pack: originalPackArgument,
|
|
||||||
elementType: elementType)
|
|
||||||
|
|
||||||
builder.createStore(
|
for (mappedDirectResult, (packIndex, elementType)) in zip(
|
||||||
source: result, destination: outputResultAddress,
|
mapped.expandedElements, zip(packIndices, indirectResult.type.packElements)
|
||||||
// The callee is responsible for initializing return pack elements
|
)
|
||||||
ownership: storeOwnership(for: result, normal: .initialize))
|
where !mappedDirectResult.isSILIndirect
|
||||||
|
{
|
||||||
|
|
||||||
|
let result = directResults.next()!
|
||||||
|
let outputResultAddress = builder.createPackElementGet(
|
||||||
|
packIndex: packIndex, pack: indirectResult,
|
||||||
|
elementType: elementType)
|
||||||
|
|
||||||
|
builder.createStore(
|
||||||
|
source: result, destination: outputResultAddress,
|
||||||
|
// The callee is responsible for initializing return pack elements
|
||||||
|
ownership: storeOwnership(for: result, normal: .initialize))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,6 +508,14 @@ private struct PackExplodedFunction {
|
|||||||
/// Index of this pack in the function's result type tuple.
|
/// Index of this pack in the function's result type tuple.
|
||||||
/// For a non-tuple result, this is 0.
|
/// For a non-tuple result, this is 0.
|
||||||
let resultIndex: Int
|
let resultIndex: Int
|
||||||
|
/// ResultInfo for the results produced by exploding the original result.
|
||||||
|
///
|
||||||
|
/// NOTE: The expandedElements members of MappedResult & MappedParameter
|
||||||
|
/// correspond to slices of the [ResultInfo] and [ParameterInfo] arrays
|
||||||
|
/// produced at the same time as the ResultMap & ParameterMap respectively.
|
||||||
|
/// Replacing these members with integer ranges or spans referring to those
|
||||||
|
/// full arrays could be an easy performance optimization if this pass
|
||||||
|
/// becomes a bottleneck.
|
||||||
let expandedElements: [ResultInfo]
|
let expandedElements: [ResultInfo]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,6 +527,7 @@ private struct PackExplodedFunction {
|
|||||||
/// order.
|
/// order.
|
||||||
struct MappedParameter {
|
struct MappedParameter {
|
||||||
let argumentIndex: Int
|
let argumentIndex: Int
|
||||||
|
/// ParameterInfo for the parameters produced by exploding the original parameter.
|
||||||
let expandedElements: [ParameterInfo]
|
let expandedElements: [ParameterInfo]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,10 +618,9 @@ private struct PackExplodedFunction {
|
|||||||
|
|
||||||
for (argument, parameterInfo) in zip(function.parameters, function.convention.parameters) {
|
for (argument, parameterInfo) in zip(function.parameters, function.convention.parameters) {
|
||||||
if argument.type.shouldExplode {
|
if argument.type.shouldExplode {
|
||||||
|
|
||||||
let mappedParameterInfos = argument.type.packElements.map { element in
|
let mappedParameterInfos = argument.type.packElements.map { element in
|
||||||
ParameterInfo(
|
ParameterInfo(
|
||||||
type: element.canonicalType,
|
type: element.hasArchetype ? element.rawType.mapOutOfEnvironment().canonical : element.canonicalType,
|
||||||
convention: explodedPackElementArgumentConvention(
|
convention: explodedPackElementArgumentConvention(
|
||||||
pack: parameterInfo, type: element, in: function),
|
pack: parameterInfo, type: element, in: function),
|
||||||
options: parameterInfo.options,
|
options: parameterInfo.options,
|
||||||
@@ -631,36 +648,42 @@ private struct PackExplodedFunction {
|
|||||||
var resultMap = ResultMap()
|
var resultMap = ResultMap()
|
||||||
var newResults = [ResultInfo]()
|
var newResults = [ResultInfo]()
|
||||||
|
|
||||||
var indirectResultIdx = 0
|
var indirectResultIterator = function.arguments[0..<function.convention.indirectSILResultCount]
|
||||||
|
.lazy.enumerated().makeIterator()
|
||||||
|
|
||||||
for (resultIndex, resultInfo) in function.convention.results.enumerated() {
|
for (resultIndex, resultInfo) in function.convention.results.enumerated() {
|
||||||
let silType = resultInfo.type.loweredType(in: function)
|
assert(
|
||||||
if silType.shouldExplode {
|
!resultInfo.isSILIndirect || !indirectResultIterator.isEmpty,
|
||||||
let mappedResultInfos = silType.packElements.map { elem in
|
"There must be exactly as many indirect results in the function convention and argument list."
|
||||||
// TODO: Determine correct values for options and hasLoweredAddress
|
)
|
||||||
ResultInfo(
|
|
||||||
type: elem.canonicalType,
|
|
||||||
convention: explodedPackElementResultConvention(in: function, type: elem),
|
|
||||||
options: resultInfo.options,
|
|
||||||
hasLoweredAddresses: resultInfo.hasLoweredAddresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
resultMap.append(
|
guard resultInfo.isSILIndirect,
|
||||||
MappedResult(
|
// There should always be a value here (expressed by the assert above).
|
||||||
argumentIndex: indirectResultIdx, resultIndex: resultIndex,
|
let (indirectResultIdx, indirectResult) = indirectResultIterator.next(),
|
||||||
expandedElements: mappedResultInfos))
|
indirectResult.type.shouldExplode
|
||||||
newResults += mappedResultInfos
|
else {
|
||||||
} else {
|
|
||||||
// Leave the original result unchanged
|
|
||||||
newResults.append(resultInfo)
|
newResults.append(resultInfo)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if resultInfo.isSILIndirect {
|
let mappedResultInfos = indirectResult.type.packElements.map { element in
|
||||||
indirectResultIdx += 1
|
ResultInfo(
|
||||||
|
type: element.hasArchetype ? element.rawType.mapOutOfEnvironment().canonical : element.canonicalType,
|
||||||
|
convention: explodedPackElementResultConvention(in: function, type: element),
|
||||||
|
options: resultInfo.options,
|
||||||
|
hasLoweredAddresses: resultInfo.hasLoweredAddresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resultMap.append(
|
||||||
|
MappedResult(
|
||||||
|
argumentIndex: indirectResultIdx, resultIndex: resultIndex,
|
||||||
|
expandedElements: mappedResultInfos))
|
||||||
|
newResults += mappedResultInfos
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
indirectResultIdx == function.argumentConventions.firstParameterIndex,
|
indirectResultIterator.isEmpty,
|
||||||
"We should have walked through all the indirect results, and no further.")
|
"We should have walked through all the indirect results, and no further.")
|
||||||
|
|
||||||
return (newResults, resultMap)
|
return (newResults, resultMap)
|
||||||
@@ -726,31 +749,36 @@ private struct PackExplodedFunction {
|
|||||||
let originalValue = originalReturn.returnedValue
|
let originalValue = originalReturn.returnedValue
|
||||||
|
|
||||||
let originalReturnTupleElements: [Value]
|
let originalReturnTupleElements: [Value]
|
||||||
if originalValue.type.isTuple {
|
if originalValue.type.isVoid {
|
||||||
|
originalReturnTupleElements = []
|
||||||
|
} else if originalValue.type.isTuple {
|
||||||
originalReturnTupleElements = [Value](
|
originalReturnTupleElements = [Value](
|
||||||
builder.createDestructureTuple(tuple: originalValue).results)
|
builder.createDestructureTuple(tuple: originalValue).results)
|
||||||
} else {
|
} else {
|
||||||
originalReturnTupleElements = [originalValue]
|
originalReturnTupleElements = [originalValue]
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnValues = [any Value]()
|
|
||||||
|
|
||||||
// Thread together the original and exploded direct return values.
|
// Thread together the original and exploded direct return values.
|
||||||
var resultMapIndex = 0
|
let theReturnValues: [any Value]
|
||||||
var originalReturnIndex = 0
|
do {
|
||||||
for (i, originalResult) in self.original.convention.results.enumerated()
|
var returnValues = [any Value]()
|
||||||
where originalResult.type.shouldExplode
|
// The next original result to process.
|
||||||
|| !originalResult.isSILIndirect
|
var resultIndex = 0
|
||||||
{
|
var originalDirectResultIterator = originalReturnTupleElements.makeIterator()
|
||||||
if !resultMap.indices.contains(resultMapIndex) || resultMap[resultMapIndex].resultIndex != i {
|
|
||||||
returnValues.append(originalReturnTupleElements[originalReturnIndex])
|
|
||||||
originalReturnIndex += 1
|
|
||||||
|
|
||||||
} else {
|
for mappedResult in resultMap {
|
||||||
|
|
||||||
let mapped = resultMap[resultMapIndex]
|
// Collect any direct results before the next mappedResult.
|
||||||
|
while resultIndex < mappedResult.resultIndex {
|
||||||
|
if !self.original.convention.results[resultIndex].isSILIndirect {
|
||||||
|
returnValues.append(originalDirectResultIterator.next()!)
|
||||||
|
}
|
||||||
|
resultIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
let argumentMapping = argumentMap[mapped.argumentIndex]!
|
assert(resultIndex == mappedResult.resultIndex, "The next pack result is not skipped.")
|
||||||
|
|
||||||
|
let argumentMapping = argumentMap[mappedResult.argumentIndex]!
|
||||||
for argument in argumentMapping.arguments {
|
for argument in argumentMapping.arguments {
|
||||||
|
|
||||||
switch argument.resources {
|
switch argument.resources {
|
||||||
@@ -769,18 +797,26 @@ private struct PackExplodedFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMapIndex += 1
|
// We have finished processing mappedResult, so step forward.
|
||||||
|
resultIndex += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect any remaining original direct results.
|
||||||
|
while let directResult = originalDirectResultIterator.next() {
|
||||||
|
returnValues.append(directResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
theReturnValues = returnValues
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the single value directly, rather than constructing a single-element tuple for it.
|
// Return a single return value directly, rather than constructing a single-element tuple for it.
|
||||||
if returnValues.count == 1 {
|
if theReturnValues.count == 1 {
|
||||||
builder.createReturn(of: returnValues[0])
|
builder.createReturn(of: theReturnValues[0])
|
||||||
} else {
|
} else {
|
||||||
let tupleElementTypes = returnValues.map { $0.type }
|
let tupleElementTypes = theReturnValues.map { $0.type }
|
||||||
let tupleType = context.getTupleType(elements: tupleElementTypes).loweredType(
|
let tupleType = context.getTupleType(elements: tupleElementTypes).loweredType(
|
||||||
in: specialized)
|
in: specialized)
|
||||||
let tuple = builder.createTuple(type: tupleType, elements: returnValues)
|
let tuple = builder.createTuple(type: tupleType, elements: theReturnValues)
|
||||||
builder.createReturn(of: tuple)
|
builder.createReturn(of: tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1026,7 +1062,7 @@ private func loadOwnership(for value: any Value, normal: LoadInst.LoadOwnership)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TypeProperties {
|
extension Type {
|
||||||
/// A pack argument can explode if it contains no pack expansion types
|
/// A pack argument can explode if it contains no pack expansion types
|
||||||
fileprivate var shouldExplode: Bool {
|
fileprivate var shouldExplode: Bool {
|
||||||
// For now, we only attempt to explode indirect packs, since these are the most common and inefficient.
|
// For now, we only attempt to explode indirect packs, since these are the most common and inefficient.
|
||||||
|
|||||||
@@ -527,7 +527,7 @@ void addFunctionPasses(SILPassPipelinePlan &P,
|
|||||||
// of embedded Swift.
|
// of embedded Swift.
|
||||||
if (!P.getOptions().EmbeddedSwift) {
|
if (!P.getOptions().EmbeddedSwift) {
|
||||||
P.addGenericSpecializer();
|
P.addGenericSpecializer();
|
||||||
// P.addPackSpecialization();
|
P.addPackSpecialization();
|
||||||
// Run devirtualizer after the specializer, because many
|
// Run devirtualizer after the specializer, because many
|
||||||
// class_method/witness_method instructions may use concrete types now.
|
// class_method/witness_method instructions may use concrete types now.
|
||||||
P.addDevirtualizer();
|
P.addDevirtualizer();
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
// RUN: %target-sil-opt %s -pack-specialization | %FileCheck %s
|
// RUN: %target-sil-opt %s -module-name A -pack-specialization | %FileCheck %s
|
||||||
|
|
||||||
sil_stage canonical
|
sil_stage canonical
|
||||||
|
|
||||||
import Builtin
|
import Builtin
|
||||||
import Swift
|
import Swift
|
||||||
|
|
||||||
protocol P {}
|
protocol P {
|
||||||
|
associatedtype A
|
||||||
|
func f() -> A
|
||||||
|
}
|
||||||
|
|
||||||
class C : P {}
|
extension P {
|
||||||
|
func f() -> some Any
|
||||||
|
}
|
||||||
|
|
||||||
|
class C : P {
|
||||||
|
typealias A = @_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>
|
||||||
|
}
|
||||||
|
|
||||||
// INDIVIDUAL PARAMETER TRANSFORMATION TESTS:
|
// INDIVIDUAL PARAMETER TRANSFORMATION TESTS:
|
||||||
//
|
//
|
||||||
@@ -32,7 +41,9 @@ class C : P {}
|
|||||||
// CHECK-NEXT: [[OUT_IDX:%[0-9]+]] = scalar_pack_index 0 of $Pack{Int}
|
// CHECK-NEXT: [[OUT_IDX:%[0-9]+]] = scalar_pack_index 0 of $Pack{Int}
|
||||||
// CHECK-NEXT: [[OUT_ADDR:%[0-9]+]] = alloc_stack $Int
|
// CHECK-NEXT: [[OUT_ADDR:%[0-9]+]] = alloc_stack $Int
|
||||||
// CHECK: copy_addr [[IN_PACK]] to [init] [[OUT_PACK]]
|
// CHECK: copy_addr [[IN_PACK]] to [init] [[OUT_PACK]]
|
||||||
// CHECK: [[RESULT:%[0-9]+]] = load [trivial] [[OUT_ADDR]]
|
// These two checks ensure a destructure_tuple is not emitted when the original result was ().
|
||||||
|
// CHECK-NEXT: [[ORIG_RESULT:%[0-9]+]] = tuple ()
|
||||||
|
// CHECK-NEXT: [[RESULT:%[0-9]+]] = load [trivial] [[OUT_ADDR]]
|
||||||
// CHECK-NEXT: dealloc_stack [[OUT_ADDR]]
|
// CHECK-NEXT: dealloc_stack [[OUT_ADDR]]
|
||||||
// CHECK-NEXT: dealloc_pack [[OUT_PACK]]
|
// CHECK-NEXT: dealloc_pack [[OUT_PACK]]
|
||||||
// CHECK-NEXT: dealloc_stack [[ADDR]]
|
// CHECK-NEXT: dealloc_stack [[ADDR]]
|
||||||
@@ -699,7 +710,8 @@ bb0(%0 : $*Pack{Int}):
|
|||||||
// BAIL OUT CONDITION TESTS:
|
// BAIL OUT CONDITION TESTS:
|
||||||
//
|
//
|
||||||
// Only perform pack specialization on functions that have indirect pack
|
// Only perform pack specialization on functions that have indirect pack
|
||||||
// parameters that contain no pack expansions.
|
// parameters and/or results that contain no pack expansions or generic type
|
||||||
|
// parameters.
|
||||||
//
|
//
|
||||||
// 2025-10-15: We currently only explode packs with address elements
|
// 2025-10-15: We currently only explode packs with address elements
|
||||||
// (SILPackType::isElementAddress), because these are the most common (since
|
// (SILPackType::isElementAddress), because these are the most common (since
|
||||||
@@ -789,3 +801,197 @@ bb0(%0 : $*Pack{Int}):
|
|||||||
%2 = apply %1(%0) : $@convention(thin) (@pack_guaranteed Pack{Int}) -> ()
|
%2 = apply %1(%0) : $@convention(thin) (@pack_guaranteed Pack{Int}) -> ()
|
||||||
return %2
|
return %2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sil [ossa] @indirect_result_pack : $@convention(thin) () -> @pack_out Pack{Int} {
|
||||||
|
bb0(%0 : $*Pack{Int}):
|
||||||
|
%1 = tuple ()
|
||||||
|
return %1
|
||||||
|
}
|
||||||
|
|
||||||
|
sil [ossa] @direct_result_pack : $@convention(thin) () -> @pack_out @direct Pack{Int} {
|
||||||
|
bb0(%0 : $*@direct Pack{Int}):
|
||||||
|
%1 = tuple ()
|
||||||
|
return %1
|
||||||
|
}
|
||||||
|
|
||||||
|
sil [ossa] @result_pack_expansion : $@convention(thin) <each A> () -> @pack_out Pack{repeat each A} {
|
||||||
|
bb0(%0 : $*Pack{repeat each A}):
|
||||||
|
%1 = tuple ()
|
||||||
|
return %1
|
||||||
|
}
|
||||||
|
|
||||||
|
sil [ossa] @mixed_result_packs : $@convention(thin) <each A> () -> (@pack_out Pack{Int}, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each A}) {
|
||||||
|
bb0(%0 : $*Pack{Int}, %1 : $*@direct Pack{Int}, %2 : $*Pack{repeat each A}):
|
||||||
|
%3 = tuple ()
|
||||||
|
return %3
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @result_call_eligibility_tests : $@convention(thin) <each A> () -> (@pack_out Pack{Int}, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each A}) {
|
||||||
|
// CHECK: bb0(%0 : $*Pack{Int}, %1 : $*@direct Pack{Int}, %2 : $*Pack{repeat each A}):
|
||||||
|
// CHECK: function_ref @$s20indirect_result_packTf8x_n : $@convention(thin) () -> Int
|
||||||
|
// CHECK: function_ref @direct_result_pack : $@convention(thin) () -> @pack_out @direct Pack{Int}
|
||||||
|
// CHECK: function_ref @result_pack_expansion : $@convention(thin) <each τ_0_0> () -> @pack_out Pack{repeat each τ_0_0}
|
||||||
|
// CHECK: function_ref @$s18mixed_result_packsTf8xnn_n : $@convention(thin) <each τ_0_0> () -> (Int, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each τ_0_0})
|
||||||
|
// CHECK-LABEL: } // end sil function 'result_call_eligibility_tests'
|
||||||
|
sil [ossa] @result_call_eligibility_tests : $@convention(thin) <each A> () -> (@pack_out Pack{Int}, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each A}) {
|
||||||
|
bb0(%1 : $*Pack{Int}, %2 : $*@direct Pack{Int}, %3 : $*Pack{repeat each A}):
|
||||||
|
|
||||||
|
%4 = function_ref @indirect_result_pack : $@convention(thin) () -> @pack_out Pack{Int}
|
||||||
|
%5 = apply %4(%1) : $@convention(thin) () -> @pack_out Pack{Int}
|
||||||
|
|
||||||
|
%8 = function_ref @direct_result_pack : $@convention(thin) () -> @pack_out @direct Pack{Int}
|
||||||
|
%9 = apply %8(%2) : $@convention(thin) () -> @pack_out @direct Pack{Int}
|
||||||
|
|
||||||
|
%10 = function_ref @result_pack_expansion : $@convention(thin) <each A> () -> @pack_out Pack{repeat each A}
|
||||||
|
%11 = apply %10<Pack{repeat each A}>(%3) : $@convention(thin) <each A> () -> @pack_out Pack{repeat each A}
|
||||||
|
|
||||||
|
%12 = function_ref @mixed_result_packs : $@convention(thin) <each A> () -> (@pack_out Pack{Int}, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each A})
|
||||||
|
%13 = apply %12<Pack{repeat each A}>(%1, %2, %3) : $@convention(thin) <each A> () -> (@pack_out Pack{Int}, @pack_out @direct Pack{Int}, @pack_out Pack{repeat each A})
|
||||||
|
|
||||||
|
%99 = tuple ()
|
||||||
|
return %99
|
||||||
|
}
|
||||||
|
|
||||||
|
// @substituted TYPE TESTS:
|
||||||
|
//
|
||||||
|
// PackSubstitution must correctly identify the types of Pack arguments in the presence of @substituted types.
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s25substitute_parameter_typeTf8x_n : $@convention(thin) @substituted <τ_0_0> (Int32) -> () for <Int32> {
|
||||||
|
// CHECK: bb0(%0 : $Int32):
|
||||||
|
// CHECK-LABEL: } // end sil function '$s25substitute_parameter_typeTf8x_n'
|
||||||
|
sil [ossa] @substitute_parameter_type : $@convention(thin) @substituted <T> (@pack_guaranteed Pack{T}) -> () for <Int32> {
|
||||||
|
bb0(%0 : $*Pack{Int32}):
|
||||||
|
%99 = tuple ()
|
||||||
|
return %99
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_substitute_parameter_type : $@convention(thin) (@pack_guaranteed Pack{Int32}) -> () {
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_substitute_parameter_type'
|
||||||
|
sil [ossa] @call_substitute_parameter_type : $@convention(thin) (@pack_guaranteed Pack{Int32}) -> () {
|
||||||
|
bb0(%0 : $*Pack{Int32}):
|
||||||
|
%1 = function_ref @substitute_parameter_type : $@convention(thin) @substituted <T> (@pack_guaranteed Pack{T}) -> () for <Int32>
|
||||||
|
%2 = apply %1(%0) : $@convention(thin) @substituted <T> (@pack_guaranteed Pack{T}) -> () for <Int32>
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s22substitute_result_typeTf8x_n : $@convention(thin) @substituted <τ_0_0> () -> Int32 for <Int32> {
|
||||||
|
// CHECK: bb0:
|
||||||
|
// CHECK-LABEL: } // end sil function '$s22substitute_result_typeTf8x_n'
|
||||||
|
sil [ossa] @substitute_result_type : $@convention(thin) @substituted <T> () -> @pack_out Pack{T} for <Int32> {
|
||||||
|
bb0(%0 : $*Pack{Int32}):
|
||||||
|
%99 = tuple ()
|
||||||
|
return %99
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_substitute_result_type : $@convention(thin) () -> @pack_out Pack{Int32} {
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_substitute_result_type'
|
||||||
|
sil [ossa] @call_substitute_result_type : $@convention(thin) () -> @pack_out Pack{Int32} {
|
||||||
|
bb0(%0 : $*Pack{Int32}):
|
||||||
|
%1 = function_ref @substitute_result_type : $@convention(thin) @substituted <T> () -> @pack_out Pack{T} for <Int32>
|
||||||
|
%2 = apply %1(%0) : $@convention(thin) @substituted <T> () -> @pack_out Pack{T} for <Int32>
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GENERIC TYPE HANDLING TESTS:
|
||||||
|
//
|
||||||
|
// Handling of generic types in packs. Since T is still generic, it is not
|
||||||
|
// loadable, so it cannot be passed directly. Also make sure generic types are
|
||||||
|
// handled correctly, even if the top-level type a pack element isn't a type
|
||||||
|
// parameter (e.g. in opaque result types and generic data structures).
|
||||||
|
//
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s17copy_pack_genericTf8xx_n : $@convention(thin) <T> (@in_guaranteed T) -> @out T {
|
||||||
|
// CHECK: bb0([[OUT_ADDR:%[0-9]+]] : $*T, [[IN_ADDR:%[0-9]+]] : $*T)
|
||||||
|
// CHECK-NEXT: [[IN_PACK:%[0-9]+]] = alloc_pack $Pack{T}
|
||||||
|
// CHECK-NEXT: [[IDX:%[0-9]+]] = scalar_pack_index 0 of $Pack{T}
|
||||||
|
// CHECK-NEXT: pack_element_set [[IN_ADDR]] into [[IDX]] of [[IN_PACK]]
|
||||||
|
// CHECK-NEXT: [[OUT_PACK:%[0-9]+]] = alloc_pack $Pack{T}
|
||||||
|
// CHECK-NEXT: [[OUT_IDX:%[0-9]+]] = scalar_pack_index 0 of $Pack{T}
|
||||||
|
// CHECK-NEXT: pack_element_set [[OUT_ADDR]] into [[OUT_IDX]] of [[OUT_PACK]]
|
||||||
|
// CHECK: [[RESULT:%[0-9]+]] = tuple ()
|
||||||
|
// CHECK-NEXT: dealloc_pack [[OUT_PACK]]
|
||||||
|
// CHECK-NEXT: dealloc_pack [[IN_PACK]]
|
||||||
|
// CHECK-NEXT: return [[RESULT]]
|
||||||
|
// CHECK-LABEL: } // end sil function 'copy_pack_generic'
|
||||||
|
sil [ossa] @copy_pack_generic : $@convention(thin) <T> (@pack_guaranteed Pack{T}) -> @pack_out Pack{T} {
|
||||||
|
bb0(%0 : $*Pack{T}, %1 : $*Pack{T}):
|
||||||
|
%2 = tuple ()
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_copy_pack_generic : $@convention(thin) <T> (@pack_guaranteed Pack{T}) -> @pack_out Pack{T} {
|
||||||
|
// CHECK: bb0(%0 : $*Pack{T}, %1 : $*Pack{T})
|
||||||
|
// CHECK: [[OUT_P_PACK_ADDR:%[0-9]+]] = pack_element_get [[OUT_PACK_GET_IDX:%[0-9]+]] of %0 as $*T
|
||||||
|
// CHECK: [[IN_P_PACK_ADDR:%[0-9]+]] = pack_element_get [[PACK_GET_IDX:%[0-9]+]] of %1 as $*T
|
||||||
|
// CHECK: [[SPECIALIZED:%[0-9]+]] = function_ref @$s17copy_pack_genericTf8xx_n : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||||
|
// CHECK-NEXT: [[RESULT:%[0-9]+]] = apply [[SPECIALIZED]]<T>([[OUT_P_PACK_ADDR]], [[IN_P_PACK_ADDR]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||||
|
// CHECK-NEXT: return [[RESULT]]
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_copy_pack_generic'
|
||||||
|
sil [ossa] @call_copy_pack_generic : $@convention(thin) <T> (@pack_guaranteed Pack{T}) -> @pack_out Pack{T} {
|
||||||
|
bb0(%0 : $*Pack{T}, %1 : $*Pack{T}):
|
||||||
|
%2 = function_ref @copy_pack_generic : $@convention(thin) <T> (@pack_guaranteed Pack{T}) -> @pack_out Pack{T}
|
||||||
|
%3 = apply %2<T>(%0, %1) : $@convention(thin) <T> (@pack_guaranteed Pack{T}) -> @pack_out Pack{T}
|
||||||
|
return %3
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s11pack_opaqueTf8x_n : $@convention(thin) (@inout @_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>) -> () {
|
||||||
|
// CHECK: bb0(%0 : $*@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>):
|
||||||
|
// CHECK-LABEL: } // end sil function '$s11pack_opaqueTf8x_n'
|
||||||
|
sil [ossa] @pack_opaque : $@convention(thin) (@pack_inout Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}) -> () {
|
||||||
|
bb0(%0 : $*Pack{C.A}):
|
||||||
|
%1 = tuple ()
|
||||||
|
return %1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_pack_opaque : $@convention(thin) (@pack_inout Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}) -> () {
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_pack_opaque'
|
||||||
|
sil [ossa] @call_pack_opaque : $@convention(thin) (@pack_inout Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}) -> () {
|
||||||
|
bb0(%0 : $*Pack{C.A}):
|
||||||
|
%1 = function_ref @pack_opaque : $@convention(thin) (@pack_inout Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}) -> ()
|
||||||
|
%2 = apply %1(%0) : $@convention(thin) (@pack_inout Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}) -> ()
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s18pack_opaque_resultTf8x_n : $@convention(thin) () -> @out @_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C> {
|
||||||
|
// CHECK: bb0(%0 : $*@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>):
|
||||||
|
// CHECK-LABEL: } // end sil function '$s18pack_opaque_resultTf8x_n'
|
||||||
|
sil [ossa] @pack_opaque_result : $@convention(thin) () -> @pack_out Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>} {
|
||||||
|
bb0(%0 : $*Pack{C.A}):
|
||||||
|
%1 = tuple ()
|
||||||
|
return %1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_pack_opaque_result : $@convention(thin) () -> @pack_out Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>} {
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_pack_opaque_result'
|
||||||
|
sil [ossa] @call_pack_opaque_result : $@convention(thin) () -> @pack_out Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>} {
|
||||||
|
bb0(%0 : $*Pack{C.A}):
|
||||||
|
%1 = function_ref @pack_opaque_result : $@convention(thin) () -> @pack_out Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}
|
||||||
|
%2 = apply %1(%0) : $@convention(thin) () -> @pack_out Pack{@_opaqueReturnTypeOf("$s1A1PPAAE1fQryF", 0) __<C>}
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Box<T> {
|
||||||
|
var v: T
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil shared [ossa] @$s19pack_nested_genericTf8xx_n : $@convention(thin) <T> (@inout Box<T>) -> @out Box<T> {
|
||||||
|
// CHECK: bb0(%0 : $*Box<T>, %1 : $*Box<T>):
|
||||||
|
// CHECK-LABEL: } // end sil function '$s19pack_nested_genericTf8xx_n'
|
||||||
|
sil [ossa] @pack_nested_generic : $@convention(thin) <T> (@pack_inout Pack{Box<T>}) -> @pack_out Pack{Box<T>} {
|
||||||
|
bb0(%0 : $*Pack{Box<T>}, %1 : $*Pack{Box<T>}):
|
||||||
|
%2 = tuple ()
|
||||||
|
return %2
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: sil [ossa] @call_pack_nested_generic : $@convention(thin) <T> (@pack_inout Pack{Box<T>}) -> @pack_out Pack{Box<T>} {
|
||||||
|
// CHECK-LABEL: } // end sil function 'call_pack_nested_generic'
|
||||||
|
sil [ossa] @call_pack_nested_generic : $@convention(thin) <T> (@pack_inout Pack{Box<T>}) -> @pack_out Pack{Box<T>} {
|
||||||
|
bb0(%0 : $*Pack{Box<T>}, %1 : $*Pack{Box<T>}):
|
||||||
|
%2 = function_ref @pack_nested_generic : $@convention(thin) <T> (@pack_inout Pack{Box<T>}) -> @pack_out Pack{Box<T>}
|
||||||
|
%3 = apply %2<T>(%0, %1) : $@convention(thin) <T> (@pack_inout Pack{Box<T>}) -> @pack_out Pack{Box<T>}
|
||||||
|
%4 = tuple ()
|
||||||
|
return %4
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// XFAIL: *
|
|
||||||
// RUN: %target-swift-frontend %s -emit-ir -O | %FileCheck %s
|
// RUN: %target-swift-frontend %s -emit-ir -O | %FileCheck %s
|
||||||
|
|
||||||
// REQUIRES: swift_in_compiler
|
// REQUIRES: swift_in_compiler
|
||||||
|
|||||||
Reference in New Issue
Block a user