LCOV - app.info - js/src/assembler/jit/ExecutableAllocator.h

LCOV - code coverage report
| Current view: | directory - js/src/assembler/jit - ExecutableAllocator.h (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 114 | 104 | 91.2 %
| Date: | 2012-04-07 | Functions: | 17 | 17 | 100.0 %

           1                 : /*
           2                 :  * Copyright (C) 2008 Apple Inc. All rights reserved.
           3                 :  *
           4                 :  * Redistribution and use in source and binary forms, with or without
           5                 :  * modification, are permitted provided that the following conditions
           6                 :  * are met:
           7                 :  * 1. Redistributions of source code must retain the above copyright
           8                 :  *    notice, this list of conditions and the following disclaimer.
           9                 :  * 2. Redistributions in binary form must reproduce the above copyright
          10                 :  *    notice, this list of conditions and the following disclaimer in the
          11                 :  *    documentation and/or other materials provided with the distribution.
          12                 :  *
          13                 :  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
          14                 :  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
          15                 :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          16                 :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
          17                 :  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
          18                 :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
          19                 :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
          20                 :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
          21                 :  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
          22                 :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
          23                 :  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
          24                 :  */
          25                 :
          26                 : #ifndef ExecutableAllocator_h
          27                 : #define ExecutableAllocator_h
          28                 :
          29                 : #include <stddef.h> // for ptrdiff_t
          30                 : #include <limits>
          31                 :
          32                 : #include "jsalloc.h"
          33                 : #include "jsapi.h"
          34                 : #include "jsprvtd.h"
          35                 :
          36                 : #include "assembler/wtf/Assertions.h"
          37                 : #include "js/HashTable.h"
          38                 : #include "js/Vector.h"
          39                 :
          40                 : #if WTF_CPU_SPARC
          41                 : #ifdef linux  // bugzilla 502369
          42                 : static void sync_instruction_memory(caddr_t v, u_int len)
          43                 : {
          44                 :     caddr_t end = v + len;
          45                 :     caddr_t p = v;
          46                 :     while (p < end) {
          47                 :         asm("flush %0" : : "r" (p));
          48                 :         p += 32;
          49                 :     }
          50                 : }
          51                 : #else
          52                 : extern  "C" void sync_instruction_memory(caddr_t v, u_int len);
          53                 : #endif
          54                 : #endif
          55                 :
          56                 : #if WTF_OS_IOS
          57                 : #include <libkern/OSCacheControl.h>
          58                 : #include <sys/mman.h>
          59                 : #endif
          60                 :
          61                 : #if WTF_OS_SYMBIAN
          62                 : #include <e32std.h>
          63                 : #endif
          64                 :
          65                 : #if WTF_CPU_MIPS && WTF_OS_LINUX
          66                 : #include <sys/cachectl.h>
          67                 : #endif
          68                 :
          69                 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
          70                 : #define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
          71                 : #define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
          72                 : #define INITIAL_PROTECTION_FLAGS PROTECTION_FLAGS_RX
          73                 : #else
          74                 : #define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
          75                 : #endif
          76                 :
          77                 : #if ENABLE_ASSEMBLER
          78                 :
          79                 : //#define DEBUG_STRESS_JSC_ALLOCATOR
          80                 :
          81                 : namespace JSC {
          82                 :
          83                 :   class ExecutableAllocator;
          84                 :
          85                 :   enum CodeKind { METHOD_CODE, REGEXP_CODE };
          86                 :
          87                 :   // These are reference-counted. A new one starts with a count of 1.
          88                 :   class ExecutablePool {
          89                 :
          90                 :     JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
          91                 :     friend class ExecutableAllocator;
          92                 : private:
          93                 :     struct Allocation {
          94                 :         char* pages;
          95                 :         size_t size;
          96                 : #if WTF_OS_SYMBIAN
          97                 :         RChunk* chunk;
          98                 : #endif
          99                 :     };
         100                 :
         101                 :     ExecutableAllocator* m_allocator;
         102                 :     char* m_freePtr;
         103                 :     char* m_end;
         104                 :     Allocation m_allocation;
         105                 :
         106                 :     // Reference count for automatic reclamation.
         107                 :     unsigned m_refCount;
         108                 :
         109                 :     // Number of bytes currently used for Method and Regexp JIT code.
         110                 :     size_t m_mjitCodeMethod;
         111                 :     size_t m_mjitCodeRegexp;
         112                 :
         113                 : public:
         114                 :     // Flag for downstream use, whether to try to release references to this pool.
         115                 :     bool m_destroy;
         116                 :
         117                 :     // GC number in which the m_destroy flag was most recently set. Used downstream to
         118                 :     // remember whether m_destroy was computed for the currently active GC.
         119                 :     size_t m_gcNumber;
         120                 :
         121          211562 :     void release(bool willDestroy = false)
         122                 :     {
         123          211562 :         JS_ASSERT(m_refCount != 0);
         124                 :         // XXX: disabled, see bug 654820.
         125                 :         //JS_ASSERT_IF(willDestroy, m_refCount == 1);
         126          211562 :         if (--m_refCount == 0) {
         127           13855 :             js::UnwantedForeground::delete_(this);
         128                 :         }
         129          211562 :     }
         130                 :
         131                 : private:
         132                 :     // It should be impossible for us to roll over, because only small
         133                 :     // pools have multiple holders, and they have one holder per chunk
         134                 :     // of generated code, and they only hold 16KB or so of code.
         135          197707 :     void addRef()
         136                 :     {
         137          197707 :         JS_ASSERT(m_refCount);
         138          197707 :         ++m_refCount;
         139          197707 :     }
         140                 :
         141           13855 :     ExecutablePool(ExecutableAllocator* allocator, Allocation a)
         142           13855 :       : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
         143           27710 :         m_refCount(1), m_mjitCodeMethod(0), m_mjitCodeRegexp(0), m_destroy(false), m_gcNumber(0)
         144           13855 :     { }
         145                 :
         146                 :     ~ExecutablePool();
         147                 :
         148          197817 :     void* alloc(size_t n, CodeKind kind)
         149                 :     {
         150          197817 :         JS_ASSERT(n <= available());
         151          197817 :         void *result = m_freePtr;
         152          197817 :         m_freePtr += n;
         153                 :
         154          197817 :         if ( kind == REGEXP_CODE )
         155           23884 :             m_mjitCodeRegexp += n;
         156                 :         else
         157          173933 :             m_mjitCodeMethod += n;
         158                 :
         159          197817 :         return result;
         160                 :     }
         161                 :
         162          696927 :     size_t available() const {
         163          696927 :         JS_ASSERT(m_end >= m_freePtr);
         164          696927 :         return m_end - m_freePtr;
         165                 :     }
         166                 : };
         167                 :
         168                 : enum AllocationBehavior
         169                 : {
         170                 :     AllocationCanRandomize,
         171                 :     AllocationDeterministic
         172                 : };
         173                 :
         174                 : class ExecutableAllocator {
         175                 :     typedef void (*DestroyCallback)(void* addr, size_t size);
         176                 :     enum ProtectionSetting { Writable, Executable };
         177                 :     DestroyCallback destroyCallback;
         178                 :
         179                 :     void initSeed();
         180                 :
         181                 : public:
         182           12005 :     explicit ExecutableAllocator(AllocationBehavior allocBehavior)
         183                 :       : destroyCallback(NULL),
         184           12005 :         allocBehavior(allocBehavior)
         185                 :     {
         186           12005 :         if (!pageSize) {
         187           10051 :             pageSize = determinePageSize();
         188                 :             /*
         189                 :              * On Windows, VirtualAlloc effectively allocates in 64K chunks.
         190                 :              * (Technically, it allocates in page chunks, but the starting
         191                 :              * address is always a multiple of 64K, so each allocation uses up
         192                 :              * 64K of address space.)  So a size less than that would be
         193                 :              * pointless.  But it turns out that 64KB is a reasonable size for
         194                 :              * all platforms.  (This assumes 4KB pages.)
         195                 :              */
         196           10051 :             largeAllocSize = pageSize * 16;
         197                 :         }
         198                 :
         199                 : #if WTF_OS_WINDOWS
         200                 :         initSeed();
         201                 : #endif
         202                 :
         203           12005 :         JS_ASSERT(m_smallPools.empty());
         204           12005 :     }
         205                 :
         206           12005 :     ~ExecutableAllocator()
         207           12005 :     {
         208           24565 :         for (size_t i = 0; i < m_smallPools.length(); i++)
         209           12560 :             m_smallPools[i]->release(/* willDestroy = */true);
         210                 :         // XXX: temporarily disabled because it fails;  see bug 654820.
         211                 :         //JS_ASSERT(m_pools.empty());     // if this asserts we have a pool leak
         212           12005 :     }
         213                 :
         214                 :     // alloc() returns a pointer to some memory, and also (by reference) a
         215                 :     // pointer to reference-counted pool. The caller owns a reference to the
         216                 :     // pool; i.e. alloc() increments the count before returning the object.
         217          197817 :     void* alloc(size_t n, ExecutablePool** poolp, CodeKind type)
         218                 :     {
         219                 :         // Round 'n' up to a multiple of word size; if all allocations are of
         220                 :         // word sized quantities, then all subsequent allocations will be
         221                 :         // aligned.
         222          197817 :         n = roundUpAllocationSize(n, sizeof(void*));
         223          197817 :         if (n == OVERSIZE_ALLOCATION) {
         224               0 :             *poolp = NULL;
         225               0 :             return NULL;
         226                 :         }
         227                 :
         228          197817 :         *poolp = poolForSize(n);
         229          197817 :         if (!*poolp)
         230               0 :             return NULL;
         231                 :
         232                 :         // This alloc is infallible because poolForSize() just obtained
         233                 :         // (found, or created if necessary) a pool that had enough space.
         234          197817 :         void *result = (*poolp)->alloc(n, type);
         235          197817 :         JS_ASSERT(result);
         236          197817 :         return result;
         237                 :     }
         238                 :
         239           13855 :     void releasePoolPages(ExecutablePool *pool) {
         240           13855 :         JS_ASSERT(pool->m_allocation.pages);
         241           13855 :         if (destroyCallback)
         242            5646 :             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
         243           13855 :         systemRelease(pool->m_allocation);
         244           13855 :         m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
         245           13855 :     }
         246                 :
         247                 :     void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
         248                 :
         249           69291 :     void setDestroyCallback(DestroyCallback destroyCallback) {
         250           69291 :         this->destroyCallback = destroyCallback;
         251           69291 :     }
         252                 :
         253          181343 :     void setRandomize(bool enabled) {
         254          181343 :         allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic;
         255          181343 :     }
         256                 :
         257                 : private:
         258                 :     static size_t pageSize;
         259                 :     static size_t largeAllocSize;
         260                 : #if WTF_OS_WINDOWS
         261                 :     static int64_t rngSeed;
         262                 : #endif
         263                 :
         264                 :     static const size_t OVERSIZE_ALLOCATION = size_t(-1);
         265                 :
         266          211672 :     static size_t roundUpAllocationSize(size_t request, size_t granularity)
         267                 :     {
         268                 :         // Something included via windows.h defines a macro with this name,
         269                 :         // which causes the function below to fail to compile.
         270                 :         #ifdef _MSC_VER
         271                 :         # undef max
         272                 :         #endif
         273                 :
         274          211672 :         if ((std::numeric_limits<size_t>::max() - granularity) <= request)
         275               0 :             return OVERSIZE_ALLOCATION;
         276                 :
         277                 :         // Round up to next page boundary
         278          211672 :         size_t size = request + (granularity - 1);
         279          211672 :         size = size & ~(granularity - 1);
         280          211672 :         JS_ASSERT(size >= request);
         281          211672 :         return size;
         282                 :     }
         283                 :
         284                 :     // On OOM, this will return an Allocation where pages is NULL.
         285                 :     ExecutablePool::Allocation systemAlloc(size_t n);
         286                 :     static void systemRelease(const ExecutablePool::Allocation& alloc);
         287                 :     void *computeRandomAllocationAddress();
         288                 :
         289           13855 :     ExecutablePool* createPool(size_t n)
         290                 :     {
         291           13855 :         size_t allocSize = roundUpAllocationSize(n, pageSize);
         292           13855 :         if (allocSize == OVERSIZE_ALLOCATION)
         293               0 :             return NULL;
         294                 :
         295           13855 :         if (!m_pools.initialized() && !m_pools.init())
         296               0 :             return NULL;
         297                 :
         298                 : #ifdef DEBUG_STRESS_JSC_ALLOCATOR
         299                 :         ExecutablePool::Allocation a = systemAlloc(size_t(4294967291));
         300                 : #else
         301           13855 :         ExecutablePool::Allocation a = systemAlloc(allocSize);
         302                 : #endif
         303           13855 :         if (!a.pages)
         304               0 :             return NULL;
         305                 :
         306           13855 :         ExecutablePool *pool = js::OffTheBooks::new_<ExecutablePool>(this, a);
         307           13855 :         if (!pool) {
         308               0 :             systemRelease(a);
         309               0 :             return NULL;
         310                 :         }
         311           13855 :         m_pools.put(pool);
         312           13855 :         return pool;
         313                 :     }
         314                 :
         315                 : public:
         316          197817 :     ExecutablePool* poolForSize(size_t n)
         317                 :     {
         318                 : #ifndef DEBUG_STRESS_JSC_ALLOCATOR
         319                 :         // Try to fit in an existing small allocator.  Use the pool with the
         320                 :         // least available space that is big enough (best-fit).  This is the
         321                 :         // best strategy because (a) it maximizes the chance of the next
         322                 :         // allocation fitting in a small pool, and (b) it minimizes the
         323                 :         // potential waste when a small pool is next abandoned.
         324          197817 :         ExecutablePool *minPool = NULL;
         325          622045 :         for (size_t i = 0; i < m_smallPools.length(); i++) {
         326          424228 :             ExecutablePool *pool = m_smallPools[i];
         327          424228 :             if (n <= pool->available() && (!minPool || pool->available() < minPool->available()))
         328          195713 :                 minPool = pool;
         329                 :         }
         330          197817 :         if (minPool) {
         331          183962 :             minPool->addRef();
         332          183962 :             return minPool;
         333                 :         }
         334                 : #endif
         335                 :
         336                 :         // If the request is large, we just provide a unshared allocator
         337           13855 :         if (n > largeAllocSize)
         338             102 :             return createPool(n);
         339                 :
         340                 :         // Create a new allocator
         341           13753 :         ExecutablePool* pool = createPool(largeAllocSize);
         342           13753 :         if (!pool)
         343               0 :             return NULL;
         344                 :             // At this point, local |pool| is the owner.
         345                 :
         346           13753 :         if (m_smallPools.length() < maxSmallPools) {
         347                 :             // We haven't hit the maximum number of live pools;  add the new pool.
         348           12560 :             m_smallPools.append(pool);
         349           12560 :             pool->addRef();
         350                 :         } else {
         351                 :             // Find the pool with the least space.
         352            1193 :             int iMin = 0;
         353            4772 :             for (size_t i = 1; i < m_smallPools.length(); i++)
         354            7158 :                 if (m_smallPools[i]->available() <
         355            3579 :                     m_smallPools[iMin]->available())
         356                 :                 {
         357            1159 :                     iMin = i;
         358                 :                 }
         359                 :
         360                 :             // If the new allocator will result in more free space than the small
         361                 :             // pool with the least space, then we will use it instead
         362            1193 :             ExecutablePool *minPool = m_smallPools[iMin];
         363            1193 :             if ((pool->available() - n) > minPool->available()) {
         364            1185 :                 minPool->release();
         365            1185 :                 m_smallPools[iMin] = pool;
         366            1185 :                 pool->addRef();
         367                 :             }
         368                 :         }
         369                 :
         370                 :             // Pass ownership to the caller.
         371           13753 :         return pool;
         372                 :     }
         373                 :
         374                 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
         375                 :     static void makeWritable(void* start, size_t size)
         376                 :     {
         377                 :         reprotectRegion(start, size, Writable);
         378                 :     }
         379                 :
         380                 :     static void makeExecutable(void* start, size_t size)
         381                 :     {
         382                 :         reprotectRegion(start, size, Executable);
         383                 :     }
         384                 : #else
         385          509270 :     static void makeWritable(void*, size_t) {}
         386          509270 :     static void makeExecutable(void*, size_t) {}
         387                 : #endif
         388                 :
         389                 :
         390                 : #if WTF_CPU_X86 || WTF_CPU_X86_64
         391          197817 :     static void cacheFlush(void*, size_t)
         392                 :     {
         393          197817 :     }
         394                 : #elif WTF_CPU_MIPS
         395                 :     static void cacheFlush(void* code, size_t size)
         396                 :     {
         397                 : #if WTF_COMPILER_GCC && (GCC_VERSION >= 40300)
         398                 : #if WTF_MIPS_ISA_REV(2) && (GCC_VERSION < 40403)
         399                 :         int lineSize;
         400                 :         asm("rdhwr %0, $1" : "=r" (lineSize));
         401                 :         //
         402                 :         // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in
         403                 :         // mips_expand_synci_loop that may execute synci one more time.
         404                 :         // "start" points to the first byte of the cache line.
         405                 :         // "end" points to the last byte of the line before the last cache line.
         406                 :         // Because size is always a multiple of 4, this is safe to set
         407                 :         // "end" to the last byte.
         408                 :         //
         409                 :         intptr_t start = reinterpret_cast<intptr_t>(code) & (-lineSize);
         410                 :         intptr_t end = ((reinterpret_cast<intptr_t>(code) + size - 1) & (-lineSize)) - 1;
         411                 :         __builtin___clear_cache(reinterpret_cast<char*>(start), reinterpret_cast<char*>(end));
         412                 : #else
         413                 :         intptr_t end = reinterpret_cast<intptr_t>(code) + size;
         414                 :         __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
         415                 : #endif
         416                 : #else
         417                 :         _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
         418                 : #endif
         419                 :     }
         420                 : #elif WTF_CPU_ARM && WTF_OS_IOS
         421                 :     static void cacheFlush(void* code, size_t size)
         422                 :     {
         423                 :         sys_dcache_flush(code, size);
         424                 :         sys_icache_invalidate(code, size);
         425                 :     }
         426                 : #elif WTF_CPU_ARM_THUMB2 && WTF_IOS
         427                 :     static void cacheFlush(void* code, size_t size)
         428                 :     {
         429                 :         asm volatile (
         430                 :             "push    {r7}\n"
         431                 :             "mov     r0, %0\n"
         432                 :             "mov     r1, %1\n"
         433                 :             "movw    r7, #0x2\n"
         434                 :             "movt    r7, #0xf\n"
         435                 :             "movs    r2, #0x0\n"
         436                 :             "svc     0x0\n"
         437                 :             "pop     {r7}\n"
         438                 :             :
         439                 :             : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
         440                 :             : "r0", "r1", "r2");
         441                 :     }
         442                 : #elif WTF_OS_SYMBIAN
         443                 :     static void cacheFlush(void* code, size_t size)
         444                 :     {
         445                 :         User::IMB_Range(code, static_cast<char*>(code) + size);
         446                 :     }
         447                 : #elif WTF_CPU_ARM_TRADITIONAL && WTF_OS_LINUX && WTF_COMPILER_RVCT
         448                 :     static __asm void cacheFlush(void* code, size_t size);
         449                 : #elif WTF_CPU_ARM_TRADITIONAL && (WTF_OS_LINUX || WTF_OS_ANDROID) && WTF_COMPILER_GCC
         450                 :     static void cacheFlush(void* code, size_t size)
         451                 :     {
         452                 :         asm volatile (
         453                 :             "push    {r7}\n"
         454                 :             "mov     r0, %0\n"
         455                 :             "mov     r1, %1\n"
         456                 :             "mov     r7, #0xf0000\n"
         457                 :             "add     r7, r7, #0x2\n"
         458                 :             "mov     r2, #0x0\n"
         459                 :             "svc     0x0\n"
         460                 :             "pop     {r7}\n"
         461                 :             :
         462                 :             : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
         463                 :             : "r0", "r1", "r2");
         464                 :     }
         465                 : #elif WTF_CPU_SPARC
         466                 :     static void cacheFlush(void* code, size_t size)
         467                 :     {
         468                 :         sync_instruction_memory((caddr_t)code, size);
         469                 :     }
         470                 : #else
         471                 :     #error "The cacheFlush support is missing on this platform."
         472                 : #endif
         473                 :
         474                 : private:
         475                 :
         476                 : #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
         477                 :     static void reprotectRegion(void*, size_t, ProtectionSetting);
         478                 : #endif
         479                 :
         480                 :     // These are strong references;  they keep pools alive.
         481                 :     static const size_t maxSmallPools = 4;
         482                 :     typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy> SmallExecPoolVector;
         483                 :     SmallExecPoolVector m_smallPools;
         484                 :
         485                 :     // All live pools are recorded here, just for stats purposes.  These are
         486                 :     // weak references;  they don't keep pools alive.  When a pool is destroyed
         487                 :     // its reference is removed from m_pools.
         488                 :     typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
         489                 :             ExecPoolHashSet;
         490                 :     ExecPoolHashSet m_pools;    // All pools, just for stats purposes.
         491                 :     AllocationBehavior allocBehavior;
         492                 :
         493                 :     static size_t determinePageSize();
         494                 : };
         495                 :
         496                 : }
         497                 :
         498                 : #endif // ENABLE(ASSEMBLER)
         499                 :
         500                 : #endif // !defined(ExecutableAllocator)

Generated by: LCOV version 1.7