Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Jit for Generators, Asyncs and Async Generators #6662

Merged
merged 6 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/Backend/Func.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
Expand Down Expand Up @@ -149,7 +150,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
, m_forInEnumeratorArrayOffset(-1)
, argInsCount(0)
, m_globalObjTypeSpecFldInfoArray(nullptr)
, m_forInEnumeratorForGeneratorSym(nullptr)
, m_generatorFrameSym(nullptr)
#if LOWER_SPLIT_INT64
, m_int64SymPairMap(nullptr)
#endif
Expand Down
13 changes: 7 additions & 6 deletions lib/Backend/Func.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
Expand Down Expand Up @@ -1063,21 +1064,21 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
StackSym* m_loopParamSym;
StackSym* m_bailoutReturnValueSym;
StackSym* m_hasBailedOutSym;
StackSym* m_forInEnumeratorForGeneratorSym;
StackSym* m_generatorFrameSym;

public:
StackSym* tempSymDouble;
StackSym* tempSymBool;

void SetForInEnumeratorSymForGeneratorSym(StackSym* sym)
void SetGeneratorFrameSym(StackSym* sym)
{
Assert(this->m_forInEnumeratorForGeneratorSym == nullptr);
this->m_forInEnumeratorForGeneratorSym = sym;
Assert(this->m_generatorFrameSym == nullptr);
this->m_generatorFrameSym = sym;
}

StackSym* GetForInEnumeratorSymForGeneratorSym() const
StackSym* GetGeneratorFrameSym() const
{
return this->m_forInEnumeratorForGeneratorSym;
return this->m_generatorFrameSym;
}

// StackSyms' corresponding getters/setters
Expand Down
76 changes: 30 additions & 46 deletions lib/Backend/IRBuilder.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
Expand Down Expand Up @@ -117,6 +118,11 @@ IRBuilder::DoBailOnNoProfile()
return false;
}

if (m_func->GetTopFunc()->GetJITFunctionBody()->IsCoroutine())
{
return false;
}

return true;
}

Expand Down Expand Up @@ -1255,7 +1261,7 @@ IRBuilder::EnsureLoopBodyForInEnumeratorArrayOpnd()
}

