Skip to content

stdlib: remove some static assertions which may not hold #21606

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
Feb 28, 2019

Conversation

compnerd
Copy link
Member

@compnerd compnerd commented Jan 3, 2019

These are based around the idea that std::atomic is trivially
constructible, which is not a guarantee that the standard fully
provides. The default initialization of the std::atomic type
may leave it in an undetermined state. These were caught using
the Visual C++ preview runtime.

Replace this paragraph with a description of your changes and rationale. Provide links to external references/discussions if appropriate.

Resolves SR-NNNN.

@compnerd
Copy link
Member Author

compnerd commented Jan 3, 2019

@lorentey @moiseev @gparker42

@lorentey
Copy link
Member

lorentey commented Jan 3, 2019

cc @mikeash

@compnerd
Copy link
Member Author

compnerd commented Feb 8, 2019

@swift-ci please test Linux platform

@swift-ci
Copy link
Contributor

swift-ci commented Feb 8, 2019

Build failed
Swift Test OS X Platform
Git Sha - e0e3b499336e138bad9e4ba81ad668d06c5cc7a6

@jrose-apple
Copy link
Contributor

I don't think we can remove these assertions. We rely on a HeapObject coming out of calloc ready to go.

@compnerd
Copy link
Member Author

compnerd commented Feb 8, 2019

@jrose-apple - that is not a guarantee that the C++ standard makes. The std::atomic needs to be initialized. This was the discussions that @lorentey and I had above, and he also seemed to agree that this was the correct thing to do.

@mikeash
Copy link
Contributor

mikeash commented Feb 8, 2019

From the random copy of C++11 that turned up in a search:

The atomic integral specializations and the specialization atomic<bool> shall have standard layout. They shall each have a trivial default constructor and a trivial destructor.

Which I think means it has to have the same representation as the integer type, and not do any initialization. I'm bad at standardese though so draw your own conclusions.

@compnerd
Copy link
Member Author

compnerd commented Feb 8, 2019

Yes, it has the same representation, but the constructor is needed to initialize it to 0 (otherwise it is in an indeterminate state). The zero-ing of the member is sufficient to make it fail the trivial constructability test. See LWG Issue 2334. LWG P0883 is designed to address this which will make the std::is_trivially_constructible no longer hold.

@jrose-apple
Copy link
Contributor

So we can still put them in constant sections because of the newly-added constexpr, but we can't just calloc a HeapObject or HeapObject-containing thing anymore. Maybe that's okay if we actually rewrite the stdlib to do that, but let's do that first and then remove the static_asserts rather than the other way around.

@compnerd
Copy link
Member Author

compnerd commented Feb 8, 2019

@jrose-apple - hmm, perhaps you can file a radar for that? I think that this would be something that Apple might want to track more closely (but, this was found by building with the next version of the C++ runtime from Microsoft which does actually implement parts of the future specification), so this will become a problem for everyone.

@jrose-apple
Copy link
Contributor

@mikeash, what do you think?

@mikeash
Copy link
Contributor

mikeash commented Feb 11, 2019

It looks like we don't actually rely on the memory being zero-filled at allocation as I was thinking. We call through to malloc, not calloc, and then initialize the header manually by setting the metadata pointer to the metadata, and setting the refcounts to whatever RefCountBits(0, 1) ends up being.

That initialization has an interesting comment:

// FIXME: this should be a placement new but that adds a null check
object->metadata = metadata;
object->refCounts.init();

Can we find a way to use placement new without penalty? Seems like that would be the best for everything.

@compnerd
Copy link
Member Author

@mikeash - yes and no. It is "removable" by switching to C++17. As of C++17, a null pointer to the placement new is UB, which means that the nullptr check should not be emitted by the compiler.

@compnerd
Copy link
Member Author

@mikeash - until we can move to C++17, what do you think is the best way to move forward here?

@compnerd
Copy link
Member Author

@mikeash - ping

@mikeash
Copy link
Contributor

mikeash commented Feb 27, 2019

Sorry to leave you hanging! I don't want to introduce an unnecessary NULL check. If placement new means we have to have the check for now, can we use placement new when compiling with VC++ and keep the existing version for other compilers?

Alternately, is it possible to avoid the NULL check with an annotation to inform the compiler that the pointer is definitely not ever NULL?

Either one would be good by me.

@compnerd
Copy link
Member Author

I'm happy with a C++17/Windows path through this codepath. Interestingly enough, I do not see a null pointer check even with C++14:

	.def	 "?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z";
	.scl	3;
	.type	32;
	.endef
	.section	.text,"xr",one_only,"?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z"
	.p2align	4, 0x90         # -- Begin function ?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z
