diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index ba687bea8bac7..6a442c454ca0f 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -214,30 +214,9 @@ void MetaspaceShared::dump_loaded_classes(const char* file_name, TRAPS) { } } -// If p is not aligned, move it up to the next address that's aligned with alignment. -// If this is not possible (because p is too high), return nullptr. Example: -// p = 0xffffffffffff0000, alignment= 0x10000 => return nullptr. -static char* align_up_or_null(char* p, size_t alignment) { - assert(p != nullptr, "sanity"); - if (is_aligned(p, alignment)) { - return p; - } - - char* down = align_down(p, alignment); - if (max_uintx - uintx(down) < uintx(alignment)) { - // Run out of address space to align up. - return nullptr; - } - - char* aligned = align_up(p, alignment); - assert(aligned >= p, "sanity"); - assert(aligned != nullptr, "sanity"); - return aligned; -} - static bool shared_base_too_high(char* specified_base, char* aligned_base, size_t cds_max) { - // Caller should have checked if align_up_or_null( returns nullptr (comparing specified_base - // with nullptr is UB). + // Caller should have checked that aligned_base was successfully aligned and is not nullptr. + // Comparing specified_base with nullptr is UB. assert(aligned_base != nullptr, "sanity"); assert(aligned_base >= specified_base, "sanity"); @@ -261,7 +240,9 @@ static char* compute_shared_base(size_t cds_max) { return specified_base; } - char* aligned_base = align_up_or_null(specified_base, alignment); + char* aligned_base = can_align_up(specified_base, alignment) + ? align_up(specified_base, alignment) + : nullptr; if (aligned_base != specified_base) { log_info(cds)("SharedBaseAddress (" INTPTR_FORMAT ") aligned up to " INTPTR_FORMAT, diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index ab74d3468e19e..a32897946b9e4 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -189,8 +189,12 @@ bool PSOldGen::expand(size_t bytes) { assert_locked_or_safepoint(Heap_lock); assert(bytes > 0, "precondition"); #endif + const size_t remaining_bytes = virtual_space()->uncommitted_size(); + if (remaining_bytes == 0) { + return false; + } const size_t alignment = virtual_space()->alignment(); - size_t aligned_bytes = align_up(bytes, alignment); + size_t aligned_bytes = align_up(MIN2(bytes, remaining_bytes), alignment); size_t aligned_expand_bytes = align_up(MinHeapDeltaBytes, alignment); if (UseNUMA) { @@ -198,13 +202,6 @@ bool PSOldGen::expand(size_t bytes) { // providing a page per lgroup. Alignment is larger or equal to the page size. aligned_expand_bytes = MAX2(aligned_expand_bytes, alignment * os::numa_get_groups_num()); } - if (aligned_bytes == 0) { - // The alignment caused the number of bytes to wrap. A call to expand - // implies a best effort to expand by "bytes" but not a guarantee. Align - // down to give a best effort. This is likely the most that the generation - // can expand since it has some capacity to start with. - aligned_bytes = align_down(bytes, alignment); - } bool success = false; if (aligned_expand_bytes > aligned_bytes) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 11c13fe3c9e19..040eb4695fbd8 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1420,7 +1420,7 @@ const int ObjectAlignmentInBytes = 8; \ product(size_t, MinHeapDeltaBytes, ScaleForWordSize(128*K), \ "The minimum change in heap space due to GC (in bytes)") \ - range(0, max_uintx) \ + range(0, max_uintx / 2 + 1) \ \ product(size_t, MinMetaspaceExpansion, ScaleForWordSize(256*K), \ "The minimum expansion of Metaspace (in bytes)") \ diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index d266b632ed1c3..be9b0eef45535 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2011,10 +2011,10 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz const size_t alignment_adjusted = MAX2(alignment, system_allocation_granularity); // Calculate first and last possible attach points: - char* const lo_att = align_up(MAX2(absolute_min, min), alignment_adjusted); - if (lo_att == nullptr) { + if (!can_align_up(MAX2(absolute_min, min), alignment_adjusted)) { return nullptr; // overflow } + char* const lo_att = align_up(MAX2(absolute_min, min), alignment_adjusted); char* const hi_end = MIN2(max, absolute_max); if ((uintptr_t)hi_end <= bytes) { diff --git a/src/hotspot/share/utilities/align.hpp b/src/hotspot/share/utilities/align.hpp index b67e61036a007..69dd1a3052bc6 100644 --- a/src/hotspot/share/utilities/align.hpp +++ b/src/hotspot/share/utilities/align.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,8 +70,15 @@ constexpr T align_down(T size, A alignment) { return result; } +// Checks whether it is possible to align size to alignment without overflowing. +template<typename T, typename A, ENABLE_IF(std::is_integral<T>::value)> +constexpr bool can_align_up(T size, A alignment) { + return align_down(std::numeric_limits<T>::max(), alignment) >= size; +} + template<typename T, typename A, ENABLE_IF(std::is_integral<T>::value)> constexpr T align_up(T size, A alignment) { + assert(can_align_up(size, alignment), "precondition"); T adjusted = checked_cast<T>(size + alignment_mask(alignment)); return align_down(adjusted, alignment); } @@ -85,6 +92,12 @@ constexpr T align_down_bounded(T size, A alignment) { // Align pointers and check for alignment. +template <typename A> +inline bool can_align_up(void* ptr, A alignment) { + static_assert(sizeof(ptr) == sizeof(uintptr_t), "assumption"); + return can_align_up((uintptr_t)ptr, alignment); +} + template <typename T, typename A> inline T* align_up(T* ptr, A alignment) { return (T*)align_up((uintptr_t)ptr, alignment); diff --git a/test/hotspot/gtest/utilities/test_align.cpp b/test/hotspot/gtest/utilities/test_align.cpp index 3c03fd5f24d9f..7413e58e7ab44 100644 --- a/test/hotspot/gtest/utilities/test_align.cpp +++ b/test/hotspot/gtest/utilities/test_align.cpp @@ -29,7 +29,7 @@ #include <limits> // A few arbitrarily chosen values to test the align functions on. -static constexpr uint64_t values[] = {1, 3, 10, 345, 1023, 1024, 1025, 23909034, INT_MAX, uint64_t(-1) / 2, uint64_t(-1) / 2 + 100, uint64_t(-1)}; +static constexpr uint64_t values[] = {1, 3, 10, 345, 1023, 1024, 1025, 23909034, INT_MAX, uint64_t(-1) / 2, uint64_t(-1) / 2 + 100, ~(uint64_t(1) << 62)}; template <typename T> static constexpr T max_alignment() { @@ -194,3 +194,78 @@ TEST(Align, alignments) { test_alignments<int8_t, int8_t>(); } + +template<typename T, typename A> +static constexpr void test_can_align_up() { + int alignment_value = 4; + int small_value = 63; + A alignment = static_cast<A>(alignment_value); + + ASSERT_TRUE(can_align_up(static_cast<T>(small_value), alignment)); + ASSERT_TRUE(can_align_up(static_cast<T>(-small_value), alignment)); + ASSERT_TRUE(can_align_up(std::numeric_limits<T>::min(), alignment)); + ASSERT_FALSE(can_align_up(std::numeric_limits<T>::max(), alignment)); + ASSERT_FALSE(can_align_up(std::numeric_limits<T>::max() - 1, alignment)); + ASSERT_TRUE(can_align_up(align_down(std::numeric_limits<T>::max(), alignment), alignment)); + ASSERT_FALSE(can_align_up(align_down(std::numeric_limits<T>::max(), alignment) + 1, alignment)); + if (std::is_signed<T>::value) { + ASSERT_TRUE(can_align_up(static_cast<T>(-1), alignment)); + ASSERT_TRUE(can_align_up(align_down(static_cast<T>(-1), alignment), alignment)); + ASSERT_TRUE(can_align_up(align_down(static_cast<T>(-1) + 1, alignment), alignment)); + } +} + +TEST(Align, test_can_align_up_int32_int32) { + test_can_align_up<int32_t, int32_t>(); +} + +TEST(Align, test_can_align_up_uint32_uint32) { + test_can_align_up<uint32_t, uint32_t>(); +} + +TEST(Align, test_can_align_up_int32_uint32) { + test_can_align_up<int32_t, uint32_t>(); +} + +TEST(Align, test_can_align_up_uint32_int32) { + test_can_align_up<uint32_t, int32_t>(); +} + +TEST(Align, test_can_align_up_ptr) { + uint alignment = 4; + char buffer[8]; + + ASSERT_TRUE(can_align_up(buffer, alignment)); + ASSERT_FALSE(can_align_up(reinterpret_cast<void*>(UINTPTR_MAX), alignment)); +} + +#ifdef ASSERT +template <typename T, typename A> +static void test_fail_alignment() { + A alignment = max_alignment<A>(); + T value = align_down(std::numeric_limits<T>::max(), alignment) + 1; + // Aligning value to alignment would now overflow. + // Assert inside align_up expected. + T aligned = align_up(value, alignment); +} + +TEST_VM_ASSERT(Align, fail_alignments_same_size) { + test_fail_alignment<uint64_t, uint64_t>(); +} + +TEST_VM_ASSERT(Align, fail_alignments_unsigned_signed) { + test_fail_alignment<uint32_t, int32_t>(); +} + +TEST_VM_ASSERT(Align, fail_alignments_signed_unsigned) { + test_fail_alignment<int64_t, uint32_t>(); +} + +TEST_VM_ASSERT(Align, fail_alignments_small_large) { + test_fail_alignment<uint8_t, uint64_t>(); +} + +TEST_VM_ASSERT(Align, fail_alignments_large_small) { + test_fail_alignment<uint64_t, uint8_t>(); +} +#endif // ASSERT