Skip to content

Commit 615a614

Browse files
authored
JIT Generator (and Async func) loops (chakra-core#6990)
* Disable JitES6Generators * Jit Generator and async func LoopBodies * Update Tests cases for Generator Jit
1 parent 4b0b554 commit 615a614

26 files changed

+548
-68
lines changed

lib/Backend/IRBuilder.cpp

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66
#include "Backend.h"
@@ -118,7 +118,7 @@ IRBuilder::DoBailOnNoProfile()
118118
return false;
119119
}
120120

121-
if (m_func->GetTopFunc()->GetJITFunctionBody()->IsCoroutine())
121+
if (m_func->GetTopFunc()->GetJITFunctionBody()->IsCoroutine() && !m_func->IsLoopBody())
122122
{
123123
return false;
124124
}
@@ -441,7 +441,7 @@ IRBuilder::Build()
441441
// Note that for generators, we insert the bailout after the jump table to allow
442442
// the generator's execution to proceed before bailing out. Otherwise, we would always
443443
// bail to the beginning of the function in the interpreter, creating an infinite loop.
444-
if (m_func->IsJitInDebugMode() && !this->m_func->GetJITFunctionBody()->IsCoroutine())
444+
if (m_func->IsJitInDebugMode() && (!this->m_func->GetJITFunctionBody()->IsCoroutine() || this->IsLoopBody()))
445445
{
446446
this->InsertBailOutForDebugger(m_functionStartOffset, IR::BailOutForceByFlag | IR::BailOutBreakPointInFunction | IR::BailOutStep, nullptr);
447447
}
@@ -1880,6 +1880,9 @@ IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::Re
18801880
break;
18811881

