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-06-02 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          521182 :     void release(bool willDestroy = false)
     122                 :     { 
     123          521182 :         JS_ASSERT(m_refCount != 0);
     124                 :         // XXX: disabled, see bug 654820.
     125                 :         //JS_ASSERT_IF(willDestroy, m_refCount == 1);
     126          521182 :         if (--m_refCount == 0) {
     127           19341 :             js::UnwantedForeground::delete_(this);
     128                 :         }
     129          521182 :     }
     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          501841 :     void addRef()
     136                 :     {
     137          501841 :         JS_ASSERT(m_refCount);
     138          501841 :         ++m_refCount;
     139          501841 :     }
     140                 : 
     141           19342 :     ExecutablePool(ExecutableAllocator* allocator, Allocation a)
     142           19342 :       : m_allocator(allocator), m_freePtr(a.pages), m_end(m_freePtr + a.size), m_allocation(a),
     143           38684 :         m_refCount(1), m_mjitCodeMethod(0), m_mjitCodeRegexp(0), m_destroy(false), m_gcNumber(0)
     144           19342 :     { }
     145                 : 
     146                 :     ~ExecutablePool();
     147                 : 
     148          501980 :     void* alloc(size_t n, CodeKind kind)
     149                 :     {
     150          501980 :         JS_ASSERT(n <= available());
     151          501980 :         void *result = m_freePtr;
     152          501980 :         m_freePtr += n;
     153                 : 
     154          501980 :         if ( kind == REGEXP_CODE )
     155           55269 :             m_mjitCodeRegexp += n;
     156                 :         else
     157          446711 :             m_mjitCodeMethod += n;
     158                 : 
     159          501980 :         return result;
     160                 :     }
     161                 :     
     162         2201728 :     size_t available() const { 
     163         2201728 :         JS_ASSERT(m_end >= m_freePtr);
     164         2201728 :         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           14217 :     explicit ExecutableAllocator(AllocationBehavior allocBehavior)
     183                 :       : destroyCallback(NULL),
     184           14217 :         allocBehavior(allocBehavior)
     185                 :     {
     186           14217 :         if (!pageSize) {
     187           11298 :             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           11298 :             largeAllocSize = pageSize * 16;
     197                 :         }
     198                 : 
     199                 : #if WTF_OS_WINDOWS
     200                 :         initSeed();
     201                 : #endif
     202                 : 
     203           14217 :         JS_ASSERT(m_smallPools.empty());
     204           14217 :     }
     205                 : 
     206           14216 :     ~ExecutableAllocator()
     207           14216 :     {
     208           29690 :         for (size_t i = 0; i < m_smallPools.length(); i++)
     209           15474 :             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           14216 :     }
     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          501980 :     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          501980 :         n = roundUpAllocationSize(n, sizeof(void*));
     223          501980 :         if (n == OVERSIZE_ALLOCATION) {
     224               0 :             *poolp = NULL;
     225               0 :             return NULL;
     226                 :         }
     227                 : 
     228          501980 :         *poolp = poolForSize(n);
     229          501980 :         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          501980 :         void *result = (*poolp)->alloc(n, type);
     235          501980 :         JS_ASSERT(result);
     236          501980 :         return result;
     237                 :     }
     238                 : 
     239           19341 :     void releasePoolPages(ExecutablePool *pool) {
     240           19341 :         JS_ASSERT(pool->m_allocation.pages);
     241           19341 :         if (destroyCallback)
     242            9875 :             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
     243           19341 :         systemRelease(pool->m_allocation);
     244           19341 :         m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
     245           19341 :     }
     246                 : 
     247                 :     void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
     248                 : 
     249          306058 :     void setDestroyCallback(DestroyCallback destroyCallback) {
     250          306058 :         this->destroyCallback = destroyCallback;
     251          306058 :     }
     252                 : 
     253         1763702 :     void setRandomize(bool enabled) {
     254         1763702 :         allocBehavior = enabled ? AllocationCanRandomize : AllocationDeterministic;
     255         1763702 :     }
     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          521322 :     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          521322 :         if ((std::numeric_limits<size_t>::max() - granularity) <= request)
     275               0 :             return OVERSIZE_ALLOCATION;
     276                 :         
     277                 :         // Round up to next page boundary
     278          521322 :         size_t size = request + (granularity - 1);
     279          521322 :         size = size & ~(granularity - 1);
     280          521322 :         JS_ASSERT(size >= request);
     281          521322 :         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           19342 :     ExecutablePool* createPool(size_t n)
     290                 :     {
     291           19342 :         size_t allocSize = roundUpAllocationSize(n, pageSize);
     292           19342 :         if (allocSize == OVERSIZE_ALLOCATION)
     293               0 :             return NULL;
     294                 : 
     295           19342 :         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           19342 :         ExecutablePool::Allocation a = systemAlloc(allocSize);
     302                 : #endif
     303           19342 :         if (!a.pages)
     304               0 :             return NULL;
     305                 : 
     306           19342 :         ExecutablePool *pool = js::OffTheBooks::new_<ExecutablePool>(this, a);
     307           19342 :         if (!pool) {
     308               0 :             systemRelease(a);
     309               0 :             return NULL;
     310                 :         }
     311           19342 :         m_pools.put(pool);
     312           19342 :         return pool;
     313                 :     }
     314                 : 
     315                 : public:
     316          501980 :     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          501980 :         ExecutablePool *minPool = NULL;
     325         1831544 :         for (size_t i = 0; i < m_smallPools.length(); i++) {
     326         1329564 :             ExecutablePool *pool = m_smallPools[i];
     327         1329564 :             if (n <= pool->available() && (!minPool || pool->available() < minPool->available()))
     328          547023 :                 minPool = pool;
     329                 :         }
     330          501980 :         if (minPool) {
     331          482638 :             minPool->addRef();
     332          482638 :             return minPool;
     333                 :         }
     334                 : #endif
     335                 : 
     336                 :         // If the request is large, we just provide a unshared allocator
     337           19342 :         if (n > largeAllocSize)
     338             131 :             return createPool(n);
     339                 : 
     340                 :         // Create a new allocator
     341           19211 :         ExecutablePool* pool = createPool(largeAllocSize);
     342           19211 :         if (!pool)
     343               0 :             return NULL;
     344                 :             // At this point, local |pool| is the owner.
     345                 : 
     346           19211 :         if (m_smallPools.length() < maxSmallPools) {
     347                 :             // We haven't hit the maximum number of live pools;  add the new pool.
     348           15475 :             m_smallPools.append(pool);
     349           15475 :             pool->addRef();
     350                 :         } else {
     351                 :             // Find the pool with the least space.
     352            3736 :             int iMin = 0;
     353           14944 :             for (size_t i = 1; i < m_smallPools.length(); i++)
     354           22416 :                 if (m_smallPools[i]->available() <
     355           11208 :                     m_smallPools[iMin]->available())
     356                 :                 {
     357            3373 :                     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            3736 :             ExecutablePool *minPool = m_smallPools[iMin];
     363            3736 :             if ((pool->available() - n) > minPool->available()) {
     364            3728 :                 minPool->release();
     365            3728 :                 m_smallPools[iMin] = pool;
     366            3728 :                 pool->addRef();
     367                 :             }
     368                 :         }
     369                 : 
     370                 :             // Pass ownership to the caller.
     371           19211 :         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         1230590 :     static void makeWritable(void*, size_t) {}
     386         1230590 :     static void makeExecutable(void*, size_t) {}
     387                 : #endif
     388                 : 
     389                 : 
     390                 : #if WTF_CPU_X86 || WTF_CPU_X86_64
     391          501980 :     static void cacheFlush(void*, size_t)
     392                 :     {
     393          501980 :     }
     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