| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "Backend.h"
- // Peeps::PeepFunc
- // Run peeps on this function. For now, it just cleans the redundant reloads
- // from the register allocator, and reg/reg movs. The code tracks which sym is
- // in which registers on extended basic blocks.
- void
- Peeps::PeepFunc()
- {
- bool peepsEnabled = true;
- if (PHASE_OFF(Js::PeepsPhase, this->func))
- {
- peepsEnabled = false;
- }
- #if defined(_M_IX86) || defined(_M_X64)
- // Agen dependency elimination pass
- // Since it can reveal load elimination opportunities for the normal peeps pass, we do it separately.
- this->peepsAgen.PeepFunc();
- #endif
- this->peepsMD.Init(this);
- // Init regMap
- memset(this->regMap, 0, sizeof(this->regMap));
- // Scratch field needs to be cleared.
- this->func->m_symTable->ClearStackSymScratch();
- bool isInHelper = false;
- FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
- {
- switch (instr->GetKind())
- {
- case IR::InstrKindLabel:
- case IR::InstrKindProfiledLabel:
- {
- if (!peepsEnabled)
- {
- break;
- }
- // Don't carry any regMap info across label
- this->ClearRegMap();
- // Remove unreferenced labels
- if (instr->AsLabelInstr()->IsUnreferenced())
- {
- bool peeped;
- instrNext = PeepUnreachableLabel(instr->AsLabelInstr(), isInHelper, &peeped);
- if(peeped)
- {
- continue;
- }
- }
- else
- {
- // Try to peep a previous branch again after dead label blocks are removed. For instance:
- // jmp L2
- // L3:
- // // dead code
- // L2:
- // L3 is unreferenced, so after that block is removed, the branch-to-next can be removed. After that, if L2 is
- // unreferenced and only has fallthrough, it can be removed as well.
- IR::Instr *const prevInstr = instr->GetPrevRealInstr();
- if(prevInstr->IsBranchInstr())
- {
- IR::BranchInstr *const branch = prevInstr->AsBranchInstr();
- if(branch->IsUnconditional() && !branch->IsMultiBranch() && branch->GetTarget() == instr)
- {
- bool peeped;
- IR::Instr *const branchNext = branch->m_next;
- IR::Instr *const branchNextAfterPeep = PeepBranch(branch, &peeped);
- if(peeped || branchNextAfterPeep != branchNext)
- {
- // The peep did something, restart from after the branch
- instrNext = branchNextAfterPeep;
- continue;
- }
- }
- }
- }
- isInHelper = instr->AsLabelInstr()->isOpHelper;
- if (instrNext->IsLabelInstr())
- {
- // CLean up double label
- instrNext = this->CleanupLabel(instr->AsLabelInstr(), instrNext->AsLabelInstr());
- }
- #if defined(_M_IX86) || defined(_M_X64)
- Assert(instrNext->IsLabelInstr() || instrNext->m_prev->IsLabelInstr());
- IR::LabelInstr *const peepCondMoveLabel =
- instrNext->IsLabelInstr() ? instrNext->AsLabelInstr() : instrNext->m_prev->AsLabelInstr();
- instrNext = PeepCondMove(peepCondMoveLabel, instrNext, isInHelper && peepCondMoveLabel->isOpHelper);
- #endif
- break;
- }
- case IR::InstrKindBranch:
- if (!peepsEnabled || instr->m_opcode == Js::OpCode::Leave)
- {
- break;
- }
- instrNext = Peeps::PeepBranch(instr->AsBranchInstr());
- #if defined(_M_IX86) || defined(_M_X64)
- Assert(instrNext && instrNext->m_prev);
- if (instrNext->m_prev->IsBranchInstr())
- {
- instrNext = this->HoistSameInstructionAboveSplit(instrNext->m_prev->AsBranchInstr(), instrNext);
- }
- #endif
- break;
- case IR::InstrKindPragma:
- if (instr->m_opcode == Js::OpCode::Nop)
- {
- instr->Remove();
- }
- break;
- default:
- if (LowererMD::IsAssign(instr))
- {
- if (!peepsEnabled)
- {
- break;
- }
- // Cleanup spill code
- instrNext = this->PeepAssign(instr);
- }
- else if (instr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn
- || instr->m_opcode == Js::OpCode::StartCall
- || instr->m_opcode == Js::OpCode::LoweredStartCall)
- {
- // ArgOut/StartCall are normally lowered by the lowering of the associated call instr.
- // If the call becomes unreachable, we could end up with an orphan ArgOut or StartCall.
- // Just delete these StartCalls
- instr->Remove();
- }
- #if defined(_M_IX86) || defined(_M_X64)
- else if (instr->m_opcode == Js::OpCode::MOVSD_ZERO)
- {
- this->peepsMD.PeepAssign(instr);
- IR::Opnd *dst = instr->GetDst();
- // Look for explicit reg kills
- if (dst && dst->IsRegOpnd())
- {
- this->ClearReg(dst->AsRegOpnd()->GetReg());
- }
- }
- else if ( (instr->m_opcode == Js::OpCode::INC ) || (instr->m_opcode == Js::OpCode::DEC) )
- {
- // Check for any of the following patterns which can cause partial flag dependency
- //
- // Jcc or SHL or SHR or SAR or SHLD(in case of x64)
- // Jcc or SHL or SHR or SAR or SHLD(in case of x64) Any Instruction
- // INC or DEC INC or DEC
- // -------------------------------------------------- OR -----------------------
- // INC or DEC INC or DEC
- // Jcc or SHL or SHR or SAR or SHLD(in case of x64) Any Instruction
- // Jcc or SHL or SHR or SAR or SHLD(in case of x64)
- //
- // With this optimization if any of the above pattern found, substitute INC/DEC with ADD/SUB respectively.
- if (!peepsEnabled)
- {
- break;
- }
- if (AutoSystemInfo::Data.IsAtomPlatform() || PHASE_FORCE(Js::AtomPhase, this->func))
- {
- bool pattern_found=false;
- if ( !(instr->IsEntryInstr()) )
- {
- IR::Instr *prevInstr = instr->GetPrevRealInstr();
- if ( IsJccOrShiftInstr(prevInstr) )
- {
- pattern_found = true;
- }
- else if ( !(prevInstr->IsEntryInstr()) && IsJccOrShiftInstr(prevInstr->GetPrevRealInstr()) )
- {
- pattern_found=true;
- }
- }
- if ( !pattern_found && !(instr->IsExitInstr()) )
- {
- IR::Instr *nextInstr = instr->GetNextRealInstr();
- if ( IsJccOrShiftInstr(nextInstr) )
- {
- pattern_found = true;
- }
- else if ( !(nextInstr->IsExitInstr() ) && (IsJccOrShiftInstr(nextInstr->GetNextRealInstr())) )
- {
- pattern_found = true;
- }
- }
- if (pattern_found)
- {
- IR::IntConstOpnd* constOne = IR::IntConstOpnd::New((IntConstType) 1, instr->GetDst()->GetType(), instr->m_func);
- IR::Instr * addOrSubInstr = IR::Instr::New(Js::OpCode::ADD, instr->GetDst(), instr->GetDst(), constOne, instr->m_func);
- if (instr->m_opcode == Js::OpCode::DEC)
- {
- addOrSubInstr->m_opcode = Js::OpCode::SUB;
- }
- instr->InsertAfter(addOrSubInstr);
- instr->Remove();
- instr = addOrSubInstr;
- }
- }
- IR::Opnd *dst = instr->GetDst();
- // Look for explicit reg kills
- if (dst && dst->IsRegOpnd())
- {
- this->ClearReg(dst->AsRegOpnd()->GetReg());
- }
- }
- #endif
- else
- {
- if (!peepsEnabled)
- {
- break;
- }
- #if defined(_M_IX86) || defined(_M_X64)
- instr = this->PeepRedundant(instr);
- #endif
- IR::Opnd *dst = instr->GetDst();
- // Look for explicit reg kills
- if (dst && dst->IsRegOpnd())
- {
- this->ClearReg(dst->AsRegOpnd()->GetReg());
- }
- // Kill callee-saved regs across calls and other implicit regs
- this->peepsMD.ProcessImplicitRegs(instr);
- #if defined(_M_IX86) || defined(_M_X64)
- if (instr->m_opcode == Js::OpCode::TEST && instr->GetSrc2()->IsIntConstOpnd()
- && ((instr->GetSrc2()->AsIntConstOpnd()->GetValue() & 0xFFFFFF00) == 0)
- && instr->GetSrc1()->IsRegOpnd() && (LinearScan::GetRegAttribs(instr->GetSrc1()->AsRegOpnd()->GetReg()) & RA_BYTEABLE))
- {
- // Only support if the branch is JEQ or JNE to ensure we don't look at the sign flag
- if (instrNext->IsBranchInstr() &&
- (instrNext->m_opcode == Js::OpCode::JNE || instrNext->m_opcode == Js::OpCode::JEQ))
- {
- instr->GetSrc1()->SetType(TyInt8);
- instr->GetSrc2()->SetType(TyInt8);
- }
- }
- if (instr->m_opcode == Js::OpCode::CVTSI2SD)
- {
- IR::Instr *xorps = IR::Instr::New(Js::OpCode::XORPS, instr->GetDst(), instr->GetDst(), instr->GetDst(), instr->m_func);
- instr->InsertBefore(xorps);
- }
- #endif
- }
- }
- } NEXT_INSTR_IN_FUNC_EDITING;
- }
- #if defined(_M_IX86) || defined(_M_X64)
- // Peeps::IsJccOrShiftInstr()
- // Check if instruction is any of the Shift or conditional jump variant
- bool
- Peeps::IsJccOrShiftInstr(IR::Instr *instr)
- {
- bool instrFound = (instr->IsBranchInstr() && instr->AsBranchInstr()->IsConditional()) ||
- (instr->m_opcode == Js::OpCode::SHL) || (instr->m_opcode == Js::OpCode::SHR) || (instr->m_opcode == Js::OpCode::SAR);
- #if defined(_M_X64)
- instrFound = instrFound || (instr->m_opcode == Js::OpCode::SHLD);
- #endif
- return (instrFound);
- }
- #endif
- // Peeps::PeepAssign
- // Remove useless MOV reg, reg as well as redundant reloads
- IR::Instr *
- Peeps::PeepAssign(IR::Instr *assign)
- {
- IR::Opnd *dst = assign->GetDst();
- IR::Opnd *src = assign->GetSrc1();
- IR::Instr *instrNext = assign->m_next;
- // MOV reg, sym
- if (src->IsSymOpnd() && src->AsSymOpnd()->m_offset == 0)
- {
- AssertMsg(src->AsSymOpnd()->m_sym->IsStackSym(), "Only expect stackSyms at this point");
- StackSym *sym = src->AsSymOpnd()->m_sym->AsStackSym();
- if (sym->scratch.peeps.reg != RegNOREG)
- {
- // Found a redundant load
- AssertMsg(this->regMap[sym->scratch.peeps.reg] == sym, "Something is wrong...");
- assign->ReplaceSrc1(IR::RegOpnd::New(sym, sym->scratch.peeps.reg, src->GetType(), this->func));
- src = assign->GetSrc1();
- }
- else
- {
- // Keep track of this load
- AssertMsg(dst->IsRegOpnd(), "For now, we assume dst = sym means dst is a reg");
- RegNum reg = dst->AsRegOpnd()->GetReg();
- this->SetReg(reg, sym);
- return instrNext;
- }
- }
- if (dst->IsRegOpnd())
- {
- // MOV reg, reg
- // Useless?
- if (src->IsRegOpnd() && src->AsRegOpnd()->IsSameReg(dst))
- {
- assign->Remove();
- if (instrNext->m_prev->IsBranchInstr())
- {
- return this->PeepBranch(instrNext->m_prev->AsBranchInstr());
- }
- else
- {
- return instrNext;
- }
- }
- else
- {
- // We could copy the a of the src, but we don't have
- // a way to track 2 regs on the sym... So let's just clear
- // the info of the dst.
- RegNum dstReg = dst->AsRegOpnd()->GetReg();
- this->ClearReg(dstReg);
- }
- }
- else if (dst->IsSymOpnd() && dst->AsSymOpnd()->m_offset == 0 && src->IsRegOpnd())
- {
- // MOV Sym, Reg
- // Track this reg
- RegNum reg = src->AsRegOpnd()->GetReg();
- StackSym *sym = dst->AsSymOpnd()->m_sym->AsStackSym();
- this->SetReg(reg, sym);
- }
- this->peepsMD.PeepAssign(assign);
- return instrNext;
- }
- // Peeps::ClearRegMap
- // Empty the regMap.
- // Note: might be faster to have a count and exit if zero?
- void
- Peeps::ClearRegMap()
- {
- for (RegNum reg = (RegNum)(RegNOREG+1); reg != RegNumCount; reg = (RegNum)(reg+1))
- {
- this->ClearReg(reg);
- }
- }
- // Peeps::SetReg
- // Track that this sym is live in this reg
- void
- Peeps::SetReg(RegNum reg, StackSym *sym)
- {
- this->ClearReg(reg);
- if (sym->m_isClosureSym)
- {
- return;
- }
- this->ClearReg(sym->scratch.peeps.reg);
- this->regMap[reg] = sym;
- sym->scratch.peeps.reg = reg;
- }
- // Peeps::ClearReg
- void
- Peeps::ClearReg(RegNum reg)
- {
- StackSym *sym = this->regMap[reg];
- if (sym)
- {
- AssertMsg(sym->scratch.peeps.reg == reg, "Something is wrong here...");
- sym->scratch.peeps.reg = RegNOREG;
- this->regMap[reg] = NULL;
- }
- }
- // Peeps::PeepBranch
- // Remove branch-to-next
- // Invert condBranch/uncondBranch/label
- // Retarget branch to branch
- IR::Instr *
- Peeps::PeepBranch(IR::BranchInstr *branchInstr, bool *const peepedRef)
- {
- if(peepedRef)
- {
- *peepedRef = false;
- }
- IR::LabelInstr *targetInstr = branchInstr->GetTarget();
- IR::Instr *instrNext;
- if (branchInstr->IsUnconditional())
- {
- // Cleanup unreachable code after unconditional branch
- instrNext = RemoveDeadBlock(branchInstr->m_next);
- }
- instrNext = branchInstr->GetNextRealInstrOrLabel();
- if (instrNext != NULL && instrNext->IsLabelInstr())
- {
- //
- // Remove branch-to-next
- //
- if (targetInstr == instrNext)
- {
- if (!branchInstr->IsLowered())
- {
- if (branchInstr->HasAnyImplicitCalls())
- {
- Assert(!branchInstr->m_func->GetJITFunctionBody()->IsAsmJsMode());
- // if (x > y) might trigger a call to valueof() or something for x and y.
- // We can't just delete them.
- Js::OpCode newOpcode;
- switch(branchInstr->m_opcode)
- {
- case Js::OpCode::BrEq_A:
- case Js::OpCode::BrNeq_A:
- case Js::OpCode::BrNotEq_A:
- case Js::OpCode::BrNotNeq_A:
- newOpcode = Js::OpCode::DeadBrEqual;
- break;
- case Js::OpCode::BrSrEq_A:
- case Js::OpCode::BrSrNeq_A:
- case Js::OpCode::BrSrNotEq_A:
- case Js::OpCode::BrSrNotNeq_A:
- newOpcode = Js::OpCode::DeadBrSrEqual;
- break;
- case Js::OpCode::BrGe_A:
- case Js::OpCode::BrGt_A:
- case Js::OpCode::BrLe_A:
- case Js::OpCode::BrLt_A:
- case Js::OpCode::BrNotGe_A:
- case Js::OpCode::BrNotGt_A:
- case Js::OpCode::BrNotLe_A:
- case Js::OpCode::BrNotLt_A:
- case Js::OpCode::BrUnGe_A:
- case Js::OpCode::BrUnGt_A:
- case Js::OpCode::BrUnLe_A:
- case Js::OpCode::BrUnLt_A:
- newOpcode = Js::OpCode::DeadBrRelational;
- break;
- case Js::OpCode::BrOnHasProperty:
- case Js::OpCode::BrOnNoProperty:
- newOpcode = Js::OpCode::DeadBrOnHasProperty;
- break;
- default:
- Assert(UNREACHED);
- newOpcode = Js::OpCode::Nop;
- }
- IR::Instr *newInstr = IR::Instr::New(newOpcode, branchInstr->m_func);
- newInstr->SetSrc1(branchInstr->GetSrc1());
- newInstr->SetSrc2(branchInstr->GetSrc2());
- branchInstr->InsertBefore(newInstr);
- newInstr->SetByteCodeOffset(branchInstr);
- }
- else if (branchInstr->GetSrc1() && !branchInstr->GetSrc2())
- {
- // We only have cases with one src
- Assert(branchInstr->GetSrc1()->IsRegOpnd());
- IR::RegOpnd *regSrc = branchInstr->GetSrc1()->AsRegOpnd();
- StackSym *symSrc = regSrc->GetStackSym();
- if (symSrc->HasByteCodeRegSlot() && !regSrc->GetIsJITOptimizedReg())
- {
- // No side-effects to worry about, but need to insert bytecodeUse.
- IR::ByteCodeUsesInstr *byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(branchInstr);
- byteCodeUsesInstr->Set(regSrc);
- branchInstr->InsertBefore(byteCodeUsesInstr);
- }
- }
- }
- // Note: if branch is conditional, we have a dead test/cmp left behind...
- if(peepedRef)
- {
- *peepedRef = true;
- }
- branchInstr->Remove();
- if (targetInstr->IsUnreferenced())
- {
- // We may have exposed an unreachable label by deleting the branch
- instrNext = Peeps::PeepUnreachableLabel(targetInstr, false);
- }
- else
- {
- instrNext = targetInstr;
- }
- IR::Instr *instrPrev = instrNext->GetPrevRealInstrOrLabel();
- if (instrPrev->IsBranchInstr())
- {
- // The branch removal could have exposed a branch to next opportunity.
- return Peeps::PeepBranch(instrPrev->AsBranchInstr());
- }
- return instrNext;
- }
- }
- else if (branchInstr->IsConditional())
- {
- AnalysisAssert(instrNext);
- if (instrNext->IsBranchInstr()
- && instrNext->AsBranchInstr()->IsUnconditional()
- && targetInstr == instrNext->AsBranchInstr()->GetNextRealInstrOrLabel()
- && !instrNext->AsBranchInstr()->IsMultiBranch())
- {
- //
- // Invert condBranch/uncondBranch/label:
- //
- // JCC L1 JinvCC L3
- // JMP L3 =>
- // L1:
- IR::BranchInstr *uncondBranch = instrNext->AsBranchInstr();
- if (branchInstr->IsLowered())
- {
- LowererMD::InvertBranch(branchInstr);
- }
- else
- {
- branchInstr->Invert();
- }
- targetInstr = uncondBranch->GetTarget();
- branchInstr->SetTarget(targetInstr);
- if (targetInstr->IsUnreferenced())
- {
- Peeps::PeepUnreachableLabel(targetInstr, false);
- }
- uncondBranch->Remove();
- return PeepBranch(branchInstr, peepedRef);
- }
- }
- if(branchInstr->IsMultiBranch())
- {
- IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
- multiBranchInstr->UpdateMultiBrLabels([=](IR::LabelInstr * targetInstr) -> IR::LabelInstr *
- {
- IR::LabelInstr * labelInstr = RetargetBrToBr(branchInstr, targetInstr);
- return labelInstr;
- });
- }
- else
- {
- RetargetBrToBr(branchInstr, targetInstr);
- }
- return branchInstr->m_next;
- }
- #if defined(_M_IX86) || defined(_M_X64)
- //
- // For conditional branch JE $LTarget, if both target and fallthrough branch has the same
- // instruction B, hoist it up and tail dup target branch:
- //
- // A <unconditional branch>
- // JE $LTarget $LTarget:
- // B B
- // ... JMP $L2
- //
- //====> hoist B up: move B up from fallthrough branch, remove B in target branch, retarget to $L2
- // $LTarget to $L2
- //
- // A <unconditional branch>
- // B $LTarget:
- // JE $L2 JMP $L2
- // ...
- //
- //====> now $LTarget becomes to be an empty BB, which can be removed if it's an unreferenced label
- //
- // A
- // B
- // JE $L2
- // ...
- //
- //====> Note B will be hoist above compare instruction A if there are no dependency between A and B
- //
- // B
- // A (cmp instr)
- // JE $L2
- // ...
- //
- IR::Instr *
- Peeps::HoistSameInstructionAboveSplit(IR::BranchInstr *branchInstr, IR::Instr *instrNext)
- {
- Assert(branchInstr);
- if (!branchInstr->IsConditional() || branchInstr->IsMultiBranch() || !branchInstr->IsLowered())
- {
- return instrNext; // this optimization only applies to single conditional branch
- }
- IR::LabelInstr *targetLabel = branchInstr->GetTarget();
- Assert(targetLabel);
- // give up if there are other branch entries to the target label
- if (targetLabel->labelRefs.Count() > 1)
- {
- return instrNext;
- }
- // Give up if previous instruction before target label has fallthrough, cannot hoist up
- IR::Instr *targetPrev = targetLabel->GetPrevRealInstrOrLabel();
- Assert(targetPrev);
- if (targetPrev->HasFallThrough())
- {
- return instrNext;
- }
- IR::Instr *instrSetCondition = NULL;
- IR::Instr *branchPrev = branchInstr->GetPrevRealInstrOrLabel();
- while (branchPrev && !branchPrev->StartsBasicBlock())
- {
- if (!instrSetCondition && EncoderMD::SetsConditionCode(branchPrev))
- { // located compare instruction for the branch
- instrSetCondition = branchPrev;
- }
- branchPrev = branchPrev->GetPrevRealInstrOrLabel(); // keep looking previous instr in BB
- }
- if (branchPrev && branchPrev->IsLabelInstr() && branchPrev->AsLabelInstr()->isOpHelper)
- { // don't apply the optimization when branch is in helper section
- return instrNext;
- }
- if (!instrSetCondition)
- { // give up if we cannot find the compare instruction in the BB, should be very rare
- return instrNext;
- }
- Assert(instrSetCondition);
- bool hoistAboveSetConditionInstr = false;
- if (instrSetCondition == branchInstr->GetPrevRealInstrOrLabel())
- { // if compare instruction is right before branch instruction, we can hoist above cmp instr
- hoistAboveSetConditionInstr = true;
- } // otherwise we hoist the identical instructions above conditional branch split only
- IR::Instr *instr = branchInstr->GetNextRealInstrOrLabel();
- IR::Instr *targetInstr = targetLabel->GetNextRealInstrOrLabel();
- IR::Instr *branchNextInstr = NULL;
- IR::Instr *targetNextInstr = NULL;
- IR::Instr *instrHasHoisted = NULL;
- Assert(instr && targetInstr);
- while (!instr->EndsBasicBlock() && !instr->IsLabelInstr() && instr->IsEqual(targetInstr) &&
- !EncoderMD::UsesConditionCode(instr) && !EncoderMD::SetsConditionCode(instr) &&
- !this->peepsAgen.DependentInstrs(instrSetCondition, instr) &&
- // cannot hoist InlineeStart from branch targets even for the same inlinee function.
- // it is used by encoder to generate InlineeFrameRecord for each inlinee
- instr->m_opcode != Js::OpCode::InlineeStart)
- {
- branchNextInstr = instr->GetNextRealInstrOrLabel();
- targetNextInstr = targetInstr->GetNextRealInstrOrLabel();
- instr->Unlink(); // hoist up instr in fallthrough branch
- if (hoistAboveSetConditionInstr)
- {
- instrSetCondition->InsertBefore(instr); // hoist above compare instruction
- }
- else
- {
- branchInstr->InsertBefore(instr); // hoist above branch split
- }
- targetInstr->Remove(); // remove the same instruction in target branch
- if (!instrHasHoisted)
- instrHasHoisted = instr; // points to the first hoisted instruction
- instr = branchNextInstr;
- targetInstr = targetNextInstr;
- Assert(instr && targetInstr);
- }
- if (instrHasHoisted)
- { // instructions have been hoisted, now check tail branch to see if it can be duplicated
- if (targetInstr->IsBranchInstr())
- {
- IR::BranchInstr *tailBranch = targetInstr->AsBranchInstr();
- if (tailBranch->IsUnconditional() && !tailBranch->IsMultiBranch())
- { // target can be replaced since tail branch is a single unconditional jmp
- branchInstr->ReplaceTarget(targetLabel, tailBranch->GetTarget());
- }
- // now targeLabel is an empty Basic Block, remove it if it's not referenced
- if (targetLabel->IsUnreferenced())
- {
- Peeps::PeepUnreachableLabel(targetLabel, targetLabel->isOpHelper);
- }
- }
- return instrHasHoisted;
- }
- return instrNext;
- }
- #endif
- IR::LabelInstr *
- Peeps::RetargetBrToBr(IR::BranchInstr *branchInstr, IR::LabelInstr * targetInstr)
- {
- AnalysisAssert(targetInstr);
- IR::Instr *targetInstrNext = targetInstr->GetNextRealInstr();
- AnalysisAssertMsg(targetInstrNext, "GetNextRealInstr() failed to get next target");
- // Removing branch to branch breaks some lexical assumptions about loop in sccliveness/linearscan/second chance.
- if (!branchInstr->IsLowered())
- {
- return targetInstr;
- }
- //
- // Retarget branch-to-branch
- //
- #if DBG
- uint counter = 0;
- #endif
- IR::LabelInstr *lastLoopTop = NULL;
- while (true)
- {
- // There's very few cases where we can safely follow a branch chain with intervening instrs.
- // One of them, which comes up occasionally, is where there is a copy of a single-def symbol
- // to another single-def symbol which is only used for the branch instruction (i.e. one dead
- // after the branch). Another is where a single-def symbol is declared of a constant (e.g. a
- // symbol created to store "True"
- // Unfortuantely, to properly do this, we'd need to do it somewhere else (i.e. not in peeps)
- // and make use of the additional information that we'd have there. Having the flow graph or
- // just any more information about variable liveness is necessary to determine that the load
- // instructions between jumps can be safely skipped.
- // The general case where this would be useful, on a higher level, is where a long statement
- // containing many branches returns a value; the branching here can put the result into some
- // different stacksym at each level, meaning that there'd be a load between each branch. The
- // result is that we don't currently optimize it.
- IR::BranchInstr *branchAtTarget = nullptr;
- if (targetInstrNext->IsBranchInstr())
- {
- branchAtTarget = targetInstrNext->AsBranchInstr();
- }
- else
- {
- // We don't have the information here to decide whether or not to continue the branch chain.
- break;
- }
- // This used to just be a targetInstrNext->AsBranchInstr()->IsUnconditional(), but, in order
- // to optimize further, it became necessary to handle more than just unconditional jumps. In
- // order to keep the code relatively clean, the "is it an inherently-taken jump chain" check
- // code now follows here:
- if (!targetInstrNext->AsBranchInstr()->IsUnconditional())
- {
- bool safetofollow = false;
- if(targetInstrNext->m_opcode == branchInstr->m_opcode)
- {
- // If it's the same branch instruction, with the same arguments, the branch decision should,
- // similarly, be the same. There's a bit more that can be done with this (e.g. for inverted,
- // but otherwise similar instructions like brTrue and brFalse, the destination could go down
- // the other path), but this is something that should probably be done more generally, so we
- // can optimize branch chains that have other interesting bechaviors.
- if (
- (
- (branchInstr->GetSrc1() && targetInstrNext->GetSrc1() && branchInstr->GetSrc1()->IsEqual(targetInstrNext->GetSrc1())) ||
- !(branchInstr->GetSrc1() || targetInstrNext->GetSrc1())
- ) && (
- (branchInstr->GetSrc2() && targetInstrNext->GetSrc2() && branchInstr->GetSrc2()->IsEqual(targetInstrNext->GetSrc2())) ||
- !(branchInstr->GetSrc2() || targetInstrNext->GetSrc2())
- )
- )
- {
- safetofollow = true;
- }
- }
- if (!safetofollow)
- {
- // We can't say safely that this branch is something that we can implicitly take, so instead
- // cut off the branch chain optimization here.
- break;
- }
- }
- // We don't want to skip the loop entry, unless we're right before the encoder
- if (targetInstr->m_isLoopTop && !branchAtTarget->IsLowered())
- {
- break;
- }
- if (targetInstr->m_isLoopTop)
- {
- if (targetInstr == lastLoopTop)
- {
- // We are back to a loopTop already visited.
- // Looks like an infinite loop somewhere...
- break;
- }
- lastLoopTop = targetInstr;
- }
- #if DBG
- if (!branchInstr->IsMultiBranch() && branchInstr->GetTarget()->m_noHelperAssert && !branchAtTarget->IsMultiBranch())
- {
- branchAtTarget->GetTarget()->m_noHelperAssert = true;
- }
- AssertMsg(++counter < 10000, "We should not be looping this many times!");
- #endif
- IR::LabelInstr * reTargetLabel = branchAtTarget->GetTarget();
- AnalysisAssert(reTargetLabel);
- if (targetInstr == reTargetLabel)
- {
- // Infinite loop.
- // JCC $L1
- // L1:
- // JMP $L1
- break;
- }
- if(branchInstr->IsMultiBranch())
- {
- IR::MultiBranchInstr * multiBranchInstr = branchInstr->AsMultiBrInstr();
- multiBranchInstr->ChangeLabelRef(targetInstr, reTargetLabel);
- }
- else
- {
- branchInstr->SetTarget(reTargetLabel);
- }
- if (targetInstr->IsUnreferenced())
- {
- Peeps::PeepUnreachableLabel(targetInstr, false);
- }
- targetInstr = reTargetLabel;
- targetInstrNext = targetInstr->GetNextRealInstr();
- }
- return targetInstr;
- }
- IR::Instr *
- Peeps::PeepUnreachableLabel(IR::LabelInstr *deadLabel, const bool isInHelper, bool *const peepedRef)
- {
- Assert(deadLabel);
- Assert(deadLabel->IsUnreferenced());
- IR::Instr *prevFallthroughInstr = deadLabel;
- do
- {
- prevFallthroughInstr = prevFallthroughInstr->GetPrevRealInstrOrLabel();
- // The previous dead label may have been kept around due to a StatementBoundary, see comment in RemoveDeadBlock.
- } while(prevFallthroughInstr->IsLabelInstr() && prevFallthroughInstr->AsLabelInstr()->IsUnreferenced());
- IR::Instr *instrReturn;
- bool removeLabel;
- // If code is now unreachable, delete block
- if (!prevFallthroughInstr->HasFallThrough())
- {
- bool wasStatementBoundaryKeptInDeadBlock = false;
- instrReturn = RemoveDeadBlock(deadLabel->m_next, &wasStatementBoundaryKeptInDeadBlock);
- // Remove label only if we didn't have to keep last StatementBoundary in the dead block,
- // see comment in RemoveDeadBlock.
- removeLabel = !wasStatementBoundaryKeptInDeadBlock;
- if(peepedRef)
- {
- *peepedRef = true;
- }
- }
- else
- {
- instrReturn = deadLabel->m_next;
- removeLabel =
- deadLabel->isOpHelper == isInHelper
- #if DBG
- && !deadLabel->m_noHelperAssert
- #endif
- ;
- if(peepedRef)
- {
- *peepedRef = removeLabel;
- }
- }
- if (removeLabel && deadLabel->IsUnreferenced())
- {
- deadLabel->Remove();
- }
- return instrReturn;
- }
- IR::Instr *
- Peeps::CleanupLabel(IR::LabelInstr * instr, IR::LabelInstr * instrNext)
- {
- IR::Instr * returnInstr;
- IR::LabelInstr * labelToRemove;
- IR::LabelInstr * labelToKeep;
- // Just for dump, always keep loop top labels
- // We also can remove label that has non branch references
- if (instrNext->m_isLoopTop || instrNext->m_hasNonBranchRef)
- {
- if (instr->m_isLoopTop || instr->m_hasNonBranchRef)
- {
- // Don't remove loop top labels or labels with non branch references
- return instrNext;
- }
- labelToRemove = instr;
- labelToKeep = instrNext;
- returnInstr = instrNext;
- }
- else
- {
- labelToRemove = instrNext;
- labelToKeep = instr;
- returnInstr = instrNext->m_next;
- }
- while (!labelToRemove->labelRefs.Empty())
- {
- bool replaced = labelToRemove->labelRefs.Head()->ReplaceTarget(labelToRemove, labelToKeep);
- Assert(replaced);
- }
- if (labelToRemove->isOpHelper)
- {
- labelToKeep->isOpHelper = true;
- #if DBG
- if (labelToRemove->m_noHelperAssert)
- {
- labelToKeep->m_noHelperAssert = true;
- }
- #endif
- }
- labelToRemove->Remove();
- return returnInstr;
- }
- //
- // Removes instrs starting from one specified by the 'instr' parameter.
- // Keeps last statement boundary in the whole block to remove.
- // Stops at label or exit instr.
- // Return value:
- // - 1st instr that is label or end instr, except the case when forceRemoveFirstInstr is true, in which case
- // we start checking for exit loop condition from next instr.
- // Notes:
- // - if wasStmtBoundaryKeptInDeadBlock is not NULL, it receives true when we didn't remove last
- // StatementBoundary pragma instr as otherwise it would be non-helper/helper move of the pragma instr.
- // If there was no stmt boundary or last stmt boundary moved to after next label, that would receive false.
- //
- IR::Instr *Peeps::RemoveDeadBlock(IR::Instr *instr, bool* wasStmtBoundaryKeptInDeadBlock /* = nullptr */)
- {
- IR::Instr* lastStatementBoundary = nullptr;
- while (instr && !instr->IsLabelInstr() && !instr->IsExitInstr())
- {
- IR::Instr *deadInstr = instr;
- instr = instr->m_next;
- if (deadInstr->IsPragmaInstr() && deadInstr->m_opcode == Js::OpCode::StatementBoundary)
- {
- if (lastStatementBoundary)
- {
- //Its enough if we keep latest statement boundary. Rest are dead anyway.
- lastStatementBoundary->Remove();
- }
- lastStatementBoundary = deadInstr;
- }
- else
- {
- deadInstr->Remove();
- }
- }
- // Do not let StatementBoundary to move across non-helper and helper blocks, very important under debugger:
- // if we let that happen, helper block can be moved to the end of the func so that statement maps will miss one statement.
- // Issues can be when (normally, StatementBoundary should never belong to a helper label):
- // - if we remove the label and prev label is a helper, StatementBoundary will be moved inside helper.
- // - if we move StatementBoundary under next label which is a helper, same problem again.
- bool canMoveStatementBoundaryUnderNextLabel = instr && instr->IsLabelInstr() && !instr->AsLabelInstr()->isOpHelper;
- if (lastStatementBoundary && canMoveStatementBoundaryUnderNextLabel)
- {
- lastStatementBoundary->Unlink();
- instr->InsertAfter(lastStatementBoundary);
- }
- if (wasStmtBoundaryKeptInDeadBlock)
- {
- *wasStmtBoundaryKeptInDeadBlock = lastStatementBoundary && !canMoveStatementBoundaryUnderNextLabel;
- }
- return instr;
- }
- // Shared code for x86 and amd64
- #if defined(_M_IX86) || defined(_M_X64)
- IR::Instr *
- Peeps::PeepRedundant(IR::Instr *instr)
- {
- IR::Instr *retInstr = instr;
- if (instr->m_opcode == Js::OpCode::ADD || instr->m_opcode == Js::OpCode::SUB || instr->m_opcode == Js::OpCode::OR)
- {
- Assert(instr->GetSrc1() && instr->GetSrc2());
- if( (instr->GetSrc2()->IsIntConstOpnd() && instr->GetSrc2()->AsIntConstOpnd()->GetValue() == 0))
- {
- // remove instruction
- retInstr = instr->m_next;
- instr->Remove();
- }
- }
- #if _M_IX86
- RegNum edx = RegEDX;
- #else
- RegNum edx = RegRDX;
- #endif
- if (instr->m_opcode == Js::OpCode::NOP && instr->GetDst() != NULL
- && instr->GetDst()->IsRegOpnd() && instr->GetDst()->AsRegOpnd()->GetReg() == edx)
- {
- // dummy def used for non-32bit ovf check for IMUL
- // check edx is not killed between IMUL and edx = NOP, then remove the NOP
- bool found = false;
- IR::Instr *nopInstr = instr;
- do
- {
- instr = instr->GetPrevRealInstrOrLabel();
- if (
- instr->m_opcode == Js::OpCode::IMUL ||
- (instr->m_opcode == Js::OpCode::CALL && this->func->GetJITFunctionBody()->IsWasmFunction())
- )
- {
- found = true;
- break;
- }
- } while(!instr->StartsBasicBlock());
- if (found)
- {
- retInstr = nopInstr->m_next;
- nopInstr->Remove();
- }
- else
- {
- instr = nopInstr;
- do
- {
- instr = instr->GetNextRealInstrOrLabel();
- if (instr->m_opcode == Js::OpCode::DIV)
- {
- found = true;
- retInstr = nopInstr->m_next;
- nopInstr->Remove();
- break;
- }
- } while (!instr->EndsBasicBlock());
- AssertMsg(found, "edx = NOP without an IMUL or DIV");
- }
- }
- return retInstr;
- }
- /*
- Work backwards from the label instruction to look for this pattern:
- Jcc $Label
- Mov
- Mov
- ..
- Mov
- Label:
- If found, we remove the Jcc, convert MOVs to CMOVcc and remove the label if unreachable.
- */
- IR::Instr*
- Peeps::PeepCondMove(IR::LabelInstr *labelInstr, IR::Instr *nextInstr, const bool isInHelper)
- {
- IR::Instr *instr = labelInstr->GetPrevRealInstrOrLabel();
- Js::OpCode newOpCode = Js::OpCode::InvalidOpCode;
- // Check if BB is all MOVs with both RegOpnd
- while(instr->m_opcode == Js::OpCode::MOV)
- {
- if (!instr->GetSrc1()->IsRegOpnd() || !instr->GetDst()->IsRegOpnd())
- return nextInstr;
- instr = instr->GetPrevRealInstrOrLabel();
- }
- // Did we hit a conditional branch ?
- if (instr->IsBranchInstr() && instr->AsBranchInstr()->IsConditional() &&
- !instr->AsBranchInstr()->IsMultiBranch() &&
- instr->AsBranchInstr()->GetTarget() == labelInstr &&
- instr->m_opcode != Js::OpCode::Leave)
- {
- IR::BranchInstr *brInstr = instr->AsBranchInstr();
- // Get the correct CMOVcc
- switch(brInstr->m_opcode)
- {
- case Js::OpCode::JA:
- newOpCode = Js::OpCode::CMOVBE;
- break;
- case Js::OpCode::JAE:
- newOpCode = Js::OpCode::CMOVB;
- break;
- case Js::OpCode::JB:
- newOpCode = Js::OpCode::CMOVAE;
- break;
- case Js::OpCode::JBE:
- newOpCode = Js::OpCode::CMOVA;
- break;
- case Js::OpCode::JEQ:
- newOpCode = Js::OpCode::CMOVNE;
- break;
- case Js::OpCode::JNE:
- newOpCode = Js::OpCode::CMOVE;
- break;
- case Js::OpCode::JNP:
- newOpCode = Js::OpCode::CMOVP;
- break;
- case Js::OpCode::JLT:
- newOpCode = Js::OpCode::CMOVGE;
- break;
- case Js::OpCode::JLE:
- newOpCode = Js::OpCode::CMOVG;
- break;
- case Js::OpCode::JGT:
- newOpCode = Js::OpCode::CMOVLE;
- break;
- case Js::OpCode::JGE:
- newOpCode = Js::OpCode::CMOVL;
- break;
- case Js::OpCode::JNO:
- newOpCode = Js::OpCode::CMOVO;
- break;
- case Js::OpCode::JO:
- newOpCode = Js::OpCode::CMOVNO;
- break;
- case Js::OpCode::JP:
- newOpCode = Js::OpCode::CMOVNP;
- break;
- case Js::OpCode::JNSB:
- newOpCode = Js::OpCode::CMOVS;
- break;
- case Js::OpCode::JSB:
- newOpCode = Js::OpCode::CMOVNS;
- break;
- default:
- Assert(UNREACHED);
- __assume(UNREACHED);
- }
- // convert the entire block to CMOVs
- instr = brInstr->GetNextRealInstrOrLabel();
- while(instr != labelInstr)
- {
- instr->m_opcode = newOpCode;
- instr = instr->GetNextRealInstrOrLabel();
- }
- // remove the Jcc
- brInstr->Remove();
- // We may have exposed an unreachable label by deleting the branch
- if (labelInstr->IsUnreferenced())
- return Peeps::PeepUnreachableLabel(labelInstr, isInHelper);
- }
- return nextInstr;
- }
- #endif
|