2011-01-03 Martin Robinson <mrobinson@igalia.com>
[WebKit-https.git] / Source / JavaScriptCore / jit / ExecutableAllocator.h
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 #include <stddef.h> // for ptrdiff_t
29 #include <limits>
30 #include <wtf/Assertions.h>
31 #include <wtf/PageAllocation.h>
32 #include <wtf/PassRefPtr.h>
33 #include <wtf/RefCounted.h>
34 #include <wtf/UnusedParam.h>
35 #include <wtf/Vector.h>
36
37 #if OS(IOS)
38 #include <libkern/OSCacheControl.h>
39 #include <sys/mman.h>
40 #endif
41
42 #if OS(SYMBIAN)
43 #include <e32std.h>
44 #endif
45
46 #if CPU(MIPS) && OS(LINUX)
47 #include <sys/cachectl.h>
48 #endif
49
50 #if OS(WINCE)
51 // From pkfuncs.h (private header file from the Platform Builder)
52 #define CACHE_SYNC_ALL 0x07F
53 extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags);
54 #endif
55
56 #if PLATFORM(BREWMP)
57 #include <AEEIMemCache1.h>
58 #include <AEEMemCache1.bid>
59 #include <wtf/brew/RefPtrBrew.h>
60 #endif
61
62 #define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
63 #define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 4)
64
65 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
66 #define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
67 #define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
68 #define EXECUTABLE_POOL_WRITABLE false
69 #else
70 #define EXECUTABLE_POOL_WRITABLE true
71 #endif
72
73 namespace JSC {
74
75 inline size_t roundUpAllocationSize(size_t request, size_t granularity)
76 {
77     if ((std::numeric_limits<size_t>::max() - granularity) <= request)
78         CRASH(); // Allocation is too large
79     
80     // Round up to next page boundary
81     size_t size = request + (granularity - 1);
82     size = size & ~(granularity - 1);
83     ASSERT(size >= request);
84     return size;
85 }
86
87 }
88
89 #if ENABLE(JIT) && ENABLE(ASSEMBLER)
90
91 namespace JSC {
92
93 class ExecutablePool : public RefCounted<ExecutablePool> {
94 public:
95 #if ENABLE(EXECUTABLE_ALLOCATOR_DEMAND)
96     typedef PageAllocation Allocation;
97 #else
98     class Allocation {
99     public:
100         Allocation(void* base, size_t size)
101             : m_base(base)
102             , m_size(size)
103         {
104         }
105         void* base() { return m_base; }
106         size_t size() { return m_size; }
107         bool operator!() const { return !m_base; }
108
109     private:
110         void* m_base;
111         size_t m_size;
112     };
113 #endif
114     typedef Vector<Allocation, 2> AllocationList;
115
116     static PassRefPtr<ExecutablePool> create(size_t n)
117     {
118         return adoptRef(new ExecutablePool(n));
119     }
120
121     void* alloc(size_t n)
122     {
123         ASSERT(m_freePtr <= m_end);
124
125         // Round 'n' up to a multiple of word size; if all allocations are of
126         // word sized quantities, then all subsequent allocations will be aligned.
127         n = roundUpAllocationSize(n, sizeof(void*));
128
129         if (static_cast<ptrdiff_t>(n) < (m_end - m_freePtr)) {
130             void* result = m_freePtr;
131             m_freePtr += n;
132             return result;
133         }
134
135         // Insufficient space to allocate in the existing pool
136         // so we need allocate into a new pool
137         return poolAllocate(n);
138     }
139     
140     void tryShrink(void* allocation, size_t oldSize, size_t newSize)
141     {
142         if (static_cast<char*>(allocation) + oldSize != m_freePtr)
143             return;
144         m_freePtr = static_cast<char*>(allocation) + roundUpAllocationSize(newSize, sizeof(void*));
145     }
146
147     ~ExecutablePool()
148     {
149         AllocationList::iterator end = m_pools.end();
150         for (AllocationList::iterator ptr = m_pools.begin(); ptr != end; ++ptr)
151             ExecutablePool::systemRelease(*ptr);
152     }
153
154     size_t available() const { return (m_pools.size() > 1) ? 0 : m_end - m_freePtr; }
155
156 private:
157     static Allocation systemAlloc(size_t n);
158     static void systemRelease(Allocation& alloc);
159
160     ExecutablePool(size_t n);
161
162     void* poolAllocate(size_t n);
163
164     char* m_freePtr;
165     char* m_end;
166     AllocationList m_pools;
167 };
168
169 class ExecutableAllocator {
170     enum ProtectionSeting { Writable, Executable };
171
172 public:
173     static size_t pageSize;
174     ExecutableAllocator()
175     {
176         if (!pageSize)
177             intializePageSize();
178         if (isValid())
179             m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
180 #if !ENABLE(INTERPRETER)
181         else
182             CRASH();
183 #endif
184     }
185
186     bool isValid() const;
187
188     static bool underMemoryPressure();
189
190     PassRefPtr<ExecutablePool> poolForSize(size_t n)
191     {
192         // Try to fit in the existing small allocator
193         ASSERT(m_smallAllocationPool);
194         if (n < m_smallAllocationPool->available())
195             return m_smallAllocationPool;
196
197         // If the request is large, we just provide a unshared allocator
198         if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
199             return ExecutablePool::create(n);
200
201         // Create a new allocator
202         RefPtr<ExecutablePool> pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
203
204         // If the new allocator will result in more free space than in
205         // the current small allocator, then we will use it instead
206         if ((pool->available() - n) > m_smallAllocationPool->available())
207             m_smallAllocationPool = pool;
208         return pool.release();
209     }
210
211 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
212     static void makeWritable(void* start, size_t size)
213     {
214         reprotectRegion(start, size, Writable);
215     }
216
217     static void makeExecutable(void* start, size_t size)
218     {
219         reprotectRegion(start, size, Executable);
220     }
221 #else
222     static void makeWritable(void*, size_t) {}
223     static void makeExecutable(void*, size_t) {}
224 #endif
225
226
227 #if CPU(X86) || CPU(X86_64)
228     static void cacheFlush(void*, size_t)
229     {
230     }
231 #elif CPU(MIPS)
232     static void cacheFlush(void* code, size_t size)
233     {
234 #if COMPILER(GCC) && GCC_VERSION_AT_LEAST(4,3,0)
235 #if WTF_MIPS_ISA_REV(2) && !GCC_VERSION_AT_LEAST(4,4,3)
236         int lineSize;
237         asm("rdhwr %0, $1" : "=r" (lineSize));
238         //
239         // Modify "start" and "end" to avoid GCC 4.3.0-4.4.2 bug in
240         // mips_expand_synci_loop that may execute synci one more time.
241         // "start" points to the fisrt byte of the cache line.
242         // "end" points to the last byte of the line before the last cache line.
243         // Because size is always a multiple of 4, this is safe to set
244         // "end" to the last byte.
245         //
246         intptr_t start = reinterpret_cast<intptr_t>(code) & (-lineSize);
247         intptr_t end = ((reinterpret_cast<intptr_t>(code) + size - 1) & (-lineSize)) - 1;
248         __builtin___clear_cache(reinterpret_cast<char*>(start), reinterpret_cast<char*>(end));
249 #else
250         intptr_t end = reinterpret_cast<intptr_t>(code) + size;
251         __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
252 #endif
253 #else
254         _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
255 #endif
256     }
257 #elif CPU(ARM_THUMB2) && OS(IOS)
258     static void cacheFlush(void* code, size_t size)
259     {
260         sys_cache_control(kCacheFunctionPrepareForExecution, code, size);
261     }
262 #elif CPU(ARM_THUMB2) && OS(LINUX)
263     static void cacheFlush(void* code, size_t size)
264     {
265         asm volatile (
266             "push    {r7}\n"
267             "mov     r0, %0\n"
268             "mov     r1, %1\n"
269             "movw    r7, #0x2\n"
270             "movt    r7, #0xf\n"
271             "movs    r2, #0x0\n"
272             "svc     0x0\n"
273             "pop     {r7}\n"
274             :
275             : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
276             : "r0", "r1", "r2");
277     }
278 #elif OS(SYMBIAN)
279     static void cacheFlush(void* code, size_t size)
280     {
281         User::IMB_Range(code, static_cast<char*>(code) + size);
282     }
283 #elif CPU(ARM_TRADITIONAL) && OS(LINUX) && COMPILER(RVCT)
284     static __asm void cacheFlush(void* code, size_t size);
285 #elif CPU(ARM_TRADITIONAL) && OS(LINUX) && COMPILER(GCC)
286     static void cacheFlush(void* code, size_t size)
287     {
288         asm volatile (
289             "push    {r7}\n"
290             "mov     r0, %0\n"
291             "mov     r1, %1\n"
292             "mov     r7, #0xf0000\n"
293             "add     r7, r7, #0x2\n"
294             "mov     r2, #0x0\n"
295             "svc     0x0\n"
296             "pop     {r7}\n"
297             :
298             : "r" (code), "r" (reinterpret_cast<char*>(code) + size)
299             : "r0", "r1", "r2");
300     }
301 #elif OS(WINCE)
302     static void cacheFlush(void* code, size_t size)
303     {
304         CacheRangeFlush(code, size, CACHE_SYNC_ALL);
305     }
306 #elif PLATFORM(BREWMP)
307     static void cacheFlush(void* code, size_t size)
308     {
309         RefPtr<IMemCache1> memCache = createRefPtrInstance<IMemCache1>(AEECLSID_MemCache1);
310         IMemCache1_ClearCache(memCache.get(), reinterpret_cast<uint32>(code), size, MEMSPACE_CACHE_FLUSH, MEMSPACE_DATACACHE);
311         IMemCache1_ClearCache(memCache.get(), reinterpret_cast<uint32>(code), size, MEMSPACE_CACHE_INVALIDATE, MEMSPACE_INSTCACHE);
312     }
313 #else
314     #error "The cacheFlush support is missing on this platform."
315 #endif
316     static size_t committedByteCount();
317
318 private:
319
320 #if ENABLE(ASSEMBLER_WX_EXCLUSIVE)
321     static void reprotectRegion(void*, size_t, ProtectionSeting);
322 #endif
323
324     RefPtr<ExecutablePool> m_smallAllocationPool;
325     static void intializePageSize();
326 };
327
328 inline ExecutablePool::ExecutablePool(size_t n)
329 {
330     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
331     Allocation mem = systemAlloc(allocSize);
332     m_pools.append(mem);
333     m_freePtr = static_cast<char*>(mem.base());
334     if (!m_freePtr)
335         CRASH(); // Failed to allocate
336     m_end = m_freePtr + allocSize;
337 }
338
339 inline void* ExecutablePool::poolAllocate(size_t n)
340 {
341     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
342     
343     Allocation result = systemAlloc(allocSize);
344     if (!result.base())
345         CRASH(); // Failed to allocate
346     
347     ASSERT(m_end >= m_freePtr);
348     if ((allocSize - n) > static_cast<size_t>(m_end - m_freePtr)) {
349         // Replace allocation pool
350         m_freePtr = static_cast<char*>(result.base()) + n;
351         m_end = static_cast<char*>(result.base()) + allocSize;
352     }
353
354     m_pools.append(result);
355     return result.base();
356 }
357
358 }
359
360 #endif // ENABLE(JIT) && ENABLE(ASSEMBLER)
361
362 #endif // !defined(ExecutableAllocator)