• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 #include "config.h"
27 
28 #include "ExecutableAllocator.h"
29 
30 #if ENABLE(EXECUTABLE_ALLOCATOR_FIXED)
31 
32 #include <errno.h>
33 
34 #include "TCSpinLock.h"
35 #include <sys/mman.h>
36 #include <unistd.h>
37 #include <wtf/AVLTree.h>
38 #include <wtf/PageReservation.h>
39 #include <wtf/VMTags.h>
40 
41 #if OS(LINUX)
42 #include <stdio.h>
43 #endif
44 
45 using namespace WTF;
46 
47 namespace JSC {
48 
49 #define TwoPow(n) (1ull << n)
50 
51 class AllocationTableSizeClass {
52 public:
AllocationTableSizeClass(size_t size,size_t blockSize,unsigned log2BlockSize)53     AllocationTableSizeClass(size_t size, size_t blockSize, unsigned log2BlockSize)
54         : m_blockSize(blockSize)
55     {
56         ASSERT(blockSize == TwoPow(log2BlockSize));
57 
58         // Calculate the number of blocks needed to hold size.
59         size_t blockMask = blockSize - 1;
60         m_blockCount = (size + blockMask) >> log2BlockSize;
61 
62         // Align to the smallest power of two >= m_blockCount.
63         m_blockAlignment = 1;
64         while (m_blockAlignment < m_blockCount)
65             m_blockAlignment += m_blockAlignment;
66     }
67 
blockSize() const68     size_t blockSize() const { return m_blockSize; }
blockCount() const69     size_t blockCount() const { return m_blockCount; }
blockAlignment() const70     size_t blockAlignment() const { return m_blockAlignment; }
71 
size()72     size_t size()
73     {
74         return m_blockSize * m_blockCount;
75     }
76 
77 private:
78     size_t m_blockSize;
79     size_t m_blockCount;
80     size_t m_blockAlignment;
81 };
82 
83 template<unsigned log2Entries>
84 class AllocationTableLeaf {
85     typedef uint64_t BitField;
86 
87 public:
88     static const unsigned log2SubregionSize = 12; // 2^12 == pagesize
89     static const unsigned log2RegionSize = log2SubregionSize + log2Entries;
90 
91     static const size_t subregionSize = TwoPow(log2SubregionSize);
92     static const size_t regionSize = TwoPow(log2RegionSize);
93     static const unsigned entries = TwoPow(log2Entries);
94     COMPILE_ASSERT(entries <= (sizeof(BitField) * 8), AllocationTableLeaf_entries_fit_in_BitField);
95 
AllocationTableLeaf()96     AllocationTableLeaf()
97         : m_allocated(0)
98     {
99     }
100 
~AllocationTableLeaf()101     ~AllocationTableLeaf()
102     {
103         ASSERT(isEmpty());
104     }
105 
allocate(AllocationTableSizeClass & sizeClass)106     size_t allocate(AllocationTableSizeClass& sizeClass)
107     {
108         ASSERT(sizeClass.blockSize() == subregionSize);
109         ASSERT(!isFull());
110 
111         size_t alignment = sizeClass.blockAlignment();
112         size_t count = sizeClass.blockCount();
113         // Use this mask to check for spans of free blocks.
114         BitField mask = ((1ull << count) - 1) << (alignment - count);
115 
116         // Step in units of alignment size.
117         for (unsigned i = 0; i < entries; i += alignment) {
118             if (!(m_allocated & mask)) {
119                 m_allocated |= mask;
120                 return (i + (alignment - count)) << log2SubregionSize;
121             }
122             mask <<= alignment;
123         }
124         return notFound;
125     }
126 
free(size_t location,AllocationTableSizeClass & sizeClass)127     void free(size_t location, AllocationTableSizeClass& sizeClass)
128     {
129         ASSERT(sizeClass.blockSize() == subregionSize);
130 
131         size_t entry = location >> log2SubregionSize;
132         size_t count = sizeClass.blockCount();
133         BitField mask = ((1ull << count) - 1) << entry;
134 
135         ASSERT((m_allocated & mask) == mask);
136         m_allocated &= ~mask;
137     }
138 
isEmpty()139     bool isEmpty()
140     {
141         return !m_allocated;
142     }
143 
isFull()144     bool isFull()
145     {
146         return !~m_allocated;
147     }
148 
size()149     static size_t size()
150     {
151         return regionSize;
152     }
153 
classForSize(size_t size)154     static AllocationTableSizeClass classForSize(size_t size)
155     {
156         return AllocationTableSizeClass(size, subregionSize, log2SubregionSize);
157     }
158 
159 #ifndef NDEBUG
dump(size_t parentOffset=0,unsigned indent=0)160     void dump(size_t parentOffset = 0, unsigned indent = 0)
161     {
162         for (unsigned i = 0; i < indent; ++i)
163             fprintf(stderr, "    ");
164         fprintf(stderr, "%08x: [%016llx]\n", (int)parentOffset, m_allocated);
165     }
166 #endif
167 
168 private:
169     BitField m_allocated;
170 };
171 
172 
173 template<class NextLevel>
174 class LazyAllocationTable {
175 public:
176     static const unsigned log2RegionSize = NextLevel::log2RegionSize;
177     static const unsigned entries = NextLevel::entries;
178 
LazyAllocationTable()179     LazyAllocationTable()
180         : m_ptr(0)
181     {
182     }
183 
~LazyAllocationTable()184     ~LazyAllocationTable()
185     {
186         ASSERT(isEmpty());
187     }
188 
allocate(AllocationTableSizeClass & sizeClass)189     size_t allocate(AllocationTableSizeClass& sizeClass)
190     {
191         if (!m_ptr)
192             m_ptr = new NextLevel();
193         return m_ptr->allocate(sizeClass);
194     }
195 
free(size_t location,AllocationTableSizeClass & sizeClass)196     void free(size_t location, AllocationTableSizeClass& sizeClass)
197     {
198         ASSERT(m_ptr);
199         m_ptr->free(location, sizeClass);
200         if (m_ptr->isEmpty()) {
201             delete m_ptr;
202             m_ptr = 0;
203         }
204     }
205 
isEmpty()206     bool isEmpty()
207     {
208         return !m_ptr;
209     }
210 
isFull()211     bool isFull()
212     {
213         return m_ptr && m_ptr->isFull();
214     }
215 
size()216     static size_t size()
217     {
218         return NextLevel::size();
219     }
220 
221 #ifndef NDEBUG
dump(size_t parentOffset=0,unsigned indent=0)222     void dump(size_t parentOffset = 0, unsigned indent = 0)
223     {
224         ASSERT(m_ptr);
225         m_ptr->dump(parentOffset, indent);
226     }
227 #endif
228 
classForSize(size_t size)229     static AllocationTableSizeClass classForSize(size_t size)
230     {
231         return NextLevel::classForSize(size);
232     }
233 
234 private:
235     NextLevel* m_ptr;
236 };
237 
238 template<class NextLevel, unsigned log2Entries>
239 class AllocationTableDirectory {
240     typedef uint64_t BitField;
241 
242 public:
243     static const unsigned log2SubregionSize = NextLevel::log2RegionSize;
244     static const unsigned log2RegionSize = log2SubregionSize + log2Entries;
245 
246     static const size_t subregionSize = TwoPow(log2SubregionSize);
247     static const size_t regionSize = TwoPow(log2RegionSize);
248     static const unsigned entries = TwoPow(log2Entries);
249     COMPILE_ASSERT(entries <= (sizeof(BitField) * 8), AllocationTableDirectory_entries_fit_in_BitField);
250 
AllocationTableDirectory()251     AllocationTableDirectory()
252         : m_full(0)
253         , m_hasSuballocation(0)
254     {
255     }
256 
~AllocationTableDirectory()257     ~AllocationTableDirectory()
258     {
259         ASSERT(isEmpty());
260     }
261 
allocate(AllocationTableSizeClass & sizeClass)262     size_t allocate(AllocationTableSizeClass& sizeClass)
263     {
264         ASSERT(sizeClass.blockSize() <= subregionSize);
265         ASSERT(!isFull());
266 
267         if (sizeClass.blockSize() < subregionSize) {
268             BitField bit = 1;
269             for (unsigned i = 0; i < entries; ++i, bit += bit) {
270                 if (m_full & bit)
271                     continue;
272                 size_t location = m_suballocations[i].allocate(sizeClass);
273                 if (location != notFound) {
274                     // If this didn't already have a subregion, it does now!
275                     m_hasSuballocation |= bit;
276                     // Mirror the suballocation's full bit.
277                     if (m_suballocations[i].isFull())
278                         m_full |= bit;
279                     return (i * subregionSize) | location;
280                 }
281             }
282             return notFound;
283         }
284 
285         // A block is allocated if either it is fully allocated or contains suballocations.
286         BitField allocated = m_full | m_hasSuballocation;
287 
288         size_t alignment = sizeClass.blockAlignment();
289         size_t count = sizeClass.blockCount();
290         // Use this mask to check for spans of free blocks.
291         BitField mask = ((1ull << count) - 1) << (alignment - count);
292 
293         // Step in units of alignment size.
294         for (unsigned i = 0; i < entries; i += alignment) {
295             if (!(allocated & mask)) {
296                 m_full |= mask;
297                 return (i + (alignment - count)) << log2SubregionSize;
298             }
299             mask <<= alignment;
300         }
301         return notFound;
302     }
303 
free(size_t location,AllocationTableSizeClass & sizeClass)304     void free(size_t location, AllocationTableSizeClass& sizeClass)
305     {
306         ASSERT(sizeClass.blockSize() <= subregionSize);
307 
308         size_t entry = location >> log2SubregionSize;
309 
310         if (sizeClass.blockSize() < subregionSize) {
311             BitField bit = 1ull << entry;
312             m_suballocations[entry].free(location & (subregionSize - 1), sizeClass);
313             // Check if the suballocation is now empty.
314             if (m_suballocations[entry].isEmpty())
315                 m_hasSuballocation &= ~bit;
316             // No need to check, it clearly isn't full any more!
317             m_full &= ~bit;
318         } else {
319             size_t count = sizeClass.blockCount();
320             BitField mask = ((1ull << count) - 1) << entry;
321             ASSERT((m_full & mask) == mask);
322             ASSERT(!(m_hasSuballocation & mask));
323             m_full &= ~mask;
324         }
325     }
326 
isEmpty()327     bool isEmpty()
328     {
329         return !(m_full | m_hasSuballocation);
330     }
331 
isFull()332     bool isFull()
333     {
334         return !~m_full;
335     }
336 
size()337     static size_t size()
338     {
339         return regionSize;
340     }
341 
classForSize(size_t size)342     static AllocationTableSizeClass classForSize(size_t size)
343     {
344         if (size < subregionSize) {
345             AllocationTableSizeClass sizeClass = NextLevel::classForSize(size);
346             if (sizeClass.size() < NextLevel::size())
347                 return sizeClass;
348         }
349         return AllocationTableSizeClass(size, subregionSize, log2SubregionSize);
350     }
351 
352 #ifndef NDEBUG
dump(size_t parentOffset=0,unsigned indent=0)353     void dump(size_t parentOffset = 0, unsigned indent = 0)
354     {
355         for (unsigned i = 0; i < indent; ++i)
356             fprintf(stderr, "    ");
357         fprintf(stderr, "%08x: [", (int)parentOffset);
358         for (unsigned i = 0; i < entries; ++i) {
359             BitField bit = 1ull << i;
360             char c = m_hasSuballocation & bit
361                 ? (m_full & bit ? 'N' : 'n')
362                 : (m_full & bit ? 'F' : '-');
363             fprintf(stderr, "%c", c);
364         }
365         fprintf(stderr, "]\n");
366 
367         for (unsigned i = 0; i < entries; ++i) {
368             BitField bit = 1ull << i;
369             size_t offset = parentOffset | (subregionSize * i);
370             if (m_hasSuballocation & bit)
371                 m_suballocations[i].dump(offset, indent + 1);
372         }
373     }
374 #endif
375 
376 private:
377     NextLevel m_suballocations[entries];
378     // Subregions exist in one of four states:
379     // (1) empty (both bits clear)
380     // (2) fully allocated as a single allocation (m_full set)
381     // (3) partially allocated through suballocations (m_hasSuballocation set)
382     // (4) fully allocated through suballocations (both bits set)
383     BitField m_full;
384     BitField m_hasSuballocation;
385 };
386 
387 
388 typedef AllocationTableLeaf<6> PageTables256KB;
389 typedef AllocationTableDirectory<PageTables256KB, 6> PageTables16MB;
390 typedef AllocationTableDirectory<LazyAllocationTable<PageTables16MB>, 1> PageTables32MB;
391 typedef AllocationTableDirectory<LazyAllocationTable<PageTables16MB>, 6> PageTables1GB;
392 
393 #if CPU(ARM)
394 typedef PageTables16MB FixedVMPoolPageTables;
395 #elif CPU(X86_64)
396 typedef PageTables1GB FixedVMPoolPageTables;
397 #else
398 typedef PageTables32MB FixedVMPoolPageTables;
399 #endif
400 
401 
402 class FixedVMPoolAllocator
403 {
404 public:
FixedVMPoolAllocator()405     FixedVMPoolAllocator()
406     {
407         ASSERT(PageTables256KB::size() == 256 * 1024);
408         ASSERT(PageTables16MB::size() == 16 * 1024 * 1024);
409         ASSERT(PageTables32MB::size() == 32 * 1024 * 1024);
410         ASSERT(PageTables1GB::size() == 1024 * 1024 * 1024);
411 
412         m_reservation = PageReservation::reserve(FixedVMPoolPageTables::size(), OSAllocator::JSJITCodePages, EXECUTABLE_POOL_WRITABLE, true);
413 #if !ENABLE(INTERPRETER)
414         if (!isValid())
415             CRASH();
416 #endif
417     }
418 
alloc(size_t requestedSize)419     ExecutablePool::Allocation alloc(size_t requestedSize)
420     {
421         ASSERT(requestedSize);
422         AllocationTableSizeClass sizeClass = classForSize(requestedSize);
423         size_t size = sizeClass.size();
424         ASSERT(size);
425 
426         if (size >= FixedVMPoolPageTables::size())
427             CRASH();
428         if (m_pages.isFull())
429             CRASH();
430 
431         size_t offset = m_pages.allocate(sizeClass);
432         if (offset == notFound)
433             CRASH();
434 
435         void* pointer = offsetToPointer(offset);
436         m_reservation.commit(pointer, size);
437         return ExecutablePool::Allocation(pointer, size);
438     }
439 
free(ExecutablePool::Allocation allocation)440     void free(ExecutablePool::Allocation allocation)
441     {
442         void* pointer = allocation.base();
443         size_t size = allocation.size();
444         ASSERT(size);
445 
446         m_reservation.decommit(pointer, size);
447 
448         AllocationTableSizeClass sizeClass = classForSize(size);
449         ASSERT(sizeClass.size() == size);
450         m_pages.free(pointerToOffset(pointer), sizeClass);
451     }
452 
allocated()453     size_t allocated()
454     {
455         return m_reservation.committed();
456     }
457 
isValid() const458     bool isValid() const
459     {
460         return !!m_reservation;
461     }
462 
463 private:
classForSize(size_t size)464     AllocationTableSizeClass classForSize(size_t size)
465     {
466         return FixedVMPoolPageTables::classForSize(size);
467     }
468 
offsetToPointer(size_t offset)469     void* offsetToPointer(size_t offset)
470     {
471         return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(m_reservation.base()) + offset);
472     }
473 
pointerToOffset(void * pointer)474     size_t pointerToOffset(void* pointer)
475     {
476         return reinterpret_cast<intptr_t>(pointer) - reinterpret_cast<intptr_t>(m_reservation.base());
477     }
478 
479     PageReservation m_reservation;
480     FixedVMPoolPageTables m_pages;
481 };
482 
483 
484 static SpinLock spinlock = SPINLOCK_INITIALIZER;
485 static FixedVMPoolAllocator* allocator = 0;
486 
487 
committedByteCount()488 size_t ExecutableAllocator::committedByteCount()
489 {
490     SpinLockHolder lockHolder(&spinlock);
491     return allocator ? allocator->allocated() : 0;
492 }
493 
intializePageSize()494 void ExecutableAllocator::intializePageSize()
495 {
496     ExecutableAllocator::pageSize = getpagesize();
497 }
498 
isValid() const499 bool ExecutableAllocator::isValid() const
500 {
501     SpinLockHolder lock_holder(&spinlock);
502     if (!allocator)
503         allocator = new FixedVMPoolAllocator();
504     return allocator->isValid();
505 }
506 
underMemoryPressure()507 bool ExecutableAllocator::underMemoryPressure()
508 {
509     // Technically we should take the spin lock here, but we don't care if we get stale data.
510     // This is only really a heuristic anyway.
511     return allocator && (allocator->allocated() > (FixedVMPoolPageTables::size() / 2));
512 }
513 
systemAlloc(size_t size)514 ExecutablePool::Allocation ExecutablePool::systemAlloc(size_t size)
515 {
516     SpinLockHolder lock_holder(&spinlock);
517     ASSERT(allocator);
518     return allocator->alloc(size);
519 }
520 
systemRelease(ExecutablePool::Allocation & allocation)521 void ExecutablePool::systemRelease(ExecutablePool::Allocation& allocation)
522 {
523     SpinLockHolder lock_holder(&spinlock);
524     ASSERT(allocator);
525     allocator->free(allocation);
526 }
527 
528 }
529 
530 
531 #endif // HAVE(ASSEMBLER)
532