d36adbacaf
The former contents of the VM isolate are now included into each isolate group. This makes each isolate group's heap independent, and in particular allows each heap to be allocated to a separate pointer cage (not done in this CL). The duplicated stubs that allowed PC relative calls are removed, since the originals can now be the target of PC relative calls. The bootstrapping needing to load an AppJIT or AppAOT snapshot is reduced to allocating the oddballs. The code is entirely dropped in the AOT runtime, but the JIT runtime still has it to allow for flags to affect the compilation of the stub code. Further refactoring might be able to remove this for the JIT runtime too, with only gen_snapshot knowing how to bootstrap. Class serialization no longer distinguishes predefined classes. The page containing null is marked as never-evacuate. null, false and true must not move because the compiler relies on their low bits having certain patterns for some optimizations. (Previously, the entire VM isolate heap never moved.) Compaction is disabled for IA32. Due to register pressure, some stub calls must not use a scratch register and embed the address of Code. The page containing the call-through-safepoint stub is frozen when running with --write-protect-code and the stub is created at runtime (instead of loaded from an AppJIT or AppAOT snapshot). This stub must remain executable even during a safepoint, as a foreign call might during return during a safepoint and only block after the stub directs it to the runtime. The snapshot symbols are renamed to kDartSnapshotData and kDartSnapshotText. There is no need to distinguish the VM isolate's snapshot, and snaphots are per isolate group not per isolate. Aliases with the old names are added to ease migration. Some global flags that were automatically set based on the VM isolate's snapshot are now isolate group flags and automatically set by the isolate group's snapshot. TEST=ci Change-Id: Iee82016057d609112e9b021d178fc3d4d18b5044 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500621 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
424 lines
14 KiB
C++
424 lines
14 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "platform/utils.h"
|
|
|
|
#include "platform/allocation.h"
|
|
#include "platform/globals.h"
|
|
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID)
|
|
#include <dlfcn.h>
|
|
#include <libgen.h>
|
|
#elif defined(DART_HOST_OS_FUCHSIA)
|
|
#include <dlfcn.h>
|
|
#include <fuchsia/io/cpp/fidl.h>
|
|
#include <lib/fdio/directory.h>
|
|
#include <lib/fdio/io.h>
|
|
#include <zircon/dlfcn.h>
|
|
#include <zircon/status.h>
|
|
#endif
|
|
|
|
namespace dart {
|
|
|
|
uint64_t Utils::ReverseBits64(uint64_t x) {
|
|
x = ((x >> 32) & 0x00000000ffffffff) | (x << 32);
|
|
x = ((x >> 16) & 0x0000ffff0000ffff) | ((x & 0x0000ffff0000ffff) << 16);
|
|
x = ((x >> 8) & 0x00ff00ff00ff00ff) | ((x & 0x00ff00ff00ff00ff) << 8);
|
|
x = ((x >> 4) & 0x0f0f0f0f0f0f0f0f) | ((x & 0x0f0f0f0f0f0f0f0f) << 4);
|
|
x = ((x >> 2) & 0x3333333333333333) | ((x & 0x3333333333333333) << 2);
|
|
x = ((x >> 1) & 0x5555555555555555) | ((x & 0x5555555555555555) << 1);
|
|
return x;
|
|
}
|
|
|
|
uint32_t Utils::ReverseBits32(uint32_t x) {
|
|
x = ((x >> 16) & 0x0000ffff) | ((x & 0x0000ffff) << 16);
|
|
x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8);
|
|
x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4);
|
|
x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2);
|
|
x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1);
|
|
return x;
|
|
}
|
|
|
|
// Implementation according to H.S.Warren's "Hacker's Delight"
|
|
// (Addison Wesley, 2002) Chapter 10 and T.Grablund, P.L.Montgomery's
|
|
// "Division by Invariant Integers Using Multiplication" (PLDI 1994).
|
|
void Utils::CalculateMagicAndShiftForDivRem(int64_t divisor,
|
|
int64_t* magic,
|
|
int64_t* shift) {
|
|
ASSERT(divisor <= -2 || divisor >= 2);
|
|
/* The magic number M and shift S can be calculated in the following way:
|
|
* Let nc be the most positive value of numerator(n) such that nc = kd - 1,
|
|
* where divisor(d) >= 2.
|
|
* Let nc be the most negative value of numerator(n) such that nc = kd + 1,
|
|
* where divisor(d) <= -2.
|
|
* Thus nc can be calculated like:
|
|
* nc = exp + exp % d - 1, where d >= 2 and exp = 2^63.
|
|
* nc = -exp + (exp + 1) % d, where d >= 2 and exp = 2^63.
|
|
*
|
|
* So the shift p is the smallest p satisfying
|
|
* 2^p > nc * (d - 2^p % d), where d >= 2
|
|
* 2^p > nc * (d + 2^p % d), where d <= -2.
|
|
*
|
|
* The magic number M is calculated by
|
|
* M = (2^p + d - 2^p % d) / d, where d >= 2
|
|
* M = (2^p - d - 2^p % d) / d, where d <= -2.
|
|
*/
|
|
int64_t p = 63;
|
|
const uint64_t exp = 1LL << 63;
|
|
|
|
// Initialize the computations.
|
|
uint64_t abs_d = (divisor >= 0) ? divisor : -static_cast<uint64_t>(divisor);
|
|
uint64_t sign_bit = static_cast<uint64_t>(divisor) >> 63;
|
|
uint64_t tmp = exp + sign_bit;
|
|
uint64_t abs_nc = tmp - 1 - (tmp % abs_d);
|
|
uint64_t quotient1 = exp / abs_nc;
|
|
uint64_t remainder1 = exp % abs_nc;
|
|
uint64_t quotient2 = exp / abs_d;
|
|
uint64_t remainder2 = exp % abs_d;
|
|
|
|
// To avoid handling both positive and negative divisor,
|
|
// "Hacker's Delight" introduces a method to handle these
|
|
// two cases together to avoid duplication.
|
|
uint64_t delta;
|
|
do {
|
|
p++;
|
|
quotient1 = 2 * quotient1;
|
|
remainder1 = 2 * remainder1;
|
|
if (remainder1 >= abs_nc) {
|
|
quotient1++;
|
|
remainder1 = remainder1 - abs_nc;
|
|
}
|
|
quotient2 = 2 * quotient2;
|
|
remainder2 = 2 * remainder2;
|
|
if (remainder2 >= abs_d) {
|
|
quotient2++;
|
|
remainder2 = remainder2 - abs_d;
|
|
}
|
|
delta = abs_d - remainder2;
|
|
} while (quotient1 < delta || (quotient1 == delta && remainder1 == 0));
|
|
|
|
*magic = (divisor > 0) ? (quotient2 + 1) : (-quotient2 - 1);
|
|
*shift = p - 64;
|
|
}
|
|
|
|
// This implementation is based on the public domain MurmurHash
|
|
// version 2.0. The constants M and R have been determined
|
|
// to work well experimentally.
|
|
static constexpr uint32_t kStringHashM = 0x5bd1e995;
|
|
static constexpr int kStringHashR = 24;
|
|
|
|
// hash and part must be lvalues.
|
|
#define MIX(hash, part) \
|
|
{ \
|
|
(part) *= kStringHashM; \
|
|
(part) ^= (part) >> kStringHashR; \
|
|
(part) *= kStringHashM; \
|
|
(hash) *= kStringHashM; \
|
|
(hash) ^= (part); \
|
|
}
|
|
|
|
uint32_t Utils::StringHash(const void* data, int length) {
|
|
int size = length;
|
|
uint32_t hash = size;
|
|
|
|
auto cursor = reinterpret_cast<const uint8_t*>(data);
|
|
|
|
if (size >= kInt32Size) {
|
|
const intptr_t misalignment =
|
|
reinterpret_cast<intptr_t>(cursor) % kInt32Size;
|
|
if (misalignment > 0) {
|
|
// Stores 4-byte values starting from the start of the string to mimic
|
|
// the algorithm on aligned data.
|
|
uint32_t data_window = 0;
|
|
|
|
// Shift sizes for adjusting the data window when adding the next aligned
|
|
// piece of data.
|
|
const uint32_t sr = misalignment * kBitsPerByte;
|
|
const uint32_t sl = kBitsPerInt32 - sr;
|
|
|
|
const intptr_t pre_alignment_length = kInt32Size - misalignment;
|
|
switch (pre_alignment_length) {
|
|
case 3:
|
|
data_window |= cursor[2] << 16;
|
|
FALL_THROUGH;
|
|
case 2:
|
|
data_window |= cursor[1] << 8;
|
|
FALL_THROUGH;
|
|
case 1:
|
|
data_window |= cursor[0];
|
|
}
|
|
cursor += pre_alignment_length;
|
|
size -= pre_alignment_length;
|
|
|
|
// Mix four bytes at a time now that we're at an aligned spot.
|
|
for (; size >= kInt32Size; cursor += kInt32Size, size -= kInt32Size) {
|
|
uint32_t aligned_part = *reinterpret_cast<const uint32_t*>(cursor);
|
|
data_window |= (aligned_part << sl);
|
|
MIX(hash, data_window);
|
|
data_window = aligned_part >> sr;
|
|
}
|
|
|
|
if (size >= misalignment) {
|
|
// There's one more full window in the data. We'll let the normal tail
|
|
// code handle any partial window.
|
|
switch (misalignment) {
|
|
case 3:
|
|
data_window |= cursor[2] << (16 + sl);
|
|
FALL_THROUGH;
|
|
case 2:
|
|
data_window |= cursor[1] << (8 + sl);
|
|
FALL_THROUGH;
|
|
case 1:
|
|
data_window |= cursor[0] << sl;
|
|
}
|
|
MIX(hash, data_window);
|
|
cursor += misalignment;
|
|
size -= misalignment;
|
|
} else {
|
|
// This is a partial window, so just xor and multiply by M.
|
|
switch (size) {
|
|
case 2:
|
|
data_window |= cursor[1] << (8 + sl);
|
|
FALL_THROUGH;
|
|
case 1:
|
|
data_window |= cursor[0] << sl;
|
|
}
|
|
hash ^= data_window;
|
|
hash *= kStringHashM;
|
|
cursor += size;
|
|
size = 0;
|
|
}
|
|
} else {
|
|
// Mix four bytes at a time into the hash.
|
|
for (; size >= kInt32Size; size -= kInt32Size, cursor += kInt32Size) {
|
|
uint32_t part = *reinterpret_cast<const uint32_t*>(cursor);
|
|
MIX(hash, part);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle the last few bytes of the string if any.
|
|
switch (size) {
|
|
case 3:
|
|
hash ^= cursor[2] << 16;
|
|
FALL_THROUGH;
|
|
case 2:
|
|
hash ^= cursor[1] << 8;
|
|
FALL_THROUGH;
|
|
case 1:
|
|
hash ^= cursor[0];
|
|
hash *= kStringHashM;
|
|
}
|
|
|
|
// Do a few final mixes of the hash to ensure the last few bytes are
|
|
// well-incorporated.
|
|
hash ^= hash >> 13;
|
|
hash *= kStringHashM;
|
|
hash ^= hash >> 15;
|
|
return hash;
|
|
}
|
|
|
|
#undef MIX
|
|
|
|
// TODO(63370): Proper 64-bit hash.
|
|
uint64_t Utils::StringHash64(const void* data, int length) {
|
|
uint64_t hi = StringHash(data, length / 2);
|
|
uint64_t lo = StringHash(reinterpret_cast<const uint8_t*>(data) + length / 2,
|
|
length / 2);
|
|
return hi << 32 | lo;
|
|
}
|
|
|
|
uint32_t Utils::WordHash(intptr_t key) {
|
|
// TODO(iposva): Need to check hash spreading.
|
|
// This example is from http://www.concentric.net/~Ttwang/tech/inthash.htm
|
|
// via. http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
|
|
uword a = static_cast<uword>(key);
|
|
a = (a + 0x7ed55d16) + (a << 12);
|
|
a = (a ^ 0xc761c23c) ^ (a >> 19);
|
|
a = (a + 0x165667b1) + (a << 5);
|
|
a = (a + 0xd3a2646c) ^ (a << 9);
|
|
a = (a + 0xfd7046c5) + (a << 3);
|
|
a = (a ^ 0xb55a4f09) ^ (a >> 16);
|
|
return static_cast<uint32_t>(a);
|
|
}
|
|
|
|
char* Utils::SCreate(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
char* buffer = VSCreate(format, args);
|
|
va_end(args);
|
|
return buffer;
|
|
}
|
|
|
|
char* Utils::VSCreate(const char* format, va_list args) {
|
|
// Measure.
|
|
va_list measure_args;
|
|
va_copy(measure_args, args);
|
|
intptr_t len = VSNPrint(nullptr, 0, format, measure_args);
|
|
va_end(measure_args);
|
|
|
|
char* buffer = reinterpret_cast<char*>(malloc(len + 1));
|
|
ASSERT(buffer != nullptr);
|
|
|
|
// Print.
|
|
va_list print_args;
|
|
va_copy(print_args, args);
|
|
VSNPrint(buffer, len + 1, format, print_args);
|
|
va_end(print_args);
|
|
return buffer;
|
|
}
|
|
|
|
static void GetLastErrorAsString(char** error) {
|
|
if (error == nullptr) return; // Nothing to do.
|
|
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
|
|
const char* status = dlerror();
|
|
*error = status != nullptr ? strdup(status) : nullptr;
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
const int status = GetLastError();
|
|
if (status != 0) {
|
|
char* description = nullptr;
|
|
int length = FormatMessageA(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
nullptr, status, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
|
reinterpret_cast<char*>(&description), 0, nullptr);
|
|
if (length == 0) {
|
|
// Seems like there is no message for this error code.
|
|
*error = Utils::SCreate("error code %i", status);
|
|
} else {
|
|
*error = Utils::SCreate("%s (error code: %i)", description, status);
|
|
}
|
|
|
|
LocalFree(description);
|
|
} else {
|
|
*error = nullptr;
|
|
}
|
|
#else
|
|
*error = Utils::StrDup("loading dynamic libraries is not supported");
|
|
#endif
|
|
}
|
|
|
|
void* Utils::LoadDynamicLibrary(const char* library_path,
|
|
bool search_dll_load_dir,
|
|
char** error) {
|
|
void* handle = nullptr;
|
|
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
|
|
handle = dlopen(library_path, RTLD_LAZY);
|
|
#if defined(DART_HOST_OS_FUCHSIA)
|
|
if (handle == nullptr) {
|
|
// Fuchsia's search path is different.
|
|
// https://fuchsia.dev/fuchsia-src/concepts/process/program_loading#zircons_standard_elf_dynamic_linker
|
|
constexpr fuchsia::io::Flags kFlags =
|
|
fuchsia::io::PERM_READABLE | fuchsia::io::PERM_EXECUTABLE;
|
|
int fd = -1;
|
|
zx_status_t status = fdio_open3_fd(library_path, uint64_t{kFlags}, &fd);
|
|
if (status != ZX_OK) {
|
|
*error = strdup(zx_status_get_string(status));
|
|
return nullptr;
|
|
}
|
|
zx_handle_t vmo = ZX_HANDLE_INVALID;
|
|
status = fdio_get_vmo_exec(fd, &vmo);
|
|
close(fd);
|
|
if (status != ZX_OK) {
|
|
*error = strdup(zx_status_get_string(status));
|
|
return nullptr;
|
|
}
|
|
handle = dlopen_vmo(vmo, RTLD_LAZY);
|
|
}
|
|
#endif // defined(DART_HOST_OS_FUCHSIA)
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
SetLastError(0); // Clear any errors.
|
|
|
|
if (library_path == nullptr) {
|
|
handle = GetModuleHandle(nullptr);
|
|
} else {
|
|
// Convert to wchar_t string.
|
|
const int name_len = MultiByteToWideChar(
|
|
CP_UTF8, /*dwFlags=*/0, library_path, /*cbMultiByte=*/-1, nullptr, 0);
|
|
if (name_len != 0) {
|
|
std::unique_ptr<wchar_t[]> name(new wchar_t[name_len]);
|
|
const int written_len =
|
|
MultiByteToWideChar(CP_UTF8, /*dwFlags=*/0, library_path,
|
|
/*cbMultiByte=*/-1, name.get(), name_len);
|
|
RELEASE_ASSERT(written_len == name_len);
|
|
if (search_dll_load_dir) {
|
|
handle =
|
|
LoadLibraryExW(name.get(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
} else {
|
|
handle = LoadLibraryW(name.get());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (handle == nullptr) {
|
|
GetLastErrorAsString(error);
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
void* Utils::ResolveSymbolInDynamicLibrary(void* library_handle,
|
|
const char* symbol,
|
|
char** error) {
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
|
|
dlerror(); // Clear any errors.
|
|
void* result = dlsym(library_handle, symbol);
|
|
// Note: nullptr might be a valid return from dlsym. Must call dlerror
|
|
// to differentiate.
|
|
GetLastErrorAsString(error);
|
|
return result;
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
SetLastError(0);
|
|
void* result = reinterpret_cast<void*>(
|
|
GetProcAddress(reinterpret_cast<HMODULE>(library_handle), symbol));
|
|
if (result == nullptr) {
|
|
GetLastErrorAsString(error);
|
|
}
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
void Utils::UnloadDynamicLibrary(void* library_handle, char** error) {
|
|
bool ok = false;
|
|
|
|
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
|
|
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
|
|
ok = dlclose(library_handle) == 0;
|
|
#elif defined(DART_HOST_OS_WINDOWS)
|
|
SetLastError(0); // Clear any errors.
|
|
|
|
ok = FreeLibrary(reinterpret_cast<HMODULE>(library_handle));
|
|
#endif
|
|
|
|
if (!ok) {
|
|
GetLastErrorAsString(error);
|
|
}
|
|
}
|
|
|
|
char* Utils::Basename(const char* path) {
|
|
#if defined(DART_HOST_OS_FUCHSIA) || defined(DART_HOST_OS_WINDOWS)
|
|
// Not handled for these operating systems.
|
|
return nullptr;
|
|
#else
|
|
if (path == nullptr) return nullptr;
|
|
char* const path_copy = Utils::StrDup(path);
|
|
char* result = basename(path_copy);
|
|
// The result may be in statically allocated memory, so copy.
|
|
result = Utils::StrDup(result);
|
|
// The result may point to a portion of the passed in string, so
|
|
// only free the copy after duplicating the result.
|
|
free(path_copy);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
} // namespace dart
|