diff --git a/libcxx/include/memory b/libcxx/include/memory
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -3198,6 +3198,17 @@
         {__alloc_traits::deallocate(__alloc_, __p, __s_);}
 };
 
+#if _LIBCPP_STD_VER > 17
+
+template <class _Tp, class... _Args>
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC
+auto construct_at(_Tp* __p, _Args&&... __args)
+    -> decltype(::new (_VSTD::declval<void*>()) _Tp(_VSTD::forward<_Args>(__args)...)) {
+  return ::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...);
+}
+
+#endif
+
 template <class _InputIterator, class _ForwardIterator>
 _ForwardIterator
 uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r)
@@ -3296,21 +3307,21 @@
 #if _LIBCPP_STD_VER > 14
 
 template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC
 void destroy_at(_Tp* __loc) {
     _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
     __loc->~_Tp();
 }
 
 template <class _ForwardIterator>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC
 void destroy(_ForwardIterator __first, _ForwardIterator __last) {
     for (; __first != __last; ++__first)
         _VSTD::destroy_at(_VSTD::addressof(*__first));
 }
 
 template <class _ForwardIterator, class _Size>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17_WITH_CONSTEXPR_DYNAMIC_ALLOC
 _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) {
     for (; __n > 0; (void)++__first, --__n)
         _VSTD::destroy_at(_VSTD::addressof(*__first));
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17
+
+// <memory>
+
+// template<class T, class... Args>
+//   constexpr T* construct_at(T* location, Args&&... args);
+
+#include <memory>
+#include <cstdlib>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct Counted {
+  int& count_;
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; }
+};
+
+TEST_CONSTEXPR_DYNAMIC_ALLOC bool test()
+{
+  {
+    std::allocator<Counted> a;
+    Counted* p = a.allocate(2);
+    int count = 0;
+    std::construct_at(p, count);
+    assert(count == 1);
+    std::construct_at(p+1, count);
+    assert(count == 2);
+    (p+1)->~Counted();
+    assert(count == 1);
+    p->~Counted();
+    assert(count == 0);
+    a.deallocate(p, 2);
+  }
+
+  {
+    std::allocator<const Counted> a;
+    const Counted* p = a.allocate(2);
+    int count = 0;
+    std::construct_at(p, count);
+    assert(count == 1);
+    std::construct_at(p+1, count);
+    assert(count == 2);
+    (p+1)->~Counted();
+    assert(count == 1);
+    p->~Counted();
+    assert(count == 0);
+    a.deallocate(p, 2);
+  }
+
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+
+#if defined(__cpp_constexpr_dynamic_alloc)
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
@@ -11,7 +11,7 @@
 // <memory>
 
 // template <class ForwardIt>
-// void destroy(ForwardIt, ForwardIt);
+// constexpr void destroy(ForwardIt, ForwardIt);
 
 #include <memory>
 #include <cstdlib>
@@ -21,28 +21,45 @@
 #include "test_iterators.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
+  int& count_;
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; }
   friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
 
-int main(int, char**)
+TEST_CONSTEXPR_DYNAMIC_ALLOC bool
+test()
 {
     using It = forward_iterator<Counted*>;
     const int N = 5;
-    alignas(Counted) char pool[sizeof(Counted)*N] = {};
-    Counted* p = (Counted*)pool;
-    std::uninitialized_fill(p, p+N, Counted());
-    assert(Counted::count == 5);
+    int count = 0;
+    std::allocator<Counted> a;
+    Counted* p = a.allocate(N);
+    for (Counted* iter = p; iter < p+N; ++iter) {
+#if TEST_STD_VER > 17
+      std::construct_at(iter, count);
+#else
+      ::new ((void*)iter) Counted(count);
+#endif
+    }
+    assert(count == 5);
     std::destroy(p, p+1);
-    p += 1;
-    assert(Counted::count == 4);
-    std::destroy(It(p), It(p + 4));
-    assert(Counted::count == 0);
+    assert(count == 4);
+    std::destroy(It(p+1), It(p+5));
+    assert(count == 0);
+    a.deallocate(p, N);
+
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+
+#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc)
+  static_assert(test());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
@@ -11,7 +11,7 @@
 // <memory>
 
 // template <class _Tp>
-// void destroy_at(_Tp*);
+// constexpr void destroy_at(_Tp*);
 
 #include <memory>
 #include <cstdlib>
@@ -20,62 +20,84 @@
 #include "test_macros.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
+  int& count_;
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; }
   friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
 
 struct VCounted {
-  static int count;
-  static void reset() { count = 0; }
-  VCounted() { ++count; }
-  VCounted(VCounted const&) { ++count; }
-  virtual ~VCounted() { --count; }
+  int& count_;
+  TEST_CONSTEXPR_DYNAMIC_ALLOC VCounted(int& count) : count_(count) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC VCounted(VCounted const& that) : count_(that.count_) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC virtual ~VCounted() { --count_; }
   friend void operator&(VCounted) = delete;
 };
-int VCounted::count = 0;
 
 struct DCounted : VCounted {
+    using VCounted::VCounted;
     friend void operator&(DCounted) = delete;
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_DYNAMIC_ALLOC bool
+test()
 {
     {
-    void* mem1 = std::malloc(sizeof(Counted));
-    void* mem2 = std::malloc(sizeof(Counted));
+    int count = 0;
+    std::allocator<Counted> a;
+    Counted* mem1 = a.allocate(1);
+    Counted* mem2 = a.allocate(1);
     assert(mem1 && mem2);
-    assert(Counted::count == 0);
-    Counted* ptr1 = ::new(mem1) Counted();
-    Counted* ptr2 = ::new(mem2) Counted();
-    assert(Counted::count == 2);
+    assert(count == 0);
+#if TEST_STD_VER > 17
+    Counted* ptr1 = std::construct_at(mem1, count);
+    Counted* ptr2 = std::construct_at(mem2, count);
+#else
+    Counted* ptr1 = ::new ((void*)mem1) Counted(count);
+    Counted* ptr2 = ::new ((void*)mem2) Counted(count);
+#endif
+    assert(count == 2);
     std::destroy_at(ptr1);
-    assert(Counted::count == 1);
+    assert(count == 1);
     std::destroy_at(ptr2);
-    assert(Counted::count == 0);
-    std::free(mem1);
-    std::free(mem2);
+    assert(count == 0);
+    a.deallocate(mem1, 1);
+    a.deallocate(mem2, 1);
     }
     {
-    void* mem1 = std::malloc(sizeof(DCounted));
-    void* mem2 = std::malloc(sizeof(DCounted));
+    int count = 0;
+    std::allocator<DCounted> a;
+    DCounted* mem1 = a.allocate(1);
+    DCounted* mem2 = a.allocate(1);
     assert(mem1 && mem2);
-    assert(DCounted::count == 0);
-    DCounted* ptr1 = ::new(mem1) DCounted();
-    DCounted* ptr2 = ::new(mem2) DCounted();
-    assert(DCounted::count == 2);
-    assert(VCounted::count == 2);
+    assert(count == 0);
+#if TEST_STD_VER > 17
+    DCounted* ptr1 = std::construct_at(mem1, count);
+    DCounted* ptr2 = std::construct_at(mem2, count);
+#else
+    DCounted* ptr1 = ::new ((void*)mem1) DCounted(count);
+    DCounted* ptr2 = ::new ((void*)mem2) DCounted(count);
+#endif
+    assert(count == 2);
     std::destroy_at(ptr1);
-    assert(VCounted::count == 1);
+    assert(count == 1);
     std::destroy_at(ptr2);
-    assert(VCounted::count == 0);
-    std::free(mem1);
-    std::free(mem2);
+    assert(count == 0);
+    a.deallocate(mem1, 1);
+    a.deallocate(mem2, 1);
     }
 
+  return true;
+}
+
+int main(int, char**)
+{
+    test();
+
+#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc)
+    static_assert(test());
+#endif
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
@@ -11,7 +11,7 @@
 // <memory>
 
 // template <class ForwardIt, class Size>
-// ForwardIt destroy_n(ForwardIt, Size s);
+// constexpr ForwardIt destroy_n(ForwardIt, Size s);
 
 #include <memory>
 #include <cstdlib>
@@ -21,30 +21,47 @@
 #include "test_iterators.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
+  int& count_;
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(int& count) : count_(count) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC Counted(Counted const& that) : count_(that.count_) { ++count_; }
+  TEST_CONSTEXPR_DYNAMIC_ALLOC ~Counted() { --count_; }
   friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
 
-int main(int, char**)
+TEST_CONSTEXPR_DYNAMIC_ALLOC bool
+test()
 {
     using It = forward_iterator<Counted*>;
     const int N = 5;
-    alignas(Counted) char pool[sizeof(Counted)*N] = {};
-    Counted* p = (Counted*)pool;
-    std::uninitialized_fill(p, p+N, Counted());
-    assert(Counted::count == 5);
+    int count = 0;
+    std::allocator<Counted> a;
+    Counted* p = a.allocate(N);
+    for (Counted* iter = p; iter < p+N; ++iter) {
+#if TEST_STD_VER > 17
+      std::construct_at(iter, count);
+#else
+      ::new ((void*)iter) Counted(count);
+#endif
+    }
+    assert(count == 5);
     Counted* np = std::destroy_n(p, 1);
     assert(np == p+1);
-    assert(Counted::count == 4);
-    p += 1;
-    It it = std::destroy_n(It(p), 4);
-    assert(it == It(p+4));
-    assert(Counted::count == 0);
+    assert(count == 4);
+    It it = std::destroy_n(It(p+1), 4);
+    assert(it == It(p+5));
+    assert(count == 0);
+    a.deallocate(p, N);
+
+  return true;
+}
+
+int main(int, char**)
+{
+    test();
+
+#if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc)
+    static_assert(test());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -125,6 +125,11 @@
 # else
 #   define TEST_THROW_SPEC(...) throw(__VA_ARGS__)
 # endif
+# if TEST_STD_VER > 17 && defined(__cpp_constexpr_dynamic_alloc)
+#   define TEST_CONSTEXPR_DYNAMIC_ALLOC constexpr
+# else
+#   define TEST_CONSTEXPR_DYNAMIC_ALLOC
+# endif
 # if TEST_STD_VER > 17
 #   define TEST_CONSTEXPR_CXX20 constexpr
 # else