"?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z": # @"?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z"
.seh_proc "?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z"
# %bb.0:                                # %entry
	push	rsi
	.seh_pushreg 6
	sub	rsp, 32
	.seh_stackalloc 32
	.seh_endprologue
	mov	rsi, rcx
	mov	rcx, rdx
	mov	rdx, r8
	call	swift_slowAlloc
	mov	qword ptr [rax], rsi
	mov	qword ptr [rax + 8], 2
	add	rsp, 32
	pop	rsi
	ret
	.seh_handlerdata
	.section	.text,"xr",one_only,"?_swift_allocObject_@@YAPEAUHeapObject@swift@@PEBU?$TargetHeapMetadata@UInProcess@swift@@@2@_K1@Z"
	.seh_endproc

@mikeash
Copy link
Contributor

mikeash commented Feb 27, 2019

Maybe the compiler has already figured out that it can't be NULL? Let me know and send me a diff if you'd like me to try with clang and see if it produces good code.

@compnerd
Copy link
Member Author

@mikeash - uploaded a new version of the change that enables it under C++17 and Windows, I'll double check this on Linux as well.

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 377a5e8dee7172dd929a20c28291af98efbeca33

@mikeash
Copy link
Contributor

mikeash commented Feb 27, 2019

I'll try the placement new path on Mac and see how it does. If we can be confident that it'll generate good code then maybe we can just use that everywhere.

@compnerd
Copy link
Member Author

@mikeash - please do, since I just ran this on Linux (with some "hacks" - namely, added -O2 -g0 -S -o - - -DNDEBUG -U_DEBUG -USWIFT_RUNTIME_ENABLE_LEAK_CHECKER -USWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS and get this:

	.p2align	4, 0x90         # -- Begin function _ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm
	.type	_ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm,@function
_ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm: # @_ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm
	.cfi_startproc
# %bb.0:                                # %entry
	pushq	%rbx
	.cfi_def_cfa_offset 16
	.cfi_offset %rbx, -16
	movq	%rdi, %rbx
	movq	%rsi, %rdi
	movq	%rdx, %rsi
	callq	swift_slowAlloc
	movq	%rbx, (%rax)
	movq	$2, 8(%rax)
	popq	%rbx
	.cfi_def_cfa_offset 8
	retq
.Lfunc_end1:
	.size	_ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm, .Lfunc_end1-_ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm
	.cfi_endproc

@mikeash
Copy link
Contributor

mikeash commented Feb 27, 2019

Here's what I get with placement new:

__ZL19_swift_allocObject_PKN5swift18TargetHeapMetadataINS_9InProcessEEEmm:
000000000033db80	pushq	%rbp
000000000033db81	movq	%rsp, %rbp
000000000033db84	pushq	%rbx
000000000033db85	pushq	%rax
000000000033db86	movq	%rdi, %rbx
000000000033db89	movq	%rsi, %rdi
000000000033db8c	movq	%rdx, %rsi
000000000033db8f	callq	_swift_slowAlloc
000000000033db94	movq	%rbx, (%rax)
000000000033db97	movq	$0x2, 0x8(%rax)
000000000033db9f	addq	$0x8, %rsp
000000000033dba3	popq	%rbx
000000000033dba4	popq	%rbp
000000000033dba5	retq

Looks great! Seems like we might as well use it everywhere now. I guess that comment must be obsolete.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 377a5e8dee7172dd929a20c28291af98efbeca33

@compnerd
Copy link
Member Author

Awesome! I'll upload a new version with that enabled globally!

The assertions here are based around the idea that `std::atomic` is
trivially constructible which is not a guarantee that the standard fully
provides.  The default initialization of the `std::atomic` type may
leave it in an undetermined state.  These were caught using the Visual
C++ preview runtime.

Ideally, the object constructor would use a placement new operator.
However, prior to C++17, the C++ standard mandated that there be a
NULL pointer check in the placement new operator.  This is something
which is no longer the case with C++17.  Switch to the placement new
operator for C++17 and newer and enable that codepath for Windows as
well (which seemingly elides the null-pointer check with clang-cl).
@compnerd
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - c2af8f9437c6f18f2898f8d60e3460a94682a9e1

@mikeash
Copy link
Contributor

mikeash commented Feb 28, 2019

Woohoo!

@compnerd
Copy link
Member Author

@mikeash - okay to merge?

@mikeash
Copy link
Contributor

mikeash commented Feb 28, 2019

Go for it!

@compnerd compnerd merged commit 48d8ebd into swiftlang:master Feb 28, 2019
@compnerd compnerd deleted the you-may-not-assert branch February 28, 2019 17:49
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