diff options
Diffstat (limited to 'js/src/jit/ExecutableAllocator.h')
-rw-r--r-- | js/src/jit/ExecutableAllocator.h | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h new file mode 100644 index 000000000..30eccd12e --- /dev/null +++ b/js/src/jit/ExecutableAllocator.h @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef jit_ExecutableAllocator_h +#define jit_ExecutableAllocator_h + +#include "mozilla/Maybe.h" +#include "mozilla/XorShift128PlusRNG.h" + +#include <limits> +#include <stddef.h> // for ptrdiff_t + +#include "jsalloc.h" + +#ifdef JS_CODEGEN_ARM +#include "jit/arm/Architecture-arm.h" +#endif +#include "jit/arm/Simulator-arm.h" +#include "jit/mips32/Simulator-mips32.h" +#include "jit/mips64/Simulator-mips64.h" +#include "jit/ProcessExecutableMemory.h" +#include "js/GCAPI.h" +#include "js/HashTable.h" +#include "js/Vector.h" + +#ifdef JS_CPU_SPARC +#ifdef __linux__ // bugzilla 502369 +static void sync_instruction_memory(caddr_t v, u_int len) +{ + caddr_t end = v + len; + caddr_t p = v; + while (p < end) { + asm("flush %0" : : "r" (p)); + p += 32; + } +} +#else +extern "C" void sync_instruction_memory(caddr_t v, u_int len); +#endif +#endif + +#if defined(__linux__) && \ + (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) && \ + (!defined(JS_SIMULATOR_MIPS32) && !defined(JS_SIMULATOR_MIPS64)) +#include <sys/cachectl.h> +#endif + +#if defined(JS_CODEGEN_ARM) && defined(XP_IOS) +#include <libkern/OSCacheControl.h> +#endif + +namespace JS { + struct CodeSizes; +} // namespace JS + +namespace js { +namespace jit { + +enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE }; + +class ExecutableAllocator; +class JitRuntime; + +// These are reference-counted. A new one starts with a count of 1. +class ExecutablePool +{ + friend class ExecutableAllocator; + + private: + struct Allocation { + char* pages; + size_t size; + }; + + ExecutableAllocator* m_allocator; + char* m_freePtr; + char* m_end; + Allocation m_allocation; + + // Reference count for automatic reclamation. + unsigned m_refCount:31; + + // Flag that can be used by algorithms operating on pools. + bool m_mark:1; + + // Number of bytes currently used for Method and Regexp JIT code. + size_t m_ionCodeBytes; + size_t m_baselineCodeBytes; + size_t m_regexpCodeBytes; + size_t m_otherCodeBytes; + + public: + void release(bool willDestroy = false); + void release(size_t n, CodeKind kind); + + void addRef(); + + ExecutablePool(ExecutableAllocator* allocator, Allocation a) + : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a), + m_refCount(1), m_mark(false), m_ionCodeBytes(0), m_baselineCodeBytes(0), + m_regexpCodeBytes(0), m_otherCodeBytes(0) + { } + + ~ExecutablePool(); + + void mark() { + MOZ_ASSERT(!m_mark); + m_mark = true; + } + void unmark() { + MOZ_ASSERT(m_mark); + m_mark = false; + } + bool isMarked() const { + return m_mark; + } + + private: + ExecutablePool(const ExecutablePool&) = delete; + void operator=(const ExecutablePool&) = delete; + + void* alloc(size_t n, CodeKind kind); + + size_t available() const; +}; + +struct JitPoisonRange +{ + jit::ExecutablePool* pool; + void* start; + size_t size; + + JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size) + : pool(pool), start(start), size(size) + {} +}; + +typedef Vector<JitPoisonRange, 0, SystemAllocPolicy> JitPoisonRangeVector; + +class ExecutableAllocator +{ + JSRuntime* rt_; + + public: + explicit ExecutableAllocator(JSRuntime* rt); + ~ExecutableAllocator(); + + void purge(); + + // alloc() returns a pointer to some memory, and also (by reference) a + // pointer to reference-counted pool. The caller owns a reference to the + // pool; i.e. alloc() increments the count before returning the object. + void* alloc(size_t n, ExecutablePool** poolp, CodeKind type); + + void releasePoolPages(ExecutablePool* pool); + + void addSizeOfCode(JS::CodeSizes* sizes) const; + + private: + static const size_t OVERSIZE_ALLOCATION = size_t(-1); + + static size_t roundUpAllocationSize(size_t request, size_t granularity); + + // On OOM, this will return an Allocation where pages is nullptr. + ExecutablePool::Allocation systemAlloc(size_t n); + static void systemRelease(const ExecutablePool::Allocation& alloc); + + ExecutablePool* createPool(size_t n); + ExecutablePool* poolForSize(size_t n); + + static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection); + + public: + MOZ_MUST_USE + static bool makeWritable(void* start, size_t size) + { + return ReprotectRegion(start, size, ProtectionSetting::Writable); + } + + MOZ_MUST_USE + static bool makeExecutable(void* start, size_t size) + { + return ReprotectRegion(start, size, ProtectionSetting::Executable); + } + + void makeAllWritable() { + reprotectAll(ProtectionSetting::Writable); + } + void makeAllExecutable() { + reprotectAll(ProtectionSetting::Executable); + } + + static void poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges); + +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || defined(JS_SIMULATOR_ARM64) + static void cacheFlush(void*, size_t) + { + } +#elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64) + static void cacheFlush(void* code, size_t size) + { + js::jit::Simulator::FlushICache(code, size); + } +#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + static void cacheFlush(void* code, size_t size) + { +#if defined(_MIPS_ARCH_LOONGSON3A) + // On Loongson3-CPUs, The cache flushed automatically + // by hardware. Just need to execute an instruction hazard. + uintptr_t tmp; + asm volatile ( + ".set push \n" + ".set noreorder \n" + "move %[tmp], $ra \n" + "bal 1f \n" + "daddiu $ra, 8 \n" + "1: \n" + "jr.hb $ra \n" + "move $ra, %[tmp] \n" + ".set pop\n" + :[tmp]"=&r"(tmp) + ); +#elif defined(__GNUC__) + intptr_t end = reinterpret_cast<intptr_t>(code) + size; + __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end)); +#else + _flush_cache(reinterpret_cast<char*>(code), size, BCACHE); +#endif + } +#elif defined(JS_CODEGEN_ARM) && (defined(__FreeBSD__) || defined(__NetBSD__)) + static void cacheFlush(void* code, size_t size) + { + __clear_cache(code, reinterpret_cast<char*>(code) + size); + } +#elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) && defined(XP_IOS) + static void cacheFlush(void* code, size_t size) + { + sys_icache_invalidate(code, size); + } +#elif defined(JS_CODEGEN_ARM) && (defined(__linux__) || defined(ANDROID)) && defined(__GNUC__) + static void cacheFlush(void* code, size_t size) + { + void* end = (void*)(reinterpret_cast<char*>(code) + size); + asm volatile ( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "mov r7, #0xf0000\n" + "add r7, r7, #0x2\n" + "mov r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (code), "r" (end) + : "r0", "r1", "r2"); + if (ForceDoubleCacheFlush()) { + void* start = (void*)((uintptr_t)code + 1); + asm volatile ( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "mov r7, #0xf0000\n" + "add r7, r7, #0x2\n" + "mov r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (start), "r" (end) + : "r0", "r1", "r2"); + } + } +#elif defined(JS_CODEGEN_ARM64) + static void cacheFlush(void* code, size_t size) + { + __clear_cache(code, (void *)((size_t)code + size)); + } +#elif JS_CPU_SPARC + static void cacheFlush(void* code, size_t size) + { + sync_instruction_memory((caddr_t)code, size); + } +#endif + + private: + ExecutableAllocator(const ExecutableAllocator&) = delete; + void operator=(const ExecutableAllocator&) = delete; + + void reprotectAll(ProtectionSetting); + + // These are strong references; they keep pools alive. + static const size_t maxSmallPools = 4; + typedef js::Vector<ExecutablePool*, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector; + SmallExecPoolVector m_smallPools; + + // All live pools are recorded here, just for stats purposes. These are + // weak references; they don't keep pools alive. When a pool is destroyed + // its reference is removed from m_pools. + typedef js::HashSet<ExecutablePool*, js::DefaultHasher<ExecutablePool*>, js::SystemAllocPolicy> + ExecPoolHashSet; + ExecPoolHashSet m_pools; // All pools, just for stats purposes. +}; + +} // namespace jit +} // namespace js + +#endif /* jit_ExecutableAllocator_h */ |