18821882
case Js::OpCode::Yield:
1883+
// Jitting Loop Bodies containing Yield is not possible, blocked at callsites of GenerateLoopBody
1884+
AssertMsg(!this->IsLoopBody(), "Attempting to JIT loop body containing Yield");
1885+
18831886
instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
18841887
this->AddInstr(instr, offset);
18851888
IR::Instr* yieldInstr = instr->ConvertToBailOutInstr(instr, IR::BailOutForGeneratorYield);
@@ -7849,6 +7852,7 @@ IRBuilder::GeneratorJumpTable::GeneratorJumpTable(Func* func, IRBuilder* irBuild
78497852
IR::Instr*
78507853
IRBuilder::GeneratorJumpTable::BuildJumpTable()
78517854
{
7855+
AssertMsg(!this->m_func->IsLoopBody(), "Coroutine Loop Bodies can be jitted but should follow a different path");
78527856
if (!this->m_func->GetJITFunctionBody()->IsCoroutine())
78537857
{
78547858
return this->m_irBuilder->m_lastInstr;

lib/Backend/Lower.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -5467,7 +5467,7 @@ Lowerer::LowerPrologEpilog()
54675467
instr = m_func->m_exitInstr;
54685468
AssertMsg(instr->IsExitInstr(), "Last instr isn't an ExitInstr...");
54695469

5470-
if (m_func->GetJITFunctionBody()->IsCoroutine())
5470+
if (m_func->GetJITFunctionBody()->IsCoroutine() && !m_func->IsLoopBody())
54715471
{
54725472
IR::LabelInstr* epilogueLabel = this->m_lowerGeneratorHelper.GetEpilogueForReturnStatements();
54735473
this->m_lowerGeneratorHelper.InsertNullOutGeneratorFrameInEpilogue(epilogueLabel);
@@ -11527,6 +11527,7 @@ Lowerer::LowerArgIn(IR::Instr *instrArgIn)
1152711527

1152811528
if (m_func->GetJITFunctionBody()->IsCoroutine())
1152911529
{
11530+
AssertMsg(!m_func->IsLoopBody(), "LoopBody Jit should not involve Rest params");
1153011531
generatorArgsPtrOpnd = LoadGeneratorArgsPtr(instrArgIn);
1153111532
}
1153211533

@@ -11544,7 +11545,7 @@ Lowerer::LowerArgIn(IR::Instr *instrArgIn)
1154411545
if (argIndex == 1)
1154511546
{
1154611547
// The "this" argument is not source-dependent and doesn't need to be checked.
11547-
if (m_func->GetJITFunctionBody()->IsCoroutine())
11548+
if (m_func->GetJITFunctionBody()->IsCoroutine() && !m_func->IsLoopBody())
1154811549
{
1154911550
generatorArgsPtrOpnd = LoadGeneratorArgsPtr(instrArgIn);
1155011551
ConvertArgOpndIfGeneratorFunction(instrArgIn, generatorArgsPtrOpnd);

lib/Backend/NativeCodeGenerator.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
34
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
45
//-------------------------------------------------------------------------------------------------------
56
#include "Backend.h"
@@ -246,8 +247,11 @@ NativeCodeGenerator::GenerateAllFunctions(Js::FunctionBody * fn)
246247
for (uint i = 0; i < fn->GetLoopCount(); i++)
247248
{
248249
Js::LoopHeader * loopHeader = fn->GetLoopHeader(i);
249-
Js::EntryPointInfo * entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
250-
this->GenerateLoopBody(fn, loopHeader, entryPointInfo);
250+
if (loopHeader->hasYield == false)
251+
{
252+
Js::EntryPointInfo * entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
253+
this->GenerateLoopBody(fn, loopHeader, entryPointInfo);
254+
}
251255
}
252256
}
253257
else
@@ -631,6 +635,7 @@ void NativeCodeGenerator::GenerateLoopBody(Js::FunctionBody * fn, Js::LoopHeader
631635
ASSERT_THREAD();
632636
Assert(fn->GetScriptContext()->GetNativeCodeGenerator() == this);
633637
Assert(entryPoint->jsMethod == nullptr);
638+
Assert(!loopHeader->hasYield);
634639

635640
#if DBG_DUMP
636641
if (PHASE_TRACE1(Js::JITLoopBodyPhase))

lib/Common/ConfigFlagsList.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -675,12 +675,12 @@ PHASE(All)
675675
#define DEFAULT_CONFIG_ESNullishCoalescingOperator (true)
676676
#define DEFAULT_CONFIG_ESGlobalThis (true)
677677

678-
// Jitting generators has not been tested on ARM
679-
// enabled only for x86 and x64 for now
678+
// Jitting generator functions is not functional on ARM
679+
// Also still contains significant bugs on x86/x64 hence disabled
680680
#ifdef _M_ARM32_OR_ARM64
681681
#define DEFAULT_CONFIG_JitES6Generators (false)
682682
#else
683-
#define DEFAULT_CONFIG_JitES6Generators (true)
683+
#define DEFAULT_CONFIG_JitES6Generators (false)
684684
#endif
685685

686686
#ifdef COMPILE_DISABLE_ES6RegExPrototypeProperties

lib/Runtime/Base/FunctionBody.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
34
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
45
//-------------------------------------------------------------------------------------------------------
56
#pragma once
@@ -725,6 +726,7 @@ namespace Js
725726
#if ENABLE_NATIVE_CODEGEN
726727
Field(uint) rejitCount;
727728
#endif
729+
Field(bool) hasYield;
728730
Field(bool) isNested;
729731
Field(bool) isInTry;
730732
Field(bool) isInTryFinally;
@@ -1153,8 +1155,9 @@ namespace Js
11531155

11541156
bool IsJitLoopBodyPhaseEnabled() const
11551157
{
1156-
// Consider: Allow JitLoopBody in generator functions for loops that do not yield.
1157-
return !PHASE_OFF(JITLoopBodyPhase, this) && !PHASE_OFF(FullJitPhase, this) && !this->IsCoroutine();
1158+
return !PHASE_OFF(JITLoopBodyPhase, this) && !PHASE_OFF(FullJitPhase, this) &&
1159+
(!this->IsCoroutine() || !CONFIG_FLAG(JitES6Generators) || this->IsModule());
1160+
// Jitting loop bodies is currently disabled when testing the Jitting of whole generator functions
11581161
}
11591162

11601163
bool IsJitLoopBodyPhaseForced() const

lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
34
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
45
//-------------------------------------------------------------------------------------------------------
56

@@ -632,7 +633,7 @@ namespace Js
632633
uint loopId = m_functionWrite->IncrLoopCount();
633634
Assert((uint)m_loopHeaders->Count() == loopId);
634635

635-
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0));
636+
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0, false));
636637
m_loopNest++;
637638
Js::OpCodeAsmJs loopBodyOpcode = Js::OpCodeAsmJs::AsmJsLoopBodyStart;
638639
this->MarkAsmJsLabel(loopEntrance);

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66
#include "RuntimeByteCodePch.h"
@@ -10477,6 +10477,9 @@ void EmitYieldAndResume(
1047710477

1047810478
auto* writer = byteCodeGenerator->Writer();
1047910479

10480+
// If in a loop mark it as containing Yield and hence not eligible for Jit loop body
10481+
writer->SetCurrentLoopHasYield();
10482+
1048010483
if (inputReg != funcInfo->yieldRegister)
1048110484
writer->Reg2(Js::OpCode::Ld_A, funcInfo->yieldRegister, inputReg);
1048210485

lib/Runtime/ByteCode/ByteCodeWriter.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66
#include "RuntimeByteCodePch.h"
@@ -210,7 +210,7 @@ namespace Js
210210
}
211211
}
212212

213-
if (this->DoJitLoopBodies() &&
213+
if (this->DoJitLoopBodies() && this->HasLoopWithoutYield() &&
214214
!(this->m_functionWrite->GetFunctionBody()->GetHasTry() && PHASE_OFF(Js::JITLoopBodyInTryCatchPhase, this->m_functionWrite)) &&
215215
!(this->m_functionWrite->GetFunctionBody()->GetHasFinally() && PHASE_OFF(Js::JITLoopBodyInTryFinallyPhase, this->m_functionWrite)))
216216
{
@@ -252,6 +252,7 @@ namespace Js
252252
loopHeader->startOffset = data.startOffset;
253253
loopHeader->endOffset = data.endOffset;
254254
loopHeader->isNested = data.isNested;
255+
loopHeader->hasYield = data.hasYield;
255256
});
256257
}
257258

@@ -3224,7 +3225,7 @@ namespace Js
32243225
uint loopId = m_functionWrite->IncrLoopCount();
32253226
Assert((uint)m_loopHeaders->Count() == loopId);
32263227

3227-
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0));
3228+
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0, false));
32283229
m_loopNest++;
32293230
m_functionWrite->SetHasNestedLoop(m_loopNest > 1);
32303231

@@ -3259,6 +3260,20 @@ namespace Js
32593260
m_loopHeaders->Item(loopId).endOffset = m_byteCodeData.GetCurrentOffset();
32603261
}
32613262

3263+
void ByteCodeWriter::SetCurrentLoopHasYield()
3264+
{
3265+
if (m_loopNest > 0)
3266+
{
3267+
for (int i = 0; i < m_loopHeaders->Count(); ++i)
3268+
{
3269+
if (m_loopHeaders->Item(i).endOffset == 0) // check for loops we're currently inside
3270+
{
3271+
m_loopHeaders->Item(i).hasYield = true;
3272+
}
3273+
}
3274+
}
3275+
}
3276+
32623277
void ByteCodeWriter::IncreaseByteCodeCount()
32633278
{
32643279
m_byteCodeCount++;

lib/Runtime/ByteCode/ByteCodeWriter.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66
#pragma once
@@ -114,8 +114,9 @@ namespace Js
114114
uint startOffset;
115115
uint endOffset;
116116
bool isNested;
117+
bool hasYield;
117118
LoopHeaderData() {}
118-
LoopHeaderData(uint startOffset, uint endOffset, bool isNested) : startOffset(startOffset), endOffset(endOffset), isNested(isNested){}
119+
LoopHeaderData(uint startOffset, uint endOffset, bool isNested, bool hasYield) : startOffset(startOffset), endOffset(endOffset), isNested(isNested), hasYield(hasYield){}
119120
};
120121

121122
JsUtil::List<uint, ArenaAllocator> * m_labelOffsets; // Label offsets, once defined
@@ -384,6 +385,18 @@ namespace Js
384385

385386
uint EnterLoop(Js::ByteCodeLabel loopEntrance);
386387
void ExitLoop(uint loopId);
388+
void SetCurrentLoopHasYield();
389+
bool HasLoopWithoutYield()
390+
{
391+
for (int i = 0; i < m_loopHeaders->Count(); ++i)
392+
{
393+
if(!m_loopHeaders->Item(i).hasYield)
394+
{
395+
return true;
396+
}
397+
}
398+
return false;
399+
}
387400

388401
bool DoJitLoopBodies() const { return m_doJitLoopBodies; }
389402
bool DoInterruptProbes() const { return m_doInterruptProbe; }

lib/Runtime/ByteCode/WasmByteCodeWriter.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
34
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
45
//-------------------------------------------------------------------------------------------------------
56
#include "RuntimeByteCodePch.h"
@@ -45,7 +46,7 @@ uint32 WasmByteCodeWriter::WasmLoopStart(ByteCodeLabel loopEntrance, __in_ecount
4546
uint loopId = m_functionWrite->IncrLoopCount();
4647
Assert((uint)m_loopHeaders->Count() == loopId);
4748

48-
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0));
49+
m_loopHeaders->Add(LoopHeaderData(m_byteCodeData.GetCurrentOffset(), 0, m_loopNest > 0, false));
4950
m_loopNest++;
5051
this->MarkAsmJsLabel(loopEntrance);
5152
MULTISIZE_LAYOUT_WRITE(WasmLoopStart, Js::OpCodeAsmJs::WasmLoopBodyStart, loopId, curRegs);