IR::Opnd *
IRBuilder::BuildForInEnumeratorOpnd(uint forInLoopLevel)
IRBuilder::BuildForInEnumeratorOpnd(uint forInLoopLevel, uint32 offset)
{
Assert(forInLoopLevel < this->m_func->GetJITFunctionBody()->GetForInLoopDepth());
if (this->IsLoopBody())
Expand All @@ -1270,7 +1276,7 @@ IRBuilder::BuildForInEnumeratorOpnd(uint forInLoopLevel)
else if (this->m_func->GetJITFunctionBody()->IsCoroutine())
{
return IR::IndirOpnd::New(
this->m_generatorJumpTable.EnsureForInEnumeratorArrayOpnd(),
this->m_generatorJumpTable.BuildForInEnumeratorArrayOpnd(offset),
forInLoopLevel * sizeof(Js::ForInObjectEnumerator),
TyMachPtr,
this->m_func
Expand Down Expand Up @@ -2949,7 +2955,7 @@ IRBuilder::BuildProfiledReg1Unsigned1(Js::OpCode newOpcode, uint32 offset, Js::R
if (newOpcode == Js::OpCode::InitForInEnumerator)
{
IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R0);
IR::Opnd * src2Opnd = this->BuildForInEnumeratorOpnd(C1);
IR::Opnd * src2Opnd = this->BuildForInEnumeratorOpnd(C1, offset);
IR::Instr *instr = IR::ProfiledInstr::New(Js::OpCode::InitForInEnumerator, nullptr, src1Opnd, src2Opnd, m_func);
instr->AsProfiledInstr()->u.profileId = profileId;
this->AddInstr(instr, offset);
Expand Down Expand Up @@ -3084,7 +3090,7 @@ IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint offset, Js::RegSlot R0,
{
IR::Instr *instr = IR::Instr::New(Js::OpCode::InitForInEnumerator, m_func);
instr->SetSrc1(this->BuildSrcOpnd(R0));
instr->SetSrc2(this->BuildForInEnumeratorOpnd(C1));
instr->SetSrc2(this->BuildForInEnumeratorOpnd(C1, offset));
this->AddInstr(instr, offset);
return;
}
Expand Down Expand Up @@ -6935,7 +6941,7 @@ IRBuilder::BuildBrReg1Unsigned1(Js::OpCode newOpcode, uint32 offset)
void
IRBuilder::BuildBrBReturn(Js::OpCode newOpcode, uint32 offset, Js::RegSlot DestRegSlot, uint32 forInLoopLevel, uint32 targetOffset)
{
IR::Opnd *srcOpnd = this->BuildForInEnumeratorOpnd(forInLoopLevel);
IR::Opnd *srcOpnd = this->BuildForInEnumeratorOpnd(forInLoopLevel, offset);
IR::RegOpnd * destOpnd = this->BuildDstOpnd(DestRegSlot);
IR::BranchInstr * branchInstr = IR::BranchInstr::New(newOpcode, destOpnd, nullptr, srcOpnd, m_func);
this->AddBranchInstr(branchInstr, offset, targetOffset);
Expand Down Expand Up @@ -7922,14 +7928,12 @@ IRBuilder::GeneratorJumpTable::BuildJumpTable()
//
// s1 = Ld_A prm1
// s2 = Ld_A s1[offset of JavascriptGenerator::frame]
// BrNotAddr_A s2 !nullptr $initializationCode
// BrNotAddr_A s2 !nullptr $jumpTable
//
// $createInterpreterStackFrame:
// call helper
//
// $initializationCode:
// load for-in enumerator address from interpreter stack frame
//
// Br $startOfFunc
//
// $jumpTable:
//
Expand Down Expand Up @@ -7963,23 +7967,25 @@ IRBuilder::GeneratorJumpTable::BuildJumpTable()
IR::LabelInstr* functionBegin = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
LABELNAMESET(functionBegin, "GeneratorFunctionBegin");

IR::LabelInstr* initCode = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
LABELNAMESET(initCode, "GeneratorInitializationAndJumpTable");
IR::LabelInstr* jumpTable = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
LABELNAMESET(jumpTable, "GeneratorJumpTable");

// BrNotAddr_A s2 nullptr $initializationCode
IR::BranchInstr* skipCreateInterpreterFrame = IR::BranchInstr::New(Js::OpCode::BrNotAddr_A, initCode, genFrameOpnd, IR::AddrOpnd::NewNull(this->m_func), this->m_func);
// If there is already a stack frame, generator function has previously begun execution - don't recreate, skip down to jump table
// BrNotAddr_A s2 nullptr $jumpTable
IR::BranchInstr* skipCreateInterpreterFrame = IR::BranchInstr::New(Js::OpCode::BrNotAddr_A, jumpTable, genFrameOpnd, IR::AddrOpnd::NewNull(this->m_func), this->m_func);
this->m_irBuilder->AddInstr(skipCreateInterpreterFrame, this->m_irBuilder->m_functionStartOffset);

// Create interpreter stack frame
IR::Instr* createInterpreterFrame = IR::Instr::New(Js::OpCode::GeneratorCreateInterpreterStackFrame, genFrameOpnd /* dst */, genRegOpnd /* src */, this->m_func);
this->m_irBuilder->AddInstr(createInterpreterFrame, this->m_irBuilder->m_functionStartOffset);

// Having created the frame, skip over the jump table and start executing from the beginning of the function
IR::BranchInstr* skipJumpTable = IR::BranchInstr::New(Js::OpCode::Br, functionBegin, this->m_func);
this->m_irBuilder->AddInstr(skipJumpTable, this->m_irBuilder->m_functionStartOffset);

// Label to insert any initialization code
// $initializationCode:
this->m_irBuilder->AddInstr(initCode, this->m_irBuilder->m_functionStartOffset);
// Label for start of jumpTable - where we look for the correct Yield resume point
// $jumpTable:
this->m_irBuilder->AddInstr(jumpTable, this->m_irBuilder->m_functionStartOffset);

// s3 = Ld_A s2[offset of InterpreterStackFrame::m_reader.m_currentLocation]
IR::RegOpnd* curLocOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
Expand Down Expand Up @@ -8015,46 +8021,24 @@ IRBuilder::GeneratorJumpTable::BuildJumpTable()

this->m_irBuilder->AddInstr(functionBegin, this->m_irBuilder->m_functionStartOffset);

// Save these values for later use
this->m_initLabel = initCode;
// Save this value for later use
this->m_generatorFrameOpnd = genFrameOpnd;
this->m_func->SetGeneratorFrameSym(genFrameOpnd->GetStackSym());

return this->m_irBuilder->m_lastInstr;
}

IR::LabelInstr*
IRBuilder::GeneratorJumpTable::GetInitLabel() const
{
Assert(this->m_initLabel != nullptr);
return this->m_initLabel;
}

IR::RegOpnd*
IRBuilder::GeneratorJumpTable::CreateForInEnumeratorArrayOpnd()
{
Assert(this->m_initLabel != nullptr);
IRBuilder::GeneratorJumpTable::BuildForInEnumeratorArrayOpnd(uint32 offset)
{
Assert(this->m_generatorFrameOpnd != nullptr);

IR::RegOpnd* forInEnumeratorOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
IR::Instr* instr = IR::Instr::New(
Js::OpCode::Ld_A,
forInEnumeratorOpnd,
IR::RegOpnd* forInEnumeratorArrayOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
IR::Instr* instr = IR::Instr::New(Js::OpCode::Ld_A, forInEnumeratorArrayOpnd,
POINTER_OFFSET(this->m_generatorFrameOpnd, Js::InterpreterStackFrame::GetOffsetOfForInEnumerators(), ForInEnumerators),
this->m_func
);
this->m_initLabel->InsertAfter(instr);

return forInEnumeratorOpnd;
}

IR::RegOpnd*
IRBuilder::GeneratorJumpTable::EnsureForInEnumeratorArrayOpnd()
{
if (this->m_forInEnumeratorArrayOpnd == nullptr)
{
this->m_forInEnumeratorArrayOpnd = this->CreateForInEnumeratorArrayOpnd();
this->m_func->SetForInEnumeratorSymForGeneratorSym(m_forInEnumeratorArrayOpnd->GetStackSym());
}
this->m_irBuilder->AddInstr(instr, offset);

return this->m_forInEnumeratorArrayOpnd;
return forInEnumeratorArrayOpnd;
}
22 changes: 3 additions & 19 deletions lib/Backend/IRBuilder.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
Expand Down Expand Up @@ -227,7 +228,7 @@ class IRBuilder
IR::RegOpnd * BuildSrcOpnd(Js::RegSlot srcRegSlot, IRType type = TyVar);
IR::AddrOpnd * BuildAuxArrayOpnd(AuxArrayValue auxArrayType, uint32 auxArrayOffset);
IR::Opnd * BuildAuxObjectLiteralTypeRefOpnd(int objectId);
IR::Opnd * BuildForInEnumeratorOpnd(uint forInLoopLevel);
IR::Opnd * BuildForInEnumeratorOpnd(uint forInLoopLevel, uint32 offset);
IR::RegOpnd * EnsureLoopBodyForInEnumeratorArrayOpnd();
private:
uint AddStatementBoundary(uint statementIndex, uint offset);
Expand Down Expand Up @@ -366,29 +367,12 @@ class IRBuilder
Func* const m_func;
IRBuilder* const m_irBuilder;

// for-in enumerators are allocated on the heap for jit'd loop body
// and on the stack for all other cases (the interpreter frame will
// reuses them when bailing out). But because we have the concept of
// "bailing in" for generator, reusing enumerators allocated on the stack
// would not work. So we have to allocate them on the generator's interpreter
// frame instead. This operand is loaded as part of the jump table, before we
// jump to any of the resume point.
IR::RegOpnd* m_forInEnumeratorArrayOpnd = nullptr;

IR::RegOpnd* m_generatorFrameOpnd = nullptr;

// This label is used to insert any initialization code that might be needed
// when bailing in and before jumping to any of the resume points.
// As of now, we only need to load the operand for for-in enumerator.
IR::LabelInstr* m_initLabel = nullptr;

IR::RegOpnd* CreateForInEnumeratorArrayOpnd();

public:
GeneratorJumpTable(Func* func, IRBuilder* irBuilder);
IR::Instr* BuildJumpTable();
IR::LabelInstr* GetInitLabel() const;
IR::RegOpnd* EnsureForInEnumeratorArrayOpnd();
IR::RegOpnd* BuildForInEnumeratorArrayOpnd(uint32 offset);
};

GeneratorJumpTable m_generatorJumpTable;
Expand Down
3 changes: 2 additions & 1 deletion lib/Backend/LinearScan.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -5301,7 +5302,7 @@ bool LinearScan::GeneratorBailIn::NeedsReloadingBackendSymWhenBailingIn(StackSym
{
// for-in enumerator in generator is loaded as part of the resume jump table.
// By the same reasoning as `initializedRegs`'s, we don't have to restore this whether or not it's been spilled.
if (this->func->GetForInEnumeratorSymForGeneratorSym() && this->func->GetForInEnumeratorSymForGeneratorSym()->m_id == sym->m_id)
if (this->func->GetGeneratorFrameSym() && this->func->GetGeneratorFrameSym()->m_id == sym->m_id)
{
return false;
}
Expand Down
11 changes: 10 additions & 1 deletion lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,15 @@ PHASE(All)
#define DEFAULT_CONFIG_ESArrayFindFromLast (false)
#define DEFAULT_CONFIG_ESNullishCoalescingOperator (true)
#define DEFAULT_CONFIG_ESGlobalThis (true)

// Jitting generators has not been tested on ARM
// enabled only for x86 and x64 for now
#ifdef _M_ARM32_OR_ARM64
#define DEFAULT_CONFIG_JitES6Generators (false)
#else
#define DEFAULT_CONFIG_JitES6Generators (true)
#endif

#ifdef COMPILE_DISABLE_ES6RegExPrototypeProperties
// If ES6RegExPrototypeProperties needs to be disabled by compile flag, DEFAULT_CONFIG_ES6RegExPrototypeProperties should be false
#define DEFAULT_CONFIG_ES6RegExPrototypeProperties (false)
Expand Down Expand Up @@ -1206,7 +1215,7 @@ FLAGR(Boolean, ESImportMeta, "Enable import.meta keyword", DEFAULT_CONFIG_ESImpo
FLAGR(Boolean, ESGlobalThis, "Enable globalThis", DEFAULT_CONFIG_ESGlobalThis)

// This flag to be removed once JITing generator functions is stable
FLAGNR(Boolean, JitES6Generators , "Enable JITing of ES6 generators", false)
FLAGNR(Boolean, JitES6Generators , "Enable JITing of ES6 generators", DEFAULT_CONFIG_JitES6Generators)

FLAGNR(Boolean, FastLineColumnCalculation, "Enable fast calculation of line/column numbers from the source.", DEFAULT_CONFIG_FastLineColumnCalculation)
FLAGR (String, Filename , "Jscript source file", nullptr)
Expand Down
11 changes: 5 additions & 6 deletions lib/Runtime/Base/FunctionBody.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeBasePch.h"
Expand Down Expand Up @@ -341,13 +342,13 @@ namespace Js
bool
FunctionBody::SkipAutoProfileForCoroutine() const
{
return this->IsCoroutine() && CONFIG_ISENABLED(Js::JitES6GeneratorsFlag);
return this->IsCoroutine() && CONFIG_FLAG(JitES6Generators);
}

bool
FunctionBody::IsGeneratorAndJitIsDisabled() const
{
return this->IsCoroutine() && !(CONFIG_ISENABLED(Js::JitES6GeneratorsFlag) && !this->GetHasTry() && !this->IsInDebugMode() && !this->IsAsync());
return this->IsCoroutine() && !(CONFIG_FLAG(JitES6Generators) && !this->GetHasTry() && !this->IsInDebugMode() && !this->IsModule());
}

ScriptContext* EntryPointInfo::GetScriptContext()
Expand Down Expand Up @@ -7151,8 +7152,7 @@ namespace Js
!GetScriptContext()->IsScriptContextInDebugMode() &&
DoInterpreterProfile() &&
#pragma warning(suppress: 6235) // (<non-zero constant> || <expression>) is always a non-zero constant.
(!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile()) &&
!IsCoroutine(); // Generator JIT requires bailout which SimpleJit cannot do since it skips GlobOpt
(!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile());
}

bool FunctionBody::DoSimpleJitWithLock() const
Expand All @@ -7166,8 +7166,7 @@ namespace Js
!this->IsInDebugMode() &&
DoInterpreterProfileWithLock() &&
#pragma warning(suppress: 6235) // (<non-zero constant> || <expression>) is always a non-zero constant.
(!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile()) &&
!IsCoroutine(); // Generator JIT requires bailout which SimpleJit cannot do since it skips GlobOpt
(!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile());
}

bool FunctionBody::DoSimpleJitDynamicProfile() const
Expand Down
5 changes: 4 additions & 1 deletion lib/Runtime/Base/FunctionInfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
namespace Js
Expand Down Expand Up @@ -42,7 +43,8 @@ namespace Js
Method = 0x400000, // The function is a method
ComputedName = 0x800000,
ActiveScript = 0x1000000,
HomeObj = 0x2000000
HomeObj = 0x2000000,
GeneratorWithComplexParams = 0x8000000 // Generator function with non-simple params needs startup yield
};
FunctionInfo(JavascriptMethod entryPoint, Attributes attributes = None, LocalFunctionId functionId = Js::Constants::NoFunctionId, FunctionProxy* functionBodyImpl = nullptr);
FunctionInfo(JavascriptMethod entryPoint, _no_write_barrier_tag, Attributes attributes = None, LocalFunctionId functionId = Js::Constants::NoFunctionId, FunctionProxy* functionBodyImpl = nullptr);
Expand Down Expand Up @@ -136,6 +138,7 @@ namespace Js
bool GetBaseConstructorKind() const { return (attributes & Attributes::BaseConstructorKind) != 0; }
bool IsActiveScript() const { return ((this->attributes & Attributes::ActiveScript) != 0); }
void SetIsActiveScript() { attributes = (Attributes)(attributes | Attributes::ActiveScript); }
bool GetGeneratorWithComplexParams() {return (attributes & Attributes::GeneratorWithComplexParams) != 0; }
protected:
FieldNoBarrier(JavascriptMethod) originalEntryPoint;
FieldWithBarrier(FunctionProxy *) functionBodyImpl; // Implementation of the function- null if the function doesn't have a body
Expand Down
Loading