Skip to content

[android] Use emulated thread-local storage for API 28 and earlier #77883

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2025

Conversation

finagolfin
Copy link
Member

@finagolfin finagolfin commented Nov 30, 2024

Android before API 29 and a few other platforms don't support native TLS, so fall back to LLVM's emulated TLS there, just like clang does. Also, make sure -Xcc -f{no-,}emulated-tls flags passed in are applied to control what the Swift compiler does.

Swift 6 added a thread-local variable to the stdlib for all linux-based platforms for the first time, ie outside linux threading (Android uses pthreads instead), which then generated code in Foundation with native TLS even for Android API 24, breaking my Android CI that runs cross-compiled tests in the Android emulator at API 24.

I assumed the LLVM codegen backend automatically handled this, but it turns out it has to be explicitly enabled by the clang frontend, so I'm doing the same with the Swift frontend now. I just ran the compiler validation suite and toolchain tests on the Nov. 20 trunk snapshot built natively on Android 13 AArch64: this patch fixed running the Foundation tests and looks like it didn't cause any test regressions.

Considering Swift doesn't appear to have a way to choose native TLS for Swift variables, this pull appears to only really be useful for inline functions from C/C++ headers that are inserted into Swift's IR and codegened together.

@Azoy, who added this TLS variable in the C/C++ header earlier this year, and @al45tair, let me know what you think.

@compnerd and @hyp, I don't know if you test your Android SDK on API 28 or earlier, let me know if you've seen this regression.

@3405691582, OpenBSD is listed as one of the platforms using emulated TLS in that second link, let us know if that's correct.

@finagolfin
Copy link
Member Author

@swift-ci please test

@@ -148,6 +148,9 @@ swift::getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx) {
if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm())
TargetOpts.ThreadModel = llvm::ThreadModel::Single;

if (Clang->getTargetInfo().getTriple().hasDefaultEmulatedTLS())
TargetOpts.EmulatedTLS = true;
Copy link
Member

Choose a reason for hiding this comment

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

We probably should honour -Xcc -femulated-tls as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's an interesting point: I tried it and right now it does nothing when generating Swift code. I'm against adding those sorts of -Xcc flags to affect codegen, particularly important ones, as I stated in the forum earlier this year.

However, I agree with you that it would be good to have a user-configurable flag for this affecting Swift codegen, and it's debatable if this rises to the level of importance of getting its own Swift frontend flag instead.

If you or anyone else has a preference on whether it should be its own Swift flag or an -Xcc flag, let me know and I will implement whatever the consensus is.

Copy link
Member Author

Choose a reason for hiding this comment

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

I chose the established -Xcc flags to control this, because this only affects inline functions from C/C++ headers that have been spliced into Swift IR anyway, as Swift doesn't seem to have a TLS annotation for its own variables.

@finagolfin
Copy link
Member Author

Rebased and updated to add support for command-line flags that manually control this backend codegen flag. I only ran this new test natively on Android, so let's see how well it works on other CI platforms.

@swift-ci please test

@finagolfin
Copy link
Member Author

@swift-ci please test

@finagolfin
Copy link
Member Author

@swift-ci please test

@finagolfin
Copy link
Member Author

@swift-ci please test windows

@@ -148,6 +148,8 @@ swift::getIRTargetOptions(const IRGenOptions &Opts, ASTContext &Ctx) {
if (Clang->getTargetInfo().getTriple().isOSBinFormatWasm())
TargetOpts.ThreadModel = llvm::ThreadModel::Single;

TargetOpts.EmulatedTLS = Opts.UseEmulatedTLS;
Copy link
Contributor

Choose a reason for hiding this comment

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

As @compnerd suggested, maybe we should copy the setting from Clang's options? A simple way might be do to something like this (untested, written in this comment):

Suggested change
TargetOpts.EmulatedTLS = Opts.UseEmulatedTLS;
TargetOpts.EmulatedTLS = Opts.UseEmulatedTLS || Clang->getCodeGenOpts().EmulatedTLS;

Copy link
Member Author

Choose a reason for hiding this comment

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

As noted above yesterday, I already implemented support for that flag then. However, I just tried your suggestion and that works too and is simpler, so thanks for the suggestion: I will update this pull to reuse your suggested ClangImporter flag instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Android before API 29 and a few other platforms don't support native TLS, so
fall back to LLVM's emulated TLS there, just like clang does. Also, make sure
`-Xcc -f{no-,}emulated-tls` flags passed in are applied to control what the
Swift compiler does.
@finagolfin
Copy link
Member Author

@swift-ci please test

1 similar comment
@finagolfin
Copy link
Member Author

@swift-ci please test

@finagolfin
Copy link
Member Author

Ping, passed CI and addressed all reviews, just waiting for final approval.

@finagolfin
Copy link
Member Author

Ping @compnerd, please review, would like to get this into 6.1 and 6.0 next.

@3405691582
Copy link
Member

OpenBSD is listed as one of the platforms using emulated TLS in that second link, let us know if that's correct.

I think this is still the case, but I haven't had a chance to confirm that.

Copy link
Member

@compnerd compnerd left a comment

Choose a reason for hiding this comment

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

The warning on the ignored emulated TLS would be nice along with an explanation of why its ignored on WASI.

// EMUTLS-macosx: __emutls_get_address
// EMUTLS-openbsd: __emutls_get_address
// EMUTLS-windows-msvc: __emutls_get_address
// EMUTLS-wasi-NOT: __emutls_get_address
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, why is this negated for WASI? Perhaps we should warn about the ignored setting?

Copy link
Member Author

Choose a reason for hiding this comment

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

I honestly didn't want to deal with WASI at all, but was forced to because the linux CI builds it and runs these tests for it too.

My understanding is that the initial __emutls_v._swift_stdlib_gettid.tid symbol is swapped in because the LLVM codegen pass runs on all platforms, but it doesn't have __emutls_get_address in compiler-rt, like the other platforms do, so it doesn't call that function. I could've simply disabled this test for WASI instead, but I figured it was worth checking the __emutls_v._swift_stdlib_gettid.tid symbol at least on all platforms.

This is just an artifact of the current CI and WASI support, I don't think it's worth addressing in the test.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, okay, its just that the call is something else? That seems reasonable enough then. Perhaps an issue to track restoring this would be good.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, okay, its just that the call is something else?

I don't know: I just did what I had to do to get the test to pass, as the CI was forcing me to address WASI but I'm not that familiar with it, didn't know if it even had threads yet. Looking at the relevant LLVM source now, the LowerToTLSEmulatedModel() method that inserts the __emutls_get_address call is not invoked for the WASM target, as it is for several other architectures. It appears that WASI threads is still in the standardization phase, so the implementation may still be in flux.

@kateinoigakukun, could you let us know if this might be a useful flag for WASM one day and if this test is okay for now?

Copy link
Member

Choose a reason for hiding this comment

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

All WebAssembly targets currently have proper TLS support (assuming dynamic linking is only for Emscripten, not with WASI), so the lowering does not implement emulation at all and silently ignores the option today as you mentioned. The thread representation model in Wasm is still unstable but I don't expect we need the emulation in the future, so it's fine as is now.

@finagolfin finagolfin merged commit f67b35c into swiftlang:main Jan 9, 2025
5 checks passed
@finagolfin
Copy link
Member Author

Merging as I want to get this into the release branches next: @compnerd, if you have any further concerns about the test, let me know and we'll get that in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants