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

Generator misc #6533

Merged
merged 6 commits into from
Jan 9, 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
2 changes: 0 additions & 2 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2957,9 +2957,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa

case Js::OpCode::NewAwaitObject:
{
IR::Opnd *src1Opnd = instr->UnlinkSrc1();
LoadScriptContext(instr);
m_lowererMD.LoadHelperArgument(instr, src1Opnd);
m_lowererMD.ChangeToHelperCall(instr, IR::HelperNewAwaitObject);
break;
}
Expand Down
1 change: 0 additions & 1 deletion lib/Backend/amd64/LinearScanMD.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class LinearScanMD : public LinearScanMDShared
void GenerateBailOut(IR::Instr * instr,
__in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms,
uint registerSaveSymsCount);
IR::Instr* GenerateBailInForGeneratorYield(IR::Instr* resumeLabelInstr, BailOutInfo* bailOutInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did this method do before?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a draft Jit implementation for resuming a generator function. Whilst jitting generators is still not enabled the current implementation no longer uses this code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can some of it be reused in the future, when we enable that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Generator Jit implementation whilst still not enabled was re-written almost completely by @nhat-nguyen and further edited pretty significantly by @zenparsing - the current version works except when it hits a bailout in the wrong place which is a bug we need to solve before enabling it.

This bit of code is left over from the first implementation prior to the work the two of them did - it's not compatible with what they've done and is just left as noise in the source tree.


private:
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);
Expand Down
6 changes: 0 additions & 6 deletions lib/Backend/arm/LinearScanMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,6 @@ LinearScanMD::GenerateBailOut(
instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func));
}

IR::Instr *
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
{
Js::Throw::NotImplemented();
}

uint LinearScanMD::GetRegisterSaveIndex(RegNum reg)
{
if (RegTypes[reg] == TyFloat64)
Expand Down
1 change: 0 additions & 1 deletion lib/Backend/arm/LinearScanMD.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ class LinearScanMD : public LinearScanMDShared
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ }

void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
private:
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);
public:
Expand Down
6 changes: 0 additions & 6 deletions lib/Backend/arm64/LinearScanMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,6 @@ LinearScanMD::GenerateBailOut(
instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func));
}

IR::Instr *
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
{
Js::Throw::NotImplemented();
}

uint LinearScanMD::GetRegisterSaveIndex(RegNum reg)
{
return reg;
Expand Down
1 change: 0 additions & 1 deletion lib/Backend/arm64/LinearScanMD.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class LinearScanMD : public LinearScanMDShared
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ }

void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
private:
static void SaveAllRegisters(BailOutRecord *const bailOutRecord);
public:
Expand Down
181 changes: 0 additions & 181 deletions lib/Backend/i386/LinearScanMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,187 +180,6 @@ LinearScanMD::GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCou
}
}

IR::Instr *
LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo)
{
BailOutRecord * bailOutRecord = bailOutInfo->bailOutRecord;
IR::Instr * instrAfter = resumeLabelInstr->m_next;
IR::Instr * newInstr;

// if (argOuts) {
// sub esp, numArgOutsTimesMachSizeMachAligned
// }
// mov eax, prm1
// mov eax, [eax + offset of JavascriptGenerator::frame]
// <restore live stack syms, out params, and registers directly from InterpreterStackFrame registers (use ecx as temporary for mov IndirOpnd, IndirOpnd before restoring registers)>

IntConstType stackAdjustSize = 0;
bailOutRecord->MapStartCallParamCounts([&stackAdjustSize](uint startCallParamCount) {
IntConstType sizeValue = startCallParamCount;
int32 stackAlignment = Math::Align<int32>(sizeValue*MachPtr, MachStackAlignment) - sizeValue*MachPtr;
if (stackAlignment != 0)
{
sizeValue += 1;
}
sizeValue *= MachPtr;
stackAdjustSize += sizeValue;
});

if (stackAdjustSize != 0)
{
if ((uint32)stackAdjustSize > AutoSystemInfo::PageSize)
{
// mov eax, sizeOpnd->m_value
// call _chkstk
IR::RegOpnd *eaxOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachReg, this->func);
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxOpnd, IR::IntConstOpnd::New(stackAdjustSize, TyInt32, this->func), this->func);
instrAfter->InsertBefore(newInstr);

newInstr = IR::Instr::New(Js::OpCode::CALL, this->func);
newInstr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperCRT_chkstk, this->func));
instrAfter->InsertBefore(newInstr);
this->func->SetHasCallsOnSelfAndParents();
}
else
{
// lea esp, [esp - sizeValue]
IR::RegOpnd * espOpnd = IR::RegOpnd::New(nullptr, LowererMD::GetRegStackPointer(), TyMachReg, this->func);
newInstr = IR::Instr::New(Js::OpCode::LEA, espOpnd, IR::IndirOpnd::New(espOpnd, -stackAdjustSize, TyMachReg, this->func), this->func);
instrAfter->InsertBefore(newInstr);
}
}

