-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable globopt for functions with try finally #2954
Conversation
67a652d
to
ea97909
Compare
lib/Backend/IRBuilder.cpp
Outdated
|
||
if (this->catchOffsetStack && !this->catchOffsetStack->Empty()) | ||
if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) || | ||
inFinallyBlock > 0) |
There was a problem hiding this comment.
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
e292898
to
bc15c38
Compare
@dotnet-bot test OSX static_osx_osx_debug |
lib/Backend/FlowGraph.cpp
Outdated
{ | ||
this->catchLabelStack = JitAnew(this->alloc, SList<IR::LabelInstr*>, this->alloc); | ||
} | ||
if (this->func->HasFinally() && (this->func->DoOptimizeTryFinally() || | ||
(this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()))) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
lib/Backend/FlowGraph.cpp
Outdated
@@ -76,6 +208,27 @@ FlowGraph::Build(void) | |||
instrPrev = brOnException; // process BrOnException before adding a new block for loop top label. | |||
continue; | |||
} | |||
if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop && | |||
this->finallyLabelStack && !this->finallyLabelStack->Empty() && | |||
instr->m_next->m_opcode != Js::OpCode::BrOnException) |
There was a problem hiding this comment.
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
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same #Resolved
// Create a new label before LeaveNull | ||
IR::LabelInstr *leaveNullLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func); | ||
instr->InsertBefore(leaveNullLabel); | ||
this->leaveNullLabelStack->Push(leaveNullLabel); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
lib/Backend/FlowGraph.cpp
Outdated
} | ||
bool assignRegionsBeforeGlobopt = this->func->HasTry() && (this->func->DoOptimizeTry() || | ||
(this->func->IsSimpleJit() && this->func->hasBailout)); | ||
|
||
FOREACH_BLOCK_ALL(block, this) | ||
{ | ||
block->SetBlockNum(blockNum++); |
There was a problem hiding this comment.
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
lib/Backend/BackwardPass.cpp
Outdated
@@ -2824,13 +2824,38 @@ BackwardPass::ProcessBlock(BasicBlock * block) | |||
} | |||
} | |||
} | |||
|
|||
if (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Finally) |
There was a problem hiding this comment.
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
lib/Backend/GlobOpt.cpp
Outdated
ProcessTryCatch(instr); | ||
ProcessTryCatchOrTryFinally(instr); | ||
} | ||
else if (instr->m_opcode == Js::OpCode::TryFinally && this->func->DoOptimizeTry()) |
There was a problem hiding this comment.
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
lib/Backend/GlobOpt.cpp
Outdated
Region *targetRegion = instr->AsBranchInstr()->GetTarget()->GetRegion(); | ||
Assert((targetRegion->GetType() == RegionType::RegionTypeFinally && | ||
targetRegion->GetMatchingTryRegion()->GetMatchingFinallyRegion(true) == targetRegion) || | ||
true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'true' left over? #Resolved
lib/Backend/Lower.h
Outdated
@@ -663,6 +664,7 @@ class Lowerer | |||
IR::Instr* LowerTry(IR::Instr* instr, bool tryCatch); | |||
void EnsureBailoutReturnValueSym(); | |||
void EnsureHasBailedOutSym(); | |||
Region * GetNonNonExceptingFinallyGrandParent(Region *region); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: double 'non' #Resolved
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
lib/Backend/Lower.cpp
Outdated
@@ -24604,7 +24660,37 @@ Lowerer::InsertReturnThunkForRegion(Region* region, IR::LabelInstr* restoreLabel | |||
} | |||
|
|||
IR::LabelOpnd * continuationAddr; | |||
if (region->GetParent()->GetType() != RegionTypeRoot) | |||
if (region->IsNonExceptingFinally()) |
There was a problem hiding this comment.
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
@@ -238,6 +280,53 @@ namespace Js | |||
{ | |||
// Clone static exception object early in case finally block overwrites it | |||
exception = exception->CloneIfStaticExceptionObject(scriptContext); | |||
|
There was a problem hiding this comment.
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
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) |
There was a problem hiding this comment.
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
// TryFinally L1 | ||
// <try code> | ||
// BrOnException L1 | ||
// Leave L1' |
There was a problem hiding this comment.
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
lib/Backend/FlowGraph.cpp
Outdated
|
||
while (leaveChain->m_opcode != Js::OpCode::Br) | ||
{ | ||
IR::LabelInstr * nextLabel = leaveChain->m_next->AsLabelInstr(); |
There was a problem hiding this comment.
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?
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) |
There was a problem hiding this comment.
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
branchTargetRegion != currentRegion->GetMatchingFinallyRegion(true)); | ||
if (!isEarly) return false; | ||
|
||
if (DoesExitLabelDominate(leaveInstr)) return false; |
There was a problem hiding this comment.
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
Looked at most of the changes and gave some comments. Flowgraph changes are definitely complicated. Looks good overall. |
9f6f38b
to
020b17f
Compare
@dotnet-bot test Ubuntu shared_ubuntu_linux_test |
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)
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)
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: