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

ChakraCore servicing update for June, 2019 #6155

Merged
merged 13 commits into from
Jun 11, 2019
1 change: 1 addition & 0 deletions Build/Common.Build.Default.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PlatformToolset Condition="'$(BuildToolVersion)'=='12.0'">v120</PlatformToolset>
<PlatformToolset Condition="'$(BuildToolVersion)'=='14.0'">v140</PlatformToolset>
<PlatformToolset Condition="'$(BuildToolVersion)'=='15.0'">v141</PlatformToolset>
<PlatformToolset Condition="'$(BuildToolVersion)'=='16.0'">v142</PlatformToolset>
</PropertyGroup>

<!-- Default ChakraDevConfigDir -->
Expand Down
2 changes: 1 addition & 1 deletion Build/NuGet/.pack-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.11.9
1.11.10
79 changes: 75 additions & 4 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,8 @@ BackwardPass::ProcessLoop(BasicBlock * lastBlock)
{
Assert(loop->symsAssignedToInLoop == nullptr);
loop->symsAssignedToInLoop = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
Assert(loop->preservesNumberValue == nullptr);
loop->preservesNumberValue = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
}

FOREACH_BLOCK_BACKWARD_IN_RANGE_DEAD_OR_ALIVE(block, lastBlock, nullptr)
Expand Down Expand Up @@ -4316,7 +4318,10 @@ BackwardPass::ProcessNoImplicitCallDef(IR::Instr *const instr)
const bool transferArrayLengthSymUse = !!currentBlock->noImplicitCallArrayLengthSymUses->TestAndClear(dstSym->m_id);

IR::Opnd *const src = instr->GetSrc1();
if(!src || instr->GetSrc2())

// Stop attempting to transfer noImplicitCallUses symbol if the instr is not a transfer instr (based on the opcode's
// flags) or does not have the attributes to be a transfer instr (based on the existance of src and src2).
if(!src || (instr->GetSrc2() && !OpCodeAttr::NonIntTransfer(instr->m_opcode)))
{
return;
}
Expand Down Expand Up @@ -5004,16 +5009,24 @@ BackwardPass::UpdateArrayBailOutKind(IR::Instr *const instr)
return;
}

instr->GetDst()->AsIndirOpnd()->AllowConversion(true);
IR::BailOutKind includeBailOutKinds = IR::BailOutInvalid;
if (!baseValueType.IsNotNativeArray() &&
(!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) &&
!currentBlock->noImplicitCallNativeArrayUses->IsEmpty() &&
!(instr->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall))
{
// There is an upwards-exposed use of a native array. Since the array referenced by this instruction can be aliased,
// this instruction needs to bail out if it converts the native array even if this array specifically is not
// upwards-exposed.
includeBailOutKinds |= IR::BailOutConvertedNativeArray;
if (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar())
{
includeBailOutKinds |= IR::BailOutConvertedNativeArray;
}
else
{
// We are assuming that array conversion is impossible here, so make sure we execute code that fails if conversion does happen.
instr->GetDst()->AsIndirOpnd()->AllowConversion(false);
}
}

if(baseOpnd->IsArrayRegOpnd() && baseOpnd->AsArrayRegOpnd()->EliminatedUpperBoundCheck())
Expand Down Expand Up @@ -7410,6 +7423,52 @@ BackwardPass::TrackFloatSymEquivalence(IR::Instr *const instr)
}
}

bool
BackwardPass::SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd)
{
Assert(sym->IsStackSym());
if (!opnd->IsRegOpnd())
{
return false;
}
StackSym *opndSym = opnd->AsRegOpnd()->m_sym;

if (sym == opndSym)
{
return true;
}

if (!opndSym->IsSingleDef())
{
return false;
}

if (opndSym->GetInstrDef()->m_opcode == Js::OpCode::LdC_A_I4)
{
return true;
}

return false;
}

bool
BackwardPass::InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym)
{
if (instr->m_opcode == Js::OpCode::Ld_A)
{
if (instr->GetSrc1()->IsRegOpnd())
{
IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd();
if (src1->m_sym->IsSingleDef())
{
instr = src1->m_sym->GetInstrDef();
}
}
}
return (OpCodeAttr::ProducesNumber(instr->m_opcode) ||
(instr->m_opcode == Js::OpCode::Add_A && this->SymIsIntconstOrSelf(defSym, instr->GetSrc1()) && this->SymIsIntconstOrSelf(defSym, instr->GetSrc2())));
}

