@@ -4427,6 +4427,309 @@ BasicBlock::CleanUpValueMaps()
44274427 }
44284428}
44294429
4430+ bool IsLegalOpcodeForPathDepBrFold (IR::Instr *instr)
4431+ {
4432+ if (!instr->IsRealInstr ())
4433+ {
4434+ return true ;
4435+ }
4436+ switch (instr->m_opcode )
4437+ {
4438+ case Js::OpCode::Ld_A:
4439+ case Js::OpCode::Ld_I4:
4440+ case Js::OpCode::ByteCodeUses:
4441+ return true ;
4442+ }
4443+ #if DBG
4444+ if (PHASE_TRACE (Js::PathDepBranchFoldingPhase, instr->m_func ) && Js::Configuration::Global.flags .Verbose )
4445+ {
4446+ Output::Print (_u (" Skipping PathDependentBranchFolding due to: " ));
4447+ instr->Dump ();
4448+ }
4449+ #endif
4450+ return false ;
4451+ }
4452+
4453+ IR::LabelInstr* BasicBlock::CanProveConditionalBranch (IR::BranchInstr *branch, GlobOpt* globOpt, GlobHashTable * localSymToValueMap)
4454+ {
4455+ if (!branch->GetSrc1 () || !branch->GetSrc1 ()->GetStackSym ())
4456+ {
4457+ return nullptr ;
4458+ }
4459+
4460+ Value *src1Val = nullptr , *src2Val = nullptr ;
4461+ Js::Var src1Var = nullptr , src2Var = nullptr ;
4462+
4463+ auto FindValueInLocalThenGlobalValueTable = [&](StackSym *sym) -> Value *
4464+ {
4465+ Value ** srcValPtr = localSymToValueMap->Get (sym);
4466+ Value * srcVal = nullptr ;
4467+ if (!srcValPtr)
4468+ {
4469+ srcVal = this ->globOptData .FindValue (sym);
4470+ }
4471+ else
4472+ {
4473+ srcVal = *srcValPtr;
4474+ }
4475+ if (!srcVal)
4476+ {
4477+ return nullptr ;
4478+ }
4479+ return srcVal;
4480+ };
4481+
4482+ src1Val = FindValueInLocalThenGlobalValueTable (branch->GetSrc1 ()->GetStackSym ());
4483+ if (!src1Val)
4484+ {
4485+ return nullptr ;
4486+ }
4487+ src1Var = globOpt->GetConstantVar (branch->GetSrc1 (), src1Val);
4488+
4489+ if (branch->GetSrc2 () != nullptr )
4490+ {
4491+ if (branch->GetSrc2 ()->GetStackSym ())
4492+ {
4493+ src2Val = FindValueInLocalThenGlobalValueTable (branch->GetSrc2 ()->GetStackSym ());
4494+ }
4495+ if (!src2Val)
4496+ {
4497+ return nullptr ;
4498+ }
4499+ src2Var = globOpt->GetConstantVar (branch->GetSrc2 (), src2Val);
4500+ }
4501+
4502+ bool provenTrue;
4503+ if (!globOpt->CanProveConditionalBranch (branch, src1Val, src2Val, src1Var, src2Var, &provenTrue))
4504+ {
4505+ return nullptr ;
4506+ }
4507+
4508+ IR::LabelInstr * newTarget = provenTrue ? branch->GetTarget () : branch->GetNextRealInstrOrLabel ()->AsLabelInstr ();
4509+
4510+ if (PHASE_TRACE (Js::PathDepBranchFoldingPhase, this ->func ))
4511+ {
4512+ Output::Print (_u (" TRACE PathDependentBranchFolding: " ));
4513+ Output::Print (_u (" Can prove retarget of branch in Block %d from Block %d to Block %d in func %s\n " ),
4514+ this ->GetBlockNum (),
4515+ this ->GetLastInstr ()->IsBranchInstr () ? this ->GetLastInstr ()->AsBranchInstr ()->GetTarget ()->GetBasicBlock ()->GetBlockNum () : this ->GetNext ()->GetBlockNum (),
4516+ newTarget->GetBasicBlock ()->GetBlockNum (),
4517+ this ->func ->GetJITFunctionBody ()->GetDisplayName ());
4518+ Output::Flush ();
4519+ }
4520+ return newTarget;
4521+ }
4522+
4523+ bool
4524+ BasicBlock::CheckLegalityAndFoldPathDepBranches (GlobOpt* globOpt)
4525+ {
4526+ IR::LabelInstr * lastBranchTarget = nullptr ;
4527+
4528+ // For the block we start processing from, we maintain a valueTable, we go over the instrs in the block
4529+ // If we find a Ld_A, we find if the src1 already has a value in the local valueTable, if not we use the value from the pred's valueTable merge
4530+ // For other instrs, we assign null value
4531+ GlobHashTable * localSymToValueMap = GlobHashTable::New (globOpt->alloc , 8 );
4532+
4533+ FOREACH_INSTR_IN_BLOCK (instr, this )
4534+ {
4535+ if (OpCodeAttr::HasDeadFallThrough (instr->m_opcode ))
4536+ {
4537+ return false ;
4538+ }
4539+ if (instr->GetDst ())
4540+ {
4541+ if (instr->GetDst ()->GetStackSym ())
4542+ {
4543+ if (instr->m_opcode == Js::OpCode::Ld_A)
4544+ {
4545+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4546+ if (instr->GetSrc1 ()->GetSym ())
4547+ {
4548+ Value **localSrc1Value = localSymToValueMap->Get (instr->GetSrc1 ()->GetSym ());
4549+ *localValue = localSrc1Value == nullptr ? this ->globOptData .FindValue (instr->GetSrc1 ()->GetSym ()) : *localSrc1Value;
4550+ }
4551+ }
4552+ else
4553+ {
4554+ // complex instr, can't track value, insert nullptr
4555+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4556+ *localValue = nullptr ;
4557+ }
4558+ }
4559+ }
4560+ } NEXT_INSTR_IN_BLOCK;
4561+
4562+ IR::Instr * instr = this ->GetLastInstr ();
4563+ IR::LabelInstr * currentLabel = nullptr ;
4564+ /* We start from the current instruction and go on scanning for legality, as long as it is legal to skip an instruction, skip.
4565+ * When we see an unconditional branch, start scanning from the branchTarget
4566+ * When we see a conditional branch, check if we can prove the branch target, if we can, adjust the flowgraph, and continue in the direction of the proven target
4567+ * We stop, when we no longer can skip instructions, either due to legality check or a non provable conditional branch
4568+ */
4569+ while (instr)
4570+ {
4571+ if (!instr->IsBranchInstr () && !instr->IsLabelInstr () && !IsLegalOpcodeForPathDepBrFold (instr))
4572+ {
4573+ return lastBranchTarget != nullptr ;
4574+ }
4575+ if (OpCodeAttr::HasDeadFallThrough (instr->m_opcode )) // BailOnNoProfile etc
4576+ {
4577+ return lastBranchTarget != nullptr ;
4578+ }
4579+ if (instr->GetDst ())
4580+ {
4581+ if (!instr->GetDst ()->IsRegOpnd ()) // complex dstOpnd, stop.
4582+ {
4583+ return lastBranchTarget != nullptr ;
4584+ }
4585+ IR::RegOpnd *dst = instr->GetDst ()->AsRegOpnd ();
4586+ if (currentLabel)
4587+ {
4588+ BasicBlock * currentBlock = currentLabel->GetBasicBlock ();
4589+ // If the dstOpnd is defined and used in the same block, it is legal, but if the successor blocks use it, stop.
4590+ if (currentBlock->successorBlockUses && currentBlock->successorBlockUses ->Test (dst->GetStackSym ()->m_id ))
4591+ {
4592+ return lastBranchTarget != nullptr ;
4593+ }
4594+ }
4595+ if (instr->GetSrc1 ())
4596+ {
4597+ // If the src of the load instruction is produced in the currentBlock, its value will not be available in the valuetable, check in local valuetable
4598+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4599+ if (instr->GetSrc1 ()->GetSym ())
4600+ {
4601+ Value **localSrc1Value = localSymToValueMap->Get (instr->GetSrc1 ()->GetSym ());
4602+ *localValue = localSrc1Value == nullptr ? this ->globOptData .FindValue (instr->GetSrc1 ()->GetSym ()) : *localSrc1Value;
4603+ }
4604+ else
4605+ {
4606+ ValueType src1Value = instr->GetSrc1 ()->GetValueType ();
4607+ if (src1Value.IsUndefined ())
4608+ {
4609+ *localValue = globOpt->NewGenericValue (ValueType::Undefined, instr->GetDst ());
4610+ }
4611+ // MGTODO : handle other constants
4612+ else
4613+ {
4614+ return lastBranchTarget != nullptr ;
4615+ }
4616+ }
4617+ }
4618+ }
4619+ if (instr->IsLabelInstr ())
4620+ {
4621+ if (instr->AsLabelInstr ()->m_isLoopTop )
4622+ {
4623+ // don't cross over to loops
4624+ return lastBranchTarget != nullptr ;
4625+ }
4626+ currentLabel = instr->AsLabelInstr ();
4627+ }
4628+ if (instr->IsBranchInstr ())
4629+ {
4630+ IR::BranchInstr* branch = instr->AsBranchInstr ();
4631+ IR::LabelInstr* branchTarget = nullptr ;
4632+
4633+ if (branch->IsUnconditional ())
4634+ {
4635+ branchTarget = branch->GetTarget ();
4636+ if (!branchTarget)
4637+ {
4638+ return lastBranchTarget != nullptr ;
4639+ }
4640+ if (branchTarget->m_isLoopTop )
4641+ {
4642+ return lastBranchTarget != nullptr ;
4643+ }
4644+ }
4645+ else
4646+ {
4647+ if (branch->GetTarget ()->m_isLoopTop )
4648+ {
4649+ return lastBranchTarget != nullptr ;
4650+ }
4651+ branchTarget = CanProveConditionalBranch (branch, globOpt, localSymToValueMap);
4652+ if (!branchTarget)
4653+ {
4654+ return lastBranchTarget != nullptr ;
4655+ }
4656+ }
4657+
4658+ if (this ->GetLastInstr ()->IsBranchInstr () && (this ->GetLastInstr ()->AsBranchInstr ()->GetTarget () == branchTarget))
4659+ {
4660+ // happens on the first block we start from only
4661+ lastBranchTarget = branchTarget;
4662+ instr = lastBranchTarget;
4663+ continue ;
4664+ }
4665+ if (branchTarget != this ->GetLastInstr ()->GetNextRealInstrOrLabel ())
4666+ {
4667+ IR::Instr* lastInstr = this ->GetLastInstr ();
4668+ IR::BranchInstr * newBranch = IR::BranchInstr::New (Js::OpCode::Br, branchTarget, branchTarget->m_func );
4669+ newBranch->SetByteCodeOffset (lastInstr);
4670+ if (lastInstr->IsBranchInstr ())
4671+ {
4672+ globOpt->ConvertToByteCodeUses (lastInstr);
4673+ }
4674+ this ->GetLastInstr ()->InsertAfter (newBranch);
4675+ globOpt->func ->m_fg ->AddEdge (this , branchTarget->GetBasicBlock ());
4676+ this ->IncrementDataUseCount ();
4677+ }
4678+ else
4679+ {
4680+ // If the new target is a fall through label, delete the branch
4681+ globOpt->ConvertToByteCodeUses (this ->GetLastInstr ());
4682+ }
4683+ // We are adding an unconditional branch, go over all the current successors and remove the ones that are dead now
4684+ FOREACH_SUCCESSOR_BLOCK_EDITING (blockSucc, this , iter)
4685+ {
4686+ if (branchTarget != blockSucc->GetFirstInstr ()->AsLabelInstr ())
4687+ {
4688+ // Change the old succ edge to dead
4689+ this ->RemoveDeadSucc (blockSucc, globOpt->func ->m_fg );
4690+ if (this ->GetDataUseCount () > 0 )
4691+ {
4692+ this ->DecrementDataUseCount ();
4693+ }
4694+ if (blockSucc->GetPredList ()->Count () == 0 )
4695+ {
4696+ this ->func ->m_fg ->RemoveBlock (blockSucc, globOpt);
4697+ }
4698+ }
4699+ }NEXT_SUCCESSOR_BLOCK_EDITING;
4700+ lastBranchTarget = branchTarget;
4701+ instr = lastBranchTarget;
4702+ }
4703+ else
4704+ {
4705+ instr = instr->m_next ;
4706+ }
4707+ }
4708+
4709+ return lastBranchTarget != nullptr ;
4710+ }
4711+
4712+ bool
4713+ BasicBlock::PathDepBranchFolding (GlobOpt* globOpt)
4714+ {
4715+ if (PHASE_OFF (Js::PathDepBranchFoldingPhase, this ->func ))
4716+ {
4717+ return false ;
4718+ }
4719+ if (globOpt->IsLoopPrePass ())
4720+ {
4721+ return false ;
4722+ }
4723+ if (func->IsOOPJIT () || !CONFIG_FLAG (OOPJITMissingOpts))
4724+ {
4725+ return false ;
4726+ }
4727+
4728+ CheckLegalityAndFoldPathDepBranches (globOpt);
4729+
4730+ return true ;
4731+ }
4732+
44304733void
44314734BasicBlock::MergePredBlocksValueMaps (GlobOpt* globOpt)
44324735{
0 commit comments