зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1777604
- wasm: Move membarrier call to separate functions. r=nbp
Differential Revision: https://phabricator.services.mozilla.com/D152305
This commit is contained in:
Родитель
55aaf02a12
Коммит
cafcea1ef2
|
@ -59,8 +59,7 @@ class MOZ_RAII AutoWritableJitCodeFallible {
|
|||
}
|
||||
});
|
||||
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(
|
||||
FlushICacheSpec::LocalThreadOnly, addr_, size_)) {
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(addr_, size_)) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
rt_->toggleAutoWritableJitCodeActive(false);
|
||||
|
|
|
@ -172,19 +172,10 @@ class ExecutableAllocator {
|
|||
MustFlushICache::No);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool makeExecutableAndFlushICache(
|
||||
FlushICacheSpec flushSpec, void* start, size_t size) {
|
||||
MustFlushICache mustFlushICache;
|
||||
switch (flushSpec) {
|
||||
case FlushICacheSpec::LocalThreadOnly:
|
||||
mustFlushICache = MustFlushICache::LocalThreadOnly;
|
||||
break;
|
||||
case FlushICacheSpec::AllThreads:
|
||||
mustFlushICache = MustFlushICache::AllThreads;
|
||||
break;
|
||||
}
|
||||
[[nodiscard]] static bool makeExecutableAndFlushICache(void* start,
|
||||
size_t size) {
|
||||
return ReprotectRegion(start, size, ProtectionSetting::Executable,
|
||||
mustFlushICache);
|
||||
MustFlushICache::Yes);
|
||||
}
|
||||
|
||||
static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges);
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/FlushICache.h"
|
||||
|
||||
#ifdef JS_CODEGEN_ARM64
|
||||
# include "jit/arm64/vixl/MozCachingDecoder.h"
|
||||
# include "jit/arm64/vixl/Simulator-vixl.h"
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
|
||||
# ifdef __linux__
|
||||
# include <linux/version.h>
|
||||
# define LINUX_HAS_MEMBARRIER (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0))
|
||||
# else
|
||||
# define LINUX_HAS_MEMBARRIER 0
|
||||
# endif
|
||||
|
||||
# if LINUX_HAS_MEMBARRIER || defined(__android__)
|
||||
# include <string.h>
|
||||
|
||||
# if LINUX_HAS_MEMBARRIER
|
||||
# include <linux/membarrier.h>
|
||||
# include <sys/syscall.h>
|
||||
# include <sys/utsname.h>
|
||||
# include <unistd.h>
|
||||
# elif defined(__android__)
|
||||
# include <sys/syscall.h>
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# error "Missing platform-specific declarations for membarrier syscall!"
|
||||
# endif // __linux__ / ANDROID
|
||||
|
||||
static int membarrier(int cmd, int flags) {
|
||||
return syscall(__NR_membarrier, cmd, flags);
|
||||
}
|
||||
|
||||
// These definitions come from the Linux kernel source, for kernels before 4.16
|
||||
// which didn't have access to these membarrier commands.
|
||||
# ifndef MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE
|
||||
# define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE (1 << 5)
|
||||
# endif
|
||||
|
||||
# ifndef MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
|
||||
# define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE (1 << 6)
|
||||
# endif
|
||||
# endif // LINUX_HAS_MEMBARRIER || defined(__android__)
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
bool CanFlushExecutionContextForAllThreads() {
|
||||
# if (LINUX_HAS_MEMBARRIER || defined(__android__))
|
||||
// On linux, check the kernel supports membarrier(2), that is, it's a kernel
|
||||
// above Linux 4.16 included.
|
||||
//
|
||||
// Note: this code has been extracted (August 2020) from
|
||||
// https://android.googlesource.com/platform/art/+/58520dfba31d6eeef75f5babff15e09aa28e5db8/libartbase/base/membarrier.cc#50
|
||||
static constexpr int kRequiredMajor = 4;
|
||||
static constexpr int kRequiredMinor = 16;
|
||||
|
||||
static bool computed = false;
|
||||
static bool kernelHasMembarrier = false;
|
||||
|
||||
if (computed) {
|
||||
return kernelHasMembarrier;
|
||||
}
|
||||
|
||||
struct utsname uts;
|
||||
int major, minor;
|
||||
kernelHasMembarrier = uname(&uts) == 0 && strcmp(uts.sysname, "Linux") == 0 &&
|
||||
sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
|
||||
major >= kRequiredMajor &&
|
||||
(major != kRequiredMajor || minor >= kRequiredMinor);
|
||||
|
||||
// As a test bed, try to run the syscall with the command registering the
|
||||
// intent to use the actual membarrier we'll want to carry out later.
|
||||
//
|
||||
// IMPORTANT: This is required or else running the membarrier later won't
|
||||
// actually interrupt the threads in this process.
|
||||
if (kernelHasMembarrier &&
|
||||
membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) {
|
||||
kernelHasMembarrier = false;
|
||||
}
|
||||
|
||||
computed = true;
|
||||
return kernelHasMembarrier;
|
||||
# else
|
||||
// On other platforms, we assume that the syscall for flushing the icache
|
||||
// will flush the execution context for other cores.
|
||||
return true;
|
||||
# endif
|
||||
}
|
||||
|
||||
void FlushExecutionContextForAllThreads() {
|
||||
// Callers must check that this operation is available.
|
||||
MOZ_RELEASE_ASSERT(CanFlushExecutionContextForAllThreads());
|
||||
|
||||
# if defined(JS_SIMULATOR_ARM64) && defined(JS_CACHE_SIMULATOR_ARM64)
|
||||
// Emulate what the real hardware would do by emitting a membarrier that'll
|
||||
// interrupt and flush the execution context of all threads.
|
||||
using js::jit::SimulatorProcess;
|
||||
js::jit::AutoLockSimulatorCache alsc;
|
||||
SimulatorProcess::membarrier();
|
||||
# elif (LINUX_HAS_MEMBARRIER || defined(__android__))
|
||||
// The caller has checked this can be performed, which will have registered
|
||||
// this process to receive the membarrier. See above.
|
||||
//
|
||||
// membarrier will trigger an inter-processor-interrupt on any active threads
|
||||
// of this process. This is an execution context synchronization event
|
||||
// equivalent to running an `isb` instruction.
|
||||
if (membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) {
|
||||
// Better safe than sorry.
|
||||
MOZ_CRASH("membarrier can't be executed");
|
||||
}
|
||||
# else
|
||||
// On other platforms, we assume that the syscall for flushing the icache
|
||||
// will flush the execution context for other cores.
|
||||
# endif
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif
|
|
@ -18,8 +18,7 @@ namespace jit {
|
|||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
|
||||
inline void FlushICache(void* code, size_t size,
|
||||
bool codeIsThreadLocal = true) {
|
||||
inline void FlushICache(void* code, size_t size) {
|
||||
// No-op. Code and data caches are coherent on x86 and x64.
|
||||
}
|
||||
|
||||
|
@ -27,14 +26,15 @@ inline void FlushICache(void* code, size_t size,
|
|||
(defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) || \
|
||||
defined(JS_CODEGEN_LOONG64)
|
||||
|
||||
extern void FlushICache(void* code, size_t size, bool codeIsThreadLocal = true);
|
||||
// Invalidate the given code range from the icache. This will also flush the
|
||||
// execution context for this core. If this code is to be executed on another
|
||||
// thread, that thread must perform an execution context flush first using
|
||||
// `FlushExecutionContext` below.
|
||||
extern void FlushICache(void* code, size_t size);
|
||||
|
||||
#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32)
|
||||
|
||||
inline void FlushICache(void* code, size_t size,
|
||||
bool codeIsThreadLocal = true) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
inline void FlushICache(void* code, size_t size) { MOZ_CRASH(); }
|
||||
|
||||
#else
|
||||
# error "Unknown architecture!"
|
||||
|
@ -47,10 +47,16 @@ inline void FlushICache(void* code, size_t size,
|
|||
inline void FlushExecutionContext() {
|
||||
// No-op. Execution context is coherent with instruction cache.
|
||||
}
|
||||
inline bool CanFlushExecutionContextForAllThreads() { return true; }
|
||||
inline void FlushExecutionContextForAllThreads() {
|
||||
// No-op. Execution context is coherent with instruction cache.
|
||||
}
|
||||
|
||||
#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_WASM32)
|
||||
|
||||
inline void FlushExecutionContext() { MOZ_CRASH(); }
|
||||
inline bool CanFlushExecutionContextForAllThreads() { MOZ_CRASH(); }
|
||||
inline void FlushExecutionContextForAllThreads() { MOZ_CRASH(); }
|
||||
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
|
||||
|
@ -63,6 +69,19 @@ inline void FlushExecutionContext() { MOZ_CRASH(); }
|
|||
// this method.
|
||||
extern void FlushExecutionContext();
|
||||
|
||||
// Some platforms can flush the excecution context for other threads using a
|
||||
// syscall. This is required when JIT'ed code will be published to multiple
|
||||
// threads without a synchronization point where a `FlushExecutionContext`
|
||||
// could be inserted.
|
||||
extern bool CanFlushExecutionContextForAllThreads();
|
||||
|
||||
// Flushes the execution context of all threads in this process, equivalent to
|
||||
// running `FlushExecutionContext` on every thread.
|
||||
//
|
||||
// Callers must ensure `CanFlushExecutionContextForAllThreads` is true, or
|
||||
// else this will crash.
|
||||
extern void FlushExecutionContextForAllThreads();
|
||||
|
||||
#else
|
||||
# error "Unknown architecture!"
|
||||
#endif
|
||||
|
|
|
@ -775,11 +775,9 @@ bool js::jit::ReprotectRegion(void* start, size_t size,
|
|||
#endif
|
||||
|
||||
// Flush ICache when making code executable, before we modify |size|.
|
||||
if (flushICache == MustFlushICache::LocalThreadOnly ||
|
||||
flushICache == MustFlushICache::AllThreads) {
|
||||
if (flushICache == MustFlushICache::Yes) {
|
||||
MOZ_ASSERT(protection == ProtectionSetting::Executable);
|
||||
bool codeIsThreadLocal = flushICache == MustFlushICache::LocalThreadOnly;
|
||||
jit::FlushICache(start, size, codeIsThreadLocal);
|
||||
jit::FlushICache(start, size);
|
||||
}
|
||||
|
||||
// Calculate the start of the page containing this region,
|
||||
|
|
|
@ -68,15 +68,9 @@ enum class ProtectionSetting {
|
|||
Executable,
|
||||
};
|
||||
|
||||
/// Whether the instruction cache must be flushed:
|
||||
//- No means no flushing will happen.
|
||||
//- LocalThreadOnly means only the local thread's icache will be flushed.
|
||||
//- AllThreads means all the threads' icaches will be flushed; this must be used
|
||||
// when the compiling thread and the executing thread might be different.
|
||||
/// Whether the instruction cache must be flushed
|
||||
|
||||
enum class MustFlushICache { No, LocalThreadOnly, AllThreads };
|
||||
|
||||
enum class FlushICacheSpec { LocalThreadOnly, AllThreads };
|
||||
enum class MustFlushICache { No, Yes };
|
||||
|
||||
[[nodiscard]] extern bool ReprotectRegion(void* start, size_t size,
|
||||
ProtectionSetting protection,
|
||||
|
|
|
@ -480,7 +480,7 @@ uint32_t FloatRegisters::ActualTotalPhys() {
|
|||
return 16;
|
||||
}
|
||||
|
||||
void FlushICache(void* code, size_t size, bool codeIsThreadLocal) {
|
||||
void FlushICache(void* code, size_t size) {
|
||||
#if defined(JS_SIMULATOR_ARM)
|
||||
js::jit::SimulatorProcess::FlushICache(code, size);
|
||||
|
||||
|
|
|
@ -119,12 +119,8 @@ uint32_t GetARM64Flags() { return 0; }
|
|||
// computed".
|
||||
bool CPUFlagsHaveBeenComputed() { return true; }
|
||||
|
||||
void FlushICache(void* code, size_t size, bool codeIsThreadLocal) {
|
||||
vixl::CPU::EnsureIAndDCacheCoherency(code, size, codeIsThreadLocal);
|
||||
}
|
||||
|
||||
bool CanFlushICacheFromBackgroundThreads() {
|
||||
return vixl::CPU::CanFlushICacheFromBackgroundThreads();
|
||||
void FlushICache(void* code, size_t size) {
|
||||
vixl::CPU::EnsureIAndDCacheCoherency(code, size);
|
||||
}
|
||||
|
||||
void FlushExecutionContext() { vixl::CPU::FlushExecutionContext(); }
|
||||
|
|
|
@ -165,11 +165,7 @@ class CPU {
|
|||
// the I and D caches. I and D caches are not automatically coherent on ARM
|
||||
// so this operation is required before any dynamically generated code can
|
||||
// safely run.
|
||||
static void EnsureIAndDCacheCoherency(void *address, size_t length, bool codeIsThreadLocal);
|
||||
|
||||
// Returns true when the current machine supports flushing the instruction
|
||||
// cache on a background thread.
|
||||
static bool CanFlushICacheFromBackgroundThreads();
|
||||
static void EnsureIAndDCacheCoherency(void* address, size_t length);
|
||||
|
||||
// Flush the local instruction pipeline, forcing a reload of any instructions
|
||||
// beyond this barrier from the icache.
|
||||
|
|
|
@ -33,40 +33,8 @@
|
|||
# include <libkern/OSCacheControl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__aarch64__) && (defined(__linux__) || defined(__android__))
|
||||
# if defined(__linux__)
|
||||
# include <linux/membarrier.h>
|
||||
# include <sys/syscall.h>
|
||||
# include <sys/utsname.h>
|
||||
# include <unistd.h>
|
||||
# elif defined(__ANDROID__)
|
||||
# include <sys/syscall.h>
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# error "Missing platform-specific declarations for membarrier syscall!"
|
||||
# endif // __linux__ / ANDROID
|
||||
|
||||
# include "vm/JSContext.h" // TlsContext
|
||||
|
||||
static int membarrier(int cmd, int flags) {
|
||||
return syscall(__NR_membarrier, cmd, flags);
|
||||
}
|
||||
|
||||
// These definitions come from the Linux kernel source, for kernels before 4.16
|
||||
// which didn't have access to these membarrier commands.
|
||||
# ifndef MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE
|
||||
# define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE (1 << 5)
|
||||
# endif
|
||||
|
||||
# ifndef MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
|
||||
# define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE (1 << 6)
|
||||
# endif
|
||||
|
||||
#endif // __aarch64__
|
||||
|
||||
namespace vixl {
|
||||
|
||||
|
||||
// Currently computes I and D cache line size.
|
||||
void CPU::SetUp() {
|
||||
uint32_t cache_type_register = GetCacheType();
|
||||
|
@ -115,45 +83,7 @@ uint32_t CPU::GetCacheType() {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool CPU::CanFlushICacheFromBackgroundThreads() {
|
||||
#if defined(__aarch64__) && (defined(__linux__) || defined(__android__))
|
||||
// On linux, check the kernel supports membarrier(2), that is, it's a kernel
|
||||
// above Linux 4.16 included.
|
||||
//
|
||||
// Note: this code has been extracted (August 2020) from
|
||||
// https://android.googlesource.com/platform/art/+/58520dfba31d6eeef75f5babff15e09aa28e5db8/libartbase/base/membarrier.cc#50
|
||||
static constexpr int kRequiredMajor = 4;
|
||||
static constexpr int kRequiredMinor = 16;
|
||||
|
||||
static bool computed = false;
|
||||
static bool kernelHasMembarrier = false;
|
||||
|
||||
if (!computed) {
|
||||
struct utsname uts;
|
||||
int major, minor;
|
||||
kernelHasMembarrier = uname(&uts) == 0 &&
|
||||
strcmp(uts.sysname, "Linux") == 0 &&
|
||||
sscanf(uts.release, "%d.%d", &major, &minor) == 2 &&
|
||||
major >= kRequiredMajor && (major != kRequiredMajor || minor >= kRequiredMinor);
|
||||
|
||||
// As a test bed, try to run the syscall with the command registering the
|
||||
// intent to use the actual membarrier we'll want to carry out later.
|
||||
if (kernelHasMembarrier &&
|
||||
membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) {
|
||||
kernelHasMembarrier = false;
|
||||
}
|
||||
|
||||
computed = true;
|
||||
}
|
||||
|
||||
return kernelHasMembarrier;
|
||||
#else
|
||||
// On other platforms, we assume that the provided syscall does the right thing.
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CPU::EnsureIAndDCacheCoherency(void *address, size_t length, bool codeIsThreadLocal) {
|
||||
void CPU::EnsureIAndDCacheCoherency(void* address, size_t length) {
|
||||
#if defined(JS_SIMULATOR_ARM64) && defined(JS_CACHE_SIMULATOR_ARM64)
|
||||
// This code attempts to emulate what the following assembly sequence is
|
||||
// doing, which is sending the information to all cores that some cache line
|
||||
|
@ -175,11 +105,6 @@ void CPU::EnsureIAndDCacheCoherency(void *address, size_t length, bool codeIsThr
|
|||
Simulator* sim = vixl::Simulator::Current();
|
||||
if (sim) {
|
||||
sim->FlushICache();
|
||||
} else if (!codeIsThreadLocal) {
|
||||
// We're on a background thread; emulate what the real hardware would do by
|
||||
// emitting a membarrier that'll interrupt and cause an icache invalidation
|
||||
// on all the threads.
|
||||
SimulatorProcess::membarrier();
|
||||
}
|
||||
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
||||
FlushInstructionCache(GetCurrentProcess(), address, length);
|
||||
|
@ -262,31 +187,18 @@ void CPU::EnsureIAndDCacheCoherency(void *address, size_t length, bool codeIsThr
|
|||
iline += isize;
|
||||
} while (iline < end);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
// Make sure that the instruction cache operations (above) take effect
|
||||
// before the isb (below).
|
||||
" dsb ish\n"
|
||||
__asm__ __volatile__(
|
||||
// Make sure that the instruction cache operations (above) take effect
|
||||
// before the isb (below).
|
||||
" dsb ish\n"
|
||||
|
||||
// Ensure that any instructions already in the pipeline are discarded and
|
||||
// reloaded from the new data.
|
||||
// isb : Instruction Synchronisation Barrier
|
||||
" isb\n"
|
||||
: : : "memory");
|
||||
|
||||
if (!codeIsThreadLocal) {
|
||||
// If we're on a background thread, emit a membarrier that will synchronize
|
||||
// all the executing threads with the new version of the code.
|
||||
JSContext* cx = js::TlsContext.get();
|
||||
if (!cx || !cx->isMainThreadContext()) {
|
||||
MOZ_RELEASE_ASSERT(CPU::CanFlushICacheFromBackgroundThreads());
|
||||
// The intent to use this command has been carried over in
|
||||
// CanFlushICacheFromBackgroundThreads.
|
||||
if (membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) {
|
||||
// Better safe than sorry.
|
||||
MOZ_CRASH("membarrier can't be executed");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure that any instructions already in the pipeline are discarded and
|
||||
// reloaded from the new data.
|
||||
// isb : Instruction Synchronisation Barrier
|
||||
" isb\n"
|
||||
:
|
||||
:
|
||||
: "memory");
|
||||
#else
|
||||
// If the host isn't AArch64, we must be using the simulator, so this function
|
||||
// doesn't have to do anything.
|
||||
|
|
|
@ -68,7 +68,7 @@ bool CPUFlagsHaveBeenComputed() {
|
|||
|
||||
uint32_t GetLOONG64Flags() { return 0; }
|
||||
|
||||
void FlushICache(void* code, size_t size, bool codeIsThreadLocal) {
|
||||
void FlushICache(void* code, size_t size) {
|
||||
#if defined(JS_SIMULATOR)
|
||||
js::jit::SimulatorProcess::FlushICache(code, size);
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ Registers::Code Registers::FromName(const char* name) {
|
|||
return Invalid;
|
||||
}
|
||||
|
||||
void FlushICache(void* code, size_t size, bool codeIsThreadLocal) {
|
||||
void FlushICache(void* code, size_t size) {
|
||||
#if defined(JS_SIMULATOR)
|
||||
js::jit::SimulatorProcess::FlushICache(code, size);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ UNIFIED_SOURCES += [
|
|||
"EdgeCaseAnalysis.cpp",
|
||||
"EffectiveAddressAnalysis.cpp",
|
||||
"ExecutableAllocator.cpp",
|
||||
"FlushICache.cpp",
|
||||
"FoldLinearArithConstants.cpp",
|
||||
"InlinableNatives.cpp",
|
||||
"InstructionReordering.cpp",
|
||||
|
|
|
@ -68,8 +68,8 @@ bool ExecuteJit(JSContext* cx, js::jit::MacroAssembler& masm) {
|
|||
if (!code) {
|
||||
return false;
|
||||
}
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(
|
||||
FlushICacheSpec::LocalThreadOnly, code->raw(), code->bufferSize())) {
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(code->raw(),
|
||||
code->bufferSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1755,9 +1755,8 @@ bool wasm::EnsureBuiltinThunksInitialized() {
|
|||
MOZ_ASSERT(masm.trapSites().empty());
|
||||
MOZ_ASSERT(masm.tryNotes().empty());
|
||||
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(
|
||||
FlushICacheSpec::LocalThreadOnly, thunks->codeBase,
|
||||
thunks->codeSize)) {
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(thunks->codeBase,
|
||||
thunks->codeSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -331,7 +331,7 @@ UniqueModuleSegment ModuleSegment::create(Tier tier, const Bytes& unlinkedBytes,
|
|||
linkData);
|
||||
}
|
||||
|
||||
bool ModuleSegment::initialize(IsTier2 isTier2, const CodeTier& codeTier,
|
||||
bool ModuleSegment::initialize(const CodeTier& codeTier,
|
||||
const LinkData& linkData,
|
||||
const Metadata& metadata,
|
||||
const MetadataTier& metadataTier) {
|
||||
|
@ -341,13 +341,9 @@ bool ModuleSegment::initialize(IsTier2 isTier2, const CodeTier& codeTier,
|
|||
|
||||
// Optimized compilation finishes on a background thread, so we must make sure
|
||||
// to flush the icaches of all the executing threads.
|
||||
FlushICacheSpec flushIcacheSpec = isTier2 == IsTier2::Tier2
|
||||
? FlushICacheSpec::AllThreads
|
||||
: FlushICacheSpec::LocalThreadOnly;
|
||||
|
||||
// Reprotect the whole region to avoid having separate RW and RX mappings.
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(
|
||||
flushIcacheSpec, base(), RoundupCodeLength(length()))) {
|
||||
base(), RoundupCodeLength(length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -499,7 +495,6 @@ static constexpr unsigned LAZY_STUB_LIFO_DEFAULT_CHUNK_SIZE = 8 * 1024;
|
|||
|
||||
bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
|
||||
const CodeTier& codeTier,
|
||||
bool flushAllThreadsIcaches,
|
||||
size_t* stubSegmentIndex) {
|
||||
MOZ_ASSERT(funcExportIndices.length());
|
||||
|
||||
|
@ -579,13 +574,7 @@ bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
|
|||
Assembler::Bind(codePtr, label);
|
||||
}
|
||||
|
||||
// Optimized compilation finishes on a background thread, so we must make sure
|
||||
// to flush the icaches of all the executing threads.
|
||||
FlushICacheSpec flushIcacheSpec = flushAllThreadsIcaches
|
||||
? FlushICacheSpec::AllThreads
|
||||
: FlushICacheSpec::LocalThreadOnly;
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(flushIcacheSpec,
|
||||
codePtr, codeLength)) {
|
||||
if (!ExecutableAllocator::makeExecutableAndFlushICache(codePtr, codeLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -629,14 +618,8 @@ bool LazyStubTier::createOneEntryStub(uint32_t funcExportIndex,
|
|||
return false;
|
||||
}
|
||||
|
||||
// This happens on the executing thread (when createOneEntryStub is called
|
||||
// from GetInterpEntryAndEnsureStubs), so no need to flush the icaches on all
|
||||
// the threads.
|
||||
bool flushAllThreadIcaches = false;
|
||||
|
||||
size_t stubSegmentIndex;
|
||||
if (!createManyEntryStubs(funcExportIndexes, codeTier, flushAllThreadIcaches,
|
||||
&stubSegmentIndex)) {
|
||||
if (!createManyEntryStubs(funcExportIndexes, codeTier, &stubSegmentIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -667,13 +650,8 @@ bool LazyStubTier::createTier2(const Uint32Vector& funcExportIndices,
|
|||
return true;
|
||||
}
|
||||
|
||||
// This compilation happens on a background compiler thread, so the icache may
|
||||
// need to be flushed on all the threads.
|
||||
bool flushAllThreadIcaches = true;
|
||||
|
||||
size_t stubSegmentIndex;
|
||||
if (!createManyEntryStubs(funcExportIndices, codeTier, flushAllThreadIcaches,
|
||||
&stubSegmentIndex)) {
|
||||
if (!createManyEntryStubs(funcExportIndices, codeTier, &stubSegmentIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -851,15 +829,15 @@ bool Metadata::getFuncName(NameContext ctx, uint32_t funcIndex,
|
|||
return AppendFunctionIndexName(funcIndex, name);
|
||||
}
|
||||
|
||||
bool CodeTier::initialize(IsTier2 isTier2, const Code& code,
|
||||
const LinkData& linkData, const Metadata& metadata) {
|
||||
bool CodeTier::initialize(const Code& code, const LinkData& linkData,
|
||||
const Metadata& metadata) {
|
||||
MOZ_ASSERT(!initialized());
|
||||
code_ = &code;
|
||||
|
||||
MOZ_ASSERT(lazyStubs_.readLock()->entryStubsEmpty());
|
||||
|
||||
// See comments in CodeSegment::initialize() for why this must be last.
|
||||
if (!segment_->initialize(isTier2, *this, linkData, metadata, *metadata_)) {
|
||||
if (!segment_->initialize(*this, linkData, metadata, *metadata_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -948,7 +926,7 @@ Code::Code(UniqueCodeTier tier1, const Metadata& metadata,
|
|||
bool Code::initialize(const LinkData& linkData) {
|
||||
MOZ_ASSERT(!initialized());
|
||||
|
||||
if (!tier1_->initialize(IsTier2::NotTier2, *this, linkData, *metadata_)) {
|
||||
if (!tier1_->initialize(*this, linkData, *metadata_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -962,7 +940,7 @@ bool Code::setAndBorrowTier2(UniqueCodeTier tier2, const LinkData& linkData,
|
|||
MOZ_RELEASE_ASSERT(tier2->tier() == Tier::Optimized &&
|
||||
tier1_->tier() == Tier::Baseline);
|
||||
|
||||
if (!tier2->initialize(IsTier2::Tier2, *this, linkData, *metadata_)) {
|
||||
if (!tier2->initialize(*this, linkData, *metadata_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -209,8 +209,6 @@ class CodeSegment {
|
|||
|
||||
using UniqueModuleSegment = UniquePtr<ModuleSegment>;
|
||||
|
||||
enum IsTier2 { Tier2, NotTier2 };
|
||||
|
||||
class ModuleSegment : public CodeSegment {
|
||||
const Tier tier_;
|
||||
uint8_t* const trapCode_;
|
||||
|
@ -224,9 +222,8 @@ class ModuleSegment : public CodeSegment {
|
|||
static UniqueModuleSegment create(Tier tier, const Bytes& unlinkedBytes,
|
||||
const LinkData& linkData);
|
||||
|
||||
bool initialize(IsTier2 isTier2, const CodeTier& codeTier,
|
||||
const LinkData& linkData, const Metadata& metadata,
|
||||
const MetadataTier& metadataTier);
|
||||
bool initialize(const CodeTier& codeTier, const LinkData& linkData,
|
||||
const Metadata& metadata, const MetadataTier& metadataTier);
|
||||
|
||||
Tier tier() const { return tier_; }
|
||||
|
||||
|
@ -591,7 +588,6 @@ class LazyStubTier {
|
|||
|
||||
[[nodiscard]] bool createManyEntryStubs(const Uint32Vector& funcExportIndices,
|
||||
const CodeTier& codeTier,
|
||||
bool flushAllThreadsIcaches,
|
||||
size_t* stubSegmentIndex);
|
||||
|
||||
public:
|
||||
|
@ -654,7 +650,7 @@ class CodeTier {
|
|||
lazyStubs_(mutexForTier(segment_->tier())) {}
|
||||
|
||||
bool initialized() const { return !!code_ && segment_->initialized(); }
|
||||
bool initialize(IsTier2 isTier2, const Code& code, const LinkData& linkData,
|
||||
bool initialize(const Code& code, const LinkData& linkData,
|
||||
const Metadata& metadata);
|
||||
|
||||
Tier tier() const { return segment_->tier(); }
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
# include "jit/ProcessExecutableMemory.h"
|
||||
#endif
|
||||
|
||||
#include "jit/FlushICache.h"
|
||||
#include "util/Text.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/Realm.h"
|
||||
|
@ -558,6 +559,11 @@ static bool TieringBeneficial(uint32_t codeSize) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Ensure that we have the non-compiler requirements to tier safely.
|
||||
static bool PlatformCanTier() {
|
||||
return CanUseExtraThreads() && jit::CanFlushExecutionContextForAllThreads();
|
||||
}
|
||||
|
||||
CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
|
||||
: state_(InitialWithArgs), args_(&args) {}
|
||||
|
||||
|
@ -576,20 +582,6 @@ void CompilerEnvironment::computeParameters() {
|
|||
state_ = Computed;
|
||||
}
|
||||
|
||||
// Check that this architecture either:
|
||||
// - is cache-coherent, which is the case for most tier-1 architectures we care
|
||||
// about.
|
||||
// - or has the ability to invalidate the instruction cache of all threads, so
|
||||
// background compilation in tiered compilation can be synchronized across all
|
||||
// threads.
|
||||
static bool IsICacheSafe() {
|
||||
#ifdef JS_CODEGEN_ARM64
|
||||
return jit::CanFlushICacheFromBackgroundThreads();
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CompilerEnvironment::computeParameters(Decoder& d) {
|
||||
MOZ_ASSERT(!isComputed());
|
||||
|
||||
|
@ -617,8 +609,9 @@ void CompilerEnvironment::computeParameters(Decoder& d) {
|
|||
codeSectionSize = range.size;
|
||||
}
|
||||
|
||||
if (baselineEnabled && hasSecondTier && CanUseExtraThreads() &&
|
||||
(TieringBeneficial(codeSectionSize) || forceTiering) && IsICacheSafe()) {
|
||||
if (baselineEnabled && hasSecondTier &&
|
||||
(TieringBeneficial(codeSectionSize) || forceTiering) &&
|
||||
PlatformCanTier()) {
|
||||
mode_ = CompileMode::Tier1;
|
||||
tier_ = Tier::Baseline;
|
||||
} else {
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
#include "js/BuildId.h" // JS::BuildIdCharVector
|
||||
#include "jit/FlushICache.h" // for FlushExecutionContextForAllThreads
|
||||
#include "js/BuildId.h" // JS::BuildIdCharVector
|
||||
#include "js/experimental/TypedData.h" // JS_NewUint8Array
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/Printf.h" // JS_smprintf
|
||||
|
@ -212,6 +213,14 @@ bool Module::finishTier2(const LinkData& linkData2,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Initializing the code above will have flushed the icache for all cores.
|
||||
// However, there could still be stale data in the execution pipeline of
|
||||
// other cores on some platforms. Force an execution context flush on all
|
||||
// threads to fix this before we commit the code.
|
||||
//
|
||||
// This is safe due to the check in `PlatformCanTier` in WasmCompile.cpp
|
||||
jit::FlushExecutionContextForAllThreads();
|
||||
|
||||
// Now that we can't fail or otherwise abort tier2, make it live.
|
||||
|
||||
MOZ_ASSERT(!code().hasTier2());
|
||||
|
|
Загрузка…
Ссылка в новой задаче