Skip to content

Enable globopt for functions with try finally - #2954

Merged
chakrabot merged 1 commit into
chakra-core:masterfrom
meg-gupta:tryfinallypr
May 17, 2017
Merged

Enable globopt for functions with try finally#2954
chakrabot merged 1 commit into
chakra-core:masterfrom
meg-gupta:tryfinallypr

Conversation

@meg-gupta

Copy link
Copy Markdown
Contributor

This change enables globopt on functions with try finally.
We transform the flowgraph such that we have 2 flow edges -
try to a excepting finally region and try to non excepting finally region.
This enables us to optimize the function on the non exception path.
We bailout on the exception path.

Special handling is needed when there are early exits (break, continue, return) within the tryfinally.
We need to execute the finally block on early exit, currently we bailout on early exit.
We transform the flow graph from (eh region -> early exit) to have an edge from (eh region -> finally) and (finally -> early exit)
This transformation can be done only after the regions are assigned in FlowGraph.

So the flowgraph builder now has the following order of phase:

  • build flow graph -> add the excepting and non excepting finallys alongside
  • remove unreachable blocks
  • assign regions
  • identify early exits and add edges (does region info update when required)
  • break blocks removal (does region info update when required)

@meg-gupta
meg-gupta force-pushed the tryfinallypr branch 3 times, most recently from 67a652d to ea97909 Compare May 10, 2017 13:53
@meg-gupta
meg-gupta requested review from LouisLaf, pleath and rajatd May 10, 2017 13:59
Comment thread lib/Backend/IRBuilder.cpp Outdated

if (this->catchOffsetStack && !this->catchOffsetStack->Empty())
if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) ||
inFinallyBlock > 0)

@rajatd rajatd May 11, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inFinallyBlock [](start = 16, length = 14)

inFinallyBlock [](start = 16, length = 14)

nit: for better readability, change name to finallyBlockLevel or something or have an API InFinallyBlock. #Resolved

@meg-gupta
meg-gupta force-pushed the tryfinallypr branch 4 times, most recently from e292898 to bc15c38 Compare May 12, 2017 03:05
@meg-gupta

Copy link
Copy Markdown
Contributor Author

@dotnet-bot test OSX static_osx_osx_debug

Comment thread lib/Backend/FlowGraph.cpp
this->catchLabelStack = JitAnew(this->alloc, SList<IR::LabelInstr*>, this->alloc);
}
if (this->func->HasFinally() && (this->func->DoOptimizeTryFinally() ||
(this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()))) [](start = 9, length = 81)

Take this condition out and check for DoOptimizeTryCatch and DoOptimizeTryFinally inside it. Actually, this makes me think - we can't turn off the opitimizer for try-catch and have it on for try-finally, or vice-versa, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I was looking at the 2nd iteration - second issue seems fixed in 3rd iteration :). We should still hoist the common conditions though


In reply to: 116533348 [](ancestors = 116533348)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. thanks. We can turn off try finally opt without affecting try catch opt, but if we turn off try catch opt, try finally is turned off too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"We can turn off try finally opt without affecting try catch opt" - how? If we're not optimizing try-finally but are try-catch, and the trycatch is inside try-finally, wouldn't that be a problem?


In reply to: 116535802 [](ancestors = 116535802)

Comment thread lib/Backend/FlowGraph.cpp Outdated
}
if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop &&
this->finallyLabelStack && !this->finallyLabelStack->Empty() &&
instr->m_next->m_opcode != Js::OpCode::BrOnException)

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hoist the common conditions #Resolved

Comment thread lib/Backend/FlowGraph.cpp Outdated
// LeaveNull is needed to get the end of the finally block, this is important when adding edge from the finally block to the early exit block
if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop &&
this->leaveNullLabelStack && !this->leaveNullLabelStack->Empty() &&
instr->m_next->m_opcode != Js::OpCode::BrOnException)

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same #Resolved

Comment thread lib/Backend/FlowGraph.cpp
// Create a new label before LeaveNull
IR::LabelInstr *leaveNullLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func);
instr->InsertBefore(leaveNullLabel);
this->leaveNullLabelStack->Push(leaveNullLabel);

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can there be multiple LeaveNulls for a finally? #Resolved

@meg-gupta meg-gupta May 15, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only one at the end of the finally #Resolved

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then it might be worth asserting after the flowgraph is built that the leaveNullLabelStack is null or empty (similar to catchLabelStack and finallyLabelStack) #Resolved

Comment thread lib/Backend/FlowGraph.cpp

FOREACH_BLOCK_ALL(block, this)
{
block->SetBlockNum(blockNum++);

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you do this in the following loop? #Resolved

Comment thread lib/Backend/BackwardPass.cpp Outdated
}
}

if (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Finally)

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the code in this block is the same as in the previous one. Please refactor into one if block. #Resolved

Comment thread lib/Backend/GlobOpt.cpp Outdated
ProcessTryCatch(instr);
ProcessTryCatchOrTryFinally(instr);
}
else if (instr->m_opcode == Js::OpCode::TryFinally && this->func->DoOptimizeTry())

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge into the if block above with doing an || of the opcodes condition #Resolved

Comment thread lib/Backend/GlobOpt.cpp Outdated
Region *targetRegion = instr->AsBranchInstr()->GetTarget()->GetRegion();
Assert((targetRegion->GetType() == RegionType::RegionTypeFinally &&
targetRegion->GetMatchingTryRegion()->GetMatchingFinallyRegion(true) == targetRegion) ||
true);

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'true' left over? #Resolved

Comment thread lib/Backend/Lower.h Outdated
IR::Instr* LowerTry(IR::Instr* instr, bool tryCatch);
void EnsureBailoutReturnValueSym();
void EnsureHasBailedOutSym();
Region * GetNonNonExceptingFinallyGrandParent(Region *region);

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: double 'non' #Resolved

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, this should be on the Region class #Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And rename to GetFirstNonExceptingAncestor, and refactor code in Lowerer::InsertReturnThunkForRegion as discussed offline

Comment thread lib/Backend/Lower.cpp

IR::LabelOpnd * continuationAddr;
if (region->GetParent()->GetType() != RegionTypeRoot)
if (region->IsNonExceptingFinally())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add lots of comments and asserts to explain the mechanics here and to enforce your invariants

exception = err.GetAndClear();
}

if (exception)

@rajatd rajatd May 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to check for hasBailedOutOffset in this function and also generate code in LowerMD.cpp to pass the param to this helper #Resolved

Comment thread lib/Backend/GlobOpt.cpp Outdated
BasicBlock * nextBlock = nextLabel->GetBasicBlock();
IR::BranchInstr * branchTofinallyBlockOrEarlyExit = nextLabel->m_next->AsBranchInstr();
IR::LabelInstr * finallyBlockLabelOrEarlyExitLabel = branchTofinallyBlockOrEarlyExit->GetTarget();
if (finallyBlockLabelOrEarlyExitLabel->GetRegion() == this->currentRegion)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not understand this condition

Comment thread lib/Backend/FlowGraph.cpp
// TryFinally L1
// <try code>
// BrOnException L1
// Leave L1'

@rajatd rajatd May 16, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears to me that L1' is really L2 from before the transformation. If so, lets keep L2 only after the transformation. Makes understanding the code below easier #Resolved

Comment thread lib/Backend/FlowGraph.cpp

while (leaveChain->m_opcode != Js::OpCode::Br)
{
IR::LabelInstr * nextLabel = leaveChain->m_next->AsLabelInstr();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leaveChain->m_next guaranteed to always be a labelInstr?

Comment thread lib/Backend/FlowGraph.cpp Outdated

IR::LabelInstr * currentLabel = nullptr;
// look for early exits from a try, and insert bailout
FOREACH_INSTR_IN_FUNC_EDITING(instr, instrPrev, func)

@rajatd rajatd May 16, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please call it instrNext #Resolved

Comment thread lib/Backend/FlowGraph.cpp
branchTargetRegion != currentRegion->GetMatchingFinallyRegion(true));
if (!isEarly) return false;

if (DoesExitLabelDominate(leaveInstr)) return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could probably cache the result of this call before region condition checks

@rajatd

rajatd commented May 16, 2017

Copy link
Copy Markdown
Contributor

Looked at most of the changes and gave some comments. Flowgraph changes are definitely complicated. Looks good overall.

@meg-gupta
meg-gupta force-pushed the tryfinallypr branch 3 times, most recently from 9f6f38b to 020b17f Compare May 16, 2017 19:41
@meg-gupta

Copy link
Copy Markdown
Contributor Author

@dotnet-bot test Ubuntu shared_ubuntu_linux_test
@dotnet-bot test Ubuntu shared_ubuntu_linux_debug
@dotnet-bot test OSX static_osx_osx_debug

@rajatd

rajatd commented May 17, 2017

Copy link
Copy Markdown
Contributor

Oh, and yes, add asserts for opcodes, for example, at places you are looping while the instruction is not a Br, and it seems that you expect the instr to be Leave.

This change enables globopt on functions with try finally.
We transform the flowgraph such that we have 2 flow edges -
try to a excepting finally region and try to non excepting finally region.
This enables us to optimize the function on the non exception path.
We bailout on the exception path.

Special handling is needed when there are early exits (break, continue, return) within the tryfinally.
We need to execute the finally block on early exit, currently we bailout on early exit.
We transform the flow graph from (eh region -> early exit) to have an edge from  (eh region -> finally) and (finally -> early exit)
This transformation can be done only after the regions are assigned in FlowGraph.

So the flowgraph builder now has the following order of phase:

- build flow graph -> add the excepting and non excepting finallys alongside
- remove unreachable blocks
- assign regions
- identify early exits and add edges (does region info update when required)
- break blocks removal (does region info update when required)
@chakrabot
chakrabot merged commit 82ff317 into chakra-core:master May 17, 2017
chakrabot pushed a commit that referenced this pull request May 17, 2017
Merge pull request #2954 from meg-gupta:tryfinallypr

This change enables globopt on functions with try finally.
We transform the flowgraph such that we have 2 flow edges -
try to a excepting finally region and try to non excepting finally region.
This enables us to optimize the function on the non exception path.
We bailout on the exception path.

Special handling is needed when there are early exits (break, continue, return) within the tryfinally.
We need to execute the finally block on early exit, currently we bailout on early exit.
We transform the flow graph from (eh region -> early exit) to have an edge from  (eh region -> finally) and (finally -> early exit)
This transformation can be done only after the regions are assigned in FlowGraph.

So the flowgraph builder now has the following order of phase:

- build flow graph -> add the excepting and non excepting finallys alongside
- remove unreachable blocks
- assign regions
- identify early exits and add edges (does region info update when required)
- break blocks removal (does region info update when required)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants