Skip to content
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

Cap the alignment of all types in Swift at 16 #15691

Merged
merged 1 commit into from
Aug 29, 2018

Conversation

rjmccall
Copy link
Contributor

@rjmccall rjmccall commented Apr 2, 2018

This is the result of some (fairly old) conversations with @stephentyrone. High-alignment types generate a variety of problems for the implementation, and meanwhile the architectural arc seems to be towards de-emphasizing the need for really high alignments just to make larger vectors performant.

rdar://31411216

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 2, 2018

@swift-ci Please test.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 2, 2018

@stephentyrone I would like to get your opinion on this. (I can also post our old email conversation if that's okay with you.)

@swift-ci
Copy link
Contributor

swift-ci commented Apr 2, 2018

Build failed
Swift Test Linux Platform
Git Sha - 657cd265f24e43852dc90c31368f4ce1f70630c9

@gparker42
Copy link
Contributor

If we're doing this then we can remove most of the recently-added unlimited alignment in swift_slowAlloc. cc @mikeash

@swift-ci
Copy link
Contributor

swift-ci commented Apr 2, 2018

Build failed
Swift Test OS X Platform
Git Sha - 657cd265f24e43852dc90c31368f4ce1f70630c9

@mikeash
Copy link
Contributor

mikeash commented Apr 3, 2018

How does this interact with imported types that require higher alignment, like the vector types from simd?

swift_slowAlloc may need to keep its smarts on other platforms, as I don't think 16-byte alignment is guaranteed from malloc everywhere. We'd also need it for UnsafeRawPointer.allocate(bytes:alignedTo:). We could use a separate allocation function for those cases if we want to skip the alignment check for the common case where we know in advance that malloc is suitable.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 3, 2018

Riskily. If you passed a pointer to a Swift-allocated object of such a type to a C API, that API might miscompile. We could decline to import such APIs, or import such pointers as opaque; I don't think we'd want to go so far as to decline to import the type at all.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 3, 2018

(This PR is meant to be more of a discussion than something we're going to merge right away.)

@jckarter
Copy link
Contributor

jckarter commented Apr 3, 2018

It'd be nice if we can get away with this. If not, maybe we can cap alignment only during unspecialized use, but still use the proper alignment in specialized code. We can only ever call C code that demands higher alignment at the ABI level from specialized contexts, so we could maintain C compatibility in most situations. We could still record the ideal full alignment in the runtime metadata somewhere so that things like Array or UnsafeMutableBufferPointer that allocate memory in bulk and are likely to be processed in bulk by specialized Swift or C code can use it to allocate their buffers with the proper alignment. Generic code would then only need to allocate stack space at 16 byte alignment; in turn, value witnesses would only get to assume 16-byte alignment, which is probably OK since there's no real hope of getting better throughput from alignment in a value witness call. Protocol witness thunks and things like withUnsafePointer might have to move over-aligned types around in order to get them properly aligned for specialized contexts.

@compnerd
Copy link
Member

compnerd commented Apr 4, 2018

@mikeash you are correct about the malloc assumptions. Linux (including android) and Windows have 8-byte alignment on 32-bit and 16-byte alignment on 64-bit IIRC.

@stephentyrone
Copy link
Contributor

As far as pure-Swift operation is concerned, this all seems fine to me. I'll discuss the importer issues with John offline and see if we can get a little bit more control over this.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 4, 2018

@jckarter Interesting idea. The idea of honoring alignment on imported types but capping it generically is definitely appealing, and you're right, it wouldn't be hard to make generic value witnesses assume a weaker alignment bound than the type normally guarantees. We'd also have to apply that to potentially-abstracted function parameters in general, I think. So the big questions are (1) whether you can otherwise exfiltrate a pointer from generic code and (2) which alignment we use for things like struct layout, which of course can be generic.

It does look like we do prevent you from expressing a @convention(c) function type using any generic type parameters (even if it's just UnsafePointer, which abstractly ought to be representable). So, for now at least, that's safe.

However, there are other ways of getting a pointer from generic code, like by passing a local inout to a callback which then passes that as a pointer to C. I guess we could do writeback in such a situation. But you can also just pass the callback a pointer, which we're then stuck about.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 5, 2018

I want to explore the idea of just importing pointers to types with a >16-byte alignment as opaque pointers. It looks like this shouldn't give us much trouble; the majority of vector types in APIs are passed directly, and the pointers-to-vectors that I've found so far (e.g. in ARKit) are pointers to simd_float3 or simd_float2, which are at most 16-byte-aligned.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 5, 2018

Also, @stephentyrone reminded me that clang does not provide a consistent ABI for larger vectors anyway — it assigns them a different alignment based on the current compiler flags. So it would be quite dangerous for an API to traffic in pointers to 32-byte vectors. (This is obviously a clang bug, but fixing it means admitting that we can't use 32-byte-aligned loads and stores for those types because of historical compatibility requirements, which might be noticeably sub-optimal on more aggressive CPU targets.)

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 6, 2018

@swift-ci Please test.

@swift-ci
Copy link
Contributor

swift-ci commented Apr 6, 2018

Build failed
Swift Test OS X Platform
Git Sha - 657cd265f24e43852dc90c31368f4ce1f70630c9

@swift-ci
Copy link
Contributor

swift-ci commented Apr 6, 2018

Build failed
Swift Test Linux Platform
Git Sha - 657cd265f24e43852dc90c31368f4ce1f70630c9

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 6, 2018

Okay, so this is perhaps obvious in retrospect, but we can't just completely refuse to import higher-aligned types into Swift because simd declares a number of such types. Unless we can change simd to cap the alignment of those types, of course.

@stephentyrone
Copy link
Contributor

Hmm, this is a bit of a weird case, which may be a bug that we should fix. These simd types only have 16 byte alignment in C unless you're compiling with -mavx2 or -mavx512xxx. I think that the right fix here is to relax the Swift and C alignment to always be 16B, and possibly introduce a set of explicitly higher-aligned C types that cannot be imported.

If the x86_64h slice were more widely used, we probably wouldn't be able to do this without breaking anyone, but I think we can still make this change at present.

@jckarter
Copy link
Contributor

jckarter commented Apr 6, 2018

By your comments about Clang above, does any C code in the OS rely in practice on those simd types having higher alignment?

@stephentyrone
Copy link
Contributor

"Probably not"; you only get it when compiling with -mavx2 or similar, and we own most of the code that does that, and tend to do explicit alignment. Clang actually changed it's behavior with these a few years ago and ~nothing broke, so at least then it worked. It will need some investigation, though.

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 6, 2018

It looks like the build actually broke on the armv7s build:

23:49:59 FAILED: stdlib/public/SDK/simd/iphoneos/armv7s/simd.o 
23:49:59 cd /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/stdlib/public/SDK/simd && /usr/bin/python /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/swift/utils/line-directive @/Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/stdlib/public/SDK/simd/VzZEE.txt -- /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/./bin/swiftc -c -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk -target armv7s-apple-ios7.0 -resource-dir /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/./lib/swift -F /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/../../../Developer/Library/Frameworks -O -D INTERNAL_CHECKS_ENABLED -D SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS -I /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/./lib/swift/iphoneos/armv7s -module-cache-path /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/./module-cache -no-link-objc-runtime -Xfrontend -sil-verify-all -Xfrontend -enable-resilience -swift-version 3 -swift-version 4 -autolink-force-load -warn-swift3-objc-inference-complete -parse-stdlib -Fsystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/PrivateFrameworks/ -module-link-name swiftsimd -force-single-frontend-invocation -parse-as-library -o /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/stdlib/public/SDK/simd/iphoneos/armv7s/simd.o @/Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/buildbot_incremental/swift-macosx-x86_64/stdlib/public/SDK/simd/VzZEE.txt

/Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/swift/stdlib/public/SDK/simd/simd.swift.gyb:639:11: error: use of undeclared type 'simd_double2x3'
23:49:59 extension simd_double2x3 {
23:49:59           ^~~~~~~~~~~~~~
23:49:59 /Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/swift/stdlib/public/SDK/simd/simd.swift.gyb:719:11: error: use of undeclared type 'simd_double2x3'
...

(which is the terrible diagnostic you get when Swift refuses to import a type)

@stephentyrone
Copy link
Contributor

stephentyrone commented Apr 6, 2018

Apparently clang aligns 32B vectors to 32B on armv7s, but not on arm64 or i386 or x86_64. That's a bug (but one that we can safely fix; there's no aligned load/store on armv7 that requires more than 4 byte alignment).

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 6, 2018

Well, it would be an ABI break for e.g. structs containing such fields. Do we think that's okay?

@stephentyrone
Copy link
Contributor

It's an ABI break that clang made for all the other targets 2 or 3 years ago without anyone noticing. =/

(The only thing worse than an ABI break is an ABI break unevenly applied?)

@rjmccall
Copy link
Contributor Author

rjmccall commented Apr 6, 2018

@bob-wilson @fredriss Thoughts on whether it's okay to change the alignment of 32-byte vectors on armv7? I wouldn't want to do that without consulting you first.

@fredriss
Copy link
Contributor

fredriss commented Apr 6, 2018

If this is only for swift, I don't really have an objection. Why do you think I should care?

@stephentyrone
Copy link
Contributor

stephentyrone commented Apr 6, 2018

@fredriss The question John is asking would be for C. Originally ext_vectors had alignment equal to their size. At some point (~3 years ago?) an open-source clang changed the default alignment to min(16B, legal_vector_size) for all targets except armv7. That was an ABI break, but it didn't effect anything visibly so no one noticed it until long after it had shipped in Xcode.

So now a type like simd_double4 is 16B aligned on arm64, i386, x86_64, but 32B aligned on armv7 and x86 with -mavx. Public library boundaries can't assume 32B alignment on x86 because we don't know what flags the caller was compiled with, so that's not a huge problem (there's a chance of breaking someone who assumes higher alignment internally, but we won't break APIs), but we don't have that on armv7. Fortunately, there are no armv7 load/store instructions that require high alignment, so the chance of introducing actual bugs is pretty small, but it's something we need to be careful about.

@stephentyrone
Copy link
Contributor

I've been thinking the same thing.

@dexonsmith
Copy link
Contributor

SGTM.

@rjmccall
Copy link
Contributor Author

Hmm. What should I do with the x86 AVX intrinsics and their typedefs? Intel's documentation says that __m512i is 64-byte-aligned, and I think that probably has specification weight, not compiler-documentation weight, so my first thought is that we should be explicitly aligning all those typedefs.

@rjmccall
Copy link
Contributor Author

Posted a Clang patch: https://reviews.llvm.org/D46042

@rjmccall
Copy link
Contributor Author

@swift-ci Please test.

@rjmccall
Copy link
Contributor Author

The plan right now is that we are going to apply this rule unconditionally in Swift. Whether we make it in the Darwin C ABI is a separable question. It's fine if some corner-case C type — like a wide vector type or a struct with a large explicit alignment — doesn't import well into Swift, as long as this doesn't impact any of the vector-centric APIs we want to expose.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 354db19d9a316318f0d27cdb44cc78900e8790d0

@rjmccall
Copy link
Contributor Author

@swift-ci Please test.

@rjmccall
Copy link
Contributor Author

@swift-ci Please test source compatibility.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 354db19d9a316318f0d27cdb44cc78900e8790d0

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 354db19d9a316318f0d27cdb44cc78900e8790d0

@rjmccall
Copy link
Contributor Author

@swift-ci Please test.

1 similar comment
@rjmccall
Copy link
Contributor Author

@swift-ci Please test.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 14d54fca02a6717590e80bd2f0fb00ba506d38fd

@rjmccall
Copy link
Contributor Author

@swift-ci Please test Linux.

@rjmccall
Copy link
Contributor Author

Fake failure that should be fixed by #19029.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 354db19d9a316318f0d27cdb44cc78900e8790d0

@rjmccall
Copy link
Contributor Author

@swift-ci Please test source compatibility.

@rjmccall
Copy link
Contributor Author

One project in the source-compatibility suite timed out; gonna ignore it.

@rjmccall rjmccall merged commit b312090 into swiftlang:master Aug 29, 2018
@rjmccall rjmccall deleted the max-alignment-16 branch August 29, 2018 01:14
@stephentyrone
Copy link
Contributor

🤘🏻

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.

10 participants