bool
BackwardPass::ProcessDef(IR::Opnd * opnd)
{
Expand All @@ -7424,7 +7483,19 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
this->InvalidateCloneStrCandidate(opnd);
if ((tag == Js::BackwardPhase) && IsPrePass())
{
this->currentPrePassLoop->symsAssignedToInLoop->Set(sym->m_id);
bool firstDef = !this->currentPrePassLoop->symsAssignedToInLoop->TestAndSet(sym->m_id);

if (firstDef)
{
if (this->InstrPreservesNumberValues(this->currentInstr, sym))
{
this->currentPrePassLoop->preservesNumberValue->Set(sym->m_id);
}
}
else if (!this->InstrPreservesNumberValues(this->currentInstr, sym))
{
this->currentPrePassLoop->preservesNumberValue->Clear(sym->m_id);
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/BackwardPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class BackwardPass
bool ProcessDef(IR::Opnd * opnd);
void ProcessTransfers(IR::Instr * instr);
void ProcessFieldKills(IR::Instr * instr);
bool SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd);
bool InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym);

template<typename T> void ClearBucketsOnFieldKill(IR::Instr *instr, HashTable<T> *table);
StackSym* ProcessByteCodeUsesDst(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
const BVSparse<JitArenaAllocator>* ProcessByteCodeUsesSrcs(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/FlowGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ class Loop
// cleanup in PreOptPeep in the pre-pass of a loop. For aggressively transferring
// values in prepass, we need to know if a source sym was ever assigned to in a loop.
BVSparse<JitArenaAllocator> *symsAssignedToInLoop;
BVSparse<JitArenaAllocator> *preservesNumberValue;

BailOutInfo * bailOutInfo;
IR::BailOutInstr * toPrimitiveSideEffectCheck;
Expand Down Expand Up @@ -733,6 +734,7 @@ class Loop
symsAssignedToInLoop(nullptr),
needImplicitCallBailoutChecksForJsArrayCheckHoist(false),
inductionVariables(nullptr),
preservesNumberValue(nullptr),
dominatingLoopCountableBlock(nullptr),
loopCount(nullptr),
loopCountBasedBoundBaseSyms(nullptr),
Expand Down
69 changes: 68 additions & 1 deletion lib/Backend/GlobOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1244,7 +1244,7 @@ void GlobOpt::InsertValueCompensation(
{
IR::Instr *const newInstr =
IR::Instr::New(
Js::OpCode::Ld_I4,
Js::OpCode::Ld_A,
IR::RegOpnd::New(mergedHeadSegmentLengthSym, mergedHeadSegmentLengthSym->GetType(), func),
IR::RegOpnd::New(predecessorHeadSegmentLengthSym, predecessorHeadSegmentLengthSym->GetType(), func),
func);
Expand Down Expand Up @@ -2694,6 +2694,48 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
return instrNext;
}

bool
GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const
{
if (opnd == nullptr)
{
return false;
}

if (opnd->m_sym->m_isNotNumber)
{
return true;
}

if (!inGlobOpt)
{
return false;
}

if (opnd->GetValueType().IsNumber() || currentBlock->globOptData.IsTypeSpecialized(opnd->m_sym))
{
if (!this->IsLoopPrePass())
{
return false;
}

Value * opndValue = this->currentBlock->globOptData.FindValue(opnd->m_sym);
ValueInfo * opndValueInfo = opndValue ? opndValue->GetValueInfo() : nullptr;
if (!opndValueInfo)
{
return true;
}
if (this->prePassLoop->preservesNumberValue->Test(opnd->m_sym->m_id))
{
return false;
}

return !this->IsSafeToTransferInPrepass(opnd->m_sym, opndValueInfo);
}

return true;
}

bool
GlobOpt::OptTagChecks(IR::Instr *instr)
{
Expand Down Expand Up @@ -12827,6 +12869,26 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr)
it.RemoveCurrent();
}
}
else if(kills.KillsObjectArraysWithNoMissingValues())
{
// Some operations may kill objects with arrays-with-no-missing-values in unlikely circumstances. Convert their value types to likely
// versions so that the checks have to be redone.
for(auto it = valuesToKillOnCalls->GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext())
{
Value *const value = it.CurrentValue();
ValueInfo *const valueInfo = value->GetValueInfo();
Assert(
valueInfo->IsArrayOrObjectWithArray() ||
valueInfo->IsOptimizedVirtualTypedArray() ||
valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym());
if(!valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsArray() || !valueInfo->HasNoMissingValues())
{
continue;
}
ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false);
it.RemoveCurrent();
}
}

if(kills.KillsNativeArrays())
{
Expand Down Expand Up @@ -13358,6 +13420,11 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
{
kills.SetKillsArrayLengths();
}

if(doArrayMissingValueCheckHoist && !(useValueTypes && arrayValueType.IsArray()))
{
kills.SetKillsObjectArraysWithNoMissingValues();
}
break;
}

Expand Down
8 changes: 7 additions & 1 deletion lib/Backend/GlobOpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ class JsArrayKills
{
bool killsAllArrays : 1;
bool killsArraysWithNoMissingValues : 1;
bool killsObjectArraysWithNoMissingValues : 1;
bool killsNativeArrays : 1;
bool killsArrayHeadSegments : 1;
bool killsArrayHeadSegmentLengths : 1;
Expand All @@ -342,6 +343,9 @@ class JsArrayKills
bool KillsArraysWithNoMissingValues() const { return killsArraysWithNoMissingValues; }
void SetKillsArraysWithNoMissingValues() { killsArraysWithNoMissingValues = true; }

bool KillsObjectArraysWithNoMissingValues() const { return killsObjectArraysWithNoMissingValues; }
void SetKillsObjectArraysWithNoMissingValues() { killsObjectArraysWithNoMissingValues = true; }

bool KillsNativeArrays() const { return killsNativeArrays; }
void SetKillsNativeArrays() { killsNativeArrays = true; }

Expand Down Expand Up @@ -769,6 +773,8 @@ class GlobOpt
const bool lossy = false, const bool forceInvariantHoisting = false, IR::BailOutKind bailoutKind = IR::BailOutInvalid);
void HoistInvariantValueInfo(ValueInfo *const invariantValueInfoToHoist, Value *const valueToUpdate, BasicBlock *const targetBlock);
void OptHoistUpdateValueType(Loop* loop, IR::Instr* instr, IR::Opnd** srcOpndPtr, Value *const srcVal);
bool IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const;

public:
static bool IsTypeSpecPhaseOff(Func const * func);
static bool DoAggressiveIntTypeSpec(Func const * func);
Expand Down Expand Up @@ -891,7 +897,7 @@ class GlobOpt
void KillLiveFields(StackSym * stackSym, BVSparse<JitArenaAllocator> * bv);
void KillLiveFields(PropertySym * propertySym, BVSparse<JitArenaAllocator> * bv);
void KillLiveFields(BVSparse<JitArenaAllocator> *const fieldsToKill, BVSparse<JitArenaAllocator> *const bv) const;
void KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func);
void KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func);
void KillAllFields(BVSparse<JitArenaAllocator> * bv);
void SetAnyPropertyMayBeWrittenTo();
void AddToPropertiesWrittenTo(Js::PropertyId propertyId);
Expand Down
34 changes: 22 additions & 12 deletions lib/Backend/GlobOptFields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void GlobOpt::KillLiveFields(BVSparse<JitArenaAllocator> *const fieldsToKill, BV
}