lib/Runtime/Language/InterpreterStackFrame.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ namespace Js
12201220
!(this->executeFunction->GetHasTry() && (PHASE_OFF((Js::JITLoopBodyInTryCatchPhase), this->executeFunction))) &&
12211221
!(this->executeFunction->GetHasFinally() && (PHASE_OFF((Js::JITLoopBodyInTryFinallyPhase), this->executeFunction))) &&
12221222
(this->executeFunction->ForceJITLoopBody() || this->executeFunction->IsJitLoopBodyPhaseEnabled()) &&
1223-
!this->executeFunction->IsInDebugMode();
1223+
!this->executeFunction->IsInDebugMode() && this->executeFunction->GetLoopHeaderArray() != nullptr;
12241224
#endif
12251225

12261226
// Pick a version of the LoopBodyStart OpCode handlers that is hardcoded to do loop body JIT and
@@ -6057,7 +6057,7 @@ namespace Js
60576057

60586058
Js::LoopEntryPointInfo * entryPointInfo = loopHeader->GetCurrentEntryPointInfo();
60596059

6060-
if (fn->ForceJITLoopBody() && loopHeader->interpretCount == 0 &&
6060+
if (fn->ForceJITLoopBody() && loopHeader->interpretCount == 0 && loopHeader->hasYield == false &&
60616061
(entryPointInfo != NULL && entryPointInfo->IsNotScheduled()))
60626062
{
60636063
#if ENABLE_PROFILE_INFO
@@ -6250,7 +6250,7 @@ namespace Js
62506250
return nullptr;
62516251
}
62526252

6253-
if (!fn->DoJITLoopBody())
6253+
if (!fn->DoJITLoopBody() || loopHeader->hasYield)
62546254
{
62556255
return nullptr;
62566256
}

test/es6/rlexe.xml

-43
Original file line numberDiff line numberDiff line change
@@ -132,48 +132,6 @@
132132
<tags>exclude_dynapogo</tags>
133133
</default>
134134
</test>
135-
<test>
136-
<default>
137-
<files>generator-jit-bugs.js</files>
138-
<compile-flags>-JitES6Generators -args summary -endargs</compile-flags>
139-
<tags>exclude_nonative</tags>
140-
</default>
141-
</test>
142-
<test>
143-
<default>
144-
<files>generator-jit-bugs.js</files>
145-
<compile-flags>-JitES6Generators -off:simplejit -args summary -endargs</compile-flags>
146-
<tags>exclude_nonative</tags>
147-
</default>
148-
</test>
149-
<test>
150-
<default>
151-
<files>generator-jit-bugs.js</files>
152-
<compile-flags>-JitES6Generators -off:fulljit -args summary -endargs</compile-flags>
153-
<tags>exclude_nonative, exclude_dynapogo</tags>
154-
</default>
155-
</test>
156-
<test>
157-
<default>
158-
<files>async-jit-bugs.js</files>
159-
<compile-flags>-JitES6Generators -args summary -endargs</compile-flags>
160-
<tags>exclude_nonative</tags>
161-
</default>
162-
</test>
163-
<test>
164-
<default>
165-
<files>async-jit-bugs.js</files>
166-
<compile-flags>-JitES6Generators -off:simplejit -args summary -endargs</compile-flags>
167-
<tags>exclude_nonative</tags>
168-
</default>
169-
</test>
170-
<test>
171-
<default>
172-
<files>async-jit-bugs.js</files>
173-
<compile-flags>-JitES6Generators -off:fulljit -args summary -endargs</compile-flags>
174-
<tags>exclude_nonative, exclude_dynapogo</tags>
175-
</default>
176-
</test>
177135
<test>
178136
<default>
179137
<files>proto_basic.js</files>
@@ -913,7 +871,6 @@
913871
<test>
914872
<default>
915873
<files>generators-functionality.js</files>
916-
<!-- <compile-flags>-ES6Generators -JitES6Generators -args summary -endargs</compile-flags> -->
917874
<compile-flags>-ES6Generators -args summary -endargs</compile-flags>
918875
<tags>exclude_arm</tags>
919876
</default>

test/es6/async-jit-bugs.js renamed to test/es6GeneratorJit/async-jit-bugs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66

test/es6/generator-jit-bugs.js renamed to test/es6GeneratorJit/generator-jit-bugs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3-
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66

0 commit comments

Comments
 (0)