From 9e0fe09b8142b0e720271a21ecfa92f6bcbeb874 Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Wed, 2 May 2018 16:43:58 -0700 Subject: [PATCH 1/5] PRE for multi-level field loads in a loop. --- lib/Backend/FlowGraph.cpp | 34 +- lib/Backend/FlowGraph.h | 1 + lib/Backend/GlobOpt.cpp | 539 ++++++++++++++++--- lib/Backend/GlobOpt.h | 36 +- lib/Backend/GlobOptBlockData.cpp | 6 +- lib/Backend/GlobOptBlockData.h | 13 +- lib/Backend/IR.cpp | 20 +- lib/Backend/IR.h | 5 +- lib/Backend/ValueInfo.h | 10 +- lib/Common/ConfigFlagsList.h | 1 + lib/Runtime/Language/JavascriptOperators.cpp | 2 +- test/PRE/pre1.baseline | 54 ++ test/PRE/pre1.js | 127 +++++ test/PRE/rlexe.xml | 11 + test/rlexedirs.xml | 5 + 15 files changed, 761 insertions(+), 103 deletions(-) create mode 100644 test/PRE/pre1.baseline create mode 100644 test/PRE/pre1.js create mode 100644 test/PRE/rlexe.xml diff --git a/lib/Backend/FlowGraph.cpp b/lib/Backend/FlowGraph.cpp index 1223e95ce54..12e38ddbfca 100644 --- a/lib/Backend/FlowGraph.cpp +++ b/lib/Backend/FlowGraph.cpp @@ -3695,6 +3695,24 @@ Loop::IsSymAssignedToInSelfOrParents(StackSym * const sym) const return false; } +BasicBlock * +Loop::GetAnyTailBlock() const +{ + BasicBlock * tail = nullptr; + + BasicBlock * loopHeader = this->GetHeadBlock(); + FOREACH_PREDECESSOR_BLOCK(pred, loopHeader) + { + if (this->IsDescendentOrSelf(pred->loop)) + { + tail = pred; + } + } NEXT_PREDECESSOR_BLOCK; + + Assert(tail); + return tail; +} + #if DBG_DUMP uint Loop::GetLoopNumber() const @@ -5216,14 +5234,14 @@ GlobOpt::CloneValues(BasicBlock *const toBlock, GlobOptBlockData *toData, GlobOp ProcessValueKills(toBlock, toData); } -PRECandidatesList * GlobOpt::FindBackEdgePRECandidates(BasicBlock *block, JitArenaAllocator *alloc) +PRECandidates * GlobOpt::FindBackEdgePRECandidates(BasicBlock *block, JitArenaAllocator *alloc) { // Iterate over the value table looking for propertySyms which are candidates to // pre-load in the landing pad for field PRE GlobHashTable *valueTable = block->globOptData.symToValueMap; Loop *loop = block->loop; - PRECandidatesList *candidates = nullptr; + PRECandidates *candidates = JitAnew(this->tempAlloc, PRECandidates); for (uint i = 0; i < valueTable->tableSize; i++) { @@ -5284,7 +5302,7 @@ PRECandidatesList * GlobOpt::FindBackEdgePRECandidates(BasicBlock *block, JitAre if (!landingPadValue) { // Value should be added as initial value or already be there. - return nullptr; + continue; } IR::Instr * ldInstr = this->prePassInstrMap->Lookup(propertySym->m_id, nullptr); @@ -5294,12 +5312,16 @@ PRECandidatesList * GlobOpt::FindBackEdgePRECandidates(BasicBlock *block, JitAre continue; } - if (!candidates) + if (!candidates->candidatesList) { - candidates = Anew(alloc, PRECandidatesList, alloc); + candidates->candidatesList = JitAnew(alloc, PRECandidatesList, alloc); + candidates->candidatesToProcess = JitAnew(alloc, BVSparse, alloc); + candidates->candidatesBv = JitAnew(alloc, BVSparse, alloc); } - candidates->Prepend(&bucket); + candidates->candidatesList->Prepend(&bucket); + candidates->candidatesToProcess->Set(propertySym->m_id); + candidates->candidatesBv->Set(propertySym->m_id); } NEXT_SLISTBASE_ENTRY; } diff --git a/lib/Backend/FlowGraph.h b/lib/Backend/FlowGraph.h index 2a3ecdf52f3..1c80e210b43 100644 --- a/lib/Backend/FlowGraph.h +++ b/lib/Backend/FlowGraph.h @@ -756,6 +756,7 @@ class Loop void SetLoopTopInstr(IR::LabelInstr * loopTop); Func * GetFunc() const { return GetLoopTopInstr()->m_func; } bool IsSymAssignedToInSelfOrParents(StackSym * const sym) const; + BasicBlock * GetAnyTailBlock() const; #if DBG_DUMP bool GetHasCall() const { return hasCall; } uint GetLoopNumber() const; diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 996177b521a..744dd25534d 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -947,11 +947,11 @@ GlobOpt::ToTypeSpec(BVSparse *bv, BasicBlock *block, IRType t } NEXT_BITSET_IN_SPARSEBV; } -PRECandidatesList * GlobOpt::FindPossiblePRECandidates(Loop *loop, JitArenaAllocator *alloc) +void GlobOpt::PRE::FindPossiblePRECandidates(Loop *loop, JitArenaAllocator *alloc) { // Find the set of PRE candidates BasicBlock *loopHeader = loop->GetHeadBlock(); - PRECandidatesList *candidates = nullptr; + PRECandidates *candidates = nullptr; bool firstBackEdge = true; FOREACH_PREDECESSOR_BLOCK(blockPred, loopHeader) @@ -963,7 +963,7 @@ PRECandidatesList * GlobOpt::FindPossiblePRECandidates(Loop *loop, JitArenaAlloc } if (firstBackEdge) { - candidates = this->FindBackEdgePRECandidates(blockPred, alloc); + candidates = this->globOpt->FindBackEdgePRECandidates(blockPred, alloc); } else { @@ -971,24 +971,60 @@ PRECandidatesList * GlobOpt::FindPossiblePRECandidates(Loop *loop, JitArenaAlloc } } NEXT_PREDECESSOR_BLOCK; - return candidates; + this->candidates = candidates; } -BOOL GlobOpt::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) +BOOL GlobOpt::PRE::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) { // Insert a load for each field PRE candidate. PropertySym *propertySym = candidate->value->AsPropertySym(); + if (!candidates->candidatesToProcess->TestAndClear(propertySym->m_id)) + { + return false; + } + Value * propSymValueOnBackEdge = candidate->element; + StackSym *objPtrSym = propertySym->m_stackSym; + Sym * objPtrCopyPropSym = nullptr; - // If objPtr isn't live, we'll retry later. - // Another PRE candidate may insert a load for it. if (!loop->landingPad->globOptData.IsLive(objPtrSym)) { - return false; + if (PHASE_OFF(Js::MakeObjSymLiveInLandingPadPhase, this->globOpt->func)) + { + return false; + } + if (objPtrSym->IsSingleDef()) + { + // We can still try to do PRE if the object sym is single def, even if its not live in the landing pad. + // We'll have to add a def instruction for the object sym in the landing pad, and then we can continue + // pre-loading the current PRE candidate. + // Case in point: + // $L1 + // value|symStore + // t1 = o.x (v1|t3) + // t2 = t1.y (v2|t4) <-- t1 is not live in the loop landing pad + // jmp $L1 + + if (!InsertSymDefinitionInLandingPad(objPtrSym, loop, &objPtrCopyPropSym)) + { +#if DBG_DUMP + TraceFailedPreloadInLandingPad(loop, propertySym, _u("Failed to insert load of object sym in landing pad")); +#endif + return false; + } + } + else + { +#if DBG_DUMP + TraceFailedPreloadInLandingPad(loop, propertySym, _u("Object sym not live in landing pad and not single-def")); +#endif + return false; + } } + Assert(loop->landingPad->globOptData.IsLive(objPtrSym)); + BasicBlock *landingPad = loop->landingPad; - Value *value = candidate->element; - Sym *symStore = value->GetValueInfo()->GetSymStore(); + Sym *symStore = propSymValueOnBackEdge->GetValueInfo()->GetSymStore(); // The symStore can't be live into the loop // The symStore needs to still have the same value @@ -1006,50 +1042,56 @@ BOOL GlobOpt::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) // Value should be added as initial value or already be there. Assert(landingPadValue); - IR::Instr * ldInstr = this->prePassInstrMap->Lookup(propertySym->m_id, nullptr); - Assert(ldInstr); + IR::Instr * ldInstrInLoop = this->globOpt->prePassInstrMap->Lookup(propertySym->m_id, nullptr); + Assert(ldInstrInLoop); // Create instr to put in landing pad for compensation - Assert(IsPREInstrCandidateLoad(ldInstr->m_opcode)); - IR::SymOpnd *ldSrc = ldInstr->GetSrc1()->AsSymOpnd(); + Assert(IsPREInstrCandidateLoad(ldInstrInLoop->m_opcode)); - if (ldSrc->m_sym != propertySym) + IR::Instr * ldInstr = InsertPropertySymPreloadWithoutDstInLandingPad(ldInstrInLoop, loop, propertySym); + if (!ldInstr) { - // It's possible that the propertySym but have equivalent objPtrs. Verify their values. - Value *val1 = CurrentBlockData()->FindValue(ldSrc->m_sym->AsPropertySym()->m_stackSym); - Value *val2 = CurrentBlockData()->FindValue(propertySym->m_stackSym); - if (!val1 || !val2 || val1->GetValueNumber() != val2->GetValueNumber()) + return false; + } + + Assert(ldInstr->GetDst() == nullptr); + if (ldInstrInLoop->GetDst()) + { + Assert(ldInstrInLoop->GetDst()->IsRegOpnd()); + if (ldInstrInLoop->GetDst()->AsRegOpnd()->m_sym != symStore) { - return false; + ldInstr->SetDst(IR::RegOpnd::New(symStore->AsStackSym(), TyVar, this->globOpt->func)); + loop->fieldPRESymStores->Set(symStore->m_id); } + else + { + ldInstr->SetDst(ldInstrInLoop->GetDst()->Copy(ldInstrInLoop->m_func)); + } + landingPad->globOptData.liveVarSyms->Set(ldInstr->GetDst()->AsRegOpnd()->m_sym->m_id); } - ldInstr = ldInstr->Copy(); - - // Consider: Shouldn't be necessary once we have copy-prop in prepass... - ldInstr->GetSrc1()->AsSymOpnd()->m_sym = propertySym; - ldSrc = ldInstr->GetSrc1()->AsSymOpnd(); + Value * objPtrValue = landingPad->globOptData.FindValue(objPtrSym); - if (ldSrc->IsPropertySymOpnd()) + objPtrCopyPropSym = objPtrCopyPropSym ? objPtrCopyPropSym : objPtrValue ? landingPad->globOptData.GetCopyPropSym(objPtrSym, objPtrValue) : nullptr; + if (objPtrCopyPropSym) { - IR::PropertySymOpnd *propSymOpnd = ldSrc->AsPropertySymOpnd(); - IR::PropertySymOpnd *newPropSymOpnd; + // If we inserted T4 = T1.y, and T3 is the copy prop sym for T1 in the landing pad, we need T3.y + // to be live on back edges to have the merge produce a value for T3.y. Having a value for T1.y + // produced from the merge is not enough as the T1.y in the loop will get obj-ptr-copy-propped to + // T3.y - newPropSymOpnd = propSymOpnd->AsPropertySymOpnd()->CopyWithoutFlowSensitiveInfo(this->func); - ldInstr->ReplaceSrc1(newPropSymOpnd); - } + // T3.y + PropertySym *newPropSym = PropertySym::FindOrCreate( + objPtrCopyPropSym->m_id, propertySym->m_propertyId, propertySym->GetPropertyIdIndex(), propertySym->GetInlineCacheIndex(), propertySym->m_fieldKind, this->globOpt->func); - if (ldInstr->GetDst()->AsRegOpnd()->m_sym != symStore) - { - ldInstr->ReplaceDst(IR::RegOpnd::New(symStore->AsStackSym(), TyVar, this->func)); - loop->fieldPRESymStores->Set(symStore->m_id); + if (!landingPad->globOptData.FindValue(newPropSym)) + { + landingPad->globOptData.SetValue(landingPadValue, newPropSym); + landingPad->globOptData.liveFields->Set(newPropSym->m_id); + MakePropertySymLiveOnBackEdges(newPropSym, loop, propSymValueOnBackEdge); + } } - ldInstr->GetSrc1()->SetIsJITOptimizedReg(true); - ldInstr->GetDst()->SetIsJITOptimizedReg(true); - - landingPad->globOptData.liveVarSyms->Set(symStore->m_id); - ValueType valueType(ValueType::Uninitialized); Value *initialValue = nullptr; @@ -1057,15 +1099,15 @@ BOOL GlobOpt::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) { if (ldInstr->IsProfiledInstr()) { - if (initialValue->GetValueNumber() == value->GetValueNumber()) + if (initialValue->GetValueNumber() == propSymValueOnBackEdge->GetValueNumber()) { - if (value->GetValueInfo()->IsUninitialized()) + if (propSymValueOnBackEdge->GetValueInfo()->IsUninitialized()) { valueType = ldInstr->AsProfiledInstr()->u.FldInfo().valueType; } else { - valueType = value->GetValueInfo()->Type(); + valueType = propSymValueOnBackEdge->GetValueInfo()->Type(); } } else @@ -1085,7 +1127,7 @@ BOOL GlobOpt::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) if (valueType.IsLikelyNumber()) { loop->likelyNumberSymsUsedBeforeDefined->Set(symStore->m_id); - if (DoAggressiveIntTypeSpec() ? valueType.IsLikelyInt() : valueType.IsInt()) + if (globOpt->DoAggressiveIntTypeSpec() ? valueType.IsLikelyInt() : valueType.IsInt()) { // Can only force int conversions in the landing pad based on likely-int values if aggressive int type // specialization is enabled @@ -1093,44 +1135,26 @@ BOOL GlobOpt::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate) } } - // Insert in landing pad - if (ldInstr->HasAnyImplicitCalls()) - { - IR::Instr * bailInstr = EnsureDisableImplicitCallRegion(loop); - - bailInstr->InsertBefore(ldInstr); - } - else if (loop->endDisableImplicitCall) - { - loop->endDisableImplicitCall->InsertBefore(ldInstr); - } - else - { - loop->landingPad->InsertAfter(ldInstr); - } - - ldInstr->ClearByteCodeOffset(); - ldInstr->SetByteCodeOffset(landingPad->GetFirstInstr()); - #if DBG_DUMP - if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldPREPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId())) + if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldPREPhase, this->globOpt->func->GetSourceContextId(), this->globOpt->func->GetLocalFunctionId())) { Output::Print(_u("** TRACE: Field PRE: field pre-loaded in landing pad of loop head #%-3d: "), loop->GetHeadBlock()->GetBlockNum()); ldInstr->Dump(); Output::Print(_u("\n")); + Output::Flush(); } #endif return true; } -void GlobOpt::PreloadPRECandidates(Loop *loop, PRECandidatesList *candidates) +void GlobOpt::PRE::PreloadPRECandidates(Loop *loop) { // Insert loads in landing pad for field PRE candidates. Iterate while(changed) // for the o.x.y cases. BOOL changed = true; - if (!candidates) + if (!candidates || !candidates->candidatesList) { return; } @@ -1140,13 +1164,18 @@ void GlobOpt::PreloadPRECandidates(Loop *loop, PRECandidatesList *candidates) while (changed) { changed = false; - FOREACH_SLIST_ENTRY_EDITING(GlobHashBucket*, candidate, (SList*)candidates, iter) + FOREACH_SLIST_ENTRY_EDITING(GlobHashBucket*, candidate, (SList*)candidates->candidatesList, iter) { if (this->PreloadPRECandidate(loop, candidate)) { changed = true; iter.RemoveCurrent(); } + if (PHASE_TRACE(Js::FieldPREPhase, this->globOpt->func)) + { + Output::Print(_u("============================\n")); + Output::Flush(); + } } NEXT_SLIST_ENTRY_EDITING; } } @@ -1158,11 +1187,8 @@ void GlobOpt::FieldPRE(Loop *loop) return; } - PRECandidatesList *candidates; - JitArenaAllocator *alloc = this->tempAlloc; - - candidates = this->FindPossiblePRECandidates(loop, alloc); - this->PreloadPRECandidates(loop, candidates); + GlobOpt::PRE pre(this); + pre.FieldPRE(loop); } void GlobOpt::InsertValueCompensation( @@ -3064,10 +3090,50 @@ GlobOpt::SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *pro return; } + StackSym * objectSym = propertySym->m_stackSym; Value *landingPadObjPtrVal, *currentObjPtrVal; - landingPadObjPtrVal = loop->landingPad->globOptData.FindValue(propertySym->m_stackSym); - currentObjPtrVal = CurrentBlockData()->FindValue(propertySym->m_stackSym); - if (!currentObjPtrVal || !landingPadObjPtrVal || currentObjPtrVal->GetValueNumber() != landingPadObjPtrVal->GetValueNumber()) + landingPadObjPtrVal = loop->landingPad->globOptData.FindValue(objectSym); + currentObjPtrVal = CurrentBlockData()->FindValue(objectSym); + + auto CanSetInitialValue = [&]() -> bool { + if (!currentObjPtrVal) + { + return false; + } + if (landingPadObjPtrVal) + { + return currentObjPtrVal->GetValueNumber() == landingPadObjPtrVal->GetValueNumber(); + } + else + { + if (!objectSym->IsSingleDef()) + { + return false; + } + IR::Instr * defInstr = objectSym->GetInstrDef(); + IR::Opnd * src1 = defInstr->GetSrc1(); + while (!(src1 && src1->IsSymOpnd() && src1->AsSymOpnd()->IsPropertySymOpnd())) + { + if (src1 && src1->IsRegOpnd() && src1->AsRegOpnd()->GetStackSym()->IsSingleDef()) + { + defInstr = src1->AsRegOpnd()->GetStackSym()->GetInstrDef(); + src1 = defInstr->GetSrc1(); + } + else + { + return false; + } + } + + return true; + + // Todo: allow other kinds of operands as src1 of instr def of the object sym of the current propertySym + // SymOpnd, but not PropertySymOpnd - LdSlotArr, some LdSlots (?) + // nullptr - NewScObject + } + }; + + if (!CanSetInitialValue()) { // objPtr has a different value in the landing pad. return; @@ -3098,6 +3164,7 @@ GlobOpt::SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *pro symStore->Dump(); Output::Print(_u("\n Instr: ")); instr->Dump(); + Output::Flush(); } #endif @@ -3666,7 +3733,7 @@ GlobOpt::CopyProp(IR::Opnd *opnd, IR::Instr *instr, Value *val, IR::IndirOpnd *p //Need to update DumpFieldCopyPropTestTrace for every new opcode that is added for fieldcopyprop if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldCopyPropPhase)) { - instr->DumpFieldCopyPropTestTrace(); + instr->DumpFieldCopyPropTestTrace(this->isRecursiveCallOnLandingPad); } #endif @@ -3807,7 +3874,7 @@ GlobOpt::CopyPropReplaceOpnd(IR::Instr * instr, IR::Opnd * opnd, StackSym * copy //Need to update DumpFieldCopyPropTestTrace for every new opcode that is added for fieldcopyprop if(Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldCopyPropPhase)) { - instr->DumpFieldCopyPropTestTrace(); + instr->DumpFieldCopyPropTestTrace(this->isRecursiveCallOnLandingPad); } #endif @@ -15903,6 +15970,20 @@ GlobOpt::IsPREInstrCandidateLoad(Js::OpCode opcode) return false; } +bool +GlobOpt::IsPREInstrSequenceCandidateLoad(Js::OpCode opcode) +{ + switch (opcode) + { + default: + return IsPREInstrCandidateLoad(opcode); + + case Js::OpCode::Ld_A: + case Js::OpCode::BytecodeArgOutCapture: + return true; + } +} + bool GlobOpt::IsPREInstrCandidateStore(Js::OpCode opcode) { @@ -17017,3 +17098,307 @@ GlobOpt::ProcessMemOp() } } NEXT_LOOP_EDITING; } + +void GlobOpt::PRE::FieldPRE(Loop *loop) +{ + JitArenaAllocator *alloc = this->globOpt->tempAlloc; + + this->FindPossiblePRECandidates(loop, alloc); + this->PreloadPRECandidates(loop); + this->RemoveOverlyOptimisticInitialValues(loop); +} + +bool +GlobOpt::PRE::InsertSymDefinitionInLandingPad(StackSym * sym, Loop * loop, Sym ** objPtrCopyPropSym) +{ + Assert(sym->IsSingleDef()); + IR::Instr * symDefInstr = sym->GetInstrDef(); + if (!GlobOpt::IsPREInstrSequenceCandidateLoad(symDefInstr->m_opcode)) + { + return false; + } + + IR::Opnd * symDefInstrSrc1 = symDefInstr->GetSrc1(); + if (symDefInstrSrc1->IsSymOpnd() && symDefInstrSrc1->AsSymOpnd()->IsPropertySymOpnd()) + { + // $L1 + // T1 = o.x (v1|T3) + // T2 = T1.y (v2|T4) <-- T1 is not live in the loop landing pad + // jmp $L1 + + // Trying to make T1 live in the landing pad + + // o.x + PropertySym* propSym = symDefInstrSrc1->AsSymOpnd()->AsPropertySymOpnd()->GetPropertySym(); + + if (candidates->candidatesBv->Test(propSym->m_id)) + { + // If propsym is a PRE candidate, then it must have had the same value on all back edges. + // So, just look up the value on one of the back edges. + + BasicBlock* loopTail = loop->GetAnyTailBlock(); + Value * valueOnBackEdge = loopTail->globOptData.FindValue(propSym); + + *objPtrCopyPropSym = valueOnBackEdge->GetValueInfo()->GetSymStore(); + + if (candidates->candidatesToProcess->Test(propSym->m_id)) + { + GlobHashBucket bucket; + bucket.element = valueOnBackEdge; + bucket.value = propSym; + if (!PreloadPRECandidate(loop, &bucket)) + { + return false; + } + Assert(!candidates->candidatesToProcess->Test(propSym->m_id)); + Assert(loop->landingPad->globOptData.IsLive(valueOnBackEdge->GetValueInfo()->GetSymStore())); + + // Inserted T3 = o.x + // Now, we want to + // 1. Insert T1 = o.x + // 2. Insert T4 = T1.y + // 3. Indentify T3 as the objptr copy prop sym for T1, and make T3.y live on the back-edges + + // #1 is done next. #2 and #3 are done as part of preloading T1.y + + // Insert T1 = o.x + if (!InsertPropertySymPreloadInLandingPad(symDefInstr, loop, propSym)) + { + return false; + } + return true; + } + else + { + // o.x was already processed as a PRE candidate. If we were successful in preloading o.x, + // we can now insert T1 = o.x + if (loop->landingPad->globOptData.IsLive(*objPtrCopyPropSym)) + { + // insert T1 = o.x + if (!InsertPropertySymPreloadInLandingPad(symDefInstr, loop, propSym)) + { + return false; + } + return true; + } + else + { + return false; + } + } + } + else + { + return false; + } + } + else if (symDefInstrSrc1->IsRegOpnd()) + { + // T2 = T1 + // T3 = T2.y + // trying to insert def of T2 + + // T1 + StackSym * symDefInstrSrc1Sym = symDefInstrSrc1->AsRegOpnd()->GetStackSym(); + if (!loop->landingPad->globOptData.IsLive(symDefInstrSrc1Sym)) + { + if (symDefInstrSrc1Sym->IsSingleDef()) + { + if (!InsertSymDefinitionInLandingPad(symDefInstrSrc1Sym, loop, objPtrCopyPropSym)) + { + return false; + } + } + } + else + { + *objPtrCopyPropSym = symDefInstrSrc1Sym; + } + + if (!(OpCodeAttr::TempNumberTransfer(symDefInstr->m_opcode) && OpCodeAttr::TempObjectTransfer(symDefInstr->m_opcode))) + { + *objPtrCopyPropSym = sym; + } + IR::Instr * instr = symDefInstr->Copy(); + if (instr->m_opcode == Js::OpCode::BytecodeArgOutCapture) + { + instr->m_opcode = Js::OpCode::Ld_A; + } + InsertInstrInLandingPad(instr, loop); + return true; + } + else + { + return false; + } +} +void +GlobOpt::PRE::InsertInstrInLandingPad(IR::Instr * instr, Loop * loop) +{ + instr->GetSrc1()->SetIsJITOptimizedReg(true); + if (instr->GetDst()) + { + instr->GetDst()->SetIsJITOptimizedReg(true); + loop->landingPad->globOptData.liveVarSyms->Set(instr->GetDst()->GetStackSym()->m_id); + } + + if (instr->HasAnyImplicitCalls()) + { + IR::Instr * bailInstr = globOpt->EnsureDisableImplicitCallRegion(loop); + + bailInstr->InsertBefore(instr); + } + else if (loop->endDisableImplicitCall) + { + loop->endDisableImplicitCall->InsertBefore(instr); + } + else + { + loop->landingPad->InsertAfter(instr); + } + + instr->ClearByteCodeOffset(); + instr->SetByteCodeOffset(loop->landingPad->GetFirstInstr()); +} + +IR::Instr * +GlobOpt::PRE::InsertPropertySymPreloadInLandingPad(IR::Instr * ldInstr, Loop * loop, PropertySym * propertySym) +{ + IR::Instr * instr = InsertPropertySymPreloadWithoutDstInLandingPad(ldInstr, loop, propertySym); + if (!instr) + { + return nullptr; + } + + if (ldInstr->GetDst()) + { + instr->SetDst(ldInstr->GetDst()->Copy(ldInstr->m_func)); + instr->GetDst()->SetIsJITOptimizedReg(true); + loop->landingPad->globOptData.liveVarSyms->Set(instr->GetDst()->GetStackSym()->m_id); + } + + return instr; +} + +IR::Instr * +GlobOpt::PRE::InsertPropertySymPreloadWithoutDstInLandingPad(IR::Instr * ldInstr, Loop * loop, PropertySym * propertySym) +{ + IR::SymOpnd *ldSrc = ldInstr->GetSrc1()->AsSymOpnd(); + + if (ldSrc->m_sym != propertySym) + { + // It's possible that the property syms are different but have equivalent objPtrs. Verify their values. + Value *val1 = globOpt->CurrentBlockData()->FindValue(ldSrc->m_sym->AsPropertySym()->m_stackSym); + Value *val2 = globOpt->CurrentBlockData()->FindValue(propertySym->m_stackSym); + if (!val1 || !val2 || val1->GetValueNumber() != val2->GetValueNumber()) + { + return nullptr; + } + } + + ldInstr = ldInstr->CopyWithoutDst(); + + // Consider: Shouldn't be necessary once we have copy-prop in prepass... + ldInstr->GetSrc1()->AsSymOpnd()->m_sym = propertySym; + ldSrc = ldInstr->GetSrc1()->AsSymOpnd(); + + if (ldSrc->IsPropertySymOpnd()) + { + IR::PropertySymOpnd *propSymOpnd = ldSrc->AsPropertySymOpnd(); + IR::PropertySymOpnd *newPropSymOpnd; + + newPropSymOpnd = propSymOpnd->AsPropertySymOpnd()->CopyWithoutFlowSensitiveInfo(this->globOpt->func); + ldInstr->ReplaceSrc1(newPropSymOpnd); + } + + InsertInstrInLandingPad(ldInstr, loop); + + return ldInstr; +} + +void +GlobOpt::PRE::MakePropertySymLiveOnBackEdges(PropertySym * propertySym, Loop * loop, Value * valueToAdd) +{ + BasicBlock * loopHeader = loop->GetHeadBlock(); + FOREACH_PREDECESSOR_BLOCK(blockPred, loopHeader) + { + if (!loop->IsDescendentOrSelf(blockPred->loop)) + { + // Not a loop back-edge + continue; + } + + // Insert it in the value table + blockPred->globOptData.SetValue(valueToAdd, propertySym); + + // Make it a live field + blockPred->globOptData.liveFields->Set(propertySym->m_id); + } NEXT_PREDECESSOR_BLOCK; +} + +void GlobOpt::PRE::RemoveOverlyOptimisticInitialValues(Loop * loop) +{ + BasicBlock * landingPad = loop->landingPad; + + // For a property sym whose obj ptr sym wasn't live in the landing pad, we can optmistically (if the obj ptr sym was + // single def) insert an initial value in the landing pad, with the hope that PRE could make the obj ptr sym live. + // But, if PRE couldn't make the obj ptr sym live, we need to clear the value for the property sym from the landing pad + + for (auto it = loop->initialValueFieldMap.GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext()) + { + PropertySym * propertySym = it.CurrentKey(); + + StackSym * objPtrSym = propertySym->m_stackSym; + if (!landingPad->globOptData.IsLive(objPtrSym)) + { + Value * landingPadPropSymValue = landingPad->globOptData.FindValue(propertySym); + Assert(landingPadPropSymValue); + Assert(landingPadPropSymValue->GetValueNumber() == it.CurrentValue()->GetValueNumber()); + Assert(landingPadPropSymValue->GetValueInfo()->GetSymStore() == propertySym); + + landingPad->globOptData.ClearSymValue(propertySym); + it.RemoveCurrent(); + } + } +} + +#if DBG_DUMP +void GlobOpt::PRE::TraceFailedPreloadInLandingPad(const Loop *const loop, PropertySym * propertySym, const char16* reason) const +{ + if (PHASE_TRACE(Js::FieldPREPhase, this->globOpt->func)) + { + int32 propertyId = propertySym->m_propertyId; + SymID objectSymId = propertySym->m_stackSym->m_id; + char16 propSymStr[32]; + switch (propertySym->m_fieldKind) + { + case PropertyKindData: + if (JITManager::GetJITManager()->IsOOPJITEnabled()) + { + swprintf_s(propSymStr, _u("s%d->#%d"), objectSymId, propertyId); + } + else + { + Js::PropertyRecord const* fieldName = propertySym->m_func->GetInProcThreadContext()->GetPropertyRecord(propertyId); + swprintf_s(propSymStr, _u("s%d->%s"), objectSymId, fieldName->GetBuffer()); + } + break; + case PropertyKindSlots: + case PropertyKindSlotArray: + swprintf_s(propSymStr, _u("s%d[%d]"), objectSymId, propertyId); + break; + case PropertyKindLocalSlots: + swprintf_s(propSymStr, _u("s%dl[%d]"), objectSymId, propertyId); + break; + default: + AssertMsg(0, "Unknown field kind"); + break; + } + + Output::Print(_u("** TRACE: Field PRE: ")); + this->globOpt->func->DumpFullFunctionName(); + Output::Print(_u(": Failed to pre-load (%s) in landing pad of loop #%d. Reason: %s "), propSymStr, loop->GetLoopNumber(), reason); + Output::Print(_u("\n")); + } +} +#endif \ No newline at end of file diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h index 18081aa28bf..b83678d82a8 100644 --- a/lib/Backend/GlobOpt.h +++ b/lib/Backend/GlobOpt.h @@ -420,6 +420,7 @@ class GlobOpt class ArrayLowerBoundCheckHoistInfo; class ArrayUpperBoundCheckHoistInfo; class ArraySrcOpt; + class PRE; friend BackwardPass; #if DBG @@ -535,12 +536,11 @@ class GlobOpt void OptLoops(Loop *loop); void TailDupPass(); bool TryTailDup(IR::BranchInstr *tailBranch); - PRECandidatesList * FindBackEdgePRECandidates(BasicBlock *block, JitArenaAllocator *alloc); - PRECandidatesList * FindPossiblePRECandidates(Loop *loop, JitArenaAllocator *alloc); - void PreloadPRECandidates(Loop *loop, PRECandidatesList *candidates); - BOOL PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate); - void SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *propertySym, PropertySym *originalPropertySym); + void FieldPRE(Loop *loop); + void SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *propertySym, PropertySym *originalPropertySym); + PRECandidates * FindBackEdgePRECandidates(BasicBlock *block, JitArenaAllocator *alloc); + void CloneBlockData(BasicBlock *const toBlock, BasicBlock *const fromBlock); void CloneValues(BasicBlock *const toBlock, GlobOptBlockData *toData, GlobOptBlockData *fromData); @@ -785,6 +785,7 @@ class GlobOpt static bool IsSwitchOptEnabledForIntTypeSpec(Func const * func); static bool DoInlineArgsOpt(Func const * func); static bool IsPREInstrCandidateLoad(Js::OpCode opcode); + static bool IsPREInstrSequenceCandidateLoad(Js::OpCode opcode); static bool IsPREInstrCandidateStore(Js::OpCode opcode); static bool ImplicitCallFlagsAllowOpts(Loop * loop); static bool ImplicitCallFlagsAllowOpts(Func const * func); @@ -1009,3 +1010,28 @@ class GlobOpt friend class InvariantBlockBackwardIterator; }; + +class GlobOpt::PRE +{ +public: + PRE(GlobOpt * globOpt) : globOpt(globOpt) {} + void FieldPRE(Loop *loop); + +private: + void FindPossiblePRECandidates(Loop *loop, JitArenaAllocator *alloc); + void PreloadPRECandidates(Loop *loop); + BOOL PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate); + IR::Instr * InsertPropertySymPreloadWithoutDstInLandingPad(IR::Instr * origLdInstr, Loop * loop, PropertySym * propertySym); + IR::Instr * InsertPropertySymPreloadInLandingPad(IR::Instr * origLdInstr, Loop * loop, PropertySym * propertySym); + void InsertInstrInLandingPad(IR::Instr * instr, Loop * loop); + bool InsertSymDefinitionInLandingPad(StackSym * sym, Loop * loop, Sym ** objPtrCopyPropSym); + void MakePropertySymLiveOnBackEdges(PropertySym * propertySym, Loop * loop, Value * valueToAdd); + void RemoveOverlyOptimisticInitialValues(Loop * loop); +#if DBG_DUMP + void TraceFailedPreloadInLandingPad(const Loop *const loop, PropertySym * propSym, const char16* reason) const; +#endif + +private: + GlobOpt * globOpt; + PRECandidates * candidates; +}; diff --git a/lib/Backend/GlobOptBlockData.cpp b/lib/Backend/GlobOptBlockData.cpp index 5d94c47d4e0..19bcc514d51 100644 --- a/lib/Backend/GlobOptBlockData.cpp +++ b/lib/Backend/GlobOptBlockData.cpp @@ -338,10 +338,10 @@ void GlobOptBlockData::CloneBlockData(BasicBlock *const toBlockContext, BasicBlo this->OnDataInitialized(alloc); } -void GlobOptBlockData::RemoveUnavailableCandidates(PRECandidatesList * candidates) +void GlobOptBlockData::RemoveUnavailableCandidates(PRECandidates * candidates) { // In case of multiple back-edges to the loop, make sure the candidates are still valid. - FOREACH_SLIST_ENTRY_EDITING(GlobHashBucket*, candidate, candidates, iter) + FOREACH_SLIST_ENTRY_EDITING(GlobHashBucket*, candidate, candidates->candidatesList, iter) { Value *candidateValue = candidate->element; PropertySym *candidatePropertySym = candidate->value->AsPropertySym(); @@ -362,6 +362,8 @@ void GlobOptBlockData::RemoveUnavailableCandidates(PRECandidatesList * candidate } iter.RemoveCurrent(); + Assert(candidates->candidatesToProcess->Test(candidatePropertySym->m_id)); + candidates->candidatesToProcess->Clear(candidatePropertySym->m_id); } NEXT_SLIST_ENTRY_EDITING; } diff --git a/lib/Backend/GlobOptBlockData.h b/lib/Backend/GlobOptBlockData.h index c0c356e78ee..a0fa76dd06f 100644 --- a/lib/Backend/GlobOptBlockData.h +++ b/lib/Backend/GlobOptBlockData.h @@ -103,6 +103,17 @@ struct StackLiteralInitFldData typedef JsUtil::BaseDictionary StackLiteralInitFldDataMap; typedef SList PRECandidatesList; +class PRECandidates +{ +public: + PRECandidates() : candidatesToProcess(nullptr), candidatesBv(nullptr), candidatesList(nullptr) {} + +public: + BVSparse * candidatesToProcess; + BVSparse * candidatesBv; + PRECandidatesList * candidatesList; +}; + class GlobOptBlockData { @@ -241,7 +252,7 @@ class GlobOptBlockData void CloneBlockData(BasicBlock *const toBlock, BasicBlock *const fromBlock); public: - void RemoveUnavailableCandidates(PRECandidatesList *candidates); + void RemoveUnavailableCandidates(PRECandidates *candidates); // Merging functions public: diff --git a/lib/Backend/IR.cpp b/lib/Backend/IR.cpp index e2adc51dd2e..92ac3d91ac4 100644 --- a/lib/Backend/IR.cpp +++ b/lib/Backend/IR.cpp @@ -448,9 +448,15 @@ Instr::SwapOpnds() m_src2 = opndTemp; } +Instr * +Instr::CopyWithoutDst() +{ + return Copy(false /*copyDst*/); +} + // Copy a vanilla instruction. Instr * -Instr::Copy() +Instr::Copy(bool copyDst) { Instr * instrCopy; @@ -491,7 +497,7 @@ Instr::Copy() } Opnd * opnd = this->GetDst(); - if (opnd) + if (copyDst && opnd) { instrCopy->SetDst(opnd->Copy(this->m_func)); } @@ -4247,7 +4253,7 @@ Instr::DumpTestTrace() ///---------------------------------------------------------------------------- void -Instr::DumpFieldCopyPropTestTrace() +Instr::DumpFieldCopyPropTestTrace(bool inLandingPad) { switch (m_opcode) { @@ -4264,9 +4270,15 @@ Instr::DumpFieldCopyPropTestTrace() case Js::OpCode::TypeofElem: char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; - Output::Print(_u("TestTrace fieldcopyprop: function %s (%s) "), + Output::Print(_u("TestTrace fieldcopyprop")); + if (inLandingPad) + { + Output::Print(_u(" [%s]"), _u("in landing pad")); + } + Output::Print(_u(": function %s (%s) "), this->m_func->GetJITFunctionBody()->GetDisplayName(), this->m_func->GetDebugNumberSet(debugStringBuffer)); + if (this->IsInlined()) { Output::Print(_u("inlined caller function %s (%s) "), diff --git a/lib/Backend/IR.h b/lib/Backend/IR.h index ac131f7cf15..197fde64767 100644 --- a/lib/Backend/IR.h +++ b/lib/Backend/IR.h @@ -280,7 +280,8 @@ class Instr void SwapOpnds(); void TransferTo(Instr * instr); void TransferDstAttributesTo(Instr * instr); - IR::Instr * Copy(); + IR::Instr * Copy(bool copyDst = true); + IR::Instr * CopyWithoutDst(); IR::Instr * Clone(); IR::Instr * ConvertToBailOutInstr(IR::Instr * bailOutTarget, BailOutKind kind, uint32 bailOutOffset = Js::Constants::NoByteCodeOffset); IR::Instr * ConvertToBailOutInstr(BailOutInfo * bailOutInfo, BailOutKind kind, bool useAuxBailout = false); @@ -327,7 +328,7 @@ class Instr #endif #if ENABLE_DEBUG_CONFIG_OPTIONS void DumpTestTrace(); - void DumpFieldCopyPropTestTrace(); + void DumpFieldCopyPropTestTrace(bool inLandingPad); #endif uint32 GetByteCodeOffset() const; uint32 GetNumber() const; diff --git a/lib/Backend/ValueInfo.h b/lib/Backend/ValueInfo.h index 80fecfa232b..f26f1a57485 100644 --- a/lib/Backend/ValueInfo.h +++ b/lib/Backend/ValueInfo.h @@ -55,15 +55,15 @@ class ValueInfo : protected ValueType } public: - static ValueInfo * New(JitArenaAllocator *const alloc, const ValueType type) + static ValueInfo * New(JitArenaAllocator *const alloc, const ValueType type) { return JitAnew(alloc, ValueInfo, type, ValueStructureKind::Generic); } - static ValueInfo * MergeLikelyIntValueInfo(JitArenaAllocator* alloc, Value *toDataVal, Value *fromDataVal, const ValueType newValueType); - static ValueInfo * NewIntRangeValueInfo(JitArenaAllocator* alloc, int32 min, int32 max, bool wasNegativeZeroPreventedByBailout); + static ValueInfo * MergeLikelyIntValueInfo(JitArenaAllocator* alloc, Value *toDataVal, Value *fromDataVal, const ValueType newValueType); + static ValueInfo * NewIntRangeValueInfo(JitArenaAllocator* alloc, int32 min, int32 max, bool wasNegativeZeroPreventedByBailout); - const ValueType & Type() const { return *this; } - ValueType & Type() { return *this; } + const ValueType & Type() const { return *this; } + ValueType & Type() { return *this; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ValueType imports. Only importing functions that are appropriate to be called on Value. diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index 6d17abf3dcb..51b9ca35482 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -142,6 +142,7 @@ PHASE(All) PHASE(Invariants) PHASE(FieldCopyProp) PHASE(FieldPRE) + PHASE(MakeObjSymLiveInLandingPad) PHASE(HostOpt) PHASE(ObjTypeSpec) PHASE(ObjTypeSpecNewObj) diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index ffebd04f89b..6bcb1bf057e 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -1939,7 +1939,7 @@ namespace Js Var value = nullptr; if (JavascriptOperators::GetRootProperty(RecyclableObject::FromVar(instance), propertyId, &value, scriptContext, info)) { - if (scriptContext->IsUndeclBlockVar(value)) + if (scriptContext->IsUndeclBlockVar(value) && scriptContext->GetThreadContext()->RecordImplicitException()) { JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration); } diff --git a/test/PRE/pre1.baseline b/test/PRE/pre1.baseline new file mode 100644 index 00000000000..a6e777ae6d2 --- /dev/null +++ b/test/PRE/pre1.baseline @@ -0,0 +1,54 @@ + +test1 +TestTrace fieldcopyprop [in landing pad]: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop [in landing pad]: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop [in landing pad]: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop [in landing pad]: function test1 ( (#1.1), #2) opcode: LdFld field: y +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: y +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: y +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: x +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: w +TestTrace fieldcopyprop: function test1 ( (#1.1), #2) opcode: LdFld field: z + +test2 +TestTrace fieldcopyprop [in landing pad]: function test2 ( (#1.2), #3) opcode: LdFld field: x + +testInlined +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: count +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: count +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +undefined +2001 +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop [in landing pad]: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: count +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: count +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop: function inlinee ( (#1.3), #4) inlined caller function testInlined ( (#1.4), #5) opcode: LdFld field: FORWARD +2001 + +test3 +10 +10 +TestTrace fieldcopyprop [in landing pad]: function Plan.prototype.size ( (#1.11), #12) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: v +TestTrace fieldcopyprop [in landing pad]: function Plan.prototype.execute ( (#1.13), #14) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop [in landing pad]: function Plan.prototype.constraintAt ( (#1.12), #13) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: v +TestTrace fieldcopyprop [in landing pad]: function OrderedCollection.prototype.at ( (#1.8), #9) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: elms +TestTrace fieldcopyprop: function Plan.prototype.size ( (#1.11), #12) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: v +TestTrace fieldcopyprop: function OrderedCollection.prototype.size ( (#1.7), #8) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: elms +TestTrace fieldcopyprop: function Plan.prototype.constraintAt ( (#1.12), #13) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: v +TestTrace fieldcopyprop: function OrderedCollection.prototype.at ( (#1.8), #9) inlined caller function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: elms +TestTrace fieldcopyprop: function Plan.prototype.execute ( (#1.13), #14) opcode: LdRootFld field: Direction +TestTrace fieldcopyprop: function Plan.prototype.execute ( (#1.13), #14) opcode: LdFld field: FORWARD +10 diff --git a/test/PRE/pre1.js b/test/PRE/pre1.js new file mode 100644 index 00000000000..8e192666aa5 --- /dev/null +++ b/test/PRE/pre1.js @@ -0,0 +1,127 @@ +function test1(o) +{ + var count = 0; + for(var i=0;i<10;i++) + { + count += o.x.y; + count += o.x.y; + count += o.x.w; + count += o.z; + } + return count; +} +var o = {x:{y:2, w:3}, z:1}; +print("\ntest1"); +test1(o); +test1(o); +test1(o); + +function test2(o) +{ + var a=0;var b =0; + o.x; + for(var i=0;i<10;i++) + { + o.x++ + } + return b; +} +o.x=1; +print("\ntest2"); +test2(o) +test2(o) +test2(o) + +var Direction = new Object(); +Direction.NONE = 0; +Direction.FORWARD = 1; +Direction.BACKWARD = -1; + +function inlinee(o) +{ + o.count += Direction.FORWARD; + return true; +} +function testInlined(o) +{ + + for(var i = 0; i < 1000; i++) + { + if(!inlinee(o)) + { + o.count = 1; + } + inlinee(o); + } +} + +print("\ntestInlined"); +var obj = {count:0, dir:1}; + +obj.count= 1; +o.dir=1; +testInlined(obj); +print(o.count); + +obj.count = 1; +obj.dir =2; +testInlined(obj); +print(obj.count); + +obj.count = 1; +obj.dir =1; +testInlined(obj); +print(obj.count); + + +function test3() +{ + function OrderedCollection() { + this.elms = new Array(); + } + + OrderedCollection.prototype.size = function() { + return this.elms.length; + } + + OrderedCollection.prototype.at = function (index) { + return this.elms[index]; + } + + function Plan() { + this.v = new OrderedCollection(); + } + + Plan.prototype.add = function(c){ + this.v.elms.push(c); + } + Plan.prototype.size = function () { + return this.v.size(); + } + + Plan.prototype.constraintAt = function (index) { + return this.v.at(index); + } + + Plan.prototype.execute = function() + { + var count = 0; + for (var i = 0; i < this.size(); i++) { + this.constraintAt(i); + count += Direction.FORWARD; + } + return count; + } + + var plan = new Plan(); + for(var i = 0; i < 10; i++) + { + plan.add(1); + } + print(plan.execute()); + print(plan.execute()); + print(plan.execute()); +} +print("\ntest3"); +test3(); + diff --git a/test/PRE/rlexe.xml b/test/PRE/rlexe.xml new file mode 100644 index 00000000000..c8000a10e80 --- /dev/null +++ b/test/PRE/rlexe.xml @@ -0,0 +1,11 @@ + + + + + pre1.js + pre1.baseline + exclude_dynapogo + -testtrace:fieldcopyprop -oopjit- + + + \ No newline at end of file diff --git a/test/rlexedirs.xml b/test/rlexedirs.xml index 84b0b7b5b21..bade87e7281 100644 --- a/test/rlexedirs.xml +++ b/test/rlexedirs.xml @@ -350,4 +350,9 @@ require_wasm,exclude_serialized,require_backend,exclude_xplat + + + PRE + + From e1904e385c3ada7b9beb374ba4f502829545f916 Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Wed, 25 Apr 2018 15:46:00 -0700 Subject: [PATCH 2/5] Aggressively transfer values of bytecode constants in loop prepass --- lib/Backend/GlobOpt.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 744dd25534d..1389307b7b9 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -5523,6 +5523,18 @@ GlobOpt::IsPrepassSrcValueInfoPrecise(IR::Opnd *const src, Value *const srcValue *isSafeToTransferInPrepass = false; } + if (src->IsAddrOpnd() && + srcValue->GetValueInfo()->GetSymStore() && + srcValue->GetValueInfo()->GetSymStore()->IsStackSym() && + srcValue->GetValueInfo()->GetSymStore()->AsStackSym()->IsFromByteCodeConstantTable()) + { + if (isSafeToTransferInPrepass) + { + *isSafeToTransferInPrepass = false; + } + return true; + } + if (!src->IsRegOpnd() || !srcValue) { return false; From 185c9f65b5d547ef780a83e86361e2ad3c28376a Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Sun, 6 May 2018 10:13:03 -0700 Subject: [PATCH 3/5] Copy prop for argument sym at InlineeEnd should check if the copy-prop candidate is live at InlineeStart --- lib/Backend/GlobOptBailOut.cpp | 10 +++++++--- lib/Backend/InlineeFrameInfo.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Backend/GlobOptBailOut.cpp b/lib/Backend/GlobOptBailOut.cpp index 8ac82da67f3..90a2140001f 100644 --- a/lib/Backend/GlobOptBailOut.cpp +++ b/lib/Backend/GlobOptBailOut.cpp @@ -558,8 +558,9 @@ GlobOpt::TrackCalls(IR::Instr * instr) instr->m_func->m_hasInlineArgsOpt = true; InlineeFrameInfo* frameInfo = InlineeFrameInfo::New(func->m_alloc); instr->m_func->frameInfo = frameInfo; - frameInfo->floatSyms = currentBlock->globOptData.liveFloat64Syms->CopyNew(this->alloc); - frameInfo->intSyms = currentBlock->globOptData.liveInt32Syms->MinusNew(currentBlock->globOptData.liveLossyInt32Syms, this->alloc); + frameInfo->floatSyms = CurrentBlockData()->liveFloat64Syms->CopyNew(this->alloc); + frameInfo->intSyms = CurrentBlockData()->liveInt32Syms->MinusNew(CurrentBlockData()->liveLossyInt32Syms, this->alloc); + frameInfo->varSyms = CurrentBlockData()->liveVarSyms->CopyNew(this->alloc); } break; @@ -769,7 +770,8 @@ void GlobOpt::RecordInlineeFrameInfo(IR::Instr* inlineeEnd) if (value) { StackSym * copyPropSym = this->currentBlock->globOptData.GetCopyPropSym(argSym, value); - if (copyPropSym) + if (copyPropSym && + frameInfo->varSyms->TestEmpty() && frameInfo->varSyms->Test(copyPropSym->m_id)) { argSym = copyPropSym; } @@ -814,6 +816,8 @@ void GlobOpt::RecordInlineeFrameInfo(IR::Instr* inlineeEnd) frameInfo->intSyms = nullptr; JitAdelete(this->alloc, frameInfo->floatSyms); frameInfo->floatSyms = nullptr; + JitAdelete(this->alloc, frameInfo->varSyms); + frameInfo->varSyms = nullptr; frameInfo->isRecorded = true; } diff --git a/lib/Backend/InlineeFrameInfo.h b/lib/Backend/InlineeFrameInfo.h index 99f82e61c59..9c3c76afab4 100644 --- a/lib/Backend/InlineeFrameInfo.h +++ b/lib/Backend/InlineeFrameInfo.h @@ -149,6 +149,7 @@ struct InlineeFrameInfo InlineeFrameRecord* record; BVSparse* floatSyms; BVSparse* intSyms; + BVSparse* varSyms; bool isRecorded; From 3647daf4167c9f8805b49d228d03456157b6875e Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Mon, 7 May 2018 15:54:18 -0700 Subject: [PATCH 4/5] fixing newly added tests --- test/PRE/pre1.js | 5 +++++ test/rlexedirs.xml | 1 + 2 files changed, 6 insertions(+) diff --git a/test/PRE/pre1.js b/test/PRE/pre1.js index 8e192666aa5..29ad8e0966c 100644 --- a/test/PRE/pre1.js +++ b/test/PRE/pre1.js @@ -1,3 +1,8 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + function test1(o) { var count = 0; diff --git a/test/rlexedirs.xml b/test/rlexedirs.xml index bade87e7281..e3d86d0f054 100644 --- a/test/rlexedirs.xml +++ b/test/rlexedirs.xml @@ -353,6 +353,7 @@ PRE + require_backend From 0c15d3898abd40ae9eb87dcc33213c41ad8cb67a Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Fri, 11 May 2018 11:03:35 -0700 Subject: [PATCH 5/5] Field PRE for multi-level field loads involving LdSlot/LdSlotArr --- lib/Backend/GlobOpt.cpp | 7 ++++--- lib/Runtime/Library/JavascriptArray.inl | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 1389307b7b9..7271a1ca0ad 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -3112,7 +3112,7 @@ GlobOpt::SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *pro } IR::Instr * defInstr = objectSym->GetInstrDef(); IR::Opnd * src1 = defInstr->GetSrc1(); - while (!(src1 && src1->IsSymOpnd() && src1->AsSymOpnd()->IsPropertySymOpnd())) + while (!(src1 && src1->IsSymOpnd() && src1->AsSymOpnd()->m_sym->IsPropertySym())) { if (src1 && src1->IsRegOpnd() && src1->AsRegOpnd()->GetStackSym()->IsSingleDef()) { @@ -17131,8 +17131,9 @@ GlobOpt::PRE::InsertSymDefinitionInLandingPad(StackSym * sym, Loop * loop, Sym * } IR::Opnd * symDefInstrSrc1 = symDefInstr->GetSrc1(); - if (symDefInstrSrc1->IsSymOpnd() && symDefInstrSrc1->AsSymOpnd()->IsPropertySymOpnd()) + if (symDefInstrSrc1->IsSymOpnd()) { + Assert(symDefInstrSrc1->AsSymOpnd()->m_sym->IsPropertySym()); // $L1 // T1 = o.x (v1|T3) // T2 = T1.y (v2|T4) <-- T1 is not live in the loop landing pad @@ -17141,7 +17142,7 @@ GlobOpt::PRE::InsertSymDefinitionInLandingPad(StackSym * sym, Loop * loop, Sym * // Trying to make T1 live in the landing pad // o.x - PropertySym* propSym = symDefInstrSrc1->AsSymOpnd()->AsPropertySymOpnd()->GetPropertySym(); + PropertySym* propSym = symDefInstrSrc1->AsSymOpnd()->m_sym->AsPropertySym(); if (candidates->candidatesBv->Test(propSym->m_id)) { diff --git a/lib/Runtime/Library/JavascriptArray.inl b/lib/Runtime/Library/JavascriptArray.inl index 953182c792d..6aaacf7e9aa 100644 --- a/lib/Runtime/Library/JavascriptArray.inl +++ b/lib/Runtime/Library/JavascriptArray.inl @@ -974,12 +974,12 @@ SECOND_PASS: LinkSegments((Js::SparseArraySegment*)startPrev, current); current->length = startOffset + length + growby; current->CheckLengthvsSize(); - } - if (current == head && HasNoMissingValues()) - { - if (ScanForMissingValues(startOffset + length, current->length)) + if (current == head && HasNoMissingValues()) { - SetHasNoMissingValues(false); + if (ScanForMissingValues(startOffset + length, current->length)) + { + SetHasNoMissingValues(false); + } } } }