void
GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
{
IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();

Expand All @@ -225,14 +225,7 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> *
// - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that
// if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next
// pass based on the value.
if (func->GetThisOrParentInlinerHasArguments() ||
(
indexOpnd &&
(
indexOpnd->m_sym->m_isNotNumber ||
(inGlobOpt && !indexOpnd->GetValueType().IsNumber() && !currentBlock->globOptData.IsTypeSpecialized(indexOpnd->m_sym))
)
))
if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indexOpnd, inGlobOpt))
{
this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms
SetAnyPropertyMayBeWrittenTo();
Expand All @@ -248,6 +241,23 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> *
// Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout
this->KillAllObjectTypes(bv);
}
else if ((!valueOpnd || valueOpnd->IsVar()) && this->objectTypeSyms != nullptr)
{
// If we wind up converting a native array, block final-type opt at this point, because we could evolve
// to a type with the wrong type ID. Do this by noting that we may have evolved any type and so must
// check it before evolving it further.
IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
Value * baseValue = baseOpnd ? this->currentBlock->globOptData.FindValue(baseOpnd->m_sym) : nullptr;
ValueInfo * baseValueInfo = baseValue ? baseValue->GetValueInfo() : nullptr;
if (!baseValueInfo || !baseValueInfo->IsNotNativeArray())
{
if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr)
{
this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
}
this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
}
}
}
}

Expand Down Expand Up @@ -340,7 +350,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::StElemI_A_Strict:
Assert(dstOpnd != nullptr);
KillLiveFields(this->lengthEquivBv, bv);
KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
Expand All @@ -350,7 +360,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::InitComputedProperty:
case Js::OpCode::InitGetElemI:
case Js::OpCode::InitSetElemI:
KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
Expand All @@ -360,7 +370,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::DeleteElemI_A:
case Js::OpCode::DeleteElemIStrict_A:
Assert(dstOpnd != nullptr);
KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), nullptr, bv, inGlobOpt, instr->m_func);
break;

case Js::OpCode::DeleteFld:
Expand Down
9 changes: 8 additions & 1 deletion lib/Backend/IR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3307,7 +3307,14 @@ bool Instr::TransfersSrcValue()

// Consider: Add opcode attribute to indicate whether the opcode would use the value or not

return this->GetDst() != nullptr && this->GetSrc2() == nullptr && !OpCodeAttr::DoNotTransfer(this->m_opcode) && !this->CallsAccessor();
return
this->GetDst() != nullptr &&

// The lack of a Src2 does not always indicate that the instr is not a transfer instr (ex: StSlotChkUndecl).
(this->GetSrc2() == nullptr || OpCodeAttr::NonIntTransfer(this->m_opcode)) &&

!OpCodeAttr::DoNotTransfer(this->m_opcode) &&
!this->CallsAccessor();
}


Expand Down
Loading