Skip to content

[libc++] Add assumption for align of begin and end pointers of vector. #108961

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 5 commits into from
Jan 16, 2025

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Sep 17, 2024

Missing information about begin and end pointers of std::vector can lead to missed optimizations in LLVM.

This patch adds alignment assumptions at the point where the begin and end pointers are loaded. If the pointers would not have the same alignment, end might never get hit when incrementing begin.

See #101372 for a discussion of missed range check optimizations in hardened mode.

Once #108958 lands, the created llvm.assume calls for the alignment should be folded into the load instructions, resulting in no extra instructions after InstCombine.

@fhahn fhahn requested a review from a team as a code owner September 17, 2024 11:14
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Sep 17, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2024

@llvm/pr-subscribers-libcxx

Author: Florian Hahn (fhahn)

Changes

Missing information about begin and end pointers of std::vector can lead to missed optimizations in LLVM.

See #101372 for a discussion of missed range check optimizations in hardened mode.

Once #108958 lands, the created llvm.assume calls for the alignment should be folded into the load instructions, resulting in no extra instructions after InstCombine.


Full diff: https://github.com/llvm/llvm-project/pull/108961.diff

1 Files Affected:

  • (modified) libcxx/include/vector (+8-4)
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 7d3aac5989a48c..720b46d573c954 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -1027,6 +1027,10 @@ private:
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector&, false_type) _NOEXCEPT {}
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __add_alignment_assumption(pointer __p) _NOEXCEPT {
+    return static_cast<pointer>(__builtin_assume_aligned(__p, alignof(decltype(*__p))));
+  }
 };
 
 #if _LIBCPP_STD_VER >= 17
@@ -1418,25 +1422,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::assign(size_type __n
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::begin() _NOEXCEPT {
-  return __make_iter(this->__begin_);
+  return __make_iter(__add_alignment_assumption(this->__begin_));
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::const_iterator
 vector<_Tp, _Allocator>::begin() const _NOEXCEPT {
-  return __make_iter(this->__begin_);
+  return __make_iter(__add_alignment_assumption(this->__begin_));
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::end() _NOEXCEPT {
-  return __make_iter(this->__end_);
+  return __make_iter(__add_alignment_assumption(this->__end_));
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::const_iterator
 vector<_Tp, _Allocator>::end() const _NOEXCEPT {
-  return __make_iter(this->__end_);
+  return __make_iter(__add_alignment_assumption(this->__end_));
 }
 
 template <class _Tp, class _Allocator>

Comment on lines 1030 to 1033

_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __add_alignment_assumption(pointer __p) _NOEXCEPT {
return static_cast<pointer>(__builtin_assume_aligned(__p, alignof(decltype(*__p))));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this work with fancy pointers? Also, shouldn't the compiler be able to infer this?

Missing information about begin and end pointers of std::vector can lead
to missed optimizations in LLVM.

See llvm#101372 for a discussion
of missed range check optimizations in hardened mode.

Once llvm#108958 lands, the created
`llvm.assume` calls for the alignment should be folded into the `load`
instructions, resulting in no extra instructions after InstCombine.
@ldionne ldionne force-pushed the libcxx-vector-begin-end-align branch from 83f60da to 94c2ede Compare January 13, 2025 17:37
@ldionne ldionne added the pending-ci Merging the PR is only pending completion of CI label Jan 13, 2025
Otherwise, this prevents us from being able to declare a flat_map containing
an incomplete type.
@@ -15,9 +15,7 @@
#include <__config>
Copy link
Member

Choose a reason for hiding this comment

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

@huixie90 Do these changes look correct to you? Is there a reason why you used ranges::iterator_t in the first place?

@ldionne ldionne merged commit eac23a5 into llvm:main Jan 16, 2025
81 checks passed
fhahn added a commit to fhahn/llvm-project that referenced this pull request Jan 17, 2025
Use known bits to remove redundant alignment assumptions.

Libc++ now adds alignment assumptions for std::vector::begin() and
std::vector::end(), so I  expect we will see quite a bit more
assumptions in C++ [1]. Try to clean up some redundant ones to start
with.

[1] llvm#108961
fhahn added a commit to fhahn/llvm-project that referenced this pull request Jan 18, 2025
Use known bits to remove redundant alignment assumptions.

Libc++ now adds alignment assumptions for std::vector::begin() and
std::vector::end(), so I  expect we will see quite a bit more
assumptions in C++ [1]. Try to clean up some redundant ones to start
with.

[1] llvm#108961
fhahn added a commit to fhahn/llvm-project that referenced this pull request Jan 18, 2025
Use known bits to remove redundant alignment assumptions.

Libc++ now adds alignment assumptions for std::vector::begin() and
std::vector::end(), so I  expect we will see quite a bit more
assumptions in C++ [1]. Try to clean up some redundant ones to start
with.

[1] llvm#108961
@@ -775,6 +780,17 @@ class _LIBCPP_TEMPLATE_VIS vector {
}

_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(vector&, false_type) _NOEXCEPT {}

static _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __add_alignment_assumption(pointer __p) _NOEXCEPT {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly to #118837 (review) I think this one needs _LIBCPP_NO_CFI since we're potentially static_cast'ing uninitialized memory (for the end pointer).

(Apologies for the late notice, we're a bit behind on libc++.)

Copy link
Member

@ldionne ldionne Jan 28, 2025

Choose a reason for hiding this comment

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

Ack. I'd be curious to see how this failure can be reproduced. Is it just a matter of adding a -fsanitize=cfi job to our pre-commit CI?

CF #124837

Copy link
Member

Choose a reason for hiding this comment

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

Also #124839 for adding NO_CFI

Copy link
Collaborator

Choose a reason for hiding this comment

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

I haven't tried to reproduce it on the libc++ tests directly, but it should be possible. I think we're currently only using a subset of the cfi checks: -fsanitize=cfi-vcall -fsanitize=cfi-derived-cast -fsanitize=cfi-unrelated-cast. The annoying thing is that cfi requires (thin) lto.

Comment on lines +785 to +786
#ifndef _LIBCPP_CXX03_LANG
if constexpr (is_pointer<pointer>::value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@ldionne Are we using constexpr if in C++11/14 as a compiler extension here? I've also noticed several other usages of constexpr if in C++14 mode in libc++.
Does this imply that we can generally use constexpr if within the context of C++11/14 in libc++?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's fine to use compiler extensions in the library as long as our supported compilers implement them.

@fhahn fhahn deleted the libcxx-vector-begin-end-align branch February 25, 2025 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. pending-ci Merging the PR is only pending completion of CI performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants