2023-06-15 17:36:52 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2022-12-19 14:37:51 +00:00
|
|
|
#define NDEBUG 1
|
|
|
|
// Required for compatibility with new glibc headers
|
|
|
|
#define __malloc__(x, y) __malloc__
|
|
|
|
#if !__has_builtin(__builtin_free)
|
2023-05-18 14:00:10 +01:00
|
|
|
#define __builtin_free(x) free(x)
|
2022-12-19 14:37:51 +00:00
|
|
|
#endif
|
|
|
|
#pragma clang diagnostic ignored "-Wunknown-attributes"
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
// The header xmmintrin.h must come first. Otherwise it results in errors
|
|
|
|
// jemalloc during JIT compilation
|
|
|
|
#include <xmmintrin.h>
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
#define C10_USING_CUSTOM_GENERATED_MACROS
|
|
|
|
|
|
|
|
constexpr int oidMagicId = 0x01DE8;
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2024-01-16 16:23:52 +00:00
|
|
|
template <typename T, size_t N>
|
|
|
|
constexpr std::array<T, N + 1> arrayPrepend(std::array<T, N> a, T t);
|
|
|
|
template <typename T, size_t N, size_t... I>
|
|
|
|
constexpr std::array<T, N + 1> arrayPrependHelper(std::array<T, N> a,
|
|
|
|
T t,
|
|
|
|
std::index_sequence<I...>);
|
|
|
|
|
2023-11-16 15:34:03 +00:00
|
|
|
template <size_t Size = (1 << 20) / sizeof(uintptr_t)>
|
|
|
|
class PointerHashSet {
|
2022-12-19 14:37:51 +00:00
|
|
|
private:
|
|
|
|
// 1 MiB of pointers
|
2023-11-16 15:34:03 +00:00
|
|
|
std::array<uintptr_t, Size> data;
|
2023-11-15 16:13:54 +00:00
|
|
|
size_t numEntries;
|
2022-12-19 14:37:51 +00:00
|
|
|
|
2023-11-15 16:13:54 +00:00
|
|
|
/*
|
|
|
|
* twang_mix64 hash function, taken from Folly where it is used as the
|
|
|
|
* default hash function for 64-bit integers.
|
|
|
|
*/
|
2022-12-19 14:37:51 +00:00
|
|
|
constexpr static uint64_t twang_mix64(uint64_t key) noexcept {
|
|
|
|
key = (~key) + (key << 21); // key *= (1 << 21) - 1; key -= 1;
|
|
|
|
key = key ^ (key >> 24);
|
|
|
|
key = key + (key << 3) + (key << 8); // key *= 1 + (1 << 3) + (1 << 8)
|
|
|
|
key = key ^ (key >> 14);
|
|
|
|
key = key + (key << 2) + (key << 4); // key *= 1 + (1 << 2) + (1 << 4)
|
|
|
|
key = key ^ (key >> 28);
|
|
|
|
key = key + (key << 31); // key *= 1 + (1 << 31)
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2023-05-18 14:00:10 +01:00
|
|
|
void initialize() noexcept {
|
|
|
|
data.fill(0);
|
2023-11-15 16:13:54 +00:00
|
|
|
numEntries = 0;
|
2023-05-18 14:00:10 +01:00
|
|
|
}
|
2022-12-19 14:37:51 +00:00
|
|
|
|
2023-11-15 16:13:54 +00:00
|
|
|
/*
|
|
|
|
* Adds the pointer to the set.
|
|
|
|
* Returns `true` if the value was newly added. `false` may be returned if
|
|
|
|
* the value was already present, a null pointer was passed or if there are
|
|
|
|
* no entries left in the array.
|
|
|
|
*/
|
2022-12-19 14:37:51 +00:00
|
|
|
bool add(uintptr_t pointer) noexcept {
|
2023-11-15 16:13:54 +00:00
|
|
|
if (pointer == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-19 14:37:51 +00:00
|
|
|
uint64_t index = twang_mix64(pointer) % data.size();
|
|
|
|
while (true) {
|
|
|
|
uintptr_t entry = data[index];
|
2023-11-15 16:13:54 +00:00
|
|
|
|
2022-12-19 14:37:51 +00:00
|
|
|
if (entry == 0) {
|
|
|
|
data[index] = pointer;
|
2023-11-15 16:13:54 +00:00
|
|
|
++numEntries;
|
2022-12-19 14:37:51 +00:00
|
|
|
return true;
|
|
|
|
}
|
2023-11-15 16:13:54 +00:00
|
|
|
|
|
|
|
if (entry == pointer || numEntries >= data.size()) {
|
2022-12-19 14:37:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
2023-11-15 16:13:54 +00:00
|
|
|
|
2022-12-19 14:37:51 +00:00
|
|
|
index = (index + 1) % data.size();
|
|
|
|
}
|
|
|
|
}
|
2023-11-15 16:13:54 +00:00
|
|
|
|
|
|
|
size_t size(void) {
|
|
|
|
return numEntries;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t capacity(void) {
|
|
|
|
return data.size();
|
|
|
|
}
|
|
|
|
|
2023-09-27 23:19:51 +01:00
|
|
|
bool add(const auto* p) {
|
|
|
|
return add((uintptr_t)p);
|
|
|
|
}
|
2023-11-16 15:34:03 +00:00
|
|
|
};
|
2022-12-19 14:37:51 +00:00
|
|
|
|
2023-05-18 14:00:10 +01:00
|
|
|
} // namespace
|
2022-12-19 14:37:51 +00:00
|
|
|
|
|
|
|
// alignas(0) is ignored according to docs so can be default
|
2023-08-24 13:42:12 +01:00
|
|
|
template <unsigned int N, unsigned int align = 0, int32_t Id = 0>
|
2022-12-19 14:37:51 +00:00
|
|
|
struct alignas(align) DummySizedOperator {
|
|
|
|
char c[N];
|
|
|
|
};
|
|
|
|
|
2023-05-18 14:00:10 +01:00
|
|
|
// The empty class specialization is, unfortunately, necessary. When this
|
|
|
|
// operator is passed as a template parameter to something like unordered_map,
|
|
|
|
// even though an empty class and a class with a single character have size one,
|
|
|
|
// there is some empty class optimization that changes the static size of the
|
|
|
|
// container if an empty class is passed.
|
2023-08-24 13:42:12 +01:00
|
|
|
template <int32_t Id>
|
|
|
|
struct DummySizedOperator<0, 0, Id> {};
|
2023-11-02 13:22:57 +00:00
|
|
|
template <int32_t Id>
|
|
|
|
struct DummySizedOperator<0, 1, Id> {};
|
2022-12-19 14:37:51 +00:00
|
|
|
|
2023-08-24 13:42:12 +01:00
|
|
|
template <template <typename, size_t, size_t, int32_t> typename DerivedT,
|
2023-05-31 14:49:47 +01:00
|
|
|
typename T,
|
|
|
|
size_t N,
|
2023-08-24 13:42:12 +01:00
|
|
|
size_t Align,
|
|
|
|
int32_t Id>
|
2023-05-31 14:49:47 +01:00
|
|
|
struct DummyAllocatorBase {
|
|
|
|
using value_type = T;
|
|
|
|
T* allocate(std::size_t n) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
void deallocate(T* p, std::size_t n) noexcept {
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename U>
|
|
|
|
struct rebind {
|
2023-08-24 13:42:12 +01:00
|
|
|
using other = DerivedT<U, N, Align, Id>;
|
2023-05-31 14:49:47 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2023-08-24 13:42:12 +01:00
|
|
|
template <typename T, size_t N, size_t Align = 0, int32_t Id = 0>
|
2023-05-31 14:49:47 +01:00
|
|
|
struct alignas(Align) DummyAllocator
|
2023-08-24 13:42:12 +01:00
|
|
|
: DummyAllocatorBase<DummyAllocator, T, N, Align, Id> {
|
2023-05-31 14:49:47 +01:00
|
|
|
char c[N];
|
|
|
|
};
|
|
|
|
|
2023-08-24 13:42:12 +01:00
|
|
|
template <typename T, int32_t Id>
|
|
|
|
struct DummyAllocator<T, 0, 0, Id>
|
|
|
|
: DummyAllocatorBase<DummyAllocator, T, 0, 0, Id> {};
|
2023-11-02 13:22:57 +00:00
|
|
|
template <typename T, int32_t Id>
|
|
|
|
struct DummyAllocator<T, 0, 1, Id>
|
|
|
|
: DummyAllocatorBase<DummyAllocator, T, 0, 1, Id> {};
|
2023-07-24 14:20:23 +01:00
|
|
|
|
2024-01-10 15:07:47 +00:00
|
|
|
template <typename Type, size_t ExpectedSize, size_t ActualSize>
|
|
|
|
struct validate_size_eq {
|
2023-08-18 16:43:31 +01:00
|
|
|
static constexpr bool value = true;
|
2023-07-24 14:20:23 +01:00
|
|
|
static_assert(ExpectedSize == ActualSize);
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Type, size_t ExpectedSize>
|
2024-01-10 15:07:47 +00:00
|
|
|
struct validate_size : validate_size_eq<Type, ExpectedSize, sizeof(Type)> {};
|
2023-07-24 14:20:23 +01:00
|
|
|
|
|
|
|
template <size_t ExpectedOffset, size_t ActualOffset>
|
2023-08-18 16:43:31 +01:00
|
|
|
struct validate_offset {
|
|
|
|
static constexpr bool value = true;
|
2023-07-24 14:20:23 +01:00
|
|
|
static_assert(ExpectedOffset == ActualOffset);
|
|
|
|
};
|
2023-09-21 16:57:08 +01:00
|
|
|
|
|
|
|
enum class StubbedPointer : uintptr_t {};
|
2023-09-27 23:19:51 +01:00
|
|
|
|
|
|
|
bool isStorageInline(const auto& c) {
|
|
|
|
return (uintptr_t)std::data(c) < (uintptr_t)(&c + sizeof(c)) &&
|
|
|
|
(uintptr_t)std::data(c) >= (uintptr_t)&c;
|
|
|
|
}
|
2024-01-04 17:01:52 +00:00
|
|
|
|
2024-01-16 16:23:52 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template <typename T, size_t N, size_t... I>
|
|
|
|
constexpr std::array<T, N + 1> arrayPrependHelper(std::array<T, N> a,
|
|
|
|
T t,
|
|
|
|
std::index_sequence<I...>) {
|
|
|
|
return {t, a[I]...};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, size_t N>
|
|
|
|
constexpr std::array<T, N + 1> arrayPrepend(std::array<T, N> a, T t) {
|
|
|
|
return arrayPrependHelper(a, t, std::make_index_sequence<N>());
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2024-01-04 17:01:52 +00:00
|
|
|
namespace OIInternal {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct Incomplete;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
constexpr bool oi_is_complete = true;
|
|
|
|
template <>
|
|
|
|
constexpr bool oi_is_complete<void> = false;
|
|
|
|
template <typename T>
|
|
|
|
constexpr bool oi_is_complete<Incomplete<T>> = false;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace OIInternal
|