summaryrefslogtreecommitdiffstats
path: root/js/src/jit/ExecutableAllocator.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/ExecutableAllocator.h')
-rw-r--r--js/src/jit/ExecutableAllocator.h330
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 */