Question: Is Swift ABI stable on Windows? How far are we from there?

Just out of curiosity — MSVC has been stated to be ABI compatible for 7 years by the time, so it seems an achievable and reasonable goal to reach ABI stability on top of it.

Once ABI stability on Windows is a thing, we can start to split the runtime off the toolchain and distribute it separately, enabling different toolchains and products sharing the latest runtime libraries.

No, there is no guarantee of ABI stability on Windows and is a non-goal. I'm not sure I read that document the same way - they only say that 2015-2022 are ABI stable, not that it will be indefinitely. The runtime is already split and can be re-distributed, at least that is the intention. They are parallel installable, the problem is that we need to figure out how to version the DLLs so as to permit the co-installation still resolving properly.

2 Likes

While this is certainly useful, I don’t see it as more achievable. We have to either distinguish different versions by file name (diverged from other platforms) or let the executable inject DLL path first (hard unless we use a custom import library IMO). A low-cost solution of this is to use an xcrun-like tool to shim and inject the paths, which I believe still need broad discussion and investigations.

ABI stability on Windows is definitely not a short-term goal, but it should be something on the roadmap. Unlike Linux, which doesn’t guarantee a stable ABI, I believe the post implies that MSVC will keep ABI stable in no less than 2 years, before the next Visual Studio release (likely 2025) comes out. By the time it should cover all the supported Windows versions, including LTSC. It is also very likely that the story will continue ever since, making a natural ground for Swift ABI stability to fit in.

Let’s look back in 2019, when @jrose wrote ABI Stability and More:

As development of Swift on Linux, Windows, and other platforms matures, the Swift Core Team will evaluate stabilizing the ABI on those platforms.

And @tkremenek spoke of ABI on Linux on the forums:

Finalizing the ABI on Linux is less valuable in the server domain (one of the most active use cases for Swift on Linux) than the (say) iOS app domain since service executables are routinely built from source for deployment in that domain anyway.

The Windows story is more like Darwin than Linux. It is client-dominated. Binary and even commercial libraries are common. It has a full and stable set of system APIs. All these make ABI stability on Windows more promising and valuable.

1 Like

I know that blog post sounds like ABI stability is a goal in and of itself, but that’s not necessarily true. ABI stability lets you do one thing in particular: closed-source libraries don’t need to be recompiled when the compiler changes. That’s useful for people who write closed-source libraries, and it was certainly requested fair amount leading up to Swift 5, but for everybody else it doesn’t matter so much. Library evolution likewise allows closed-source libraries to depend on other closed-source libraries, and apps to depend on libraries outside their “app bundle” (on Windows, the program directory)

What was different about Apple is that Apple wanted to use Swift in the OS, and always did. That’s why Swift jumped through so many hoops to get library evolution working; not just the availability model, but the part where libraries could change out from under an already-built app without breaking ABI. I don’t think there’s any driver of that from Microsoft at this time, and I don’t think there’s likely to be since their everyday platform APIs are for the CLR, not compiled to native code. But I fully admit I don’t know much about the Windows development situation these days.

So without that, the main driver of ABI stability + library evolution would be so multiple apps could share common libraries without duplicating them. The main driver of ABI stability without library evolution (but also included in the above) would be distributing closed-source libraries for app developers. Are either of those important enough goals on Windows that we want to lock ourselves in? Commit to everything public having the memory layout it does, using the runtime functions and calling conventions it currently has? We’ve talked idly before about making classes COM-compatible on Windows; should we give up on that, or limit it? Those are the kinds of questions to ask for deciding when to commit to an ABI.

For Apple, the answer was obviously “yes, eventually”, and because of that there are some suboptimalities in the Swift 5 ABI. Some get worked around with deployment targets, as a proxy for the runtime version, but others are stuck forever. For Linux, the answer thus far has been “no, it doesn’t provide enough benefit”, because there’s not nearly as big a closed-source library ecosystem, particularly not for the server focus Swift has gone with. For Windows? I trust @compnerd to evaluate whether the benefits make it worth it, but at the moment I don’t see a strong reason to push for it.

But maybe I’m just missing it. What would you, specifically, do with a stable ABI on Windows, that you can’t do today?

