[base] Unify wrappers for malloc and friends

- Unify AIX and Starboard wrapping code.
- Move all wrapping code into `platform/memory.h`

Change-Id: I42c04dd1e982edff2db7bbfa9eecdbdd67f40714
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3858226
Reviewed-by: Jakob Linke <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82794}
This commit is contained in:
Michael Lippautz 2022-08-29 15:40:19 +02:00 committed by V8 LUCI CQ
parent 81f15091a4
commit 76d61b2195
18 changed files with 183 additions and 147 deletions

View File

@ -645,6 +645,7 @@ filegroup(
"src/base/platform/condition-variable.cc",
"src/base/platform/condition-variable.h",
"src/base/platform/elapsed-timer.h",
"src/base/platform/memory.h",
"src/base/platform/memory-protection-key.cc",
"src/base/platform/memory-protection-key.h",
"src/base/platform/mutex.cc",

View File

@ -5474,6 +5474,7 @@ v8_component("v8_libbase") {
"src/base/platform/elapsed-timer.h",
"src/base/platform/memory-protection-key.cc",
"src/base/platform/memory-protection-key.h",
"src/base/platform/memory.h",
"src/base/platform/mutex.cc",
"src/base/platform/mutex.h",
"src/base/platform/platform.h",

View File

@ -30,6 +30,7 @@
#include "src/api/api-natives.h"
#include "src/base/functional.h"
#include "src/base/logging.h"
#include "src/base/platform/memory.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/base/safe_conversions.h"
@ -387,38 +388,16 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
void* Allocate(size_t length) override {
#if V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
void* data = __linux_calloc(length, 1);
#else
void* data = base::Calloc(length, 1);
#endif
return data;
}
void* Allocate(size_t length) override { return base::Calloc(length, 1); }
void* AllocateUninitialized(size_t length) override {
#if V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
void* data = __linux_malloc(length);
#else
void* data = base::Malloc(length);
#endif
return data;
return base::Malloc(length);
}
void Free(void* data, size_t) override { base::Free(data); }
void* Reallocate(void* data, size_t old_length, size_t new_length) override {
#if V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
void* new_data = __linux_realloc(data, new_length);
#else
void* new_data = base::Realloc(data, new_length);
#endif
if (new_length > old_length) {
memset(reinterpret_cast<uint8_t*>(new_data) + old_length, 0,
new_length - old_length);

View File

@ -10,7 +10,6 @@
#include "src/base/compiler-specific.h"
#include "src/base/logging.h"
#include "src/base/platform/wrappers.h"
// No-op macro which is used to work around MSVC's funky VA_ARGS support.
#define EXPAND(x) x

160
src/base/platform/memory.h Normal file
View File

@ -0,0 +1,160 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BASE_PLATFORM_MEMORY_H_
#define V8_BASE_PLATFORM_MEMORY_H_
#include <cstddef>
#include <cstdlib>
#include "include/v8config.h"
#include "src/base/bits.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
#if V8_OS_STARBOARD
#include "starboard/memory.h"
#endif // V8_OS_STARBOARD
#if V8_OS_DARWIN
#include <malloc/malloc.h>
#else // !V8_OS_DARWIN
#include <malloc.h>
#endif // !V8_OS_DARWIN
#if (V8_OS_POSIX && !V8_OS_AIX) || V8_OS_WIN
#define V8_HAS_MALLOC_USABLE_SIZE 1
#endif // (V8_OS_POSIX && !V8_OS_AIX) || V8_OS_WIN
namespace v8::base {
inline void* Malloc(size_t size) {
#if V8_OS_STARBOARD
return SbMemoryAllocate(size);
#elif V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX.
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
return __linux_malloc(length);
#else
return malloc(size);
#endif
}
inline void* Realloc(void* memory, size_t size) {
#if V8_OS_STARBOARD
return SbMemoryReallocate(memory, size);
#elif V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX, see Malloc().
// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
return __linux_realloc(data, new_length);
#else
return realloc(memory, size);
#endif
}
inline void Free(void* memory) {
#if V8_OS_STARBOARD
return SbMemoryDeallocate(memory);
#else // !V8_OS_STARBOARD
return free(memory);
#endif // !V8_OS_STARBOARD
}
inline void* Calloc(size_t count, size_t size) {
#if V8_OS_STARBOARD
return SbMemoryCalloc(count, size);
#elif V8_OS_AIX && _LINUX_SOURCE_COMPAT
// Work around for GCC bug on AIX, see Malloc().
return __linux_calloc(count, size);
#else
return calloc(count, size);
#endif
}
// Aligned allocation. Memory must be freed with `AlignedFree()` as not all
// platforms support using general free for aligned allocations.
inline void* AlignedAlloc(size_t size, size_t alignment) {
DCHECK_LE(alignof(void*), alignment);
DCHECK(base::bits::IsPowerOfTwo(alignment));
#if V8_OS_WIN
return _aligned_malloc(size, alignment);
#elif V8_LIBC_BIONIC
// posix_memalign is not exposed in some Android versions, so we fall back to
// memalign. See http://code.google.com/p/android/issues/detail?id=35391.
return memalign(alignment, size);
#elif V8_OS_STARBOARD
return SbMemoryAllocateAligned(alignment, size);
#else // POSIX
void* ptr;
if (posix_memalign(&ptr, alignment, size)) ptr = nullptr;
return ptr;
#endif // POSIX
}
inline void AlignedFree(void* ptr) {
#if V8_OS_WIN
_aligned_free(ptr);
#elif V8_OS_STARBOARD
SbMemoryFreeAligned(ptr);
#else
// Using regular Free() is not correct in general. For most platforms,
// including V8_LIBC_BIONIC, it is though.
base::Free(ptr);
#endif
}
#if V8_HAS_MALLOC_USABLE_SIZE
// Note that the use of additional bytes that deviate from the original
// `Malloc()` request returned by `MallocUsableSize()` is not UBSan-safe. Use
// `AllocateAtLeast()` for a safe version.
inline size_t MallocUsableSize(void* ptr) {
#if V8_OS_WIN
return _msize(ptr);
#elif V8_OS_DARWIN
return malloc_size(ptr);
#else // POSIX.
return malloc_usable_size(ptr);
#endif // POSIX.
}
#endif // V8_HAS_MALLOC_USABLE_SIZE
// Mimics C++23 `allocation_result`.
template <class Pointer>
struct AllocationResult {
Pointer ptr;
size_t count;
};
// Allocates at least `n * sizeof(T)` uninitialized storage but may allocate
// more which is indicated by the return value. Mimics C++23
// `allocate_ate_least()`.
template <typename T>
V8_NODISCARD AllocationResult<T*> AllocateAtLeast(size_t n) {
const size_t min_wanted_size = n * sizeof(T);
auto* memory = static_cast<T*>(Malloc(min_wanted_size));
#if !V8_HAS_MALLOC_USABLE_SIZE
return {memory, min_wanted_size};
#else // V8_HAS_MALLOC_USABLE_SIZE
const size_t usable_size = MallocUsableSize(memory);
#if V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
// UBSan (specifically, -fsanitize=bounds) assumes that any access outside
// of the requested size for malloc is UB and will trap in ud2 instructions.
// This can be worked around by using `Realloc()` on the specific memory
// region, assuming that the allocator doesn't actually reallocate the
// buffer.
if (usable_size != min_wanted_size) {
CHECK_EQ(static_cast<T*>(Realloc(memory, usable_size)), memory);
}
#endif // V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
return {memory, usable_size};
#endif // V8_HAS_MALLOC_USABLE_SIZE
}
} // namespace v8::base
#undef V8_HAS_MALLOC_USABLE_SIZE
#endif // V8_BASE_PLATFORM_MEMORY_H_

View File

@ -1265,18 +1265,5 @@ Stack::StackSlot Stack::GetCurrentStackPosition() {
#undef MAP_ANONYMOUS
#undef MADV_FREE
// static
size_t Malloc::GetUsableSize(void* ptr) {
#if defined(V8_OS_DARWIN)
return malloc_size(ptr);
#elif defined(V8_OS_AIX)
// malloc_usable_size is not available, and there is no equivalent
UNIMPLEMENTED();
return 0;
#else
return malloc_usable_size(ptr);
#endif // !defined(V8_OS_DARWIN)
}
} // namespace base
} // namespace v8

View File

@ -1766,8 +1766,5 @@ Stack::StackSlot Stack::GetCurrentStackPosition() {
#endif
}
// static
size_t Malloc::GetUsableSize(void* ptr) { return _msize(ptr); }
} // namespace base
} // namespace v8

View File

@ -662,47 +662,6 @@ class V8_BASE_EXPORT Stack {
}
};
class V8_BASE_EXPORT Malloc final {
public:
// Returns the usable size in bytes for a `ptr` allocated using `malloc()`.
// Note that the bytes returned may not be generally accessed on e.g. UBSan
// builds. Use `AllocateAtLeast()` for a malloc version that works with UBSan.
static size_t GetUsableSize(void* ptr);
// Mimics C++23 `allocation_result`.
template <class Pointer>
struct AllocationResult {
Pointer ptr;
std::size_t count;
};
// Allocates at least `n * sizeof(T)` uninitialized storage but may allocate
// more which is indicated by the return value. Mimics C++23
// `allocate_ate_least()`.
template <typename T>
V8_NODISCARD static AllocationResult<T*> AllocateAtLeast(std::size_t n) {
const size_t min_wanted_size = n * sizeof(T);
auto* memory = static_cast<T*>(malloc(min_wanted_size));
#if defined(V8_OS_AIX)
// GetUsableSize() is not implemented on Aix
return {memory, min_wanted_size};
#else
const size_t usable_size = v8::base::Malloc::GetUsableSize(memory);
#if V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
// UBSan (specifically, -fsanitize=bounds) assumes that any access outside
// of the requested size for malloc is UB and will trap in ud2 instructions.
// This can be worked around by using `realloc()` on the specific memory
// regon, assuming that the allocator doesn't actually reallocate the
// buffer.
if (usable_size != min_wanted_size) {
CHECK_EQ(static_cast<T*>(realloc(memory, usable_size)), memory);
}
#endif // V8_USE_UNDEFINED_BEHAVIOR_SANITIZER
return {memory, usable_size};
#endif // defined(V8_OS_AIX)
}
};
} // namespace base
} // namespace v8

View File

@ -11,6 +11,7 @@
#include <string.h>
#include "src/base/base-export.h"
#include "src/base/platform/memory.h"
#if defined(V8_OS_STARBOARD)
#include "starboard/memory.h"
@ -24,16 +25,6 @@ namespace base {
// Common libstd implementations.
// inline implementations are preferred here due to performance concerns.
inline void* Malloc(size_t size) { return malloc(size); }
inline void* Realloc(void* memory, size_t size) {
return realloc(memory, size);
}
inline void Free(void* memory) { return free(memory); }
inline void* Calloc(size_t count, size_t size) { return calloc(count, size); }
inline char* Strdup(const char* source) { return strdup(source); }
inline FILE* Fopen(const char* filename, const char* mode) {
@ -44,18 +35,6 @@ inline int Fclose(FILE* stream) { return fclose(stream); }
#else // V8_OS_STARBOARD
inline void* Malloc(size_t size) { return SbMemoryAllocate(size); }
inline void* Realloc(void* memory, size_t size) {
return SbMemoryReallocate(memory, size);
}
inline void Free(void* memory) { return SbMemoryDeallocate(memory); }
inline void* Calloc(size_t count, size_t size) {
return SbMemoryCalloc(count, size);
}
inline char* Strdup(const char* source) { return SbStringDuplicate(source); }
inline FILE* Fopen(const char* filename, const char* mode) { return NULL; }

View File

@ -5,6 +5,7 @@
#ifndef V8_DEOPTIMIZER_FRAME_DESCRIPTION_H_
#define V8_DEOPTIMIZER_FRAME_DESCRIPTION_H_
#include "src/base/platform/memory.h"
#include "src/codegen/register.h"
#include "src/execution/frame-constants.h"
#include "src/utils/boxed-float.h"

View File

@ -10,8 +10,8 @@
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/platform/memory.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h"
namespace heap::base {
namespace internal {
@ -206,7 +206,7 @@ class Worklist<EntryType, MinSegmentSize>::Segment final
: public internal::SegmentBase {
public:
static Segment* Create(uint16_t min_segment_size) {
auto result = v8::base::Malloc::AllocateAtLeast<char>(
auto result = v8::base::AllocateAtLeast<char>(
MallocSizeForCapacity(min_segment_size));
return new (result.ptr)
Segment(CapacityForMallocSize(result.count * sizeof(char)));

View File

@ -94,7 +94,7 @@ class PossiblyEmptyBuckets {
DCHECK(!IsAllocated());
size_t words = WordsForBuckets(buckets);
uintptr_t* ptr = reinterpret_cast<uintptr_t*>(
AlignedAlloc(words * kWordSize, kSystemPointerSize));
AlignedAllocWithRetry(words * kWordSize, kSystemPointerSize));
ptr[0] = bitmap_ >> 1;
for (size_t word_idx = 1; word_idx < words; word_idx++) {
@ -155,7 +155,7 @@ class SlotSet {
// calculating the size of this data structure.
size_t buckets_size = buckets * sizeof(Bucket*);
size_t size = kInitialBucketsSize + buckets_size;
void* allocation = AlignedAlloc(size, kSystemPointerSize);
void* allocation = AlignedAllocWithRetry(size, kSystemPointerSize);
SlotSet* slot_set = reinterpret_cast<SlotSet*>(
reinterpret_cast<uint8_t*>(allocation) + kInitialBucketsSize);
DCHECK(

View File

@ -46,7 +46,7 @@ void* StringForwardingTable::Block::operator new(size_t size, int capacity) {
// sizeof(Record).
const size_t new_size = size + elements_size - sizeof(Record);
DCHECK_LE(alignof(StringForwardingTable::Block), kSystemPointerSize);
return AlignedAlloc(new_size, kSystemPointerSize);
return AlignedAllocWithRetry(new_size, kSystemPointerSize);
}
void StringForwardingTable::Block::operator delete(void* block) {

View File

@ -197,8 +197,8 @@ void* StringTable::Data::operator new(size_t size, int capacity) {
// Subtract 1 from capacity, as the member elements_ already supplies the
// storage for the first element.
return AlignedAlloc(size + (capacity - 1) * sizeof(Tagged_t),
alignof(StringTable::Data));
return AlignedAllocWithRetry(size + (capacity - 1) * sizeof(Tagged_t),
alignof(StringTable::Data));
}
void StringTable::Data::operator delete(void* table) { AlignedFree(table); }

View File

@ -335,7 +335,7 @@ void SamplingEventsProcessor::SetSamplingInterval(base::TimeDelta period) {
}
void* SamplingEventsProcessor::operator new(size_t size) {
return AlignedAlloc(size, alignof(SamplingEventsProcessor));
return AlignedAllocWithRetry(size, alignof(SamplingEventsProcessor));
}
void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }

View File

@ -11,6 +11,7 @@
#include "src/base/lazy-instance.h"
#include "src/base/logging.h"
#include "src/base/page-allocator.h"
#include "src/base/platform/memory.h"
#include "src/base/platform/wrappers.h"
#include "src/base/sanitizer/lsan-page-allocator.h"
#include "src/base/sanitizer/lsan-virtual-address-space.h"
@ -29,22 +30,6 @@ namespace internal {
namespace {
void* AlignedAllocInternal(size_t size, size_t alignment) {
void* ptr;
#if V8_OS_WIN
ptr = _aligned_malloc(size, alignment);
#elif V8_LIBC_BIONIC
// posix_memalign is not exposed in some Android versions, so we fall back to
// memalign. See http://code.google.com/p/android/issues/detail?id=35391.
ptr = memalign(alignment, size);
#elif V8_OS_STARBOARD
ptr = SbMemoryAllocateAligned(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size)) ptr = nullptr;
#endif
return ptr;
}
class PageAllocatorInitializer {
public:
PageAllocatorInitializer() {
@ -145,30 +130,17 @@ void* AllocWithRetry(size_t size, MallocFn malloc_fn) {
return result;
}
void* AlignedAlloc(size_t size, size_t alignment) {
DCHECK_LE(alignof(void*), alignment);
DCHECK(base::bits::IsPowerOfTwo(alignment));
void* AlignedAllocWithRetry(size_t size, size_t alignment) {
void* result = nullptr;
for (int i = 0; i < kAllocationTries; ++i) {
result = AlignedAllocInternal(size, alignment);
result = base::AlignedAlloc(size, alignment);
if (V8_LIKELY(result != nullptr)) return result;
OnCriticalMemoryPressure();
}
V8::FatalProcessOutOfMemory(nullptr, "AlignedAlloc");
}
void AlignedFree(void* ptr) {
#if V8_OS_WIN
_aligned_free(ptr);
#elif V8_LIBC_BIONIC
// Using free is not correct in general, but for V8_LIBC_BIONIC it is.
base::Free(ptr);
#elif V8_OS_STARBOARD
SbMemoryFreeAligned(ptr);
#else
base::Free(ptr);
#endif
}
void AlignedFree(void* ptr) { base::AlignedFree(ptr); }
size_t AllocatePageSize() {
return GetPlatformPageAllocator()->AllocatePageSize();

View File

@ -8,6 +8,7 @@
#include "include/v8-platform.h"
#include "src/base/address-region.h"
#include "src/base/compiler-specific.h"
#include "src/base/platform/memory.h"
#include "src/init/v8.h"
namespace v8 {
@ -93,7 +94,7 @@ using MallocFn = void* (*)(size_t);
// Call free to release memory allocated with this function.
void* AllocWithRetry(size_t size, MallocFn = base::Malloc);
V8_EXPORT_PRIVATE void* AlignedAlloc(size_t size, size_t alignment);
V8_EXPORT_PRIVATE void* AlignedAllocWithRetry(size_t size, size_t alignment);
V8_EXPORT_PRIVATE void AlignedFree(void* ptr);
// Returns platfrom page allocator instance. Guaranteed to be a valid pointer.

View File

@ -149,8 +149,8 @@ TEST_WITH_PLATFORM(AlignedAllocOOM, AllocationPlatform) {
CcTest::isolate()->SetFatalErrorHandler(OnAlignedAllocOOM);
// On failure, this won't return, since an AlignedAlloc failure is fatal.
// In that case, behavior is checked in OnAlignedAllocOOM before exit.
void* result = v8::internal::AlignedAlloc(GetHugeMemoryAmount(),
v8::internal::AllocatePageSize());
void* result = v8::internal::AlignedAllocWithRetry(
GetHugeMemoryAmount(), v8::internal::AllocatePageSize());
// On a few systems, allocation somehow succeeds.
CHECK_EQ(result == nullptr, platform.oom_callback_called);
}