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