Skip to content

Conversation

@aidan-hall
Copy link
Contributor

@aidan-hall aidan-hall commented Sep 25, 2025

rdar://161182876

There are two key barriers that prevent variadic generic specialisations from being optimised in most cases:

  • Indirect packs in the calling convention: These force the caller to make a large number of stack allocations, since the pack is passed indirectly, and pack elements are currently always pointers, so every element must be stack-allocated separately from the pack.
  • Pack loops and open_pack_element: These perform one iteration per pack element, so the loop body must be generic, with witness_method instructions that cannot be eliminated, since the type they are called on is not known.

Targeted modifications to eliminate these barriers will allow other passes to fully optimise variadic generic specialisations.

This PR only addresses the first barrier, indirect packs as function arguments.

The simplest way to eliminate indirect packs from the calling convention is effectively to move the stack allocations for the packs and pack elements inside the callee, and use the addresses of these local allocations in the main body, which is otherwise unchanged. These allocations can be eliminated by other passes once dynamic pack loops are optimised.

This transformation is performed by a function pass (currently called pack-specialization). For example, it would transform the following specialisation like so:

sil shared @$s11addTogether8doubleUp2xsxxQp_xxQptxxQp_tRvzlFSi_SdSSSJQP_Tg5 : $@convention(thin) (@pack_guaranteed Pack{Int, Double, String, Character}) -> (@pack_out Pack{Int, Double, String, Character}, @pack_out Pack{Int, Double, String, Character}) {
bb0(%0 : $*Pack{Int, Double, String, Character}, %1 : $*Pack{Int, Double, String, Character}, %2 : $*Pack{Int, Double, String, Character}):
  debug_value %2, let, name "xs", argno 1, expr op_deref
  …

sil shared @$s11addTogether8doubleUp2xsxxQp_xxQptxxQp_tRvzlFSi_SdSSSJQP_Tg5Tf8xxx_n : $@convention(thin) (Int, Double, @guaranteed String, @guaranteed Character) -> (Int, Double, @owned String, @owned Character, Int, Double, @owned String, @owned Character) {
bb0(%0 : $Int, %1 : $Double, %2 : $String, %3 : $Character):
  %4 = alloc_pack $Pack{Int, Double, String, Character}
  %5 = scalar_pack_index 0 of $Pack{Int, Double, String, Character}
  %6 = alloc_stack $Int
  store %0 to %6
  …
  debug_value %4, let, name "xs", argno 1, expr op_deref
  …

Note that the return values are all now direct, so bb0 only needs arguments corresponding to the xs parameter pack.

To optimise a pack loop, it must be unrolled. The pack element type is then statically known in each unrolled instance of the body. The existing loop unrolling pass can consistently do this in variadic generic specialisations, since the (known constant) pack length is used as the number of iterations. This still leaves constructs like the following:

  %5 = integer_literal $Builtin.Word, 0
  %6 = dynamic_pack_index %5 of $Pack{Int, Double}
  %7 = open_pack_element %6 of <each A where repeat each A : Numeric> at <Pack{Int, Double}>, shape $each A, uuid "063BDCF4-98A3-11F0-B2E8-929C59BC796C"
  %20 = witness_method $@pack_element("063BDCF4-98A3-11F0-B2E8-929C59BC796C") each A, #Numeric."*" …
  %21 = apply %20<@pack_element("063BDCF4-98A3-11F0-B2E8-929C59BC796C") each A>(%10, %14, %18, %11) … // type-defs: %7

In this context, we could easily replace all instances of the @pack_element type with Int. This would allow the appropriate witness method to be inlined. We will perform this in an instruction simplification for open_pack_element.

@aidan-hall aidan-hall self-assigned this Sep 25, 2025
@aidan-hall aidan-hall added the parameter packs Feature → generics: Parameter packs label Sep 25, 2025
@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macOS

1 similar comment
@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macOS


public var packExpansionPatternType: Type { bridged.getPackExpansionPatternType().type }

public var hasParameterPack: Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to move (most of) these APIs to TypeProperties so that they are also available for AST.Type and AST.CanonicalType. The SILType implementation forwards them to the AST type, anyway

@aidan-hall aidan-hall marked this pull request as draft October 14, 2025 14:18
eeckstein
eeckstein previously approved these changes Oct 16, 2025
Copy link
Contributor

@eeckstein eeckstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great!

@aidan-hall aidan-hall closed this Oct 17, 2025
@aidan-hall aidan-hall deleted the pack-opt branch October 17, 2025 09:40
@aidan-hall aidan-hall restored the pack-opt branch October 17, 2025 09:40
@aidan-hall aidan-hall reopened this Oct 17, 2025
@aidan-hall aidan-hall force-pushed the pack-opt branch 2 times, most recently from 9e37826 to 6b9f9ec Compare October 17, 2025 10:30
@aidan-hall aidan-hall marked this pull request as ready for review October 17, 2025 10:31
@aidan-hall
Copy link
Contributor Author

@swift-ci test

@aidan-hall aidan-hall requested a review from eeckstein October 17, 2025 10:34
@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test linux

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test linux

@aidan-hall
Copy link
Contributor Author

@swift-ci test windows

@aidan-hall
Copy link
Contributor Author

@swift-ci test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci test windows

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci Please Test Source Compatibility Release

@aidan-hall
Copy link
Contributor Author

@swift-ci test macos

1 similar comment
@aidan-hall
Copy link
Contributor Author

@swift-ci test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci Please Test Source Compatibility Release

@aidan-hall
Copy link
Contributor Author

@swift-ci test

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test

@aidan-hall
Copy link
Contributor Author

@swift-ci Please Test Source Compatibility Release

@aidan-hall
Copy link
Contributor Author

All tests passed previously except 1 source compatibility test (which does not use packs), due to an unrelated bug.
Force-pushed to invalidate the previous results, now running minimum smoke tests to merge.
@swift-ci smoke test linux
@swift-ci smoke test windows
@swift-ci test windows
@swift-ci smoke benchmark

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test linux

@aidan-hall
Copy link
Contributor Author

@swift-ci smoke test macos

@aidan-hall
Copy link
Contributor Author

@swift-ci test windows

@aidan-hall aidan-hall merged commit a8cef44 into swiftlang:main Oct 31, 2025
4 checks passed
@finagolfin
Copy link
Member

finagolfin commented Nov 1, 2025

Hello Aidan, this new test you added is failing for the new 32-bit Android armv7 compiler validation suite run that was added, and will probably fail on the 32-bit watchOS CI also:

[2025-11-01T06:57:34.217Z] FAIL: Swift(android-armv7) :: SILOptimizer/pack_specialization.swift (8889 of 20537)
[2025-11-01T06:57:34.217Z] ******************** TEST 'Swift(android-armv7) :: SILOptimizer/pack_specialization.swift' FAILED ********************
[2025-11-01T06:57:34.217Z] Exit Code: 1
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] Command Output (stderr):
[2025-11-01T06:57:34.217Z] --
[2025-11-01T06:57:34.217Z] /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/bin/swift-frontend -target armv7-unknown-linux-android -sdk /home/build-user/build/ndk/android-ndk-r27d/toolchains/llvm/prebuilt/linux-x86_64/sysroot -resource-dir /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/lib/swift -module-cache-path /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/swift-test-results/armv7-unknown-linux-androideabi/clang-module-cache -swift-version 4  -define-availability 'SwiftStdlib 9999:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999' -define-availability 'StdlibDeploymentTarget 9999:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999' -define-availability 'SwiftStdlib 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2' -define-availability 'StdlibDeploymentTarget 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2' -define-availability 'SwiftStdlib 5.1:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' -define-availability 'StdlibDeploymentTarget 5.1:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' -define-availability 'SwiftStdlib 5.2:macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4' -define-availability 'StdlibDeploymentTarget 5.2:macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4' -define-availability 'SwiftStdlib 5.3:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0' -define-availability 'StdlibDeploymentTarget 5.3:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0' -define-availability 'SwiftStdlib 5.4:macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5' -define-availability 'StdlibDeploymentTarget 5.4:macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5' -define-availability 'SwiftStdlib 5.5:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0' -define-availability 'StdlibDeploymentTarget 5.5:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0' -define-availability 'SwiftStdlib 5.6:macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4' -define-availability 'StdlibDeploymentTarget 5.6:macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4' -define-availability 'SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0' -define-availability 'StdlibDeploymentTarget 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0' -define-availability 'SwiftStdlib 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4' -define-availability 'StdlibDeploymentTarget 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4' -define-availability 'SwiftStdlib 5.9:macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0' -define-availability 'StdlibDeploymentTarget 5.9:macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0' -define-availability 'SwiftStdlib 5.10:macOS 14.4, iOS 17.4, watchOS 10.4, tvOS 17.4, visionOS 1.1' -define-availability 'StdlibDeploymentTarget 5.10:macOS 14.4, iOS 17.4, watchOS 10.4, tvOS 17.4, visionOS 1.1' -define-availability 'SwiftStdlib 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0' -define-availability 'StdlibDeploymentTarget 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0' -define-availability 'SwiftStdlib 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4' -define-availability 'StdlibDeploymentTarget 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4' -define-availability 'SwiftStdlib 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -define-availability 'StdlibDeploymentTarget 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -define-availability 'SwiftStdlib 6.3:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999' -define-availability 'StdlibDeploymentTarget 6.3:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999' -define-availability 'SwiftCompatibilitySpan 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.0' -define-availability 'SwiftCompatibilitySpan 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -typo-correction-limit 10  /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift -emit-ir -O | /usr/bin/python3.12 /source/swift-project/swift/utils/PathSanitizingFileCheck --allow-unused-prefixes --sanitize TMP_DIR=/home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/test-android-armv7/SILOptimizer/Output/pack_specialization.swift.tmp --sanitize BUILD_DIR=/home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64 --sanitize SOURCE_DIR=/source/swift-project/swift --ignore-runtime-warnings --use-filecheck /home/build-user/build/swift-project/Ninja-Release/llvm-linux-x86_64/bin/FileCheck   /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift # RUN: at line 1
[2025-11-01T06:57:34.217Z] + /usr/bin/python3.12 /source/swift-project/swift/utils/PathSanitizingFileCheck --allow-unused-prefixes --sanitize TMP_DIR=/home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/test-android-armv7/SILOptimizer/Output/pack_specialization.swift.tmp --sanitize BUILD_DIR=/home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64 --sanitize SOURCE_DIR=/source/swift-project/swift --ignore-runtime-warnings --use-filecheck /home/build-user/build/swift-project/Ninja-Release/llvm-linux-x86_64/bin/FileCheck /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift
[2025-11-01T06:57:34.217Z] + /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/bin/swift-frontend -target armv7-unknown-linux-android -sdk /home/build-user/build/ndk/android-ndk-r27d/toolchains/llvm/prebuilt/linux-x86_64/sysroot -resource-dir /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/lib/swift -module-cache-path /home/build-user/build/swift-project/Ninja-Release/swift-linux-x86_64/swift-test-results/armv7-unknown-linux-androideabi/clang-module-cache -swift-version 4 -define-availability 'SwiftStdlib 9999:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999' -define-availability 'StdlibDeploymentTarget 9999:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999' -define-availability 'SwiftStdlib 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2' -define-availability 'StdlibDeploymentTarget 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2' -define-availability 'SwiftStdlib 5.1:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' -define-availability 'StdlibDeploymentTarget 5.1:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' -define-availability 'SwiftStdlib 5.2:macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4' -define-availability 'StdlibDeploymentTarget 5.2:macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4' -define-availability 'SwiftStdlib 5.3:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0' -define-availability 'StdlibDeploymentTarget 5.3:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0' -define-availability 'SwiftStdlib 5.4:macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5' -define-availability 'StdlibDeploymentTarget 5.4:macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5' -define-availability 'SwiftStdlib 5.5:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0' -define-availability 'StdlibDeploymentTarget 5.5:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0' -define-availability 'SwiftStdlib 5.6:macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4' -define-availability 'StdlibDeploymentTarget 5.6:macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4' -define-availability 'SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0' -define-availability 'StdlibDeploymentTarget 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0' -define-availability 'SwiftStdlib 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4' -define-availability 'StdlibDeploymentTarget 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4' -define-availability 'SwiftStdlib 5.9:macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0' -define-availability 'StdlibDeploymentTarget 5.9:macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0' -define-availability 'SwiftStdlib 5.10:macOS 14.4, iOS 17.4, watchOS 10.4, tvOS 17.4, visionOS 1.1' -define-availability 'StdlibDeploymentTarget 5.10:macOS 14.4, iOS 17.4, watchOS 10.4, tvOS 17.4, visionOS 1.1' -define-availability 'SwiftStdlib 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0' -define-availability 'StdlibDeploymentTarget 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0' -define-availability 'SwiftStdlib 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4' -define-availability 'StdlibDeploymentTarget 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4' -define-availability 'SwiftStdlib 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -define-availability 'StdlibDeploymentTarget 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -define-availability 'SwiftStdlib 6.3:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999' -define-availability 'StdlibDeploymentTarget 6.3:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999' -define-availability 'SwiftCompatibilitySpan 5.0:macOS 10.14.4, iOS 12.2, watchOS 5.2, tvOS 12.2, visionOS 1.0' -define-availability 'SwiftCompatibilitySpan 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0' -typo-correction-limit 10 /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift -emit-ir -O
[2025-11-01T06:57:34.217Z] /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift:6:11: error: CHECK: expected string not found in input
[2025-11-01T06:57:34.217Z] // CHECK: define {{.*}} { i64, i64, ptr, double } @"$s19pack_specialization8copyPack2xsxxQp_txxQp_tRvzlFSi_SSSdQP_Tg5Tf8xx_n"(i64 %0, i64 %1, ptr %2, double %3)
[2025-11-01T06:57:34.217Z]           ^
[2025-11-01T06:57:34.217Z] <stdin>:1:1: note: scanning from here
[2025-11-01T06:57:34.217Z] ; ModuleID = '<swift-imported-modules>'
[2025-11-01T06:57:34.217Z] ^
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] Input file: <stdin>
[2025-11-01T06:57:34.217Z] Check file: /source/swift-project/swift/test/SILOptimizer/pack_specialization.swift
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] -dump-input=help explains the following input dump.
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] Input was:
[2025-11-01T06:57:34.217Z] <<<<<<
[2025-11-01T06:57:34.217Z]          1: ; ModuleID = '<swift-imported-modules>' 
[2025-11-01T06:57:34.217Z] check:6     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
[2025-11-01T06:57:34.217Z]          2: source_filename = "<swift-imported-modules>" 
[2025-11-01T06:57:34.217Z] check:6     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[2025-11-01T06:57:34.217Z]          3: target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" 
[2025-11-01T06:57:34.217Z] check:6     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[2025-11-01T06:57:34.217Z]          4: target triple = "armv7-unknown-linux-android" 
[2025-11-01T06:57:34.217Z] check:6     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[2025-11-01T06:57:34.217Z]          5:  
[2025-11-01T06:57:34.217Z] check:6     ~
[2025-11-01T06:57:34.217Z]          6: %swift.type = type { i32 } 
[2025-11-01T06:57:34.217Z] check:6     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
[2025-11-01T06:57:34.217Z]          .
[2025-11-01T06:57:34.217Z]          .
[2025-11-01T06:57:34.217Z]          .
[2025-11-01T06:57:34.217Z] >>>>>>
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] --
[2025-11-01T06:57:34.217Z] 
[2025-11-01T06:57:34.217Z] ********************

Can you take a look?

@aidan-hall
Copy link
Contributor Author

Will do. Looks like I can probably just replace with Int with Int32 in the test source.

@aidan-hall
Copy link
Contributor Author

@finagolfin #85279

@aidan-hall aidan-hall deleted the pack-opt branch November 11, 2025 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parameter packs Feature → generics: Parameter packs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants