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

Finished LazyBailOut and added OOPJIT support. #5992

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 55 additions & 33 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2215,15 +2215,20 @@ BackwardPass::IsLazyBailOutCurrentlyNeeeded(IR::Instr * instr) const
"liveFixedField is null, MergeSuccBlocksInfo might have not initialized it?"
);

if (instr->IsStFldVariant())
wyrichte marked this conversation as resolved.
Show resolved Hide resolved
// StFld LazyBailOut tag removal optimization. Given that this instr is a StFld variant and
// that there already is an BailOutOnImplicitCall tag on this instr, we can remove the
// LazyBailOut tag on this instr if the StFld is writing to a live fixed field. We cannot
// perform this optimization if a BailOutOnImplicitCall tag is abscent because writing to
// a property can result in an implicit call that then can result in a lazy bailout.
if (instr->IsStFldVariant() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind()))
{
Assert(instr->GetDst());
Js::PropertyId id = instr->GetDst()->GetSym()->AsPropertySym()->m_propertyId;

// We only need to protect against SetFld if it is setting to one of the live fixed fields
return this->currentBlock->liveFixedFields->Test(id);
// We only need to protect against StFld if it is setting to one of the live fixed fields.
return currentBlock->liveFixedFields->Test(instr->GetDst()->GetSym()->AsPropertySym()->m_propertyId);
}

// If no more fixed fields exist at this point in the block it is safe to assume that any field marked as
// a fixed field has been verified to have not been modified and thus a LazyBailOut tag is not necessary.
return !this->currentBlock->liveFixedFields->IsEmpty();
}

Expand Down Expand Up @@ -2333,7 +2338,7 @@ BackwardPass::DeadStoreTypeCheckBailOut(IR::Instr * instr)
return;
}

// If bailOutKind is equivTypeCheck then leave alone the bailout
// If bailOutKind is equivTypeCheck then leave the bailout alone.
if (bailOutKind == IR::BailOutFailedEquivalentTypeCheck ||
bailOutKind == IR::BailOutFailedEquivalentFixedFieldTypeCheck)
{
Expand All @@ -2357,9 +2362,9 @@ BackwardPass::DeadStoreTypeCheckBailOut(IR::Instr * instr)
}

void
BackwardPass::DeadStoreLazyBailOut(IR::Instr * instr, bool needsLazyBailOut)
BackwardPass::DeadStoreLazyBailOut(IR::Instr * instr)
{
if (!this->IsPrePass() && !needsLazyBailOut && instr->HasLazyBailOut())
if (!this->IsPrePass() && instr->HasLazyBailOut())
{
instr->ClearLazyBailOut();
if (!instr->HasBailOutInfo())
Expand Down Expand Up @@ -2441,12 +2446,13 @@ BackwardPass::DeadStoreImplicitCallBailOut(IR::Instr * instr, bool hasLiveFields
// We have an implicit call bailout in the code, and we want to make sure that it's required.
// Do this now, because only in the dead store pass do we have complete forward and backward liveness info.
bool needsBailOutOnImplicitCall = this->IsImplicitCallBailOutCurrentlyNeeded(instr, mayNeedBailOnImplicitCall, needsLazyBailOut, hasLiveFields);

if(!UpdateImplicitCallBailOutKind(instr, needsBailOutOnImplicitCall, needsLazyBailOut))
{
instr->ClearBailOutInfo();
if (preOpBailOutInstrToProcess == instr)
if (this->preOpBailOutInstrToProcess == instr)
{
preOpBailOutInstrToProcess = nullptr;
this->preOpBailOutInstrToProcess = nullptr;
}
#if DBG
if (this->DoMarkTempObjectVerify())
Expand Down Expand Up @@ -2476,36 +2482,41 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa

const bool hasMarkTempObject = bailOutKindWithBits & IR::BailOutMarkTempObject;

// Firstly, we remove the mark temp object bit, as it is not needed after the dead store pass.
// We will later skip removing BailOutOnImplicitCalls when there is a mark temp object bit regardless
// of `needsBailOutOnImplicitCall`.
// First we remove the mark temp object bit as it is not needed after the dead
// store pass. We will later skip removing BailOutOnImplicitCalls when there
// is a mark temp object bit regardless of needsBailOutOnImplicitCall.
if (hasMarkTempObject)
{
instr->SetBailOutKind(bailOutKindWithBits & ~IR::BailOutMarkTempObject);
}

if (needsBailOutOnImplicitCall)
{
// We decided that BailOutOnImplicitCall is needed. So lazy bailout is unnecessary
// because we are already protected from potential side effects unless the operation
// itself can change fields' values (StFld/StElem).
// We decided that BailOutOnImplicitCall is needed; LazyBailOut is unnecessary because
// the modification of a property would trigger an implicit call bailout before a LazyBailOut
// would trigger. An edge case is when the act of checking the type of the object with the
// property, which occurs before the implicit call check, results in a property guard invalidation.
// In this case a LazyBailOut is necessary.
if (needsLazyBailOut && !instr->CanChangeFieldValueWithoutImplicitCall())
{
instr->ClearLazyBailOut();
}

return true;
}
else

// needsBailOutOnImplicitCall also captures our intention to keep BailOutOnImplicitCalls
// because we want to do fixed field lazy bailout optimization. So if we don't need them,
// just remove our lazy bailout unless this instr can cause a PropertyGuard invalidation
// during the type check.
if (!instr->CanChangeFieldValueWithoutImplicitCall())
{
// `needsBailOutOnImplicitCall` also captures our intention to keep BailOutOnImplicitCalls
// because we want to do fixed field lazy bailout optimization. So if we don't need them,
// just remove our lazy bailout.
instr->ClearLazyBailOut();
if (!instr->HasBailOutInfo())
{
return true;
}
}

if (!instr->HasBailOutInfo())
{
return true;
}

const IR::BailOutKind bailOutKindWithoutBits = instr->GetBailOutKindNoBits();
Expand All @@ -2517,8 +2528,8 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa
return true;
}

// At this point, we don't need the bail on implicit calls.
// Simply use the bailout kind bits as our new bailout kind.
// At this point we don't need the bail on implicit calls,
// use the bailout kind bits as our new bailout kind.
IR::BailOutKind newBailOutKind = bailOutKindWithBits - bailOutKindWithoutBits;

if (newBailOutKind == IR::BailOutInvalid)
Expand Down Expand Up @@ -3721,7 +3732,10 @@ BackwardPass::ProcessBlock(BasicBlock * block)
);

DeadStoreTypeCheckBailOut(instr);
DeadStoreLazyBailOut(instr, needsLazyBailOut);
if (!needsLazyBailOut)
{
DeadStoreLazyBailOut(instr);
}
DeadStoreImplicitCallBailOut(instr, hasLiveFields, needsLazyBailOut);

AssertMsg(
Expand Down Expand Up @@ -5714,8 +5728,13 @@ BackwardPass::TrackAddPropertyTypes(IR::PropertySymOpnd *opnd, BasicBlock *block
typeWithProperty == typeWithoutProperty ||
(opnd->IsTypeChecked() && !opnd->IsInitialTypeChecked()))
{
if (!this->IsPrePass() && block->stackSymToFinalType != nullptr && !this->currentInstr->HasBailOutInfo())
if (
!this->IsPrePass() &&
block->stackSymToFinalType != nullptr &&
(!this->currentInstr->HasBailOutInfo() || currentInstr->OnlyHasLazyBailOut())
)
{

PropertySym *propertySym = opnd->m_sym->AsPropertySym();
AddPropertyCacheBucket *pBucket =
block->stackSymToFinalType->Get(propertySym->m_stackSym->m_id);
Expand Down Expand Up @@ -6009,12 +6028,15 @@ BackwardPass::InsertTypeTransitionsAtPotentialKills()
// Final types can't be pushed up past certain instructions.
IR::Instr *instr = this->currentInstr;

if (instr->HasBailOutInfo() || instr->m_opcode == Js::OpCode::UpdateNewScObjectCache)
// Final types can't be pushed up past a BailOut point. Insert any transitions called
// for by the current state of add-property buckets. Also do this for ctor cache updates
// to avoid putting a type in the ctor cache that extends past the end of the ctor that
// the cache covers.
// TODO: explain why LBO gets exempted from this rule.
if (instr->m_opcode == Js::OpCode::UpdateNewScObjectCache ||
(instr->HasBailOutInfo() && !instr->OnlyHasLazyBailOut())
)
{
// Final types can't be pushed up past a bailout point.
// Insert any transitions called for by the current state of add-property buckets.
// Also do this for ctor cache updates, to avoid putting a type in the ctor cache that extends past
// the end of the ctor that the cache covers.
this->ForEachAddPropertyCacheBucket([&](int symId, AddPropertyCacheBucket *data)->bool {
this->InsertTypeTransitionAfterInstr(instr, symId, data, this->currentBlock->upwardExposedUses);
return false;
Expand Down
4 changes: 2 additions & 2 deletions lib/Backend/BackwardPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class BackwardPass
void DumpMarkTemp();
#endif

static bool UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBailOutOnImplicitCall, bool needsLazyBailOut);
bool UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBailOutOnImplicitCall, bool needsLazyBailOut);

bool ProcessNoImplicitCallUses(IR::Instr *const instr);
void ProcessNoImplicitCallDef(IR::Instr *const instr);
Expand Down Expand Up @@ -106,7 +106,7 @@ class BackwardPass
bool IsLazyBailOutCurrentlyNeeeded(IR::Instr * instr) const;
void DeadStoreImplicitCallBailOut(IR::Instr * instr, bool hasLiveFields, bool needsLazyBailOut);
void DeadStoreTypeCheckBailOut(IR::Instr * instr);
void DeadStoreLazyBailOut(IR::Instr * instr, bool needsLazyBailOut);
void DeadStoreLazyBailOut(IR::Instr * instr);
bool IsImplicitCallBailOutCurrentlyNeeded(IR::Instr * instr, bool mayNeedImplicitCallBailOut, bool needLazyBailOut, bool hasLiveFields);
bool NeedBailOutOnImplicitCallsForTypedArrayStore(IR::Instr* instr);
bool TrackNoImplicitCallInlinees(IR::Instr *instr);
Expand Down
8 changes: 0 additions & 8 deletions lib/Backend/BailOut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2974,14 +2974,6 @@ SharedBailOutRecord::SharedBailOutRecord(uint32 bailOutOffset, uint bailOutCache
this->type = BailoutRecordType::Shared;
}

#if DBG
void LazyBailOutRecord::Dump(Js::FunctionBody* functionBody) const
{
OUTPUT_PRINT(functionBody);
Output::Print(_u("Bytecode Offset: #%04x opcode: %s"), this->bailOutRecord->GetBailOutOffset(), Js::OpCodeUtil::GetOpCodeName(this->bailOutRecord->GetBailOutOpCode()));
}
#endif

void GlobalBailOutRecordDataTable::Finalize(NativeCodeData::Allocator *allocator, JitArenaAllocator *tempAlloc)
{
GlobalBailOutRecordDataRow *newRows = NativeCodeDataNewArrayZNoFixup(allocator, GlobalBailOutRecordDataRow, length);
Expand Down
11 changes: 4 additions & 7 deletions lib/Backend/BailOut.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,10 @@ class BailOutInfo
void PartialDeepCopyTo(BailOutInfo *const bailOutInfo) const;
void Clear(JitArenaAllocator * allocator);

// Lazy bailout
//
// Workaround for dealing with use of destination register of `call` instructions with postop lazy bailout.
// As an example, in globopt, we have s1 = Call and s1 is in byteCodeUpwardExposedUse,
// but after lowering, the instructions are: s3 = Call, s1 = s3.
// If we add a postop lazy bailout to s3 = call, we will create a use of s1 right at that instructions.
// However, s1 at that point is not initialized yet.
// Related to Lazy bailout. Workaround for dealing with use of destination register of `call` instructions
// with postop lazy bailout. As an example, in globopt, we have s1 = Call and s1 is in byteCodeUpwardExposedUse,
// but after lowering, the instructions are: s3 = Call, s1 = s3. If we add a postop lazy bailout to s3 = call,
// we will create a use of s1 right at that instructions. However, s1 at that point is not initialized yet.
// As a workaround, we will clear the use of s1 and restore it if we determine that lazy bailout is not needed.
void ClearUseOfDst(SymID id);
void RestoreUseOfDst();
Expand Down
5 changes: 4 additions & 1 deletion lib/Backend/DbCheckPostLower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ DbCheckPostLower::IsAssign(IR::Instr *instr)
return LowererMD::IsAssign(instr)
#ifdef _M_X64
|| instr->m_opcode == Js::OpCode::MOVQ
|| instr->m_opcode == Js::OpCode::MOV_TRUNC
#endif
;
}
Expand Down Expand Up @@ -364,7 +365,9 @@ DbCheckPostLower::EnsureOnlyMovesToRegisterOpnd(IR::Instr *instr)
if (this->IsCallToHelper(instr, IR::HelperOp_Equal) ||
this->IsCallToHelper(instr, IR::HelperOp_StrictEqual) ||
this->IsCallToHelper(instr, IR::HelperOP_CmEq_A) ||
this->IsCallToHelper(instr, IR::HelperOP_CmNeq_A)
this->IsCallToHelper(instr, IR::HelperOP_CmNeq_A) ||
this->IsCallToHelper(instr, IR::HelperOP_CmSrNeq_A) ||
this->IsCallToHelper(instr, IR::HelperOP_CmSrEq_A)
)
{
// Pattern matched
Expand Down