-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathCodeGenNumberAllocator.h
189 lines (170 loc) · 7.61 KB
/
CodeGenNumberAllocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#if !FLOATVAR
/****************************************************************************
* CodeGenNumberThreadAllocator
*
* Recycler can't be used by multiple threads. This allocator is used by
* the JIT to allocate numbers in the background thread. It allocates
* pages directly from the OS and then allocates numbers from these pages,
* along with a linked list of chunks that hold the numbers so the native
* entry points can keep these numbers alive. Then this memory is
* integrated back into the main thread's recycler at the beginning of
* GC.
*
* This class requires intimate knowledge of how recycler allocates memory
* so the memory can be integrated back. So changes in the recycler
* will affect this allocator.
*
* An alternative solution is to record the number we need to create,
* and only create them on the main thread when the entry point is used.
* However, since we don't have the address of the number at JIT time,
* we will have to incur two indirections (loading the array of pointers to
* numbers and loading the numbers) in the JIT code which is not desirable.
*
* Implementation details:
*
* The segments can be integrated back to the recycler's page allocator
* immediately. They are all marked as used initially.
*
* Numbers and the linked list chunks are allocated in separate pages
* as number will be a leaf page when integrated back to the recycler
* and the linked list chunks will be normal pages.
*
* Generally, when a page is full, it is (almost) ready to be integrated
* back to the recycler. However the number pages need to wait until
* the referencing linked list chunk is integrated before it can be
* integrated (otherwise, the recycler will not see the reference that
* keeps the numbers alive). So when a number page is full, it will
* go to pendingReferenceNumberBlock list. Then, when a linked list
* chunk is full, all the pages in pendingReferenceNumberBlock can go to
* pendingFlushNumberBlock list and the linked list chunk page will
* go to pendingFlushChunkBlock.
*
* Once we finish jitting a function and the number link list is set on the
* entry point, these pages are ready to be integrated back to recycler
* and be moved to pendingIntegration*Pages lists. Access to the
* pendingIntegration*Pages are synchronized, therefore the main thread
* can do the integration before GC happens.
*
****************************************************************************/
struct CodeGenNumberChunk
{
static int const MaxNumberCount = 3;
Field(Js::JavascriptNumber*) numbers[MaxNumberCount];
Field(CodeGenNumberChunk*) next;
};
CompileAssert(
sizeof(CodeGenNumberChunk) == HeapConstants::ObjectGranularity ||
sizeof(CodeGenNumberChunk) == HeapConstants::ObjectGranularity * 2);
class CodeGenNumberThreadAllocator
{
friend struct XProcNumberPageSegmentManager;
public:
CodeGenNumberThreadAllocator(Recycler * recycler);
~CodeGenNumberThreadAllocator();
// All the public API's need to be guarded by critical sections.
// Multiple jit threads access this.
Js::JavascriptNumber * AllocNumber();
CodeGenNumberChunk * AllocChunk();
void Integrate();
void FlushAllocations();
private:
// All allocations are small allocations
const size_t BlockSize = SmallAllocationBlockAttributes::PageCount * AutoSystemInfo::PageSize;
void AllocNewNumberBlock();
void AllocNewChunkBlock();
size_t GetNumberAllocSize();
size_t GetChunkAllocSize();
CriticalSection cs;
Recycler * recycler;
PageSegment * currentNumberSegment;
PageSegment * currentChunkSegment;
char * numberSegmentEnd;
char * currentNumberBlockEnd;
char * nextNumber;
char * chunkSegmentEnd;
char * currentChunkBlockEnd;
char * nextChunk;
bool hasNewNumberBlock;
bool hasNewChunkBlock;
struct BlockRecord
{
BlockRecord(__in_ecount_pagesize char * blockAddress, PageSegment * segment)
: blockAddress(blockAddress), segment(segment)
{
}
char * blockAddress;
PageSegment * segment;
};
// Keep track of segments and pages that needs to be integrated to the recycler.
uint pendingIntegrationNumberSegmentCount;
uint pendingIntegrationChunkSegmentCount;
size_t pendingIntegrationNumberSegmentPageCount;
size_t pendingIntegrationChunkSegmentPageCount;
DListBase<PageSegment> pendingIntegrationNumberSegment;
DListBase<PageSegment> pendingIntegrationChunkSegment;
SListBase<BlockRecord, NoThrowHeapAllocator> pendingIntegrationNumberBlock;
SListBase<BlockRecord, NoThrowHeapAllocator> pendingIntegrationChunkBlock;
// These are finished pages during the code gen of the current function
// We can't integrate them until the code gen is done for the function,
// because the references for the number is not set on the entry point yet.
SListBase<BlockRecord, NoThrowHeapAllocator> pendingFlushNumberBlock;
SListBase<BlockRecord, NoThrowHeapAllocator> pendingFlushChunkBlock;
// Numbers are reference by the chunks, so we need to wait until that is ready
// to be flushed before the number page can be flushed. Otherwise, we might have number
// integrated back to the GC, but the chunk hasn't yet, thus GC won't see the reference.
SListBase<BlockRecord, NoThrowHeapAllocator> pendingReferenceNumberBlock;
};
class CodeGenNumberAllocator
{
public:
CodeGenNumberAllocator(CodeGenNumberThreadAllocator * threadAlloc, Recycler * recycler);
// We should never call this function if we are using tagged float
#if !FLOATVAR
Js::JavascriptNumber * Alloc();
#endif
CodeGenNumberChunk * Finalize();
private:
Recycler * recycler;
CodeGenNumberThreadAllocator * threadAlloc;
CodeGenNumberChunk * chunk;
CodeGenNumberChunk * chunkTail;
uint currentChunkNumberCount;
#if DBG
bool finalized;
#endif
};
namespace Js
{
class StaticType;
}
struct XProcNumberPageSegmentImpl : public XProcNumberPageSegment
{
XProcNumberPageSegmentImpl();
Js::JavascriptNumber* AllocateNumber(Func* func, double value);
unsigned int GetTotalSize() { return PageCount * AutoSystemInfo::PageSize; }
void* GetEndAddress() { return (void*)(this->pageAddress + PageCount * AutoSystemInfo::PageSize); }
void* GetCommitEndAddress() { return (void*)(this->pageAddress + this->committedEnd); }
static const uint BlockSize = SmallAllocationBlockAttributes::PageCount*AutoSystemInfo::PageSize;
static const uint PageCount = Memory::IdleDecommitPageAllocator::DefaultMaxAllocPageCount;
static uint sizeCat;
static void Initialize(bool recyclerVerifyEnabled, uint recyclerVerifyPad);
};
static_assert(sizeof(XProcNumberPageSegmentImpl) == sizeof(XProcNumberPageSegment), "should not have data member in XProcNumberPageSegmentImpl");
struct XProcNumberPageSegmentManager
{
CriticalSection cs;
XProcNumberPageSegmentImpl* segmentsList;
Recycler* recycler;
unsigned int integratedSegmentCount;
XProcNumberPageSegmentManager(Recycler* recycler);
~XProcNumberPageSegmentManager();
XProcNumberPageSegment * GetFreeSegment(Memory::ArenaAllocator* alloc);
Field(Js::JavascriptNumber*)* RegisterSegments(XProcNumberPageSegment* segments);
void Integrate();
};
#endif