Skip to content

Commit bd86f13

Browse files
committed
implement NumberAllocator for OOP JIT
Allocate pages in JIT process with VirtualAllocEx. Initially reserve 0x20 pages(required by recycler leaf page allocator), then commit 2 pages (small block) incrementally. Main process send over the available page through RPC, JIT process will use the free part of the page before committing new pages. Number chunks are allocated after JIT is done, use same allocating mechanism as in-proc JIT The pageSegment is created when integrating to recycler page allocator
1 parent 3861bf8 commit bd86f13

25 files changed

+434
-17
lines changed

lib/Backend/CodeGenNumberAllocator.cpp

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ CodeGenNumberThreadAllocator::Integrate()
211211

212212
while (!pendingIntegrationChunkBlock.Empty())
213213
{
214+
// REVIEW: the above number block integration can be moved into this loop
215+
214216
TRACK_ALLOC_INFO(recycler, CodeGenNumberChunk, Recycler, 0, (size_t)-1);
215217

216218
BlockRecord& record = pendingIntegrationChunkBlock.Head();
@@ -287,3 +289,235 @@ CodeGenNumberAllocator::Finalize()
287289
this->currentChunkNumberCount = 0;
288290
return finalizedChunk;
289291
}
292+
293+
294+
Js::JavascriptNumber* XProcNumberPageSegmentImpl::AllocateNumber(HANDLE hProcess, double value, Js::StaticType* numberTypeStatic, void* javascriptNumberVtbl)
295+
{
296+
XProcNumberPageSegmentImpl* tail = this;
297+
298+
if (this->pageAddress != 0)
299+
{
300+
while (tail->nextSegment)
301+
{
302+
tail = (XProcNumberPageSegmentImpl*)tail->nextSegment;
303+
}
304+
305+
if (tail->pageAddress + tail->committedEnd - tail->allocEndAddress >= sizeCat)
306+
{
307+
auto number = tail->allocEndAddress;
308+
tail->allocEndAddress += sizeCat;
309+
310+
Js::JavascriptNumber localNumber(value, numberTypeStatic, true);
311+
312+
// change vtable to the remote one
313+
*(void**)&localNumber = javascriptNumberVtbl;
314+
315+
// initialize number by WriteProcessMemory
316+
SIZE_T bytesWritten;
317+
WriteProcessMemory(hProcess, (void*)number, &localNumber, sizeof(localNumber), &bytesWritten);
318+
319+
return (Js::JavascriptNumber*) number;
320+
}
321+
322+
// alloc blocks
323+
if ((void*)tail->committedEnd < tail->GetEndAddress())
324+
{
325+
Assert((unsigned int)((char*)tail->GetEndAddress() - (char*)tail->committedEnd) >= tail->blockSize);
326+
// TODO: implement guard pages (still necessary for OOP JIT?)
327+
auto ret = ::VirtualAllocEx(hProcess, tail->GetCommitEndAddress(), tail->blockSize, MEM_COMMIT, PAGE_READWRITE);
328+
if (!ret)
329+
{
330+
Js::Throw::OutOfMemory();
331+
}
332+
tail->committedEnd += tail->blockSize;
333+
return AllocateNumber(hProcess, value, numberTypeStatic, javascriptNumberVtbl);
334+
}
335+
}
336+
337+
// alloc new segment
338+
void* pages = ::VirtualAllocEx(hProcess, nullptr, this->pageCount * AutoSystemInfo::PageSize, MEM_RESERVE, PAGE_READWRITE);
339+
if (pages == nullptr)
340+
{
341+
Js::Throw::OutOfMemory();
342+
}
343+
344+
if (tail->pageAddress == 0)
345+
{
346+
tail = new (tail) XProcNumberPageSegmentImpl();
347+
tail->pageAddress = (int)pages;
348+
tail->allocStartAddress = this->pageAddress;
349+
tail->allocEndAddress = this->pageAddress;
350+
tail->nextSegment = nullptr;
351+
return AllocateNumber(hProcess, value, numberTypeStatic, javascriptNumberVtbl);
352+
}
353+
else
354+
{
355+
XProcNumberPageSegmentImpl* seg = new (midl_user_allocate(sizeof(XProcNumberPageSegment))) XProcNumberPageSegmentImpl();
356+
tail->nextSegment = seg;
357+
return seg->AllocateNumber(hProcess, value, numberTypeStatic, javascriptNumberVtbl);
358+
}
359+
}
360+
361+
362+
XProcNumberPageSegmentImpl::XProcNumberPageSegmentImpl()
363+
{
364+
this->pageCount = Memory::IdleDecommitPageAllocator::DefaultMaxAllocPageCount;
365+
this->sizeCat = HeapInfo::GetAlignedSizeNoCheck(sizeof(Js::JavascriptNumber));
366+
this->blockSize = SmallAllocationBlockAttributes::PageCount*AutoSystemInfo::PageSize;
367+
this->blockIntegratedSize = 0;
368+
this->pageSegment = 0;
369+
}
370+
371+
CodeGenNumberChunk* ::XProcNumberPageSegmentManager::RegisterSegments(XProcNumberPageSegment* segments)
372+
{
373+
Assert(segments->pageAddress && segments->allocStartAddress && segments->allocEndAddress);
374+
375+
376+
XProcNumberPageSegmentImpl* segmentImpl = (XProcNumberPageSegmentImpl*)segments;
377+
378+
auto temp = segmentImpl;
379+
CodeGenNumberChunk* chunk = nullptr;
380+
int numberCount = CodeGenNumberChunk::MaxNumberCount;
381+
while (temp)
382+
{
383+
auto start = temp->allocStartAddress;
384+
385+
if (temp->GetChunkAllocator() == nullptr)
386+
{
387+
temp->chunkAllocator = (intptr_t)HeapNew(CodeGenNumberThreadAllocator, this->recycler);
388+
}
389+
390+
while (start < temp->allocEndAddress)
391+
{
392+
if (numberCount == CodeGenNumberChunk::MaxNumberCount)
393+
{
394+
auto newChunk = temp->GetChunkAllocator()->AllocChunk();
395+
newChunk->next = chunk;
396+
chunk = newChunk;
397+
numberCount = 0;
398+
}
399+
chunk->numbers[numberCount++] = (Js::JavascriptNumber*)start;
400+
start += temp->sizeCat;
401+
}
402+
403+
temp->GetChunkAllocator()->FlushAllocations();
404+
405+
temp = (XProcNumberPageSegmentImpl*)temp->nextSegment;
406+
}
407+
408+
AutoCriticalSection autoCS(&cs);
409+
if (this->segmentsList == nullptr)
410+
{
411+
this->segmentsList = segmentImpl;
412+
}
413+
else
414+
{
415+
auto temp = segmentsList;
416+
while (temp->nextSegment)
417+
{
418+
temp = (XProcNumberPageSegmentImpl*)temp->nextSegment;
419+
}
420+
temp->nextSegment = segmentImpl;
421+
}
422+
423+
return chunk;
424+
}
425+
426+
void XProcNumberPageSegmentManager::GetFreeSegment(XProcNumberPageSegment& seg)
427+
{
428+
AutoCriticalSection autoCS(&cs);
429+
430+
memset(&seg, 0, sizeof(seg));
431+
432+
if (segmentsList == nullptr)
433+
{
434+
new (&seg) XProcNumberPageSegmentImpl();
435+
return;
436+
}
437+
438+
auto temp = segmentsList;
439+
auto prev = &segmentsList;
440+
while (temp)
441+
{
442+
if (temp->allocEndAddress != temp->pageAddress + (int)(temp->pageCount*AutoSystemInfo::PageSize)) // not full
443+
{
444+
*prev = (XProcNumberPageSegmentImpl*)temp->nextSegment;
445+
446+
// remove from the list
447+
memcpy(&seg, temp, sizeof(seg));
448+
midl_user_free(temp);
449+
return;
450+
}
451+
prev = (XProcNumberPageSegmentImpl**)&temp->nextSegment;
452+
temp = (XProcNumberPageSegmentImpl*)temp->nextSegment;
453+
}
454+
}
455+
456+
void XProcNumberPageSegmentManager::Integrate()
457+
{
458+
AutoCriticalSection autoCS(&cs);
459+
460+
auto temp = this->segmentsList;
461+
auto prev = &this->segmentsList;
462+
while (temp)
463+
{
464+
if (temp->pageSegment == 0)
465+
{
466+
auto leafPageAllocator = recycler->GetRecyclerLeafPageAllocator();
467+
DListBase<PageSegment> segmentList;
468+
temp->pageSegment = (intptr_t)leafPageAllocator->AllocPageSegment(segmentList, leafPageAllocator,
469+
(void*)temp->pageAddress, temp->pageCount, temp->committedEnd / AutoSystemInfo::PageSize);
470+
leafPageAllocator->IntegrateSegments(segmentList, 1, temp->pageCount);
471+
472+
this->integratedSegmentCount++;
473+
}
474+
475+
if (!temp->GetChunkAllocator()->pendingIntegrationChunkBlock.Empty())
476+
{
477+
Assert(sizeof(CodeGenNumberChunk) == sizeof(Js::JavascriptNumber));
478+
size_t minIntegrateSize = temp->blockSize*CodeGenNumberChunk::MaxNumberCount;
479+
for (; temp->pageAddress + temp->blockIntegratedSize + minIntegrateSize < (unsigned int)temp->allocEndAddress;
480+
temp->blockIntegratedSize += minIntegrateSize)
481+
{
482+
TRACK_ALLOC_INFO(recycler, Js::JavascriptNumber, Recycler, 0, (size_t)-1);
483+
484+
if (!recycler->IntegrateBlock<LeafBit>((char*)temp->pageAddress + temp->blockIntegratedSize, (PageSegment*)temp->pageSegment, temp->sizeCat, sizeof(Js::JavascriptNumber)))
485+
{
486+
Js::Throw::OutOfMemory();
487+
}
488+
}
489+
}
490+
491+
temp->GetChunkAllocator()->Integrate();
492+
493+
if (temp->blockIntegratedSize >= temp->pageCount*AutoSystemInfo::PageSize)
494+
{
495+
// all pages are integrated, don't need this segment any more
496+
*prev = (XProcNumberPageSegmentImpl*)temp->nextSegment;
497+
HeapDelete(temp->GetChunkAllocator());
498+
midl_user_free(temp);
499+
temp = *prev;
500+
}
501+
else
502+
{
503+
prev = (XProcNumberPageSegmentImpl**)&temp->nextSegment;
504+
temp = (XProcNumberPageSegmentImpl*)temp->nextSegment;
505+
}
506+
507+
}
508+
}
509+
510+
XProcNumberPageSegmentManager::~XProcNumberPageSegmentManager()
511+
{
512+
auto temp = segmentsList;
513+
while (temp)
514+
{
515+
auto next = temp->nextSegment;
516+
if (temp->chunkAllocator)
517+
{
518+
HeapDelete((CodeGenNumberThreadAllocator*)temp->chunkAllocator);
519+
}
520+
midl_user_free(temp);
521+
temp = (XProcNumberPageSegmentImpl*)next;
522+
}
523+
}

lib/Backend/CodeGenNumberAllocator.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ CompileAssert(
6363

6464
class CodeGenNumberThreadAllocator
6565
{
66+
friend struct XProcNumberPageSegmentManager;
6667
public:
6768
CodeGenNumberThreadAllocator(Recycler * recycler);
6869
~CodeGenNumberThreadAllocator();
@@ -144,3 +145,41 @@ class CodeGenNumberAllocator
144145
bool finalized;
145146
#endif
146147
};
148+
149+
namespace Js
150+
{
151+
class StaticType;
152+
}
153+
154+
struct XProcNumberPageSegmentImpl : public XProcNumberPageSegment
155+
{
156+
XProcNumberPageSegmentImpl();
157+
Js::JavascriptNumber* AllocateNumber(HANDLE hProcess, double value, Js::StaticType* numberTypeStatic, void* javascriptNumberVtbl);
158+
unsigned int GetTotalSize() { return this->pageCount * AutoSystemInfo::PageSize; }
159+
void* GetEndAddress() { return (void*)(this->pageAddress + this->pageCount * AutoSystemInfo::PageSize); }
160+
void* GetCommitEndAddress() { return (void*)(this->pageAddress + this->committedEnd); }
161+
// TODO: using CodeGenNumberThreadAllocator to allocate the chunks only, abstract chunk alloc code out of CodeGenNumberThreadAllocator
162+
CodeGenNumberThreadAllocator* GetChunkAllocator() { return (CodeGenNumberThreadAllocator*) this->chunkAllocator; }
163+
};
164+
165+
static_assert(sizeof(XProcNumberPageSegmentImpl) == sizeof(XProcNumberPageSegment), "should not have data member in XProcNumberPageSegmentImpl");
166+
167+
struct XProcNumberPageSegmentManager
168+
{
169+
CriticalSection cs;
170+
XProcNumberPageSegmentImpl* segmentsList;
171+
Recycler* recycler;
172+
unsigned int integratedSegmentCount;
173+
XProcNumberPageSegmentManager(Recycler* recycler)
174+
:segmentsList(nullptr), recycler(recycler), integratedSegmentCount(0)
175+
{
176+
}
177+
178+
~XProcNumberPageSegmentManager();
179+
180+
void GetFreeSegment(XProcNumberPageSegment& seg);
181+
CodeGenNumberChunk* RegisterSegments(XProcNumberPageSegment* segments);
182+
183+
void Integrate();
184+
};
185+

lib/Backend/Encoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ Encoder::Encode()
312312
{
313313

314314
// TODO: OOP JIT, inlinee frame map
315-
if (false) // in-proc JIT
315+
if (!m_func->IsOOPJIT()) // in-proc JIT
316316
{
317317
entryPointInfo->RecordInlineeFrameMap(m_inlineeFrameMap);
318318
}

lib/Backend/Func.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,15 @@ Func::GetFunctionEntryInsertionPoint()
15051505
return insertInsert->m_next;
15061506
}
15071507

1508+
Js::JavascriptNumber*
1509+
Func::AllocateOOPNumber(double value)
1510+
{
1511+
return this->GetXProcNumberAllocator()->AllocateNumber(this->GetThreadContextInfo()->GetProcessHandle(),
1512+
value,
1513+
(Js::StaticType*)this->GetScriptContextInfo()->GetNumberTypeStaticAddr(),
1514+
(void*)this->GetScriptContextInfo()->GetVTableAddress(VTableValue::VtableJavascriptNumber));
1515+
}
1516+
15081517
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
15091518
void
15101519
Func::DumpFullFunctionName()

lib/Backend/Func.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ class Func
100100
{
101101
return &this->m_codeGenAllocators->emitBufferManager;
102102
}
103+
XProcNumberPageSegmentImpl* GetXProcNumberAllocator()
104+
{
105+
return (XProcNumberPageSegmentImpl*)this->GetJITOutput()->GetOutputData()->numberPageSegments;
106+
}
103107

104108
Js::ScriptContextProfiler *GetCodeGenProfiler() const
105109
{
@@ -110,6 +114,8 @@ class Func
110114
#endif
111115
}
112116

117+
bool IsOOPJIT() { return true; }
118+
113119
void InitLocalClosureSyms();
114120

115121
bool HasAnyStackNestedFunc() const { return this->hasAnyStackNestedFunc; }
@@ -435,6 +441,8 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
435441
return this->isTJLoopBody;
436442
}
437443

444+
Js::JavascriptNumber* AllocateOOPNumber(double value);
445+
438446
Js::ObjTypeSpecFldInfo* GetObjTypeSpecFldInfo(const uint index) const;
439447
Js::ObjTypeSpecFldInfo* GetGlobalObjTypeSpecFldInfo(uint propertyInfoId) const;
440448
void SetGlobalObjTypeSpecFldInfo(uint propertyInfoId, Js::ObjTypeSpecFldInfo* info);

lib/Backend/IR.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3439,7 +3439,7 @@ IR::Instr* IR::Instr::NewConstantLoad(IR::RegOpnd* dstOpnd, intptr_t varConst, J
34393439
{
34403440
// TODO (michhol): OOP JIT. we may need to unbox before sending over const table
34413441

3442-
if (false)
3442+
if (!func->IsOOPJIT())
34433443
{
34443444
srcOpnd = IR::FloatConstOpnd::New((Js::Var)varConst, TyFloat64, func);
34453445
}

lib/Backend/InlineeFrameInfo.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,29 @@ Js::Var BailoutConstantValue::ToVar(Func* func, Js::ScriptContext* scriptContext
4242
}
4343
else if (this->type == TyFloat64)
4444
{
45-
varValue = Js::JavascriptNumber::NewCodeGenInstance(func->GetNumberAllocator(), (double)this->u.floatConst.value, scriptContext);
45+
if (!func->IsOOPJIT()) // in-proc jit
46+
{
47+
varValue = Js::JavascriptNumber::NewCodeGenInstance(func->GetNumberAllocator(), (double)this->u.floatConst.value, scriptContext);
48+
}
49+
else // OOP JIT
50+
{
51+
varValue = func->AllocateOOPNumber((double)this->u.floatConst.value);
52+
}
4653
}
4754
else if (IRType_IsSignedInt(this->type) && TySize[this->type] <= 4 && !Js::TaggedInt::IsOverflow((int32)this->u.intConst.value))
4855
{
4956
varValue = Js::TaggedInt::ToVarUnchecked((int32)this->u.intConst.value);
5057
}
5158
else
52-
{
53-
varValue = Js::JavascriptNumber::NewCodeGenInstance(func->GetNumberAllocator(), (double)this->u.intConst.value, scriptContext);
59+
{
60+
if (!func->IsOOPJIT()) // in-proc jit
61+
{
62+
varValue = Js::JavascriptNumber::NewCodeGenInstance(func->GetNumberAllocator(), (double)this->u.intConst.value, scriptContext);
63+
}
64+
else // OOP JIT
65+
{
66+
varValue = func->AllocateOOPNumber((double)this->u.intConst.value);
67+
}
5468
}
5569
return varValue;
5670

0 commit comments

Comments
 (0)