IR::RegOpnd * eaxRegOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachPtr, this->func);
IR::RegOpnd * ecxRegOpnd = IR::RegOpnd::New(nullptr, RegECX, TyVar, this->func);

StackSym * sym = StackSym::NewParamSlotSym(1, this->func);
this->func->SetArgOffset(sym, LowererMD::GetFormalParamOffset() * MachPtr);
IR::SymOpnd * symOpnd = IR::SymOpnd::New(sym, TyMachPtr, this->func);
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, symOpnd, this->func);
instrAfter->InsertBefore(newInstr);

IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, Js::JavascriptGenerator::GetFrameOffset(), TyMachPtr, this->func);
newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, indirOpnd, this->func);
instrAfter->InsertBefore(newInstr);


// eax points to the frame, restore stack syms and registers except eax, restore eax last

IR::Instr * eaxRestoreInstr = nullptr;
IR::Instr * instrInsertStackSym = instrAfter;
IR::Instr * instrInsertRegSym = instrAfter;

Assert(bailOutInfo->capturedValues->constantValues.Empty());
Assert(bailOutInfo->capturedValues->copyPropSyms.Empty());

auto restoreSymFn = [this, &eaxRegOpnd, &ecxRegOpnd, &eaxRestoreInstr, &instrInsertStackSym, &instrInsertRegSym](SymID symId)
{
StackSym * stackSym = this->func->m_symTable->FindStackSym(symId);
Assert(stackSym->IsVar());

Assert(stackSym->HasByteCodeRegSlot());
Js::RegSlot byteCodeReg = stackSym->GetByteCodeRegSlot();
int32 offset = byteCodeReg * sizeof(Js::Var) + Js::InterpreterStackFrame::GetOffsetOfLocals();

IR::Opnd * srcOpnd = IR::IndirOpnd::New(eaxRegOpnd, offset, stackSym->GetType(), this->func);
Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;

if (lifetime->isSpilled)
{
// stack restores require an extra register since we can't move an indir directly to an indir on x86
IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, srcOpnd, this->func);
instrInsertStackSym->InsertBefore(instr);

IR::SymOpnd * dstOpnd = IR::SymOpnd::New(stackSym, stackSym->GetType(), this->func);
instr = IR::Instr::New(Js::OpCode::MOV, dstOpnd, ecxRegOpnd, this->func);
instrInsertStackSym->InsertBefore(instr);
}
else
{
// register restores must come after stack restores so that we have EAX and ECX free to
// use for stack restores and further EAX must be restored last since it holds the
// pointer to the InterpreterStackFrame from which we are restoring values.
// We must also track these restores using RecordDef in case the symbols are spilled.

IR::RegOpnd * dstRegOpnd = IR::RegOpnd::New(stackSym, stackSym->GetType(), this->func);
dstRegOpnd->SetReg(lifetime->reg);

IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, dstRegOpnd, srcOpnd, this->func);
instrInsertRegSym->InsertBefore(instr);

if (instrInsertRegSym == instrInsertStackSym)
{
// this is the first register sym, make sure we don't insert stack stores
// after this instruction so we can ensure eax and ecx remain free to use
// for restoring spilled stack syms.
instrInsertStackSym = instr;
}

if (lifetime->reg == RegEAX)
{
// ensure eax is restored last
Assert(instrInsertRegSym != instrInsertStackSym);

instrInsertRegSym = instr;

if (eaxRestoreInstr != nullptr)
{
AssertMsg(false, "this is unexpected until copy prop is enabled");
// eax was mapped to multiple bytecode registers. Obviously only the first
// restore we do will work so change all following stores to `mov eax, eax`.
// We still need to keep them around for RecordDef in case the corresponding
// dst sym is spilled later on.
eaxRestoreInstr->FreeSrc1();
eaxRestoreInstr->SetSrc1(eaxRegOpnd);
}

eaxRestoreInstr = instr;
}

this->linearScan->RecordDef(lifetime, instr, 0);
}
};

FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->byteCodeUpwardExposedUsed)
{
restoreSymFn(symId);
}
NEXT_BITSET_IN_SPARSEBV;

if (bailOutInfo->capturedValues->argObjSyms)
{
FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->capturedValues->argObjSyms)
{
restoreSymFn(symId);
}
NEXT_BITSET_IN_SPARSEBV;
}

// Restore out params.
// Would be nice to use restoreSymFn on a walk of the SymIds where the walk matches
// the logic in LinearScan::FillBailOutRecord, but the walk is very complicated and
// requires state to enumerate the exact syms that are actually mapped in the bail
// out record. So instead, since we have disabled most of GlobOpt for the time
// being, just enumerate the argouts from the BailOutRecord and ignore the syms.
// This may need to be improved to use the syms when the optimizations are brought
// online.
bailOutRecord->MapArgOutOffsets([this, &eaxRegOpnd, &ecxRegOpnd, &instrInsertStackSym](Js::RegSlot regSlot, int32 stackOffset) {
// mov ecx, [eax + bytecode reg offset]
// mov [ebp + native stack offset], ecx
int32 regSlotOffset = Js::InterpreterStackFrame::GetOffsetOfLocals() + (this->func->GetJITFunctionBody()->GetLocalsCount() + regSlot) * sizeof(Js::Var);
IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, regSlotOffset, TyVar, this->func);
IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, indirOpnd, this->func);
instrInsertStackSym->InsertBefore(instr);

IR::RegOpnd* ebpRegOpnd = IR::RegOpnd::New(nullptr, RegEBP, TyMachPtr, this->func);
indirOpnd = IR::IndirOpnd::New(ebpRegOpnd, stackOffset, TyVar, this->func);
instr = IR::Instr::New(Js::OpCode::RestoreOutParam, indirOpnd, ecxRegOpnd, this->func);
instrInsertStackSym->InsertBefore(instr);
});

return instrAfter;
}

__declspec(naked) void LinearScanMD::SaveAllRegisters(BailOutRecord *const bailOutRecord)
{
__asm
Expand Down
1 change: 0 additions & 1 deletion lib/Backend/i386/LinearScanMD.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class LinearScanMD : public LinearScanMDShared
void LegalizeUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ }
void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ }
void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount);
IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo);
void InsertOpHelperSpillAndRestores(SList<OpHelperBlock> *opHelperBlockList);
void EndOfHelperBlock(uint32 helperSpilledLiveranges) { /* NOP */ }

Expand Down
22 changes: 16 additions & 6 deletions lib/Runtime/ByteCode/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,10 @@ void ByteCodeGenerator::FinalizeRegisters(FuncInfo* funcInfo, Js::FunctionBody*
// NOTE: The FB expects the yield reg to be the final non-temp.
if (byteCodeFunction->IsCoroutine())
{
if (funcInfo->root->IsAsync())
{
funcInfo->AssignAwaitRegister();
}
funcInfo->AssignYieldRegister();
}

Expand Down Expand Up @@ -2903,6 +2907,11 @@ void ByteCodeGenerator::EmitOneFunction(ParseNodeFnc *pnodeFnc)
LoadAllConstants(funcInfo);
HomeArguments(funcInfo);

if (funcInfo->root->IsAsync())
{
Writer()->Reg1(Js::OpCode::NewAwaitObject, funcInfo->awaitRegister);
}

if (!funcInfo->IsBodyAndParamScopeMerged())
{
byteCodeFunction->SetParamAndBodyScopeNotMerged();
Expand Down Expand Up @@ -10547,17 +10556,18 @@ void EmitAwait(
ByteCodeGenerator* byteCodeGenerator,
FuncInfo* funcInfo)
{
// OPTIMIZE: We should only have to allocate this object once before any awaits.
// Awaiting can merely set the value property of that object.

auto* writer = byteCodeGenerator->Writer();
writer->Reg2(Js::OpCode::NewAwaitObject, funcInfo->yieldRegister, inputReg);

auto writer = byteCodeGenerator->Writer();
writer->PatchableProperty(
Js::OpCode::StFld,
inputReg,
funcInfo->awaitRegister,
funcInfo->FindOrAddInlineCacheId(funcInfo->awaitRegister, Js::PropertyIds::value, false, true));
Js::ByteCodeLabel resumeNormal = writer->DefineLabel();

EmitYieldAndResume(
resultReg,
funcInfo->yieldRegister,
funcInfo->awaitRegister,
resumeNormal,
Js::Constants::NoByteCodeLabel,
byteCodeGenerator,
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/ByteCode/FuncInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ FuncInfo::FuncInfo(
frameDisplayRegister(Js::Constants::NoRegister),
funcObjRegister(Js::Constants::NoRegister),
localClosureReg(Js::Constants::NoRegister),
awaitRegister(Js::Constants::NoRegister),
yieldRegister(Js::Constants::NoRegister),
firstTmpReg(Js::Constants::NoRegister),
curTmpReg(Js::Constants::NoRegister),
Expand Down
8 changes: 8 additions & 0 deletions lib/Runtime/ByteCode/FuncInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class FuncInfo
Js::RegSlot frameDisplayRegister; // location, if any, of the display of nested frames
Js::RegSlot funcObjRegister;
Js::RegSlot localClosureReg;
Js::RegSlot awaitRegister;
Js::RegSlot yieldRegister;
Js::RegSlot firstTmpReg;
Js::RegSlot curTmpReg;
Expand Down Expand Up @@ -568,6 +569,13 @@ class FuncInfo
return this->falseConstantRegister;
}

Js::RegSlot AssignAwaitRegister()
{
AssertMsg(this->awaitRegister == Js::Constants::NoRegister, "await register should only be assigned once by FinalizeRegisters()");
this->awaitRegister = NextVarRegister();
return this->awaitRegister;
}

Js::RegSlot AssignYieldRegister()
{
AssertMsg(this->yieldRegister == Js::Constants::NoRegister, "yield register should only be assigned once by FinalizeRegisters()");
Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/ByteCode/OpCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ MACRO_EXTEND_WMS( NewScGenFuncHomeObj, ElementSlot, OpSideEffec
MACRO_EXTEND_WMS( NewInnerScFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new ScriptFunction instance that has home object
MACRO_EXTEND_WMS( NewInnerScGenFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new JavascriptGeneratorFunction instance that has home object
MACRO_EXTEND_WMS( NewAsyncFromSyncIterator, Reg2, OpSideEffect) // Create new JavascriptAsyncFromSyncOperator instance
MACRO_EXTEND_WMS( NewAwaitObject, Reg2, OpSideEffect) // Create new internal await object instance
MACRO_EXTEND_WMS( NewAwaitObject, Reg1, OpSideEffect) // Create new internal await object instance
MACRO_BACKEND_ONLY( NewScopeObject, Reg1, None) // Create new NewScopeObject
MACRO_BACKEND_ONLY( InitCachedScope, Reg2Aux, None) // Retrieve cached scope; create if not cached
MACRO_BACKEND_ONLY( InitLetCachedScope, Reg2Aux, OpSideEffect) // Retrieve cached scope; create if not cached (formals are let-like instead of var-like)
Expand Down
22 changes: 20 additions & 2 deletions lib/Runtime/Debug/TTSnapObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace TTD
TTDAssert(!obj->IsExternal(), "We are not prepared for custom external objects yet");

sobj->ObjectPtrId = TTD_CONVERT_VAR_TO_PTR_ID(obj);
sobj->SnapObjectTag = obj->GetSnapTag_TTD();
sobj->SnapObjectTag = obj->GetTypeId() == Js::TypeId::TypeIds_AwaitObject ? SnapObjectType::SnapAwaitObject : obj->GetSnapTag_TTD();

TTD_WELLKNOWN_TOKEN lookupToken = isWellKnown ? obj->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownObject(obj) : TTD_INVALID_WELLKNOWN_TOKEN;
sobj->OptWellKnownToken = alloc.CopyRawNullTerminatedStringInto(lookupToken);
Expand All @@ -39,7 +39,10 @@ namespace TTD
NSSnapObjects::StdPropertyExtract_DynamicType(sobj, static_cast<Js::DynamicObject*>(obj), alloc);
}

obj->ExtractSnapObjectDataInto(sobj, alloc);
if (sobj->SnapObjectTag != SnapObjectType::SnapAwaitObject)
{
obj->ExtractSnapObjectDataInto(sobj, alloc);
}
}

void StdPropertyExtract_StaticType(SnapObject* snpObject, Js::RecyclableObject* obj)
Expand Down Expand Up @@ -681,6 +684,21 @@ namespace TTD
}
#endif

Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator)
{
Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);
if(rcObj != nullptr)
{
return rcObj;
}
else
{
Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId);

return Js::DynamicObject::New(ctx->GetRecycler(), ctx->GetLibrary()->GetAwaitObjectType());
}
}

Js::RecyclableObject* DoObjectInflation_SnapDynamicObject(const SnapObject* snpObject, InflateMap* inflator)
{
Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator);
Expand Down
2 changes: 2 additions & 0 deletions lib/Runtime/Debug/TTSnapObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ namespace TTD
//ParseAddtlInfo is a nop
//AssertSnapEquiv is a nop

Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator);

//////////////////

//A struct that represents a script function object
Expand Down
Loading