Skip to content

Commit c6759d6

Browse files
author
Meghana Gupta
committed
[MERGE chakra-core#5051 @meg-gupta] Path dependent branch folding
Merge pull request chakra-core#5051 from meg-gupta:pathDepBrFold This optimization redirects branches to their eventual destination, when we can prove the result of all indermediate branches, and is legal to skip all the intermediate instructions. Example : ``` function foo(x) { var testValue = 100; if (x > 100) { testValue = 0; // At this block we can directly redirect the branch to return block, instead of doing following test again } if (testValue != 0) { print(testValue); } ``` This helps in efficient merge of valueInfos, so that out global optimizer can aggressively work on blocks, for which it was previously limited due to conservative merge of valueInfos. This is the first part of the optimization, a second implementation supporting additional instructions.
2 parents 3fd052e + df60efd commit c6759d6

15 files changed

+878
-74
lines changed

lib/Backend/BackwardPass.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,10 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
11441144
}
11451145
}
11461146
block->upwardExposedUses = upwardExposedUses;
1147+
if (this->tag == Js::BackwardPhase)
1148+
{
1149+
block->successorBlockUses = upwardExposedUses ? upwardExposedUses->CopyNew(this->globOpt->alloc) : nullptr;
1150+
}
11471151
block->upwardExposedFields = upwardExposedFields;
11481152
block->typesNeedingKnownObjectLayout = typesNeedingKnownObjectLayout;
11491153
block->byteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed;

lib/Backend/FlowGraph.cpp

+303
Original file line numberDiff line numberDiff line change
@@ -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+
44304733
void
44314734
BasicBlock::MergePredBlocksValueMaps(GlobOpt* globOpt)
44324735
{

lib/Backend/FlowGraph.h

+15
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,9 @@ class BasicBlock
349349

350350
// GlobOpt Stuff
351351
public:
352+
IR::LabelInstr* CanProveConditionalBranch(IR::BranchInstr *branch, GlobOpt* globOpt, GlobHashTable * localSymToValueMap);
353+
bool CheckLegalityAndFoldPathDepBranches(GlobOpt* globOpt);
354+
bool PathDepBranchFolding(GlobOpt* globOptState);
352355
void MergePredBlocksValueMaps(GlobOpt* globOptState);
353356
private:
354357
void CleanUpValueMaps();
@@ -381,6 +384,7 @@ class BasicBlock
381384

382385
// Deadstore data
383386
BVSparse<JitArenaAllocator> * upwardExposedUses;
387+
BVSparse<JitArenaAllocator> * successorBlockUses;
384388
BVSparse<JitArenaAllocator> * upwardExposedFields;
385389
BVSparse<JitArenaAllocator> * typesNeedingKnownObjectLayout;
386390
BVSparse<JitArenaAllocator> * slotDeadStoreCandidates;
@@ -426,6 +430,7 @@ class BasicBlock
426430
isLoopHeader(false),
427431
hasCall(false),
428432
upwardExposedUses(nullptr),
433+
successorBlockUses(nullptr),
429434
upwardExposedFields(nullptr),
430435
typesNeedingKnownObjectLayout(nullptr),
431436
slotDeadStoreCandidates(nullptr),
@@ -1005,6 +1010,16 @@ struct MemCopyEmitData : public MemOpEmitData
10051010
}\
10061011
NEXT_EDGE_IN_LIST
10071012

1013+
#define FOREACH_PREDECESSOR_BLOCK_EDITING(blockPred, block, iter)\
1014+
FOREACH_EDGE_IN_LIST_EDITING(__edge, block->GetPredList(), iter)\
1015+
{\
1016+
BasicBlock * blockPred = __edge->GetPred(); \
1017+
AnalysisAssert(blockPred);
1018+
1019+
#define NEXT_PREDECESSOR_BLOCK_EDITING\
1020+
}\
1021+
NEXT_EDGE_IN_LIST_EDITING
1022+
10081023
#define FOREACH_DEAD_PREDECESSOR_BLOCK(blockPred, block)\
10091024
FOREACH_EDGE_IN_LIST(__edge, block->GetDeadPredList())\
10101025
{\

0 commit comments

Comments
 (0)