P.S. As discussed, Swift ABI stability does depend on the underlying C ABI being stable*, but that isn’t really the main concern; glibc is pretty stable in the forward-compatibility direction, but that doesn’t automatically mean we want to stabilize the Swift ABI on Linux.

* Technically I think this is a library limitation more than a language limitation; I believe you could define the Swift ABI without referring to C at all, but as soon as you call a function provided by a C-based library, from memory allocation to I/O to COM to a custom library, you’re back depending on C again.

P.P.S. There’s another benefit to ABI stability, which is writing cross-language bridges that don’t go through C. But while that’s not an anti-goal for Swift, it isn’t really something the project has focused on either. It’s technically possible but in practice only the Swift compiler can really tell you how to call Swift APIs, so any tool that interoperates with Swift is likely to do so very narrowly anyway, or embed SourceKit / the compiler in the tool itself. But I digress.

7 Likes

The main benefits from my side:

  • Allowing different toolchains and applications to share the same runtime libraries;
  • Enabling closed-source library to be distributed more freely.

Of course these two things doesn’t happen for Swift now, but they’re really common on Windows (for the C++ world) — much more common than Linux, and, AFAIK, even compared to macOS / iOS.

I agree that locking the ABI now is pretty unwise, since Swift on Windows is far from mature at the moment. I simply wants to check if this is still on the roadmap. It should be something to consider before we invest too much into working around an ABI-unstable Swift on Windows.

2 Likes

This is already needed on Linux as well. Yes, DT_RUNPATH helps, but the naming is needed and an actual issue. I am not sure if I like the xcrun-style tooling.

I fully agree with all the points here. I think that the first point bears re-emphasizing, most issues like toolchain size and all really replicated across Windows and Darwin and Linux is the odd one out.

I agree with @jrose here - ABI stability isn't something that is currently something that has a reason to be pursued.

Re-distributing the closed source libraries is possible with re-packaging the runtime. I have tried to make that easy, and the runtime is a separate MSI actually. It should be possible to even convert that to a MSM if desired.

Here we have successfully build Swift Windows 11 apps and packaged the executable with the runtime components all in the same folder. This work well. For distributing a library, is the resulting DLL build with Swift 6 and distributed with the runtime in the same folder (as we do with exe) will be usable by a programmer later building his app with a different Swift compiler version, said 6.2 ?

I’m not sure I understand what you’re asking. The DLLs are not usable by a developer - they are purely runtime components. The developer needs access to the import libraries and swift modules which are in the Swift SDK.

If you’re asking about an upgrade path, you would need to update the runtime distribution if you update the compiler/SDK.

I'll try to clarify my question. Let's say we want to develop a package named "FlightSimKit" using Swift 6.0 on Windows 11 and distribute it to customers as a closed-source binary dynamic library.

  • What should the zip package for distributing the "FlightSimKit" product contain?

  • To use "FlightSimKit," the customer will need to install it on their Windows 11 computer with the Swift 6.0 environment or later, say Swift 6.2. Will the customer will be able to build a Swift 6.2 application using the "FlightSimKit" package built with Swift 6.0?

You could bundle it as such (at least what I would recommend):

  • FlightSimKit/
    • Headers/
      • FlightSimKit/
        • FlightSimKitCore.h
    • Modules/
      • FlightSimKit.swiftmodule
        • aarch64-unknown-windows-msvc.swiftmodule
        • aarch64-unknown-windows-msvc.swiftdoc
        • aarch64-unknown-windows-msvc.swiftinterface
        • x86_64-unknown-windows-msvc.swiftmodule
        • x86_64-unknown-windows-msvc.swiftdoc
        • x86_64-unknown-windows-msvc.swiftinterface
    • Versions/
      • 0.0.1/
        • usr/
          • bin/
            • FlightSimKit.dll
          • lib64/
            • FlightSimKit.lib
          • liba64/
            • FlightSimKit.lib
    • Redistributables/
      • FlightSimKit.aarch64.msm
      • FlightSimKit.x86_64.msm

No, that is not supported or possible. You would be tied to the same compiler release. There are no guarantees of ABI stability, so while you could potentially use the swiftinterface to load the module (which will also require you build with reliance), there is no guarantee about the layout being compatible.

Crystal clear, thanks Saleem !
I hope we will have a stable Swift ABI on Windows in the future to remove this limitation.

Some day :slight_smile: I think that we are making good progress towards improving the experience on Windows and unlocking plenty of opportunities to improve things. Locking ourselves into an ABI too early could be detrimental to those efforts.

Note that I left out one thing that I hope we will be able to introduce at some point soon - Documentation/ which would contain the DocC bundles. There is an issue with the DocC renderer/DocC thata I am hoping we can resolve soon to enable DocC bundles to be usable on Windows by default.

CC: @Joannis_Orlandos

I fully understand the situation and we can live with such limitation for now.

All the good forward progress on Swift for Windows - and the WinUI projections - are so much appreciated !

1 Like

I disagree that it's a non-goal. It's not something we should rush into, and we've had to tweak the Swift ABI on Windows a little bit as support for the platform matured. Once that calms down (and I think it has [*]), we should consider the merits of providing ABI stability for that platform.

This is a thing that ABI stability provides, but it's far from the only benefit.

To expand on this, benefits of ABI stability include:

  • Code size savings from not having N copies of the same libraries across the system embedded in executables
  • Memory usage savings from not having those N copies when all of those executables are running at once
  • The ability to deploy correctness, performance, and security fixes to a shared library and have all clients updated at once (vs. tracking down and recompiling all of the dependencies)
  • The ability to distribute binaries across multiple versions of a platform without having to build them separately for each.

The above applies just as well to open-source libraries as it does to closed-source libraries. Linux and Windows are mostly built on top of shared libraries, but they all talk via the stable C ABI.

I'm considering ABI stability and library evolution as a pair, because you don't get the benefits above without both.

We try not to customize Swift all that much for each platform, because it makes ongoing development and porting to new platforms harder. The memory layout and Swift runtime calls we make are not likely to diverge much across platforms, so ABI stability on one family of platforms gets us most of the way there for all of the other platforms so long as we don't diverge.

Right, this is a good question. COM-compatible support would be nice, but absent an actual plan to do it, it shouldn't block us from introducing ABI stability.

I think this implies a move toward using the Swift runtime version for #available checks rather than vendor OS versions. On Windows, for example, an ABI-stable Swift standard library isn't likely to ship in the OS, so I wouldn't expect an #available(Windows 11...) anywhere. I'd propose that we move to using #available(Swift 6.2) or similar for cross-platform APIs, then have an optional mapping to a vendor platform version for vendors that do ship them, e.g., iOS 26 -> Swift 6.2. In this world, one could use #available(iOS 26) to access an API with Swift 6.2 availability, but not the opposite. Should Swift ship as part of some other OS at some point (Ubuntu or whatever), we could add Ubuntu 2027.4 -> Swift 7.0 or whatever.

Right. The situation is less great with C++ libraries, which is something we'd need to contend with.

Doug

[*] We are still unhappy with the ABI with have for async functions due to the unwinding issue, but I suspect the platform doesn't make it possible to do better, so we might have to live with it.

11 Likes

Sorry about the delay on that Windows DocC issue. CC @Obbut as well

I would now agree with you. The original statement from ~2022 was reasonable then. I think that we have seen a significant amount of maturity gained in the Windows toolchain. I don't think that we are quite ready to pin down ourselves yet, but we are making great progress towards that end. In the mean time, new avenues of resolving some of the older problems have now become viable :slight_smile:

As to the Concurrency ABI issue - word is that it should be possible and this might just be an artifact of LLVM itself.

I mostly agree with this, but we have seen a significant chunk of Swift exclude Windows as a host due to its decisions. That does result in a good deal of divergence. One area that I would actively want to diverge though would be the object layout. I think that it would be good to consider object layouts which are COM friendly by default.

I think that it is less a plan and more so the lack of ability to execute on it due to the continual stream of Windows behind left as an exercise for the interested party that prevent this. I would say that this is a completely reasonable thing to hold ABI stability on. (If you want more than a vague statement, the early swift-driver is a great current example. Macros were something which required last second heorics on Windows by me to get them into the releease).

I think that this is reasonable. I think that once the early swift-driver is enabled, @available being usable with Windows would be a good item to tackle. Unlike #available, this would use the OS spellings so that we can cluster system APIs for releases that we are targeting,

3 Likes