diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp index e295d6c65fd..b6c9d8fb871 100644 --- a/lib/Backend/BackwardPass.cpp +++ b/lib/Backend/BackwardPass.cpp @@ -48,7 +48,7 @@ bool BackwardPass::DoByteCodeUpwardExposedUsed() const { return (this->tag == Js::DeadStorePhase && this->func->hasBailout) || - (this->func->HasTry() && this->func->DoOptimizeTryCatch() && this->tag == Js::BackwardPhase); + (this->func->HasTry() && this->func->DoOptimizeTry() && this->tag == Js::BackwardPhase); } bool @@ -114,7 +114,7 @@ BackwardPass::DoDeadStore(Func* func) { return !PHASE_OFF(Js::DeadStorePhase, func) && - (!func->HasTry() || func->DoOptimizeTryCatch()); + (!func->HasTry() || func->DoOptimizeTry()); } bool @@ -1111,7 +1111,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block) Assert(block->typesNeedingKnownObjectLayout == nullptr); Assert(block->fieldHoistCandidates == nullptr); // byteCodeUpwardExposedUsed is required to populate the writeThroughSymbolsSet for the try region in the backwards pass - Assert(block->byteCodeUpwardExposedUsed == nullptr || (this->tag == Js::BackwardPhase && this->func->HasTry() && this->func->DoOptimizeTryCatch())); + Assert(block->byteCodeUpwardExposedUsed == nullptr || (this->tag == Js::BackwardPhase && this->func->HasTry() && this->func->DoOptimizeTry())); Assert(block->byteCodeRestoreSyms == nullptr); Assert(block->stackSymToFinalType == nullptr); Assert(block->stackSymToGuardedProperties == nullptr); @@ -1847,7 +1847,7 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr) { // We don't need to fill in the bailout instruction in backward pass Assert(this->func->hasBailout || !instr->HasBailOutInfo()); - Assert(!instr->HasBailOutInfo() || instr->GetBailOutInfo()->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTryCatch())); + Assert(!instr->HasBailOutInfo() || instr->GetBailOutInfo()->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTry())); if (instr->IsByteCodeUsesInstr()) { @@ -2298,7 +2298,7 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo) // The byteCodeUpwardExposedUsed should only be assigned once. The only case which would break this // assumption is when we are optimizing a function having try-catch. In that case, we need the // byteCodeUpwardExposedUsed analysis in the initial backward pass too. - Assert(bailOutInfo->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTryCatch())); + Assert(bailOutInfo->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTry())); // Make a copy of the byteCodeUpwardExposedUsed so we can remove the constants if (!this->IsPrePass()) @@ -2765,7 +2765,7 @@ BackwardPass::ProcessBlock(BasicBlock * block) } case Js::OpCode::Catch: { - if (this->func->DoOptimizeTryCatch() && !this->IsPrePass()) + if (this->func->DoOptimizeTry() && !this->IsPrePass()) { // Execute the "Catch" in the JIT'ed code, and bailout to the next instruction. This way, the bailout will restore the exception object automatically. IR::BailOutInstr* bailOnException = IR::BailOutInstr::New(Js::OpCode::BailOnException, IR::BailOutOnException, instr->m_next, instr->m_func); @@ -2792,15 +2792,15 @@ BackwardPass::ProcessBlock(BasicBlock * block) this->ProcessInlineeEnd(instr); } - if (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Catch) + if ((instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Catch) || (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Finally)) { if (!this->currentRegion) { - Assert(!this->func->DoOptimizeTryCatch() && !(this->func->IsSimpleJit() && this->func->hasBailout)); + Assert(!this->func->DoOptimizeTry() && !(this->func->IsSimpleJit() && this->func->hasBailout)); } else { - Assert(this->currentRegion->GetType() == RegionTypeCatch); + Assert(this->currentRegion->GetType() == RegionTypeCatch || this->currentRegion->GetType() == RegionTypeFinally); Region * matchingTryRegion = this->currentRegion->GetMatchingTryRegion(); Assert(matchingTryRegion); @@ -2827,10 +2827,11 @@ BackwardPass::ProcessBlock(BasicBlock * block) #if DBG if (instr->m_opcode == Js::OpCode::TryCatch) { - if (!this->IsPrePass() && (this->func->DoOptimizeTryCatch() || (this->func->IsSimpleJit() && this->func->hasBailout))) + if (!this->IsPrePass() && (this->func->DoOptimizeTry() || (this->func->IsSimpleJit() && this->func->hasBailout))) { Assert(instr->m_next->IsLabelInstr() && (instr->m_next->AsLabelInstr()->GetRegion() != nullptr)); Region * tryRegion = instr->m_next->AsLabelInstr()->GetRegion(); + Assert(tryRegion && tryRegion->GetType() == RegionType::RegionTypeTry && tryRegion->GetMatchingCatchRegion() != nullptr); Assert(tryRegion->writeThroughSymbolsSet); } } @@ -7496,16 +7497,16 @@ BackwardPass::FoldCmBool(IR::Instr *instr) } void -BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchBlock, Region * tryRegion) +BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchOrFinallyBlock, Region * tryRegion) { tryRegion->writeThroughSymbolsSet = JitAnew(this->func->m_alloc, BVSparse, this->func->m_alloc); if (this->DoByteCodeUpwardExposedUsed()) { - Assert(catchBlock->byteCodeUpwardExposedUsed); - if (!catchBlock->byteCodeUpwardExposedUsed->IsEmpty()) + Assert(catchOrFinallyBlock->byteCodeUpwardExposedUsed); + if (!catchOrFinallyBlock->byteCodeUpwardExposedUsed->IsEmpty()) { - FOREACH_BITSET_IN_SPARSEBV(id, catchBlock->byteCodeUpwardExposedUsed) + FOREACH_BITSET_IN_SPARSEBV(id, catchOrFinallyBlock->byteCodeUpwardExposedUsed) { tryRegion->writeThroughSymbolsSet->Set(id); } @@ -7550,7 +7551,7 @@ BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchBlock, Region bool BackwardPass::CheckWriteThroughSymInRegion(Region* region, StackSym* sym) { - if (region->GetType() == RegionTypeRoot || region->GetType() == RegionTypeFinally) + if (region->GetType() == RegionTypeRoot) { return false; } diff --git a/lib/Backend/BailOut.cpp b/lib/Backend/BailOut.cpp index 776620dce7f..ec3dcfdfea3 100644 --- a/lib/Backend/BailOut.cpp +++ b/lib/Backend/BailOut.cpp @@ -1050,7 +1050,7 @@ Js::Var BailOutRecord::BailOut(BailOutRecord const * bailOutRecord) void * argoutRestoreAddr = nullptr; #ifdef _M_IX86 void * addressOfRetAddress = _AddressOfReturnAddress(); - if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0)) + if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally)) { // For a bailout in argument evaluation from an EH region, the esp is offset by the TryCatch helper's frame. So, the argouts are not at the offsets // stored in the bailout record, which are relative to ebp. Need to restore the argouts from the actual value of esp before calling the Bailout helper @@ -2708,7 +2708,7 @@ Js::Var BranchBailOutRecord::BailOut(BranchBailOutRecord const * bailOutRecord, void * argoutRestoreAddr = nullptr; #ifdef _M_IX86 void * addressOfRetAddress = _AddressOfReturnAddress(); - if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0)) + if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally)) { argoutRestoreAddr = (void *)((char*)addressOfRetAddress + ((2 + 1) * MachPtr)); // Account for the parameters and return address of this function } diff --git a/lib/Backend/BailOutKind.h b/lib/Backend/BailOutKind.h index a51e4788260..c73c0d264fa 100644 --- a/lib/Backend/BailOutKind.h +++ b/lib/Backend/BailOutKind.h @@ -46,6 +46,7 @@ BAIL_OUT_KIND(LazyBailOut, 0) BAIL_OUT_KIND(BailOutOnFailedHoistedLoopCountBasedBoundCheck, 0) BAIL_OUT_KIND(BailOutForGeneratorYield, 0) BAIL_OUT_KIND(BailOutOnException, 0) +BAIL_OUT_KIND(BailOutOnEarlyExit, 0) // SIMD_JS BAIL_OUT_KIND(BailOutSimd128F4Only, 0) diff --git a/lib/Backend/FlowGraph.cpp b/lib/Backend/FlowGraph.cpp index c890081411c..fc27fdebc20 100644 --- a/lib/Backend/FlowGraph.cpp +++ b/lib/Backend/FlowGraph.cpp @@ -14,6 +14,136 @@ FlowGraph::New(Func * func, JitArenaAllocator * alloc) return graph; } + +// When there is an early exit within an EH region, +// Leave instructions are inserted in the bytecode to jump up the region tree one by one +// We delete this Leave chain of instructions, and add an edge to Finally +IR::LabelInstr * FlowGraph::DeleteLeaveChainBlocks(IR::BranchInstr *leaveInstr, IR::Instr * &instrPrev) +{ + // Cleanup Rest of the Leave chain + IR::LabelInstr * leaveTarget = leaveInstr->GetTarget(); + Assert(leaveTarget->GetNextRealInstr()->IsBranchInstr()); + IR::BranchInstr *leaveChain = leaveTarget->GetNextRealInstr()->AsBranchInstr(); + IR::LabelInstr * curLabel = leaveTarget->AsLabelInstr(); + + while (leaveChain->m_opcode != Js::OpCode::Br) + { + Assert(leaveChain->m_opcode == Js::OpCode::Leave || leaveChain->m_opcode == Js::OpCode::BrOnException); + IR::LabelInstr * nextLabel = leaveChain->m_next->AsLabelInstr(); + leaveChain = nextLabel->m_next->AsBranchInstr(); + BasicBlock *curBlock = curLabel->GetBasicBlock(); + FOREACH_SLISTBASECOUNTED_ENTRY_EDITING(FlowEdge*, edge, curBlock->GetPredList(), iter1) + { + BasicBlock * pred = edge->GetPred(); + pred->RemoveSucc(curBlock, this); + } NEXT_SLISTBASECOUNTED_ENTRY_EDITING; + this->RemoveBlock(curBlock); + curLabel = nextLabel; + } + + instrPrev = leaveChain->m_next; + IR::LabelInstr * exitLabel = leaveChain->GetTarget(); + BasicBlock * curBlock = curLabel->GetBasicBlock(); + FOREACH_SLISTBASECOUNTED_ENTRY_EDITING(FlowEdge*, edge, curBlock->GetPredList(), iter1) + { + BasicBlock * pred = edge->GetPred(); + pred->RemoveSucc(curBlock, this); + } NEXT_SLISTBASECOUNTED_ENTRY_EDITING; + this->RemoveBlock(curBlock); + return exitLabel; +} + +bool FlowGraph::Dominates(Region *region1, Region *region2) +{ + Assert(region1); + Assert(region2); + Region *startR1 = region1; + Region *startR2 = region2; + int region1Depth = 0; + while (startR1 != nullptr) + { + region1Depth++; + startR1 = startR1->GetParent(); + } + int region2Depth = 0; + while (startR2 != nullptr) + { + region2Depth++; + startR2 = startR2->GetParent(); + } + return region1Depth > region2Depth; +} + +bool FlowGraph::DoesExitLabelDominate(IR::BranchInstr *leaveInstr) +{ + IR::LabelInstr * leaveTarget = leaveInstr->GetTarget(); + Assert(leaveTarget->GetNextRealInstr()->IsBranchInstr()); + IR::BranchInstr *leaveChain = leaveTarget->GetNextRealInstr()->AsBranchInstr(); + + while (leaveChain->m_opcode != Js::OpCode::Br) + { + Assert(leaveChain->m_opcode == Js::OpCode::Leave || leaveChain->m_opcode == Js::OpCode::BrOnException); + IR::LabelInstr * nextLabel = leaveChain->m_next->AsLabelInstr(); + leaveChain = nextLabel->m_next->AsBranchInstr(); + } + IR::LabelInstr * exitLabel = leaveChain->GetTarget(); + return Dominates(exitLabel->GetRegion(), finallyLabelStack->Top()->GetRegion()); +} + +bool FlowGraph::IsEarlyExitFromFinally(IR::BranchInstr *leaveInstr, Region *currentRegion, Region *branchTargetRegion, IR::Instr * &instrPrev, IR::LabelInstr * &exitLabel) +{ + if (finallyLabelStack->Empty()) + { + return false; + } + + if (currentRegion->GetType() == RegionTypeTry) + { + if (currentRegion->GetMatchingCatchRegion() == nullptr) + { + // try of try-finally + bool isEarly = + (branchTargetRegion != currentRegion->GetMatchingFinallyRegion(false) && + branchTargetRegion != currentRegion->GetMatchingFinallyRegion(true)); + if (!isEarly) return false; + + if (DoesExitLabelDominate(leaveInstr)) return false; + + // Cleanup Rest of the Leave chain + exitLabel = DeleteLeaveChainBlocks(leaveInstr, instrPrev); + return true; + } + // try of try-catch + + IR::BranchInstr *leaveChain = leaveInstr; + IR::LabelInstr * leaveTarget = leaveChain->GetTarget(); + IR::Instr *target = leaveTarget->GetNextRealInstr(); + if (target->m_opcode == Js::OpCode::Br) + { + IR::BranchInstr *tryExit = target->AsBranchInstr(); + instrPrev = tryExit; + return false; + } + + if (DoesExitLabelDominate(leaveInstr)) return false; + + // Cleanup Rest of the Leave chain + exitLabel = DeleteLeaveChainBlocks(leaveInstr, instrPrev); + return true; + } + if (currentRegion->GetType() == RegionTypeCatch) + { + // We don't care for early exits in catch blocks, because we bailout anyway + return false; + } + Assert(currentRegion->GetType() == RegionTypeFinally); + // All Leave's inside Finally region are early exits + // Since we execute non-excepting Finallys in JIT now, we should convert Leave to Br + if (DoesExitLabelDominate(leaveInstr)) return false; + exitLabel = DeleteLeaveChainBlocks(leaveInstr, instrPrev); + return true; +} + ///---------------------------------------------------------------------------- /// /// FlowGraph::Build @@ -25,27 +155,31 @@ void FlowGraph::Build(void) { Func * func = this->func; - BEGIN_CODEGEN_PHASE(func, Js::FGPeepsPhase); this->RunPeeps(); END_CODEGEN_PHASE(func, Js::FGPeepsPhase); // We don't optimize fully with SimpleJit. But, when JIT loop body is enabled, we do support // bailing out from a simple jitted function to do a full jit of a loop body in the function - // (BailOnSimpleJitToFullJitLoopBody). For that purpose, we need the flow from try to catch. - if (this->func->HasTry() && - (this->func->DoOptimizeTryCatch() || - (this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()) - ) - ) + // (BailOnSimpleJitToFullJitLoopBody). For that purpose, we need the flow from try to handler. + if (this->func->HasTry() && (this->func->DoOptimizeTry() || + (this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()))) { this->catchLabelStack = JitAnew(this->alloc, SList, this->alloc); } + if (this->func->HasFinally() && (this->func->DoOptimizeTry() || + (this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()))) + { + this->finallyLabelStack = JitAnew(this->alloc, SList, this->alloc); + this->leaveNullLabelStack = JitAnew(this->alloc, SList, this->alloc); + this->regToFinallyEndMap = JitAnew(this->alloc, RegionToFinallyEndMapType, this->alloc, 0); + } IR::Instr * currLastInstr = nullptr; BasicBlock * currBlock = nullptr; BasicBlock * nextBlock = nullptr; bool hasCall = false; + FOREACH_INSTR_IN_FUNC_BACKWARD_EDITING(instr, instrPrev, func) { if (currLastInstr == nullptr || instr->EndsBasicBlock()) @@ -65,16 +199,35 @@ FlowGraph::Build(void) if (instr->StartsBasicBlock()) { - // Insert a BrOnException after the loop top if we are in a try-catch. This is required to - // model flow from the loop to the catch block for loops that don't have a break condition. - if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop && - this->catchLabelStack && !this->catchLabelStack->Empty() && - instr->m_next->m_opcode != Js::OpCode::BrOnException) + // Insert a BrOnException after the loop top if we are in a try-catch/try-finally. This is required to + // model flow from the loop to the catch/finally block for loops that don't have a break condition. + if (instr->IsLabelInstr() && instr->AsLabelInstr()->m_isLoopTop && instr->m_next->m_opcode != Js::OpCode::BrOnException) { - IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, this->catchLabelStack->Top(), instr->m_func); - instr->InsertAfter(brOnException); - instrPrev = brOnException; // process BrOnException before adding a new block for loop top label. - continue; + IR::BranchInstr * brOnException = nullptr; + IR::Instr *instrNext = instr->m_next; + if (this->catchLabelStack && !this->catchLabelStack->Empty()) + { + brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, this->catchLabelStack->Top(), instr->m_func); + instr->InsertAfter(brOnException); + } + if (this->finallyLabelStack && !this->finallyLabelStack->Empty()) + { + brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, this->finallyLabelStack->Top(), instr->m_func); + instr->InsertAfter(brOnException); + } + // Insert a BrOnException after the loop top if we are in a finally block. This is required so + // that LeaveNull is not eliminated in the presence of loops with no exit condition + // 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 (this->leaveNullLabelStack && !this->leaveNullLabelStack->Empty()) + { + brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, this->leaveNullLabelStack->Top(), instr->m_func); + instr->InsertAfter(brOnException); + } + if (brOnException) + { + instrPrev = instrNext->m_prev; + continue; + } } // Wrap up the current block and get ready to process a new one. @@ -95,6 +248,69 @@ FlowGraph::Build(void) } break; + case Js::OpCode::Finally: + { + if (!this->finallyLabelStack) + { + break; + } + + //To enable globopt on functions with try finallys we transform the flowgraph as below : + // TryFinally L1 + // + // Leave L2 + // L2 : Br L3 + // L1 : + // LeaveNull + // L3 : + // + //to: + // + // TryFinally L1 + // + // BrOnException L1 + // Leave L1' + // L1 : BailOnException + // L2 : Finally + // + // LeaveNull + // L3: + //We generate 2 flow edges from the try - an exception path and a non exception path. + //This transformation enables us to optimize on the non-excepting finally path + + Assert(instr->m_prev->IsLabelInstr()); + + IR::LabelInstr * finallyLabel = instr->m_prev->AsLabelInstr(); + + // Find leave label + + Assert(finallyLabel->m_prev->m_opcode == Js::OpCode::Br && finallyLabel->m_prev->m_prev->m_opcode == Js::OpCode::Label); + + IR::Instr * insertPoint = finallyLabel->m_prev; + IR::LabelInstr * leaveTarget = finallyLabel->m_prev->m_prev->AsLabelInstr(); + + leaveTarget->Unlink(); + finallyLabel->InsertBefore(leaveTarget); + finallyLabel->Unlink(); + insertPoint->InsertBefore(finallyLabel); + + // Bailout from the opcode following Finally + IR::Instr * bailOnException = IR::BailOutInstr::New(Js::OpCode::BailOnException, IR::BailOutOnException, instr->m_next, instr->m_func); + insertPoint->InsertBefore(bailOnException); + insertPoint->Remove(); + + this->finallyLabelStack->Push(finallyLabel); + Assert(!this->leaveNullLabelStack->Empty()); + this->leaveNullLabelStack->Pop(); + + Assert(leaveTarget->labelRefs.HasOne()); + IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, finallyLabel, instr->m_func); + leaveTarget->labelRefs.Head()->InsertBefore(brOnException); + + instrPrev = instr->m_prev; + } + break; + case Js::OpCode::TryCatch: if (this->catchLabelStack) { @@ -102,6 +318,39 @@ FlowGraph::Build(void) } break; + case Js::OpCode::TryFinally: + if (this->finallyLabelStack) + { + this->finallyLabelStack->Pop(); + } + break; + case Js::OpCode::LeaveNull: + { + if (!this->leaveNullLabelStack) + { + break; + } + IR::Instr * label = instr->GetPrevRealInstrOrLabel(); + if (label->IsLabelInstr()) + { + this->leaveNullLabelStack->Push(label->AsLabelInstr()); + break; + } + // Create a new label before LeaveNull + IR::LabelInstr *leaveNullLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func); + instr->InsertBefore(leaveNullLabel); + this->leaveNullLabelStack->Push(leaveNullLabel); + leaveNullLabel->SetByteCodeOffset(instr); + + // Wrap up the current block and get ready to process a new one. + nextBlock = currBlock; + currBlock = this->AddBlock(leaveNullLabel, instr, nextBlock); + currBlock->hasCall = hasCall; + hasCall = false; + currLastInstr = nullptr; + } + break; + case Js::OpCode::CloneBlockScope: case Js::OpCode::CloneInnerScopeSlots: // It would be nice to do this in IRBuilder, but doing so gives us @@ -110,7 +359,6 @@ FlowGraph::Build(void) // here in FlowGraph. instr->SetDst(instr->GetSrc1()); break; - } if (OpCodeAttr::UseAllFields(instr->m_opcode)) @@ -162,6 +410,8 @@ FlowGraph::Build(void) NEXT_INSTR_IN_FUNC_BACKWARD_EDITING; this->func->isFlowGraphValid = true; Assert(!this->catchLabelStack || this->catchLabelStack->Empty()); + Assert(!this->finallyLabelStack || this->finallyLabelStack->Empty()); + Assert(!this->leaveNullLabelStack || this->leaveNullLabelStack->Empty()); // We've been walking backward so that edge lists would be in the right order. Now walk the blocks // forward to number the blocks in lexical order. @@ -174,17 +424,6 @@ FlowGraph::Build(void) this->RemoveUnreachableBlocks(); - this->FindLoops(); - - bool breakBlocksRelocated = this->CanonicalizeLoops(); - -#if DBG - this->VerifyLoopGraph(); -#endif - - // Renumber the blocks. Break block remove code has likely inserted new basic blocks. - blockNum = 0; - // Regions need to be assigned before Globopt because: // 1. FullJit: The Backward Pass will set the write-through symbols on the regions and the forward pass will // use this information to insert ToVars for those symbols. Also, for a symbol determined as write-through @@ -195,13 +434,10 @@ FlowGraph::Build(void) // we have in Simple Jitted code right now is BailOnSimpleJitToFullJitLoopBody, installed in IRBuilder. So, // for now, we can just check if the func has a bailout to assign regions pre globopt while running SimpleJit. - bool assignRegionsBeforeGlobopt = this->func->HasTry() && - (this->func->DoOptimizeTryCatch() || (this->func->IsSimpleJit() && this->func->hasBailout)); - Region ** blockToRegion = nullptr; - if (assignRegionsBeforeGlobopt) - { - blockToRegion = JitAnewArrayZ(this->alloc, Region*, this->blockCount); - } + bool assignRegionsBeforeGlobopt = this->func->HasTry() && (this->func->DoOptimizeTry() || + (this->func->IsSimpleJit() && this->func->hasBailout)); + + blockNum = 0; FOREACH_BLOCK_ALL(block, this) { block->SetBlockNum(blockNum++); @@ -211,22 +447,220 @@ FlowGraph::Build(void) { continue; } - this->UpdateRegionForBlock(block, blockToRegion); + this->UpdateRegionForBlock(block); + } + } NEXT_BLOCK_ALL; + AssertMsg(blockNum == this->blockCount, "Block count is out of whack"); + + if (this->finallyLabelStack) + { + Assert(this->finallyLabelStack->Empty()); + + // Add s0 definition at the beginning of the function + // We need this because - s0 symbol can get added to bytcodeUpwardExposed use when there are early returns, + // And globopt will complain that s0 is uninitialized, because we define it to undefined only at the end of the function + const auto addrOpnd = IR::AddrOpnd::New(this->func->GetScriptContextInfo()->GetUndefinedAddr(), IR::AddrOpndKindDynamicVar, this->func, true); + addrOpnd->SetValueType(ValueType::Undefined); + IR::RegOpnd *regOpnd = IR::RegOpnd::New(this->func->m_symTable->FindStackSym(0), TyVar, this->func); + IR::Instr *ldRet = IR::Instr::New(Js::OpCode::Ld_A, regOpnd, addrOpnd, this->func); + this->func->m_headInstr->GetNextRealInstr()->InsertBefore(ldRet); + + IR::LabelInstr * currentLabel = nullptr; + // look for early exits from a try, and insert bailout + FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, func) + { + if (instr->IsLabelInstr()) + { + currentLabel = instr->AsLabelInstr(); + } + else if (instr->m_opcode == Js::OpCode::TryFinally) + { + Assert(instr->AsBranchInstr()->GetTarget()->GetRegion()->GetType() == RegionTypeFinally); + this->finallyLabelStack->Push(instr->AsBranchInstr()->GetTarget()); + } + else if (instr->m_opcode == Js::OpCode::Leave) + { + IR::LabelInstr *branchTarget = instr->AsBranchInstr()->GetTarget(); + IR::LabelInstr *exitLabel = nullptr; + // An early exit (break, continue, return) from an EH region appears as Leave opcode + // When there is an early exit within a try finally, we have to execute finally code + // Currently we bailout on early exits + // For all such edges add edge from eh region -> finally and finally -> earlyexit + Assert(currentLabel != nullptr); + if (currentLabel && IsEarlyExitFromFinally(instr->AsBranchInstr(), currentLabel->GetRegion(), branchTarget->GetRegion(), instrNext, exitLabel)) + { + Assert(exitLabel); + IR::Instr * bailOnEarlyExit = IR::BailOutInstr::New(Js::OpCode::BailOnEarlyExit, IR::BailOutOnEarlyExit, instr, instr->m_func); + instr->InsertBefore(bailOnEarlyExit); + IR::LabelInstr *exceptFinallyLabel = this->finallyLabelStack->Top(); + IR::LabelInstr *nonExceptFinallyLabel = exceptFinallyLabel->m_next->m_next->AsLabelInstr(); + BasicBlock *nonExceptFinallyBlock = nonExceptFinallyLabel->GetBasicBlock(); + + if (Dominates(nonExceptFinallyBlock->firstInstr->AsLabelInstr()->GetRegion(), exitLabel->GetRegion())) + { + IR::Instr * leaveToFinally = IR::BranchInstr::New(Js::OpCode::Leave, exceptFinallyLabel, this->func); + instr->InsertBefore(leaveToFinally); + instr->Remove(); + this->AddEdge(currentLabel->GetBasicBlock(), exceptFinallyLabel->GetBasicBlock()); + + Assert(this->regToFinallyEndMap->ContainsKey(nonExceptFinallyLabel->GetRegion())); + BasicBlock * finallyEndBlock = this->regToFinallyEndMap->Item(nonExceptFinallyLabel->GetRegion()); + Assert(finallyEndBlock); + Assert(finallyEndBlock->GetFirstInstr()->AsLabelInstr()->GetRegion() == nonExceptFinallyLabel->GetRegion()); + InsertEdgeFromFinallyToEarlyExit(finallyEndBlock, exitLabel); + } + } + else if (currentLabel && currentLabel->GetRegion()->GetType() == RegionTypeFinally) + { + Assert(currentLabel->GetRegion()->GetMatchingTryRegion()->GetMatchingFinallyRegion(false) == currentLabel->GetRegion()); + // Convert Leave to Br because we execute non-excepting Finally in native code + instr->m_opcode = Js::OpCode::Br; + } + } + else if (instr->m_opcode == Js::OpCode::Finally) + { + this->finallyLabelStack->Pop(); + } + } + NEXT_INSTR_IN_FUNC_EDITING; + } + + blockNum = 0; + FOREACH_BLOCK_ALL(block, this) + { + block->SetBlockNum(blockNum++); + } NEXT_BLOCK_ALL; + + this->FindLoops(); +#if DBG_DUMP + if (PHASE_DUMP(Js::FGBuildPhase, this->GetFunc())) + { + if (assignRegionsBeforeGlobopt) + { + Output::Print(_u("Before CanonicalizeLoops\n")); + FOREACH_BLOCK_ALL(block, this) + { + block->DumpHeader(true); + Region *region = block->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (region) { + const char16 * regMap[] = { _u("RegionTypeInvalid"), + _u("RegionTypeRoot"), + _u("RegionTypeTry"), + _u("RegionTypeCatch"), + _u("RegionTypeFinally") }; + Output::Print(_u("Region %p RegionParent %p RegionType %s\n"), region, region->GetParent(), regMap[region->GetType()]); + } + } NEXT_BLOCK_ALL; + } + } +#endif + bool breakBlocksRelocated = this->CanonicalizeLoops(); + + blockNum = 0; + FOREACH_BLOCK_ALL(block, this) + { + block->SetBlockNum(blockNum++); + } NEXT_BLOCK_ALL; + +#if DBG + FOREACH_BLOCK_ALL(block, this) + { + if (assignRegionsBeforeGlobopt) + { + if (block->GetFirstInstr()->AsLabelInstr()->GetRegion()) + { + Assert(block->GetFirstInstr()->AsLabelInstr()->GetRegion()->ehBailoutData); + } } } NEXT_BLOCK_ALL; +#endif - AssertMsg (blockNum == this->blockCount, "Block count is out of whack"); +#if DBG + FOREACH_BLOCK_ALL(block, this) + { + if (PHASE_DUMP(Js::FGBuildPhase, this->GetFunc())) + { + if (assignRegionsBeforeGlobopt) + { + block->DumpHeader(true); + Region *region = block->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (region) { + const char16 * regMap[] = { _u("RegionTypeInvalid"), + _u("RegionTypeRoot"), + _u("RegionTypeTry"), + _u("RegionTypeCatch"), + _u("RegionTypeFinally") }; + Output::Print(_u("Region %p RegionParent %p RegionType %s\n"), region, region->GetParent(), regMap[region->GetType()]); + } + } + } + } NEXT_BLOCK_ALL; +#endif if (breakBlocksRelocated) { // Sort loop lists only if there is break block removal. SortLoopLists(); } + +#if DBG + this->VerifyLoopGraph(); +#endif + #if DBG_DUMP this->Dump(false, nullptr); #endif } +void FlowGraph::InsertEdgeFromFinallyToEarlyExit(BasicBlock *finallyEndBlock, IR::LabelInstr *exitLabel) +{ + IR::Instr * lastInstr = finallyEndBlock->GetLastInstr(); + IR::LabelInstr * lastLabel = finallyEndBlock->GetFirstInstr()->AsLabelInstr(); + + Assert(lastInstr->m_opcode == Js::OpCode::LeaveNull || lastInstr->m_opcode == Js::OpCode::Leave || lastInstr->m_opcode == Js::OpCode::BrOnException); + // Add a new block, add BrOnException to earlyexit, assign region + // Finally + // ... + // L1: + // LeaveNull + // to + // Finally + // ... + // L1: + // BrOnException earlyExitLabel + // L1': + // LeaveNull + + BasicBlock *nextBB = finallyEndBlock->GetNext(); + + IR::LabelInstr *leaveLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func); + lastInstr->InsertBefore(leaveLabel); + + this->AddBlock(leaveLabel, lastInstr, finallyEndBlock->GetNext(), finallyEndBlock /*prevBlock*/); + leaveLabel->SetRegion(lastLabel->GetRegion()); + + this->AddEdge(finallyEndBlock, leaveLabel->GetBasicBlock()); + + IR::LabelInstr *brLabel = IR::LabelInstr::New(Js::OpCode::Label, this->func); + leaveLabel->InsertBefore(brLabel); + + IR::BranchInstr *brToExit = IR::BranchInstr::New(Js::OpCode::BrOnException, exitLabel, this->func); + leaveLabel->InsertBefore(brToExit); + + this->AddBlock(brLabel, brToExit, finallyEndBlock->GetNext(), finallyEndBlock /*prevBlock*/); + + brLabel->SetRegion(lastLabel->GetRegion()); + + // in case of throw/non-terminating loop, there maybe no edge to the next block + if (this->FindEdge(finallyEndBlock, nextBB)) + { + finallyEndBlock->RemoveSucc(nextBB, this); + } + this->AddEdge(finallyEndBlock, brLabel->GetBasicBlock()); + + this->regToFinallyEndMap->Item(lastLabel->GetRegion(), finallyEndBlock->next->next); +} + void FlowGraph::SortLoopLists() { @@ -441,7 +875,7 @@ Loop::InsertLandingPad(FlowGraph *fg) headInstr->InsertBefore(landingPadLabel); landingPadLabel->SetBasicBlock(landingPad); - + landingPadLabel->SetRegion(headBlock->GetFirstInstr()->AsLabelInstr()->GetRegion()); landingPad->SetBlockNum(fg->blockCount++); landingPad->SetFirstInstr(landingPadLabel); landingPad->SetLastInstr(landingPadLabel); @@ -489,6 +923,11 @@ Loop::InsertLandingPad(FlowGraph *fg) } NEXT_PREDECESSOR_EDGE_EDITING; fg->AddEdge(landingPad, headBlock); + + if (headBlock->GetFirstInstr()->AsLabelInstr()->GetRegion() && headBlock->GetFirstInstr()->AsLabelInstr()->GetRegion()->GetType() != RegionTypeRoot) + { + landingPadLabel->m_hasNonBranchRef = true; + } } bool @@ -601,6 +1040,10 @@ FlowGraph::MoveBlocksBefore(BasicBlock *blockStart, BasicBlock *blockEnd, BasicB } IR::Instr *dstLastInstr = dstPredBlockLastInstr; + + bool assignRegionsBeforeGlobopt = this->func->HasTry() && (this->func->DoOptimizeTry() || + (this->func->IsSimpleJit() && this->func->hasBailout)); + if (dstLastInstr->IsBranchInstr() && dstLastInstr->AsBranchInstr()->HasFallThrough()) { //There is a fallthrough in the block after which break block is inserted. @@ -610,6 +1053,14 @@ FlowGraph::MoveBlocksBefore(BasicBlock *blockStart, BasicBlock *blockEnd, BasicB BasicBlock *compensationBlock = this->InsertCompensationCodeForBlockMove(dstEdge, true /*insert compensation block to loop list*/, true /*At sink*/); Assert(compensationBlock); } + + // We have to update region info for blocks whose predecessors changed + if (assignRegionsBeforeGlobopt) + { + UpdateRegionForBlockFromEHPred(dstPredBlock, true); + UpdateRegionForBlockFromEHPred(blockStart, true); + UpdateRegionForBlockFromEHPred(srcNextBlock, true); + } } FlowEdge * @@ -659,7 +1110,7 @@ FlowGraph::CanonicalizeLoops() for (Loop *loop = this->loopList; loop; loop = loop->next) { loop->InsertLandingPad(this); - if (!this->func->HasTry() || this->func->DoOptimizeTryCatch()) + if (!this->func->HasTry() || this->func->DoOptimizeTry()) { bool relocated = loop->RemoveBreakBlocks(this); if (!breakBlockRelocated && relocated) @@ -911,7 +1362,8 @@ BasicBlock * FlowGraph::AddBlock( IR::Instr * firstInstr, IR::Instr * lastInstr, - BasicBlock * nextBlock) + BasicBlock * nextBlock, + BasicBlock *prevBlock) { BasicBlock * block; IR::LabelInstr * labelInstr; @@ -1015,13 +1467,22 @@ FlowGraph::AddBlock( block->SetFirstInstr(firstInstr); block->SetLastInstr(lastInstr); - if (this->blockList) + if (!prevBlock) { - this->blockList->prev = block; + if (this->blockList) + { + this->blockList->prev = block; + } + block->next = this->blockList; + this->blockList = block; + } + else + { + prevBlock->next = block; + block->prev = prevBlock; + block->next = nextBlock; + nextBlock->prev = block; } - block->next = this->blockList; - this->blockList = block; - return block; } @@ -1076,10 +1537,8 @@ void FlowGraph::Destroy(void) { BOOL fHasTry = this->func->HasTry(); - Region ** blockToRegion = nullptr; if (fHasTry) { - blockToRegion = JitAnewArrayZ(this->alloc, Region*, this->blockCount); // Do unreachable code removal up front to avoid problems // with unreachable back edges, etc. this->RemoveUnreachableBlocks(); @@ -1157,7 +1616,7 @@ FlowGraph::Destroy(void) if (fHasTry) { - this->UpdateRegionForBlock(block, blockToRegion); + this->UpdateRegionForBlock(block); } if (firstInstr->IsLabelInstr()) @@ -1202,11 +1661,11 @@ FlowGraph::Destroy(void) // block boundaries. FOREACH_BLOCK(block, this) { - Region * region = blockToRegion[block->GetBlockNum()]; + Region * region = block->GetFirstInstr()->AsLabelInstr()->GetRegion(); Region * predRegion = nullptr; FOREACH_PREDECESSOR_BLOCK(predBlock, block) { - predRegion = blockToRegion[predBlock->GetBlockNum()]; + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); if (predBlock->GetLastInstr() == nullptr) { AssertMsg(region == predRegion, "Bad region propagation through empty block"); @@ -1235,6 +1694,9 @@ FlowGraph::Destroy(void) } break; case Js::OpCode::Leave: + AssertMsg(region == predRegion->GetParent() || (predRegion->GetType() == RegionTypeTry && predRegion->GetMatchingFinallyRegion(false) == region) || + (region == predRegion && this->func->IsLoopBodyInTry()), "Bad region prop on leaving try-catch/finally"); + break; case Js::OpCode::LeaveNull: AssertMsg(region == predRegion->GetParent() || (region == predRegion && this->func->IsLoopBodyInTry()), "Bad region prop on leaving try-catch/finally"); break; @@ -1246,33 +1708,26 @@ FlowGraph::Destroy(void) // - FullJit: BrOnException is removed in the forward pass. case Js::OpCode::BrOnException: Assert(!this->func->DoGlobOpt()); + break; case Js::OpCode::BrOnNoException: - Assert(this->func->HasTry() && - ((!this->func->HasFinally() && !this->func->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, this->func)) || - (this->func->IsSimpleJit() && this->func->GetJITFunctionBody()->DoJITLoopBody()))); // should be relaxed as more bailouts are added in Simple Jit - - Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch); - if (region->GetType() == RegionTypeCatch) - { - Assert((predRegion->GetType() == RegionTypeTry) || (predRegion->GetType() == RegionTypeCatch)); - } - else if (region->GetType() == RegionTypeTry) - { - Assert(region == predRegion); - } + Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch || region->GetType() == RegionTypeFinally); break; case Js::OpCode::Br: if (region->GetType() == RegionTypeCatch && region != predRegion) { AssertMsg(predRegion->GetType() == RegionTypeTry, "Bad region type for the try"); } + else if (region->GetType() == RegionTypeFinally && region != predRegion) + { + // When we add edge from finally to early exit, and break block removal moves the edge into finally region, we can end up with an edge between finally and non eh region + } else { - AssertMsg(region == predRegion, "Bad region propagation through interior block"); + // Leave's within non excepting finallys that are not early exit edges are converted to br + AssertMsg((predRegion->IsNonExceptingFinally() && region == predRegion->GetParent()) || region == predRegion, "Bad region propagation through interior block"); } break; default: - AssertMsg(region == predRegion, "Bad region propagation through interior block"); break; } } @@ -1282,11 +1737,18 @@ FlowGraph::Destroy(void) switch (region->GetType()) { case RegionTypeRoot: - Assert(!region->GetMatchingTryRegion() && !region->GetMatchingCatchRegion() && !region->GetMatchingFinallyRegion()); + Assert(!region->GetMatchingTryRegion() && !region->GetMatchingCatchRegion() && !region->GetMatchingFinallyRegion(true) && !region->GetMatchingFinallyRegion(false)); break; case RegionTypeTry: - Assert(!(region->GetMatchingCatchRegion() && region->GetMatchingFinallyRegion())); + if ((this->func->IsSimpleJit() && this->func->hasBailout) || !this->func->DoOptimizeTry()) + { + Assert((region->GetMatchingCatchRegion() != nullptr) ^ (region->GetMatchingFinallyRegion(true) && !region->GetMatchingFinallyRegion(false))); + } + else + { + Assert((region->GetMatchingCatchRegion() != nullptr) ^ (region->GetMatchingFinallyRegion(true) && region->GetMatchingFinallyRegion(false))); + } break; case RegionTypeCatch: @@ -1315,11 +1777,38 @@ FlowGraph::Destroy(void) this->func->isFlowGraphValid = false; } +bool FlowGraph::IsEHTransitionInstr(IR::Instr *instr) +{ + Js::OpCode op = instr->m_opcode; + return (op == Js::OpCode::TryCatch || op == Js::OpCode::TryFinally || op == Js::OpCode::Leave || op == Js::OpCode::LeaveNull); +} + +BasicBlock * FlowGraph::GetPredecessorForRegionPropagation(BasicBlock *block) +{ + BasicBlock *ehPred = nullptr; + FOREACH_PREDECESSOR_BLOCK(predBlock, block) + { + Region * predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (IsEHTransitionInstr(predBlock->GetLastInstr()) && predRegion) + { + // MGTODO : change this to return, once you know there can exist only one eh transitioning pred + Assert(ehPred == nullptr); + ehPred = predBlock; + } + AssertMsg(predBlock->GetBlockNum() < this->blockCount, "Misnumbered block at teardown time?"); + } + NEXT_PREDECESSOR_BLOCK; + return ehPred; +} + // Propagate the region forward from the block's predecessor(s), tracking the effect // of the flow transition. Record the region in the block-to-region map provided // and on the label at the entry to the block (if any). + +// We need to know the end of finally for inserting edge at the end of finally to early exit +// Store it in regToFinallyEndMap as we visit blocks instead of recomputing later while adding early exit edges void -FlowGraph::UpdateRegionForBlock(BasicBlock * block, Region ** blockToRegion) +FlowGraph::UpdateRegionForBlock(BasicBlock * block) { Region *region; Region * predRegion = nullptr; @@ -1327,8 +1816,7 @@ FlowGraph::UpdateRegionForBlock(BasicBlock * block, Region ** blockToRegion) IR::Instr * firstInstr = block->GetFirstInstr(); if (firstInstr->IsLabelInstr() && firstInstr->AsLabelInstr()->GetRegion()) { - Assert(this->func->HasTry() && (this->func->DoOptimizeTryCatch() || (this->func->IsSimpleJit() && this->func->hasBailout))); - blockToRegion[block->GetBlockNum()] = firstInstr->AsLabelInstr()->GetRegion(); + Assert(this->func->HasTry() && (this->func->DoOptimizeTry() || (this->func->IsSimpleJit() && this->func->hasBailout))); return; } @@ -1337,15 +1825,50 @@ FlowGraph::UpdateRegionForBlock(BasicBlock * block, Region ** blockToRegion) // Head of the graph: create the root region. region = Region::New(RegionTypeRoot, nullptr, this->func); } + else if (block->GetLastInstr()->m_opcode == Js::OpCode::LeaveNull) + { + region = nullptr; + if (block->GetPredList()->Count() == 1) + { + FOREACH_PREDECESSOR_BLOCK(predBlock, block) + { + AssertMsg(predBlock->GetBlockNum() < this->blockCount, "Misnumbered block at teardown time?"); + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (predRegion) + { + region = this->PropagateRegionFromPred(block, predBlock, predRegion, tryInstr); + break; + } + } + NEXT_PREDECESSOR_BLOCK; + } + else + { + FOREACH_PREDECESSOR_BLOCK(predBlock, block) + { + AssertMsg(predBlock->GetBlockNum() < this->blockCount, "Misnumbered block at teardown time?"); + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (predRegion && predRegion->GetType() == RegionTypeFinally) + { + region = this->PropagateRegionFromPred(block, predBlock, predRegion, tryInstr); + break; + } + } + NEXT_PREDECESSOR_BLOCK; + } + if (this->regToFinallyEndMap) + { + regToFinallyEndMap->Item(region, block); + } + } else { // Propagate the region forward by finding a predecessor we've already processed. - // We require that there be one, since we've already removed unreachable blocks. region = nullptr; FOREACH_PREDECESSOR_BLOCK(predBlock, block) { AssertMsg(predBlock->GetBlockNum() < this->blockCount, "Misnumbered block at teardown time?"); - predRegion = blockToRegion[predBlock->GetBlockNum()]; + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); if (predRegion != nullptr) { region = this->PropagateRegionFromPred(block, predBlock, predRegion, tryInstr); @@ -1355,14 +1878,13 @@ FlowGraph::UpdateRegionForBlock(BasicBlock * block, Region ** blockToRegion) NEXT_PREDECESSOR_BLOCK; } - AnalysisAssertMsg(region != nullptr, "Failed to find region for block"); - if (!region->ehBailoutData) + Assert(region || block->GetPredList()->Count() == 0); + if (region && !region->ehBailoutData) { region->AllocateEHBailoutData(this->func, tryInstr); } - // Record the region in the block-to-region map. - blockToRegion[block->GetBlockNum()] = region; + Assert(firstInstr->IsLabelInstr()); if (firstInstr->IsLabelInstr()) { // Record the region on the label and make sure it stays around as a region @@ -1373,6 +1895,104 @@ FlowGraph::UpdateRegionForBlock(BasicBlock * block, Region ** blockToRegion) { labelInstr->m_hasNonBranchRef = true; } + + if (region && this->func->HasFinally() && region->GetType() == RegionTypeRoot && !labelInstr->m_hasNonBranchRef) + { + FOREACH_PREDECESSOR_BLOCK(predBlock, block) + { + AssertMsg(predBlock->GetBlockNum() < this->blockCount, "Misnumbered block at teardown time?"); + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (predRegion != region) + { + labelInstr->m_hasNonBranchRef = true; + break; + } + } + NEXT_PREDECESSOR_BLOCK; + } + } +} + +void +FlowGraph::UpdateRegionForBlockFromEHPred(BasicBlock * block, bool reassign) +{ + Region *region; + Region * predRegion = nullptr; + IR::Instr * tryInstr = nullptr; + IR::Instr * firstInstr = block->GetFirstInstr(); + if (!reassign && firstInstr->IsLabelInstr() && firstInstr->AsLabelInstr()->GetRegion()) + { + Assert(this->func->HasTry() && (this->func->DoOptimizeTry() || (this->func->IsSimpleJit() && this->func->hasBailout))); + return; + } + + if (block == this->blockList) + { + // Head of the graph: create the root region. + region = Region::New(RegionTypeRoot, nullptr, this->func); + } + else + { + // Propagate the region forward by finding a predecessor we've already processed. + // Since we do break block remval after region propagation, we cannot pick the first predecessor which has an assigned region + // If there is a eh transitioning pred, we pick that + // There cannot be more than one eh transitioning pred (?) + region = nullptr; + BasicBlock *ehPred = this->GetPredecessorForRegionPropagation(block); + if (ehPred) + { + predRegion = ehPred->GetFirstInstr()->AsLabelInstr()->GetRegion(); + Assert(predRegion != nullptr); + region = this->PropagateRegionFromPred(block, ehPred, predRegion, tryInstr); + } + else + { + FOREACH_PREDECESSOR_BLOCK(predBlock, block) + { + predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion(); + if (predRegion != nullptr) + { + if (predBlock->GetLastInstr()->m_opcode == Js::OpCode::BrOnException && predRegion->IsNonExceptingFinally()) + { + // early exit + continue; + } + if (predBlock->GetLastInstr()->m_opcode == Js::OpCode::BrOnException && predRegion->IsNonExceptingFinally()) + { + // early exit + continue; + } + region = this->PropagateRegionFromPred(block, predBlock, predRegion, tryInstr); + break; + } + } + NEXT_PREDECESSOR_BLOCK; + } + if (!region) + { + Assert(reassign && (block->GetPredList()->Count() == 0 || block->firstInstr->AsLabelInstr()->GetRegion())); + } + } + + if (region) + { + if (!region->ehBailoutData) + { + region->AllocateEHBailoutData(this->func, tryInstr); + } + + Assert(firstInstr->IsLabelInstr()); + if (firstInstr->IsLabelInstr()) + { + // Record the region on the label and make sure it stays around as a region + // marker if we're entering a region at this point. + IR::LabelInstr * labelInstr = firstInstr->AsLabelInstr(); + labelInstr->SetRegion(region); + if (region != predRegion) + { + labelInstr->m_hasNonBranchRef = true; + } + } } } @@ -1425,10 +2045,11 @@ FlowGraph::PropagateRegionFromPred(BasicBlock * block, BasicBlock * predBlock, R if (firstInstr == predLastInstr->AsBranchInstr()->GetTarget()) { + Assert(tryRegion && tryRegion->GetType() == RegionType::RegionTypeTry); region = Region::New(RegionTypeFinally, predRegion, this->func); - Assert(tryRegion); region->SetMatchingTryRegion(tryRegion); - tryRegion->SetMatchingFinallyRegion(region); + tryRegion->SetMatchingFinallyRegion(region, true); + tryInstr = predLastInstr; } else { @@ -1438,6 +2059,43 @@ FlowGraph::PropagateRegionFromPred(BasicBlock * block, BasicBlock * predBlock, R break; case Js::OpCode::Leave: + // When there is an unconditional Leave within a finally due to early exit, + // we may not be able to find LeaveNull, due to unreachable block elimination + // Update the endOfFinally here to handle such cases + if (this->regToFinallyEndMap && predRegion->IsNonExceptingFinally()) + { + BasicBlock *endOfFinally = regToFinallyEndMap->ContainsKey(predRegion) ? regToFinallyEndMap->Item(predRegion) : nullptr; + if (!endOfFinally) + { + regToFinallyEndMap->Add(predRegion, predBlock); + } + else if (endOfFinally && endOfFinally->GetLastInstr()->m_opcode != Js::OpCode::LeaveNull) + { + regToFinallyEndMap->Item(predRegion, predBlock); + } + } + if (firstInstr->m_next && firstInstr->m_next->m_opcode == Js::OpCode::Finally) + { + tryRegion = predRegion; + Assert(tryRegion->GetMatchingFinallyRegion(true) != nullptr); + region = Region::New(RegionTypeFinally, predRegion->GetParent(), this->func); + Assert(tryRegion && tryRegion->GetType() == RegionType::RegionTypeTry); + region->SetMatchingTryRegion(tryRegion); + tryRegion->SetMatchingFinallyRegion(region, false); + break; + } + + // Exiting a try or handler. Retrieve the current region's parent. + region = predRegion->GetParent(); + if (region == nullptr) + { + // We found a Leave in the root region- this can only happen when a jitted loop body + // in a try block has a return statement. + Assert(this->func->IsLoopBodyInTry()); + predLastInstr->AsBranchInstr()->m_isOrphanedLeave = true; + region = predRegion; + } + break; case Js::OpCode::LeaveNull: // Exiting a try or handler. Retrieve the current region's parent. region = predRegion->GetParent(); @@ -1450,7 +2108,29 @@ FlowGraph::PropagateRegionFromPred(BasicBlock * block, BasicBlock * predBlock, R region = predRegion; } break; - + case Js::OpCode::BailOnException: + // Infinite loop, no edge to non excepting finally + if (firstInstr->m_next && firstInstr->m_next->m_opcode == Js::OpCode::Finally) + { + tryRegion = predRegion->GetMatchingTryRegion(); + region = Region::New(RegionTypeFinally, predRegion->GetParent(), this->func); + Assert(tryRegion && tryRegion->GetType() == RegionType::RegionTypeTry); + region->SetMatchingTryRegion(tryRegion); + tryRegion->SetMatchingFinallyRegion(region, false); + } + break; + case Js::OpCode::BrOnException: + // Infinite loop inside another EH region within finally, + // We have added edges for all infinite loops inside a finally, identify that and transition to parent + if (predRegion->GetType() != RegionTypeFinally && firstInstr->GetNextRealInstr()->m_opcode == Js::OpCode::LeaveNull) + { + region = predRegion->GetParent(); + } + else + { + region = predRegion; + } + break; default: // Normal (non-EH) transition: just propagate the region. region = predRegion; @@ -1687,6 +2367,14 @@ FlowGraph::InsertCompensationCodeForBlockMove(FlowEdge * edge, bool insertToLoo } } + bool assignRegionsBeforeGlobopt = this->func->HasTry() && (this->func->DoOptimizeTry() || + (this->func->IsSimpleJit() && this->func->hasBailout)); + // MGTODO : maybe we can just set the pred region instead of calling Propagate function ? + if (assignRegionsBeforeGlobopt) + { + UpdateRegionForBlockFromEHPred(compBlock); + } + return compBlock; } @@ -2655,6 +3343,8 @@ FlowGraph::RemoveBlock(BasicBlock *block, GlobOpt * globOpt, bool tailDuping) { Assert(instr->IsLabelInstr()); instr->AsLabelInstr()->m_isLoopTop = false; + instr->AsLabelInstr()->SetRegion(nullptr); + instr->AsLabelInstr()->m_hasNonBranchRef = false; } else { @@ -3177,7 +3867,7 @@ FlowGraph::VerifyLoopGraph() if (!PHASE_OFF(Js::RemoveBreakBlockPhase, this->GetFunc())) { // Make sure all break blocks have been removed. - if (loop && !block->isLoopHeader && !(this->func->HasTry() && !this->func->DoOptimizeTryCatch())) + if (loop && !block->isLoopHeader && !(this->func->HasTry() && !this->func->DoOptimizeTry())) { Assert(loop->IsDescendentOrSelf(block->GetPrev()->loop)); } diff --git a/lib/Backend/FlowGraph.h b/lib/Backend/FlowGraph.h index b2e84ee7ed2..2a45dcc6f3a 100644 --- a/lib/Backend/FlowGraph.h +++ b/lib/Backend/FlowGraph.h @@ -139,6 +139,9 @@ class FlowGraph tailBlock(nullptr), loopList(nullptr), catchLabelStack(nullptr), + finallyLabelStack(nullptr), + leaveNullLabelStack(nullptr), + regToFinallyEndMap(nullptr), hasBackwardPassInfo(false), hasLoop(false), implicitCallFlags(Js::ImplicitCall_HasNoInfo) @@ -149,7 +152,7 @@ class FlowGraph void Destroy(void); void RunPeeps(); - BasicBlock * AddBlock(IR::Instr * firstInstr, IR::Instr * lastInstr, BasicBlock * nextBlock); + BasicBlock * AddBlock(IR::Instr * firstInstr, IR::Instr * lastInstr, BasicBlock * nextBlock, BasicBlock *prevBlock = nullptr); FlowEdge * AddEdge(BasicBlock * predBlock, BasicBlock * succBlock); BasicBlock * InsertCompensationCodeForBlockMove(FlowEdge * edge, // Edge where compensation code needs to be inserted bool insertCompensationBlockToLoopList = false, @@ -166,7 +169,12 @@ class FlowGraph static void SafeRemoveInstr(IR::Instr *instr); void SortLoopLists(); FlowEdge * FindEdge(BasicBlock *predBlock, BasicBlock *succBlock); - + IR::LabelInstr * DeleteLeaveChainBlocks(IR::BranchInstr *leaveInstr, IR::Instr * &instrPrev); + bool IsEarlyExitFromFinally(IR::BranchInstr *leaveInstr, Region *currentRegion, Region *branchTargetRegion, IR::Instr *&instrPrev, IR::LabelInstr *&exitLabel); + bool Dominates(Region *finallyRegion, Region *exitLabelRegion); + bool DoesExitLabelDominate(IR::BranchInstr *leaveInstr); + void InsertEdgeFromFinallyToEarlyExit(BasicBlock * finallyEndBlock, IR::LabelInstr * exitLabel); + BasicBlock * FindInfiniteLoop(BasicBlock * finallyBlock); #if DBG_DUMP void Dump(); void Dump(bool verbose, const char16 *form); @@ -177,6 +185,10 @@ class FlowGraph BasicBlock * tailBlock; Loop * loopList; SList * catchLabelStack; + SList * finallyLabelStack; + SList * leaveNullLabelStack; + typedef JsUtil::BaseDictionary RegionToFinallyEndMapType; + RegionToFinallyEndMapType * regToFinallyEndMap; bool hasBackwardPassInfo; bool hasLoop; Js::ImplicitCallFlags implicitCallFlags; @@ -186,7 +198,10 @@ class FlowGraph void BuildLoop(BasicBlock *headBlock, BasicBlock *tailBlock, Loop *parentLoop = nullptr); void WalkLoopBlocks(BasicBlock *block, Loop *loop, JitArenaAllocator *tempAlloc); void AddBlockToLoop(BasicBlock *block, Loop *loop); - void UpdateRegionForBlock(BasicBlock *block, Region **blockToRegion); + bool IsEHTransitionInstr(IR::Instr *instr); + BasicBlock * GetPredecessorForRegionPropagation(BasicBlock *block); + void UpdateRegionForBlock(BasicBlock *block); + void UpdateRegionForBlockFromEHPred(BasicBlock *block, bool reassign = false); Region * PropagateRegionFromPred(BasicBlock *block, BasicBlock *predBlock, Region *predRegion, IR::Instr * &tryInstr); IR::Instr * PeepCm(IR::Instr *instr); IR::Instr * PeepTypedCm(IR::Instr *instr); diff --git a/lib/Backend/Func.h b/lib/Backend/Func.h index 36f39422ded..46bf5023311 100644 --- a/lib/Backend/Func.h +++ b/lib/Backend/Func.h @@ -196,7 +196,8 @@ class Func { return !PHASE_OFF(Js::GlobOptPhase, this) && !IsSimpleJit() && - (!GetTopFunc()->HasTry() || GetTopFunc()->CanOptimizeTryCatch()); + (!GetTopFunc()->HasTry() || GetTopFunc()->CanOptimizeTryCatch()) && + (!GetTopFunc()->HasFinally() || GetTopFunc()->CanOptimizeTryFinally()); } bool DoInline() const @@ -204,15 +205,20 @@ class Func return DoGlobOpt() && !GetTopFunc()->HasTry(); } - bool DoOptimizeTryCatch() const + bool DoOptimizeTry() const { Assert(IsTopFunc()); return DoGlobOpt(); } + bool CanOptimizeTryFinally() const + { + return !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryFinallyPhase, this); + } + bool CanOptimizeTryCatch() const { - return !this->HasFinally() && !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, this); + return !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, this); } bool DoSimpleJitDynamicProfile() const; @@ -834,7 +840,7 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece; bool HasArrayInfo() { const auto top = this->GetTopFunc(); - return this->HasProfileInfo() && this->GetWeakFuncRef() && !(top->HasTry() && !top->DoOptimizeTryCatch()) && + return this->HasProfileInfo() && this->GetWeakFuncRef() && !(top->HasTry() && !top->DoOptimizeTry()) && top->DoGlobOpt() && !PHASE_OFF(Js::LoopFastPathPhase, top); } diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 8bb49ba4cc2..6a42991bcb4 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -870,7 +870,7 @@ GlobOpt::TryTailDup(IR::BranchInstr *tailBranch) // If we've duplicated everywhere, tail block is dead and should be removed. if (dupCount == origPredCount) { - AssertMsg(mergeLabel->IsUnreferenced(), "Should not remove block with referenced label."); + AssertMsg(mergeLabel->labelRefs.Empty(), "Should not remove block with referenced label."); func->m_fg->RemoveBlock(labelBlock, nullptr, true); } @@ -2423,7 +2423,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) IR::Instr *instrPrev = instr->m_prev; IR::Instr *instrNext = instr->m_next; - if (instr->IsLabelInstr() && this->func->HasTry() && this->func->DoOptimizeTryCatch()) + if (instr->IsLabelInstr() && this->func->HasTry() && this->func->DoOptimizeTry()) { this->currentRegion = instr->AsLabelInstr()->GetRegion(); Assert(this->currentRegion); @@ -2536,27 +2536,45 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) src1Val = src2Val; src2Val = nullptr; } - else if (instr->m_opcode == Js::OpCode::TryCatch && this->func->DoOptimizeTryCatch()) + else if ((instr->m_opcode == Js::OpCode::TryCatch && this->func->DoOptimizeTry()) || (instr->m_opcode == Js::OpCode::TryFinally && this->func->DoOptimizeTry())) { - ProcessTryCatch(instr); + ProcessTryHandler(instr); } else if (instr->m_opcode == Js::OpCode::BrOnException) { - // BrOnException was added to model flow from try region to the catch region to assist - // the backward pass in propagating bytecode upward exposed info from the catch block - // to the try, and to handle break blocks. Removing it here as it has served its purpose - // and keeping it around might also have unintended effects while merging block data for - // the catch block's predecessors. - // Note that the Deadstore pass will still be able to propagate bytecode upward exposed info - // because it doesn't skip dead blocks for that. - this->RemoveFlowEdgeToCatchBlock(instr); - *isInstrRemoved = true; - this->currentBlock->RemoveInstr(instr); - return instrNext; + if (instr->AsBranchInstr()->GetTarget()->GetRegion()->GetType() != RegionType::RegionTypeFinally) + { + // BrOnException was added to model flow from try region to the catch region to assist + // the backward pass in propagating bytecode upward exposed info from the catch block + // to the try, and to handle break blocks. Removing it here as it has served its purpose + // and keeping it around might also have unintended effects while merging block data for + // the catch block's predecessors. + // Note that the Deadstore pass will still be able to propagate bytecode upward exposed info + // because it doesn't skip dead blocks for that. + this->RemoveFlowEdgeToCatchBlock(instr); + *isInstrRemoved = true; + this->currentBlock->RemoveInstr(instr); + return instrNext; + } + else + { + // We add BrOnException from a finally region to early exit + this->RemoveFlowEdgeToFinallyOnExceptionBlock(instr); + *isInstrRemoved = true; + this->currentBlock->RemoveInstr(instr); + return instrNext; + } } else if (instr->m_opcode == Js::OpCode::BrOnNoException) { - this->RemoveFlowEdgeToCatchBlock(instr); + if (instr->AsBranchInstr()->GetTarget()->GetRegion()->GetType() == RegionType::RegionTypeCatch) + { + this->RemoveFlowEdgeToCatchBlock(instr); + } + else + { + this->RemoveFlowEdgeToFinallyOnExceptionBlock(instr); + } } bool isAlreadyTypeSpecialized = false; @@ -2647,7 +2665,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved) instrNext = instr->m_next; if (dst) { - if (this->func->HasTry() && this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && this->func->DoOptimizeTry()) { this->InsertToVarAtDefInTryRegion(instr, dst); } @@ -17387,11 +17405,25 @@ GlobOpt::PreOptPeep(IR::Instr *instr) } case Js::OpCode::BailOnException: { - Assert(this->func->HasTry() && this->func->DoOptimizeTryCatch() && - instr->m_prev->m_opcode == Js::OpCode::Catch && - instr->m_prev->m_prev->IsLabelInstr() && - instr->m_prev->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeCatch); // Should also handle RegionTypeFinally - + Assert( + ( + this->func->HasTry() && this->func->DoOptimizeTry() && + instr->m_prev->m_opcode == Js::OpCode::Catch && + instr->m_prev->m_prev->IsLabelInstr() && + instr->m_prev->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeCatch + ) + || + ( + this->func->HasFinally() && this->func->DoOptimizeTry() && + instr->m_prev->AsLabelInstr() && + instr->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeFinally + ) + ); + break; + } + case Js::OpCode::BailOnEarlyExit: + { + Assert(this->func->HasFinally() && this->func->DoOptimizeTry()); break; } default: @@ -17445,7 +17477,7 @@ GlobOpt::RemoveCodeAfterNoFallthroughInstr(IR::Instr *instr) } void -GlobOpt::ProcessTryCatch(IR::Instr* instr) +GlobOpt::ProcessTryHandler(IR::Instr* instr) { Assert(instr->m_next->IsLabelInstr() && instr->m_next->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeTry); @@ -17487,8 +17519,9 @@ GlobOpt::RemoveFlowEdgeToCatchBlock(IR::Instr * instr) catchBlock = instr->AsBranchInstr()->GetTarget()->GetBasicBlock(); predBlock = this->currentBlock; } - else if (instr->m_opcode == Js::OpCode::BrOnNoException) + else { + Assert(instr->m_opcode == Js::OpCode::BrOnNoException); IR::Instr * nextInstr = instr->GetNextRealInstrOrLabel(); Assert(nextInstr->IsLabelInstr()); IR::LabelInstr * nextLabel = nextInstr->AsLabelInstr(); @@ -17522,6 +17555,59 @@ GlobOpt::RemoveFlowEdgeToCatchBlock(IR::Instr * instr) } } +void +GlobOpt::RemoveFlowEdgeToFinallyOnExceptionBlock(IR::Instr * instr) +{ + Assert(instr->IsBranchInstr()); + + BasicBlock * finallyBlock = nullptr; + BasicBlock * predBlock = nullptr; + if (instr->m_opcode == Js::OpCode::BrOnException) + { + finallyBlock = instr->AsBranchInstr()->GetTarget()->GetBasicBlock(); + predBlock = this->currentBlock; + Assert(finallyBlock && predBlock); + } + else + { + Assert(instr->m_opcode == Js::OpCode::BrOnNoException); + IR::Instr * nextInstr = instr->GetNextRealInstrOrLabel(); + Assert(nextInstr->IsLabelInstr()); + IR::LabelInstr * nextLabel = nextInstr->AsLabelInstr(); + + if (nextLabel->GetRegion() && nextLabel->GetRegion()->GetType() == RegionTypeFinally) + { + finallyBlock = nextLabel->GetBasicBlock(); + predBlock = this->currentBlock; + } + else + { + if (!(nextLabel->m_next->IsBranchInstr() && nextLabel->m_next->AsBranchInstr()->IsUnconditional())) + { + // Already processed in loop prepass + return; + } + BasicBlock * nextBlock = nextLabel->GetBasicBlock(); + IR::BranchInstr * branchTofinallyBlockOrEarlyExit = nextLabel->m_next->AsBranchInstr(); + IR::LabelInstr * finallyBlockLabelOrEarlyExitLabel = branchTofinallyBlockOrEarlyExit->GetTarget(); + finallyBlock = finallyBlockLabelOrEarlyExitLabel->GetBasicBlock(); + predBlock = nextBlock; + } + } + + if (finallyBlock && predBlock) + { + if (this->func->m_fg->FindEdge(predBlock, finallyBlock)) + { + predBlock->RemoveDeadSucc(finallyBlock, this->func->m_fg); + if (predBlock == this->currentBlock) + { + predBlock->DecrementDataUseCount(); + } + } + } +} + IR::Instr * GlobOpt::OptPeep(IR::Instr *instr, Value *src1Val, Value *src2Val) { diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h index eace3a22f02..7cd32dc7d3f 100644 --- a/lib/Backend/GlobOpt.h +++ b/lib/Backend/GlobOpt.h @@ -918,9 +918,10 @@ class GlobOpt IR::Instr * OptPeep(IR::Instr *instr, Value *src1Val, Value *src2Val); void OptimizeIndirUses(IR::IndirOpnd *indir, IR::Instr * *pInstr, Value **indirIndexValRef); void RemoveCodeAfterNoFallthroughInstr(IR::Instr *instr); - void ProcessTryCatch(IR::Instr* instr); + void ProcessTryHandler(IR::Instr* instr); void InsertToVarAtDefInTryRegion(IR::Instr * instr, IR::Opnd * dstOpnd); void RemoveFlowEdgeToCatchBlock(IR::Instr * instr); + void RemoveFlowEdgeToFinallyOnExceptionBlock(IR::Instr * instr); void CSEAddInstr(BasicBlock *block, IR::Instr *instr, Value *dstVal, Value *src1Val, Value *src2Val, Value *dstIndirIndexVal, Value *src1IndirIndexVal); void OptimizeChecks(IR::Instr * const instr); diff --git a/lib/Backend/GlobOptBailOut.cpp b/lib/Backend/GlobOptBailOut.cpp index 8b117c842c2..dd3f59d9571 100644 --- a/lib/Backend/GlobOptBailOut.cpp +++ b/lib/Backend/GlobOptBailOut.cpp @@ -984,7 +984,7 @@ GlobOpt::FillBailOutInfo(BasicBlock *block, BailOutInfo * bailOutInfo) bailOutInfo->startCallFunc[startCallNumber] = sym->m_instrDef->m_func; #ifdef _M_IX86 - if (this->currentRegion && this->currentRegion->GetType() == RegionTypeTry) + if (this->currentRegion && (this->currentRegion->GetType() == RegionTypeTry || this->currentRegion->GetType() == RegionTypeFinally)) { // For a bailout in argument evaluation from an EH region, the esp is offset by the TryCatch helper�s frame. So, the argouts are not actually pushed at the // offsets stored in the bailout record, which are relative to ebp. Need to restore the argouts from the actual value of esp before calling the Bailout helper. @@ -1361,6 +1361,12 @@ GlobOpt::GenerateBailAfterOperation(IR::Instr * *const pInstr, IR::BailOutKind k { nextInstr = nextInstr->GetNextRealInstrOrLabel(); } + // This can happen due to break block removal + while (nextInstr->GetByteCodeOffset() == Js::Constants::NoByteCodeOffset || + nextInstr->GetByteCodeOffset() < currentOffset) + { + nextInstr = nextInstr->GetNextRealInstrOrLabel(); + } IR::Instr * bailOutInstr = instr->ConvertToBailOutInstr(nextInstr, kind); if (this->currentBlock->GetLastInstr() == instr) { diff --git a/lib/Backend/IRBuilder.cpp b/lib/Backend/IRBuilder.cpp index acf42ff57fb..209597f62cd 100644 --- a/lib/Backend/IRBuilder.cpp +++ b/lib/Backend/IRBuilder.cpp @@ -390,10 +390,10 @@ IRBuilder::Build() this->branchRelocList = JitAnew(m_tempAlloc, SList, m_tempAlloc); Func * topFunc = this->m_func->GetTopFunc(); if (topFunc->HasTry() && - ((!topFunc->HasFinally() && !topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) || + ((!topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) || (topFunc->IsSimpleJit() && topFunc->GetJITFunctionBody()->DoJITLoopBody()))) // should be relaxed as more bailouts are added in Simple Jit { - this->catchOffsetStack = JitAnew(m_tempAlloc, SList, m_tempAlloc); + this->handlerOffsetStack = JitAnew(m_tempAlloc, SList, m_tempAlloc); } this->firstTemp = m_func->GetJITFunctionBody()->GetFirstTmpReg(); @@ -876,7 +876,7 @@ IRBuilder::Build() InsertLabels(); - Assert(!this->catchOffsetStack || this->catchOffsetStack->Empty()); + Assert(!this->handlerOffsetStack || this->handlerOffsetStack->Empty()); // Insert bailout for ignore exception for labels, after all labels were finalized. ignoreExBranchInstrToOffsetMap.Map([this](IR::Instr* instr, int byteCodeOffset) { @@ -1677,8 +1677,8 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0) case Js::OpCode::Throw: { srcOpnd = this->BuildSrcOpnd(srcRegOpnd); - - if (this->catchOffsetStack && !this->catchOffsetStack->Empty()) + if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) || + finallyBlockLevel > 0) { newOpcode = Js::OpCode::EHThrow; } @@ -1791,9 +1791,10 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0) return; case Js::OpCode::Catch: - if (this->catchOffsetStack) + if (this->handlerOffsetStack) { - this->catchOffsetStack->Pop(); + Assert(this->handlerOffsetStack->Top().Second() == true); + this->handlerOffsetStack->Pop(); } dstIsCatchObject = true; break; @@ -2076,7 +2077,7 @@ IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot ds ValueType arrayType(ldElemInfo->GetArrayType()); if(arrayType.IsLikelyNativeArray() && ( - (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) || + (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) || m_func->IsJitInDebugMode() )) { @@ -2090,7 +2091,7 @@ IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot ds } src1Opnd->SetValueType(arrayType); - if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) + if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) { isProfiled = false; } @@ -5369,7 +5370,7 @@ IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRe { if(arrayType.IsLikelyNativeArray() && ( - (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) || + (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) || m_func->IsJitInDebugMode() )) { @@ -5392,7 +5393,7 @@ IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRe } indirOpnd->GetBaseOpnd()->SetValueType(arrayType); - if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) + if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) { isProfiledLoad = false; isProfiledStore = false; @@ -6764,7 +6765,7 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset) IR::BranchInstr * branchInstr; IR::LabelInstr * labelInstr; - if (this->catchOffsetStack && !this->catchOffsetStack->Empty()) + if (this->handlerOffsetStack && !this->handlerOffsetStack->Empty() && this->handlerOffsetStack->Top().Second()) { // If the try region has a break block, we don't want the Flowgraph to move all of that code out of the loop // because an exception will bring the control back into the loop. The branch out of the loop (which is the @@ -6773,17 +6774,31 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset) // "BrOnException $catch" is inserted before Leave's in the try region to instrument flow from the try region // to the catch region (which is in the loop). IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, nullptr, this->m_func); - this->AddBranchInstr(brOnException, offset, this->catchOffsetStack->Top()); + this->AddBranchInstr(brOnException, offset, this->handlerOffsetStack->Top().First()); } labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); branchInstr = IR::BranchInstr::New(newOpcode, labelInstr, this->m_func); this->AddInstr(branchInstr, offset); this->AddInstr(labelInstr, Js::Constants::NoByteCodeOffset); - break; } + case Js::OpCode::LeaveNull: + finallyBlockLevel--; + this->AddInstr(instr, offset); + break; + + case Js::OpCode::Finally: + if (this->handlerOffsetStack) + { + Assert(this->handlerOffsetStack->Top().Second() == false); + this->handlerOffsetStack->Pop(); + } + finallyBlockLevel++; + this->AddInstr(IR::Instr::New(Js::OpCode::Finally, this->m_func), offset); + break; + case Js::OpCode::Break: if (m_func->IsJitInDebugMode()) { @@ -7058,9 +7073,13 @@ IRBuilder::BuildBr(Js::OpCode newOpcode, uint32 offset) } #endif - if ((newOpcode == Js::OpCode::TryCatch) && this->catchOffsetStack) + if ((newOpcode == Js::OpCode::TryCatch) && this->handlerOffsetStack) + { + this->handlerOffsetStack->Push(Pair(targetOffset, true)); + } + else if ((newOpcode == Js::OpCode::TryFinally) && this->handlerOffsetStack) { - this->catchOffsetStack->Push(targetOffset); + this->handlerOffsetStack->Push(Pair(targetOffset, false)); } branchInstr = IR::BranchInstr::New(newOpcode, nullptr, m_func); this->AddBranchInstr(branchInstr, offset, targetOffset); diff --git a/lib/Backend/IRBuilder.h b/lib/Backend/IRBuilder.h index 207ca47848b..93a85add4ff 100644 --- a/lib/Backend/IRBuilder.h +++ b/lib/Backend/IRBuilder.h @@ -64,8 +64,9 @@ class IRBuilder , m_ldSlots(nullptr) , m_loopCounterSym(nullptr) , callTreeHasSomeProfileInfo(false) + , finallyBlockLevel(0) , m_saveLoopImplicitCallFlags(nullptr) - , catchOffsetStack(nullptr) + , handlerOffsetStack(nullptr) , m_switchAdapter(this) , m_switchBuilder(&m_switchAdapter) , m_stackFuncPtrSym(nullptr) @@ -323,7 +324,8 @@ class IRBuilder Js::StatementReader m_statementReader; SList *m_argStack; SList *branchRelocList; - SList *catchOffsetStack; + typedef Pair handlerStackElementType; + SList *handlerOffsetStack; SymID * tempMap; BVFixed * fbvTempUsed; Js::RegSlot firstTemp; @@ -339,6 +341,7 @@ class IRBuilder StackSym* m_loopCounterSym; StackSym * m_stackFuncPtrSym; bool callTreeHasSomeProfileInfo; + uint finallyBlockLevel; // Keep track of how many args we have on the stack whenever // we make a call so that the max stack used over all calls can be diff --git a/lib/Backend/JnHelperMethod.cpp b/lib/Backend/JnHelperMethod.cpp index e05665d6361..9131585452a 100644 --- a/lib/Backend/JnHelperMethod.cpp +++ b/lib/Backend/JnHelperMethod.cpp @@ -295,6 +295,10 @@ DECLSPEC_GUARDIGNORE _NOINLINE intptr_t GetNonTableMethodAddress(ThreadContextI case HelperOp_TryFinally: return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinally); + + case HelperOp_TryFinallySimpleJit: + return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinallySimpleJit); + // // Methods that we don't want to get marked as CFG targets as they dump all registers to a controlled address // diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index 186a49f6b80..2f7f7807a1f 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -353,6 +353,7 @@ HELPERCALL(AllocUninitializedSimdI4, Js::JavascriptSIMDInt32x4::AllocUninitializ HELPERCALL(Op_TryCatch, nullptr, 0) HELPERCALL(Op_TryFinally, nullptr, AttrCanThrow) +HELPERCALL(Op_TryFinallySimpleJit, nullptr, AttrCanThrow) #if _M_X64 HELPERCALL(Op_ReturnFromCallWithFakeFrame, amd64_ReturnFromCallWithFakeFrame, 0) #endif diff --git a/lib/Backend/LinearScan.cpp b/lib/Backend/LinearScan.cpp index a4c43958739..01826991241 100644 --- a/lib/Backend/LinearScan.cpp +++ b/lib/Backend/LinearScan.cpp @@ -187,7 +187,7 @@ LinearScan::RegAlloc() } else if (instr->IsBranchInstr()) { - if (this->func->HasTry() && this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && this->func->DoOptimizeTry()) { this->ProcessEHRegionBoundary(instr); } @@ -206,8 +206,7 @@ LinearScan::RegAlloc() if (this->currentRegion) { RegionType curRegType = this->currentRegion->GetType(); - Assert(curRegType != RegionTypeFinally); //Finally regions are not optimized yet - if (curRegType == RegionTypeTry || curRegType == RegionTypeCatch) + if (curRegType == RegionTypeTry || curRegType == RegionTypeCatch || curRegType == RegionTypeFinally) { this->func->hasBailoutInEHRegion = true; } @@ -1005,7 +1004,7 @@ LinearScan::NeedsWriteThrough(StackSym * sym) bool LinearScan::NeedsWriteThroughForEH(StackSym * sym) { - if (!this->func->HasTry() || !this->func->DoOptimizeTryCatch() || !sym->HasByteCodeRegSlot()) + if (!this->func->HasTry() || !this->func->DoOptimizeTry() || !sym->HasByteCodeRegSlot()) { return false; } @@ -1351,7 +1350,7 @@ LinearScan::FillBailOutRecord(IR::Instr * instr) if (this->func->HasTry()) { RegionType currentRegionType = this->currentRegion->GetType(); - if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch) + if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch || currentRegionType == RegionTypeFinally) { bailOutInfo->bailOutRecord->ehBailoutData = this->currentRegion->ehBailoutData; } @@ -2239,7 +2238,7 @@ LinearScan::RecordUse(Lifetime * lifetime, IR::Instr * instr, IR::RegOpnd * regO // have real accurate flow info for the later. if ((regOpnd && regOpnd->m_sym->IsConst()) || ( - (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) && + (this->func->HasTry() && !this->func->DoOptimizeTry()) && this->IsInLoop() && lifetime->lastUseLabel != this->lastLabel && this->liveOnBackEdgeSyms->Test(lifetime->sym->m_id) && @@ -2279,7 +2278,7 @@ void LinearScan::RecordLoopUse(Lifetime *lifetime, RegNum reg) return; } - if (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && !this->func->DoOptimizeTry()) { return; } @@ -3122,8 +3121,8 @@ void LinearScan::ProcessEHRegionBoundary(IR::Instr * instr) { Assert(instr->IsBranchInstr()); - Assert(instr->m_opcode != Js::OpCode::TryFinally); // finallys are not supported for optimization yet. - if (instr->m_opcode != Js::OpCode::TryCatch && instr->m_opcode != Js::OpCode::Leave) + + if (instr->m_opcode != Js::OpCode::TryCatch && instr->m_opcode != Js::OpCode::TryFinally && instr->m_opcode != Js::OpCode::Leave) { return; } @@ -3275,7 +3274,7 @@ LinearScan::InsertStores(Lifetime *lifetime, RegNum reg, IR::Instr *insertionIns uint localStoreCost = LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr)); // Is it cheaper to spill all the defs we've seen so far or just insert a store at the current point? - if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || localStoreCost >= lifetime->allDefsCost) + if ((this->func->HasTry() && !this->func->DoOptimizeTry()) || localStoreCost >= lifetime->allDefsCost) { // Insert a store for each def point we've seen so far FOREACH_SLIST_ENTRY(IR::Instr *, instr, &(lifetime->defList)) @@ -3856,7 +3855,7 @@ LinearScan::GetUseSpillCost(uint loopNest, BOOL isInHelperBlock) void LinearScan::ProcessSecondChanceBoundary(IR::BranchInstr *branchInstr) { - if (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && !this->func->DoOptimizeTry()) { return; } @@ -3946,7 +3945,7 @@ LinearScan::ProcessSecondChanceBoundaryHelper(IR::BranchInstr *branchInstr, IR:: void LinearScan::ProcessSecondChanceBoundary(IR::LabelInstr *labelInstr) { - if (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && !this->func->DoOptimizeTry()) { return; } @@ -4718,7 +4717,7 @@ IR::Instr * LinearScan::TryHoistLoad(IR::Instr *instr, Lifetime *lifetime) return insertInstr; } - if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || (this->currentRegion && this->currentRegion->GetType() != RegionTypeRoot)) + if ((this->func->HasTry() && !this->func->DoOptimizeTry()) || (this->currentRegion && this->currentRegion->GetType() != RegionTypeRoot)) { return insertInstr; } diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index bce3157c3ae..c6f6c5c0783 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -2561,12 +2561,23 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa instrPrev = m_lowererMD.LowerCatch(instr); break; + case Js::OpCode::Finally: + instr->Remove(); + break; + case Js::OpCode::LeaveNull: - instrPrev = m_lowererMD.LowerLeaveNull(instr); + if (this->m_func->IsSimpleJit() || !this->m_func->DoOptimizeTry()) + { + instrPrev = m_lowererMD.LowerLeaveNull(instr); + } + else + { + instr->Remove(); + } break; case Js::OpCode::Leave: - if (this->m_func->HasTry() && this->m_func->DoOptimizeTryCatch()) + if (this->m_func->HasTry() && this->m_func->DoOptimizeTry()) { // Required in Register Allocator to mark region boundaries break; @@ -2578,6 +2589,10 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa instrPrev = this->LowerBailOnException(instr); break; + case Js::OpCode::BailOnEarlyExit: + instrPrev = this->LowerBailOnEarlyExit(instr); + break; + case Js::OpCode::RuntimeTypeError: case Js::OpCode::InlineRuntimeTypeError: this->LowerUnaryHelperMem(instr, IR::HelperOp_RuntimeTypeError); @@ -12078,7 +12093,17 @@ Lowerer::LowerBailOnException(IR::Instr * instr) { Assert(instr->HasBailOutInfo()); IR::Instr * instrPrev = instr->m_prev; - Assert(instrPrev->m_opcode == Js::OpCode::Catch); + + this->GenerateBailOut(instr, nullptr, nullptr); + + return instrPrev; +} + +IR::Instr* +Lowerer::LowerBailOnEarlyExit(IR::Instr * instr) +{ + Assert(instr->HasBailOutInfo()); + IR::Instr * instrPrev = instr->m_prev; this->GenerateBailOut(instr, nullptr, nullptr); @@ -19972,8 +19997,21 @@ Lowerer::EHBailoutPatchUp() if (this->currentRegion) { RegionType currentRegionType = this->currentRegion->GetType(); - if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch) + if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch || currentRegionType == RegionTypeFinally) { + if (this->currentRegion->IsNonExceptingFinally()) + { + Region * parent = this->currentRegion->GetParent(); + + while (parent->IsNonExceptingFinally()) + { + parent = parent->GetParent(); + } + if (parent->GetType() == RegionTypeRoot) + { + continue; + } + } this->InsertReturnThunkForRegion(this->currentRegion, restoreReturnValueFromBailoutLabel); if (instr->HasBailOutInfo()) { @@ -23578,7 +23616,7 @@ Lowerer::ValidOpcodeAfterLower(IR::Instr* instr, Func * func) case Js::OpCode::Leave: Assert(!func->IsLoopBodyInTry()); - Assert(func->HasTry() && func->DoOptimizeTryCatch()); + Assert(func->HasTry() && func->DoOptimizeTry()); return func && !func->isPostFinalLower; //Lowered in FinalLower phase }; @@ -24580,7 +24618,8 @@ Lowerer::LowerTry(IR::Instr* instr, bool tryCatch) instr->InsertBefore(setInstr); LowererMD::Legalize(setInstr); - return m_lowererMD.LowerTry(instr, tryCatch ? IR::HelperOp_TryCatch : IR::HelperOp_TryFinally); + return m_lowererMD.LowerTry(instr, tryCatch ? IR::HelperOp_TryCatch : ((this->m_func->IsSimpleJit() && !this->m_func->hasBailout) || !this->m_func->DoOptimizeTry()) ? + IR::HelperOp_TryFinallySimpleJit : IR::HelperOp_TryFinally); } void @@ -24607,7 +24646,7 @@ void Lowerer::InsertReturnThunkForRegion(Region* region, IR::LabelInstr* restoreLabel) { Assert(this->m_func->isPostLayout); - Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch); + Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch || region->GetType() == RegionTypeFinally); if (!region->returnThunkEmitted) { @@ -24625,7 +24664,37 @@ Lowerer::InsertReturnThunkForRegion(Region* region, IR::LabelInstr* restoreLabel } IR::LabelOpnd * continuationAddr; - if (region->GetParent()->GetType() != RegionTypeRoot) + // We insert return thunk to the region's parent return thunk label + // For non exception finallys, we do not need a return thunk + // Because, we are not calling none xception finallys from within amd64_callWithFakeFrame + // But a non exception finally maybe within other eh regions that need a return thunk + if (region->IsNonExceptingFinally()) + { + Assert(region->GetParent()->GetType() != RegionTypeRoot); + Region *ancestor = region->GetFirstAncestorOfNonExceptingFinallyParent(); + Assert(ancestor && !ancestor->IsNonExceptingFinally()); + if (ancestor->GetType() != RegionTypeRoot) + { + continuationAddr = IR::LabelOpnd::New(ancestor->GetBailoutReturnThunkLabel(), this->m_func); + } + else + { + continuationAddr = IR::LabelOpnd::New(restoreLabel, this->m_func); + } + } + else if (region->GetParent()->IsNonExceptingFinally()) + { + Region *ancestor = region->GetFirstAncestorOfNonExceptingFinally(); + if (ancestor && ancestor->GetType() != RegionTypeRoot) + { + continuationAddr = IR::LabelOpnd::New(ancestor->GetBailoutReturnThunkLabel(), this->m_func); + } + else + { + continuationAddr = IR::LabelOpnd::New(restoreLabel, this->m_func); + } + } + else if (region->GetParent()->GetType() != RegionTypeRoot) { continuationAddr = IR::LabelOpnd::New(region->GetParent()->GetBailoutReturnThunkLabel(), this->m_func); } diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 3d734d2bee3..acaee8198a1 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -474,6 +474,7 @@ class Lowerer IR::Instr * LowerBailForDebugger(IR::Instr* instr, bool isInsideHelper = false); IR::Instr * LowerBailOnException(IR::Instr* instr); void LowerReinterpretPrimitive(IR::Instr* instr); + IR::Instr * LowerBailOnEarlyExit(IR::Instr* instr); void LowerOneBailOutKind(IR::Instr *const instr, const IR::BailOutKind bailOutKindToLower, const bool isInHelperBlock, const bool preserveBailOutKindInInstr = false); diff --git a/lib/Backend/LowerMDShared.cpp b/lib/Backend/LowerMDShared.cpp index e8aa3306ffb..def57d498cb 100644 --- a/lib/Backend/LowerMDShared.cpp +++ b/lib/Backend/LowerMDShared.cpp @@ -370,13 +370,12 @@ LowererMD::LowerTry(IR::Instr *tryInstr, IR::JnHelperMethod helperMethod) // Arg 5: ScriptContext this->m_lowerer->LoadScriptContext(tryAddr); - if (tryInstr->m_opcode == Js::OpCode::TryCatch) + if (tryInstr->m_opcode == Js::OpCode::TryCatch || this->m_func->DoOptimizeTry()) { // Arg 4 : hasBailedOutOffset IR::Opnd * hasBailedOutOffset = IR::IntConstOpnd::New(this->m_func->m_hasBailedOutSym->m_offset, TyInt32, this->m_func); this->LoadHelperArgument(tryAddr, hasBailedOutOffset); } - #ifdef _M_X64 // Arg: args size IR::RegOpnd *argsSizeOpnd = IR::RegOpnd::New(TyMachReg, m_func); diff --git a/lib/Backend/Region.cpp b/lib/Backend/Region.cpp index 67a5c7808d9..ce1ebeb1323 100644 --- a/lib/Backend/Region.cpp +++ b/lib/Backend/Region.cpp @@ -24,11 +24,11 @@ Region::AllocateEHBailoutData(Func * func, IR::Instr * tryInstr) { if (this->GetType() == RegionTypeRoot) { - this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, -1 /*nestingDepth*/, 0 /*catchOffset*/, nullptr /*parent*/); + this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, -1 /*nestingDepth*/, 0 /*catchOffset*/, 0 /*finallyOffset*/, Js::HandlerType::HT_None, nullptr /*parent*/); } else { - this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, this->GetParent()->ehBailoutData->nestingDepth + 1, 0, this->GetParent()->ehBailoutData); + this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, this->GetParent()->ehBailoutData->nestingDepth + 1, 0, 0, Js::HandlerType::HT_None, this->GetParent()->ehBailoutData); if (this->GetType() == RegionTypeTry) { Assert(tryInstr); @@ -36,6 +36,18 @@ Region::AllocateEHBailoutData(Func * func, IR::Instr * tryInstr) { this->ehBailoutData->catchOffset = tryInstr->AsBranchInstr()->GetTarget()->GetByteCodeOffset(); // ByteCode offset of the Catch } + else if (tryInstr->m_opcode == Js::OpCode::TryFinally) + { + this->ehBailoutData->finallyOffset = tryInstr->AsBranchInstr()->GetTarget()->GetByteCodeOffset(); // ByteCode offset of the Finally + } + } + else if (this->GetType() == RegionTypeCatch) + { + this->ehBailoutData->ht = Js::HandlerType::HT_Catch; + } + else + { + this->ehBailoutData->ht = Js::HandlerType::HT_Finally; } } } @@ -54,3 +66,36 @@ Region::GetSelfOrFirstTryAncestor() } return this->selfOrFirstTryAncestor; } + +// Return the first ancestor of the region's parent which is not a non exception finally +Region * +Region::GetFirstAncestorOfNonExceptingFinallyParent() +{ + Region * ancestor = this->GetParent(); + while (ancestor && ancestor->IsNonExceptingFinally()) + { + ancestor = ancestor->GetParent(); + } + // ancestor is the first ancestor which is not a non exception finally + Assert(ancestor && !ancestor->IsNonExceptingFinally()); + // If the ancestor's parent is a non exception finally, recurse + if (ancestor && ancestor->GetType() != RegionTypeRoot && ancestor->GetParent()->IsNonExceptingFinally()) + { + return ancestor->GetParent()->GetFirstAncestorOfNonExceptingFinallyParent(); + } + + return ancestor ? (ancestor->GetType() == RegionTypeRoot ? ancestor : ancestor->GetParent()) : nullptr; +} + +// Return first ancestor which is not a non exception finally +Region * +Region::GetFirstAncestorOfNonExceptingFinally() +{ + Region *ancestor = this->GetParent(); + while (ancestor->IsNonExceptingFinally()) + { + ancestor = ancestor->GetParent(); + } + return ancestor; +} + diff --git a/lib/Backend/Region.h b/lib/Backend/Region.h index 9734af19884..c5353242f6a 100644 --- a/lib/Backend/Region.h +++ b/lib/Backend/Region.h @@ -17,7 +17,7 @@ class Region { public: Region() : type(RegionTypeInvalid), - parent(NULL), matchingTryRegion(nullptr), matchingCatchRegion(nullptr), matchingFinallyRegion(nullptr), selfOrFirstTryAncestor(nullptr), + parent(NULL), matchingTryRegion(nullptr), matchingCatchRegion(nullptr), matchingFinallyOnExceptRegion(nullptr), matchingFinallyOnNoExceptRegion(nullptr), selfOrFirstTryAncestor(nullptr), start(NULL), end(NULL), writeThroughSymbolsSet(nullptr), ehBailoutData(nullptr), bailoutReturnThunkLabel(nullptr), returnThunkEmitted(false), @@ -37,8 +37,25 @@ class Region inline Region * GetMatchingCatchRegion() const { return this->matchingCatchRegion; } inline void SetMatchingCatchRegion(Region* catchRegion) { this->matchingCatchRegion = catchRegion; } - inline Region * GetMatchingFinallyRegion() const { return this->matchingFinallyRegion; } - inline void SetMatchingFinallyRegion(Region* finallyRegion) { this->matchingFinallyRegion = finallyRegion; } + inline Region * GetMatchingFinallyRegion(bool isExcept) const + { + return isExcept ? this->matchingFinallyOnExceptRegion : this->matchingFinallyOnNoExceptRegion; + } + inline void SetMatchingFinallyRegion(Region* finallyRegion, bool isExcept) + { + if (isExcept) + { + this->matchingFinallyOnExceptRegion = finallyRegion; + } + else + { + this->matchingFinallyOnNoExceptRegion = finallyRegion; + } + } + bool IsNonExceptingFinally() + { + return (this->GetType() == RegionTypeFinally && this->GetMatchingTryRegion()->GetMatchingFinallyRegion(false) == this); + } inline IR::Instr * GetStart() const { return this->start; } inline void SetStart(IR::Instr * instr) { this->start = instr; } @@ -49,13 +66,19 @@ class Region inline void SetExceptionObjectSym(StackSym * sym) { this->exceptionObjectSym = sym; } void AllocateEHBailoutData(Func * func, IR::Instr * tryInstr); Region * GetSelfOrFirstTryAncestor(); + Region * GetFirstAncestorOfNonExceptingFinallyParent(); + Region * GetFirstAncestorOfNonExceptingFinally(); private: RegionType type; Region * parent; Region * matchingTryRegion; Region * matchingCatchRegion; - Region * matchingFinallyRegion; + Region * matchingFinallyOnExceptRegion; + Region * matchingFinallyOnNoExceptRegion; + // We need to mark a non-expecting finally region we execute in the JIT, as in EH region. + // We can bailout from the non excepting EH region, in that case we need ehBailoutData to reconstruct eh frames in the interpreter + Region * selfOrFirstTryAncestor; // = self, if try region, otherwise // = first try ancestor IR::Instr * start; diff --git a/lib/Backend/SccLiveness.cpp b/lib/Backend/SccLiveness.cpp index 24838d03ff7..1e319de9770 100644 --- a/lib/Backend/SccLiveness.cpp +++ b/lib/Backend/SccLiveness.cpp @@ -309,7 +309,7 @@ SCCLiveness::Build() // Check for lifetimes that have been extended such that they now span multiple regions. this->curRegion->SetEnd(this->func->m_exitInstr); - if (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) + if (this->func->HasTry() && !this->func->DoOptimizeTry()) { FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList) { @@ -527,7 +527,7 @@ SCCLiveness::ProcessStackSymUse(StackSym * stackSym, IR::Instr * instr, int usag } else { - if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch()) + if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry()) { lifetime->dontAllocate = true; } @@ -628,7 +628,7 @@ SCCLiveness::ProcessRegDef(IR::RegOpnd *regDef, IR::Instr *instr) ExtendLifetime(lifetime, instr); - if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch()) + if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry()) { lifetime->dontAllocate = true; } diff --git a/lib/Backend/amd64/LowererMDArch.cpp b/lib/Backend/amd64/LowererMDArch.cpp index 165a0302a1e..22264264e4f 100644 --- a/lib/Backend/amd64/LowererMDArch.cpp +++ b/lib/Backend/amd64/LowererMDArch.cpp @@ -3148,7 +3148,7 @@ LowererMDArch::FinalLower() break; case Js::OpCode::Leave: - Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry()); + Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry()); instrPrev = this->lowererMD->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/); break; diff --git a/lib/Backend/arm/LowerMD.cpp b/lib/Backend/arm/LowerMD.cpp index 7d20e107064..1f8e90cad40 100644 --- a/lib/Backend/arm/LowerMD.cpp +++ b/lib/Backend/arm/LowerMD.cpp @@ -1524,7 +1524,7 @@ LowererMD::LowerExitInstr(IR::ExitInstr * exitInstr) int32 stackAdjust; if (hasTry) { - if (this->m_func->DoOptimizeTryCatch()) + if (this->m_func->DoOptimizeTry()) { this->EnsureEpilogLabel(); } @@ -1725,7 +1725,7 @@ LowererMD::LowerTry(IR::Instr * tryInstr, IR::JnHelperMethod helperMethod) // Arg 7: ScriptContext this->m_lowerer->LoadScriptContext(tryAddr); - if (tryInstr->m_opcode == Js::OpCode::TryCatch) + if (tryInstr->m_opcode == Js::OpCode::TryCatch || this->m_func->DoOptimizeTry()) { // Arg 6 : hasBailedOutOffset IR::Opnd * hasBailedOutOffset = IR::IntConstOpnd::New(this->m_func->m_hasBailedOutSym->m_offset, TyInt32, this->m_func); @@ -8688,7 +8688,7 @@ LowererMD::FinalLower() switch (instr->m_opcode) { case Js::OpCode::Leave: - Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry()); + Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry()); instrPrev = this->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/); break; } diff --git a/lib/Backend/i386/LowererMDArch.cpp b/lib/Backend/i386/LowererMDArch.cpp index c9bb8165034..948947c861f 100644 --- a/lib/Backend/i386/LowererMDArch.cpp +++ b/lib/Backend/i386/LowererMDArch.cpp @@ -3982,7 +3982,7 @@ LowererMDArch::FinalLower() switch (instr->m_opcode) { case Js::OpCode::Leave: - Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry()); + Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry()); this->lowererMD->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/); break; diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index e32cca9278b..072a5975942 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -88,6 +88,7 @@ PHASE(All) PHASE(InlinerConstFold) PHASE(ExecBOIFastPath) PHASE(FGBuild) + PHASE(OptimizeTryFinally) PHASE(RemoveBreakBlock) PHASE(TailDup) PHASE(FGPeeps) diff --git a/lib/Runtime/Base/ThreadContext.h b/lib/Runtime/Base/ThreadContext.h index b7a30818e15..d158311281e 100644 --- a/lib/Runtime/Base/ThreadContext.h +++ b/lib/Runtime/Base/ThreadContext.h @@ -448,6 +448,7 @@ class ThreadContext sealed : #endif private: + Js::JavascriptExceptionObject * pendingFinallyException; bool noScriptScope; Js::DebugManager * debugManager; @@ -1261,6 +1262,16 @@ class ThreadContext sealed : void SetNoScriptScope(bool noScriptScope) { this->noScriptScope = noScriptScope; } bool IsNoScriptScope() { return this->noScriptScope; } + void SetPendingFinallyException(Js::JavascriptExceptionObject * exceptionObj) + { + pendingFinallyException = exceptionObj; + } + + Js::JavascriptExceptionObject * GetPendingFinallyException() + { + return pendingFinallyException; + } + Js::EntryPointInfo ** RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint); void UnregisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo ** entryPoint); void RegisterProtoInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId); diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 59bf728dc79..24bd409f6a1 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -6741,6 +6741,7 @@ void EmitTopLevelFinally(Js::ByteCodeLabel finallyLabel, byteCodeGenerator->Writer()->Br(afterFinallyBlockLabel); byteCodeGenerator->Writer()->MarkLabel(finallyLabel); + byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally); ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, yieldExceptionLocation, yieldOffsetLocation); if (isCoroutine) @@ -11873,6 +11874,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func byteCodeGenerator->Writer()->Br(pnode->sxStmt.breakLabel); byteCodeGenerator->Writer()->MarkLabel(finallyLabel); + byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally); ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, regException, regOffset); if (funcInfo->byteCodeFunction->IsCoroutine()) diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index 524a3b22be8..58d558c8c50 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -556,9 +556,10 @@ MACRO( TryCatch, Br, OpSideEffect) MACRO( TryFinally, Br, OpSideEffect|OpPostOpDbgBailOut) MACRO_EXTEND_WMS( TryFinallyWithYield, BrReg2, OpSideEffect|OpPostOpDbgBailOut) MACRO_WMS( Catch, Reg1, OpSideEffect) +MACRO_EXTEND( Finally, Empty, OpSideEffect) MACRO_EXTEND( ResumeCatch, Empty, OpSideEffect) MACRO_EXTEND_WMS( ResumeFinally, BrReg2, OpSideEffect) -MACRO( LeaveNull, Empty, OpSideEffect|OpNoFallThrough) +MACRO( LeaveNull, Empty, OpSideEffect) MACRO( Leave, Empty, OpSideEffect|OpNoFallThrough) MACRO_BACKEND_ONLY( InlineRuntimeTypeError, W1, OpSideEffect|OpPostOpDbgBailOut) // Throws TypeError at runtime. @@ -641,6 +642,7 @@ MACRO_BACKEND_ONLY( BailOnNotArray, Empty, OpBailOutRe MACRO_BACKEND_ONLY( BailForDebugger, Empty, OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpSideEffect) // Bail out so that we can continue the function under debugger. Disable optimizations for this instr so that it's not moved. MACRO_BACKEND_ONLY( BailOnNotBuiltIn, Empty, OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpCanCSE) MACRO_BACKEND_ONLY( BailOnException, Empty, OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpDeadFallThrough) +MACRO_BACKEND_ONLY( BailOnEarlyExit, Empty, OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpDeadFallThrough) MACRO_BACKEND_ONLY( BailOnTaggedValue, Empty, OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpCanCSE) MACRO_BACKEND_ONLY( BytecodeArgOutCapture, Empty, OpTempNumberTransfer|OpTempObjectTransfer|OpNonIntTransfer) // Represents snapshotting of bytecode ArgOut_A in backend for purpose of bailout MACRO_BACKEND_ONLY( BytecodeArgOutUse, Empty, OpTempNumberSources | OpTempObjectSources) // Represents bytecode ArgOut_A use in the backend to keep args alive for the globopt diff --git a/lib/Runtime/Language/EHBailoutData.h b/lib/Runtime/Language/EHBailoutData.h index 3b3c16e3407..c1943d725a1 100644 --- a/lib/Runtime/Language/EHBailoutData.h +++ b/lib/Runtime/Language/EHBailoutData.h @@ -6,20 +6,30 @@ namespace Js { + enum HandlerType + { + HT_None, + HT_Catch, + HT_Finally + }; class EHBailoutData { public: int32 nestingDepth; int32 catchOffset; + int32 finallyOffset; + HandlerType ht; EHBailoutData * parent; EHBailoutData * child; public: - EHBailoutData() : nestingDepth(-1), catchOffset(0), parent(nullptr), child(nullptr) {} - EHBailoutData(int32 nestingDepth, int32 catchOffset, EHBailoutData * parent) + EHBailoutData() : nestingDepth(-1), catchOffset(0), finallyOffset(0), parent(nullptr), child(nullptr), ht(HT_None) {} + EHBailoutData(int32 nestingDepth, int32 catchOffset, int32 finallyOffset, HandlerType ht, EHBailoutData * parent) { this->nestingDepth = nestingDepth; this->catchOffset = catchOffset; + this->finallyOffset = finallyOffset; + this->ht = ht; this->parent = parent; this->child = nullptr; } diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 727ada672ce..a249ebeb678 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -3374,7 +3374,7 @@ namespace Js topLevelEHBailoutData->parent->child = topLevelEHBailoutData; topLevelEHBailoutData = topLevelEHBailoutData->parent; } - ProcessTryCatchBailout(topLevelEHBailoutData, this->ehBailoutData->nestingDepth); + ProcessTryHandlerBailout(topLevelEHBailoutData, this->ehBailoutData->nestingDepth); m_flags &= ~Js::InterpreterStackFrameFlags_ProcessingBailOutFromEHCode; this->ehBailoutData = nullptr; } @@ -6640,8 +6640,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) int InterpreterStackFrame::ProcessFinally() { this->nestedFinallyDepth++; - // mark the stackFrame as 'in finally block' - this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock; int newOffset = 0; if (this->IsInDebugMode()) @@ -6653,20 +6651,16 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) newOffset = ::Math::PointerCastToIntegral(this->Process()); } - if (--this->nestedFinallyDepth == -1) - { - // unmark the stackFrame as 'in finally block' - this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock; - } return newOffset; } - void InterpreterStackFrame::ProcessTryCatchBailout(EHBailoutData * ehBailoutData, uint32 tryNestingDepth) + void InterpreterStackFrame::ProcessTryHandlerBailout(EHBailoutData * ehBailoutData, uint32 tryNestingDepth) { int catchOffset = ehBailoutData->catchOffset; + int finallyOffset = ehBailoutData->finallyOffset; Js::JavascriptExceptionObject* exception = NULL; - if (catchOffset != 0) + if (catchOffset != 0 || finallyOffset != 0) { try { @@ -6676,7 +6670,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) if (tryNestingDepth != 0) { - this->ProcessTryCatchBailout(ehBailoutData->child, --tryNestingDepth); + this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth); } Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext); @@ -6711,7 +6705,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) exception = err.GetAndClear(); } } - else + else if (ehBailoutData->ht == HandlerType::HT_Catch) { this->nestedCatchDepth++; // mark the stackFrame as 'in catch block' @@ -6719,7 +6713,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) if (tryNestingDepth != 0) { - this->ProcessTryCatchBailout(ehBailoutData->child, --tryNestingDepth); + this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth); } this->ProcessCatch(); @@ -6730,6 +6724,40 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) } return; } + else + { + Assert(ehBailoutData->ht == HandlerType::HT_Finally); + this->nestedFinallyDepth++; + // mark the stackFrame as 'in finally block' + this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock; + + if (tryNestingDepth != 0) + { + this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth); + } + + int finallyEndOffset = this->ProcessFinally(); + + if (--this->nestedFinallyDepth == -1) + { + // unmark the stackFrame as 'in finally block' + this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock; + } + + volatile Js::JavascriptExceptionObject * exceptionObj = this->scriptContext->GetThreadContext()->GetPendingFinallyException(); + this->scriptContext->GetThreadContext()->SetPendingFinallyException(nullptr); + // Finally exited with LeaveNull, We don't throw for early returns + if (finallyEndOffset == 0 && exceptionObj) + { + JavascriptExceptionOperators::DoThrow(const_cast(exceptionObj), scriptContext); + } + if (finallyEndOffset != 0) + { + m_reader.SetCurrentOffset(finallyEndOffset); + } + + return; + } if (--this->nestedTryDepth == -1) { @@ -6746,41 +6774,124 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) JavascriptExceptionOperators::DoThrow(exception, scriptContext); } - exception = exception->CloneIfStaticExceptionObject(scriptContext); - // We've got a JS exception. Grab the exception object and assign it to the - // catch object's location, then call the handler (i.e., we consume the Catch op here). - Var catchObject = exception->GetThrownObject(scriptContext); + if (catchOffset != 0) + { + exception = exception->CloneIfStaticExceptionObject(scriptContext); + // We've got a JS exception. Grab the exception object and assign it to the + // catch object's location, then call the handler (i.e., we consume the Catch op here). + Var catchObject = exception->GetThrownObject(scriptContext); - m_reader.SetCurrentOffset(catchOffset); + m_reader.SetCurrentOffset(catchOffset); - LayoutSize layoutSize; - OpCode catchOp = m_reader.ReadOp(layoutSize); + LayoutSize layoutSize; + OpCode catchOp = m_reader.ReadOp(layoutSize); #ifdef BYTECODE_BRANCH_ISLAND - if (catchOp == Js::OpCode::BrLong) - { - Assert(layoutSize == SmallLayout); - auto playoutBrLong = m_reader.BrLong(); - m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset); - catchOp = m_reader.ReadOp(layoutSize); - } + if (catchOp == Js::OpCode::BrLong) + { + Assert(layoutSize == SmallLayout); + auto playoutBrLong = m_reader.BrLong(); + m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset); + catchOp = m_reader.ReadOp(layoutSize); + } #endif - AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset"); - RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 : - layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0; - SetReg(reg, catchObject); + AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset"); + RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 : + layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0; + SetReg(reg, catchObject); - ResetOut(); + ResetOut(); - this->nestedCatchDepth++; - // mark the stackFrame as 'in catch block' - this->m_flags |= InterpreterStackFrameFlags_WithinCatchBlock; + this->nestedCatchDepth++; + // mark the stackFrame as 'in catch block' + this->m_flags |= InterpreterStackFrameFlags_WithinCatchBlock; - this->ProcessCatch(); + this->ProcessCatch(); - if (--this->nestedCatchDepth == -1) + if (--this->nestedCatchDepth == -1) + { + // unmark the stackFrame as 'in catch block' + this->m_flags &= ~InterpreterStackFrameFlags_WithinCatchBlock; + } + } + else { - // unmark the stackFrame as 'in catch block' - this->m_flags &= ~InterpreterStackFrameFlags_WithinCatchBlock; + Assert(finallyOffset != 0); + exception = exception->CloneIfStaticExceptionObject(scriptContext); + + m_reader.SetCurrentOffset(finallyOffset); + + ResetOut(); + + this->nestedFinallyDepth++; + // mark the stackFrame as 'in finally block' + this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock; + + LayoutSize layoutSize; + OpCode finallyOp = m_reader.ReadOp(layoutSize); +#ifdef BYTECODE_BRANCH_ISLAND + if (finallyOp == Js::OpCode::BrLong) + { + Assert(layoutSize == SmallLayout); + auto playoutBrLong = m_reader.BrLong(); + m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset); + finallyOp = m_reader.ReadOp(layoutSize); + } +#endif + Assert(finallyOp == Js::OpCode::Finally); + + int finallyEndOffset = this->ProcessFinally(); + + if (--this->nestedFinallyDepth == -1) + { + // unmark the stackFrame as 'in finally block' + this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock; + } + if (finallyEndOffset == 0) + { + JavascriptExceptionOperators::DoThrow(exception, scriptContext); + } + m_reader.SetCurrentOffset(finallyEndOffset); + } + } + else + { + if (finallyOffset != 0) + { + int currOffset = m_reader.GetCurrentOffset(); + + m_reader.SetCurrentOffset(finallyOffset); + + ResetOut(); + + this->nestedFinallyDepth++; + + // mark the stackFrame as 'in finally block' + this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock; + + LayoutSize layoutSize; + OpCode finallyOp = m_reader.ReadOp(layoutSize); +#ifdef BYTECODE_BRANCH_ISLAND + if (finallyOp == Js::OpCode::BrLong) + { + Assert(layoutSize == SmallLayout); + auto playoutBrLong = m_reader.BrLong(); + m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset); + finallyOp = m_reader.ReadOp(layoutSize); + } +#endif + Assert(finallyOp == Js::OpCode::Finally); + + int finallyEndOffset = this->ProcessFinally(); + + if (--this->nestedFinallyDepth == -1) + { + // unmark the stackFrame as 'in finally block' + this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock; + } + if (finallyEndOffset == 0) + { + m_reader.SetCurrentOffset(currOffset); + } } } } @@ -6940,9 +7051,30 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) m_reader.SetCurrentRelativeOffset(ip, jumpOffset); RestoreSp(); + // mark the stackFrame as 'in finally block' + this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock; + + LayoutSize layoutSize; + OpCode finallyOp = m_reader.ReadOp(layoutSize); +#ifdef BYTECODE_BRANCH_ISLAND + if (finallyOp == Js::OpCode::BrLong) + { + Assert(layoutSize == SmallLayout); + auto playoutBrLong = m_reader.BrLong(); + m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset); + finallyOp = m_reader.ReadOp(layoutSize); + } +#endif + AssertMsg(finallyOp == OpCode::Finally, "Finally op not found at catch offset"); newOffset = this->ProcessFinally(); + if (--this->nestedFinallyDepth == -1) + { + // unmark the stackFrame as 'in finally block' + this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock; + } + bool endOfFinallyBlock = newOffset == 0; if (endOfFinallyBlock) { @@ -6954,7 +7086,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) // Finally seized the flow with a jump out of its scope. Resume at the jump target and // force the runtime to return to this frame without executing the catch. m_reader.SetCurrentOffset(newOffset); - return; } @@ -7002,7 +7133,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) // Finally seized the flow with a jump out of its scope. Resume at the jump target and // force the runtime to return to this frame without executing the catch. m_reader.SetCurrentOffset(newOffset); - return; } diff --git a/lib/Runtime/Language/InterpreterStackFrame.h b/lib/Runtime/Language/InterpreterStackFrame.h index 1cb804d65d2..ac2df3fc5d4 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.h +++ b/lib/Runtime/Language/InterpreterStackFrame.h @@ -706,7 +706,7 @@ namespace Js void OP_TryCatch(const unaligned OpLayoutBr* playout); void ProcessCatch(); int ProcessFinally(); - void ProcessTryCatchBailout(EHBailoutData * innermostEHBailoutData, uint32 tryNestingDepth); + void ProcessTryHandlerBailout(EHBailoutData * innermostEHBailoutData, uint32 tryNestingDepth); void OP_TryFinally(const unaligned OpLayoutBr* playout); void OP_TryFinallyWithYield(const byte* ip, Js::JumpOffset jumpOffset, Js::RegSlot regException, Js::RegSlot regOffset); void OP_ResumeCatch(); diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.cpp b/lib/Runtime/Language/JavascriptExceptionOperators.cpp index 3bd4936d034..5abd6010d2a 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.cpp +++ b/lib/Runtime/Language/JavascriptExceptionOperators.cpp @@ -118,10 +118,10 @@ namespace Js void *frame, size_t spillSize, size_t argsSize, + int hasBailedOutOffset, ScriptContext *scriptContext) { void *tryContinuation = nullptr; - void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize); @@ -141,6 +141,47 @@ namespace Js exception = exception->CloneIfStaticExceptionObject(scriptContext); } + if (exception) + { + bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative + if (hasBailedOut) + { + // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; + // it so happens that this catch was on the stack and caught the exception. + // Re-throw! + JavascriptExceptionOperators::DoThrow(exception, scriptContext); + } + // MGTODO : We need to set the exception object, so that we can access in the interpreter, better way out ? + scriptContext->GetThreadContext()->SetPendingFinallyException(exception); + void *continuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize, exception); + return continuation; + } + + return tryContinuation; + } + + void * JavascriptExceptionOperators::OP_TryFinallySimpleJit(void * tryAddr, void * finallyAddr, void * frame, size_t spillSize, size_t argsSize, ScriptContext * scriptContext) + { + void *tryContinuation = nullptr; + void *finallyContinuation = nullptr; + JavascriptExceptionObject *exception = nullptr; + + PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize); + try + { + tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + } + + if (exception) + { + // Clone static exception object early in case finally block overwrites it + exception = exception->CloneIfStaticExceptionObject(scriptContext); + } + finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize); if (finallyContinuation) { @@ -154,6 +195,7 @@ namespace Js return tryContinuation; } + #elif defined(_M_ARM32_OR_ARM64) void *JavascriptExceptionOperators::OP_TryCatch( @@ -213,10 +255,10 @@ namespace Js void *framePtr, void *localsPtr, size_t argsSize, + int hasBailedOutOffset, ScriptContext *scriptContext) { void *tryContinuation = nullptr; - void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize); @@ -234,6 +276,57 @@ namespace Js exception = err.GetAndClear(); } + if (exception) + { + // Clone static exception object early in case finally block overwrites it + exception = exception->CloneIfStaticExceptionObject(scriptContext); + bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative + if (hasBailedOut) + { + // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; + // it so happens that this catch was on the stack and caught the exception. + // Re-throw! + JavascriptExceptionOperators::DoThrow(exception, scriptContext); + } + scriptContext->GetThreadContext()->SetPendingFinallyException(exception); +#if defined(_M_ARM) + void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); +#elif defined(_M_ARM64) + void * finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); +#endif + return finallyContinuation; + } + + return tryContinuation; + } + + void *JavascriptExceptionOperators::OP_TryFinallySimpleJit( + void *tryAddr, + void *finallyAddr, + void *framePtr, + void *localsPtr, + size_t argsSize, + ScriptContext *scriptContext) + { + void *tryContinuation = nullptr; + void *finallyContinuation = nullptr; + JavascriptExceptionObject *exception = nullptr; + + PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize); + + try + { +#if defined(_M_ARM) + tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); +#elif defined(_M_ARM64) + tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); +#endif + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + } + if (exception) { // Clone static exception object early in case finally block overwrites it @@ -402,7 +495,7 @@ namespace Js return continuationAddr; } - void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext) + void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext) { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; @@ -475,6 +568,149 @@ namespace Js pExceptionObject = err.GetAndClear(); } + if (pExceptionObject) + { + // Clone static exception object early in case finally block overwrites it + pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext); + bool hasBailedOut = *(bool*)((char*)framePtr + hasBailedOutOffset); // stack offsets are negative + if (hasBailedOut) + { + // If we have bailed out, this exception is coming from the interpreter. It should not have been caught; + // it so happens that this catch was on the stack and caught the exception. + // Re-throw! + JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); + } + scriptContext->GetThreadContext()->SetPendingFinallyException(pExceptionObject); + + void* newContinuationAddr = NULL; +#ifdef _M_IX86 + void *savedEsp; + + __asm + { + // Save and restore the callee-saved registers around the call. + // TODO: track register kills by region and generate per-region prologs and epilogs + push esi + push edi + push ebx + + // 8-byte align frame to improve floating point perf of our JIT'd code. + // Save ESP + mov ecx, esp + mov savedEsp, ecx + and esp, -8 + + // Set up the call target + mov eax, handlerAddr + +#if 0 && defined(_CONTROL_FLOW_GUARD) + // verify that the call target is valid + mov ebx, eax; save call target + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ebx; restore call target +#endif + + // save the current frame ptr, and adjust the frame to access + // locals in native code. + push ebp + mov ebp, framePtr + call eax + pop ebp + + // The native code gives us the address where execution should continue on exit + // from the finally, but only if flow leaves the finally before it completes. + mov newContinuationAddr, eax + + // Restore ESP + mov ecx, savedEsp + mov esp, ecx + + pop ebx + pop edi + pop esi + } +#else + AssertMsg(FALSE, "Unsupported native try-finally handler"); +#endif + return newContinuationAddr; + } + return continuationAddr; + } + + void* JavascriptExceptionOperators::OP_TryFinallySimpleJit(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext) + { + Js::JavascriptExceptionObject* pExceptionObject = NULL; + void* continuationAddr = NULL; + + PROBE_STACK(scriptContext, Constants::MinStackDefault); + + try + { + // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function + // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. + // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). + // In order to circumvent that we are throwing OutOfMemory. + if (!tryAddr) + { + Assert(false); + ThrowOutOfMemory(scriptContext); + } + +#ifdef _M_IX86 + void *savedEsp; + __asm + { + // Save and restore the callee-saved registers around the call. + // TODO: track register kills by region and generate per-region prologs and epilogs + push esi + push edi + push ebx + + // 8-byte align frame to improve floating point perf of our JIT'd code. + // Save ESP + mov ecx, esp + mov savedEsp, ecx + and esp, -8 + + // Set up the call target, save the current frame ptr, and adjust the frame to access + // locals in native code. + mov eax, tryAddr + +#if 0 && defined(_CONTROL_FLOW_GUARD) + // verify that the call target is valid + mov ebx, eax; save call target + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ebx; restore call target +#endif + + push ebp + mov ebp, framePtr + call eax + pop ebp + + // The native code gives us the address where execution should continue on exit + // from the region. + mov continuationAddr, eax + + // Restore ESP + mov ecx, savedEsp + mov esp, ecx + + pop ebx + pop edi + pop esi + } +#else + AssertMsg(FALSE, "Unsupported native try-finally handler"); +#endif + } + catch (const Js::JavascriptException& err) + { + pExceptionObject = err.GetAndClear(); + } + if (pExceptionObject) { // Clone static exception object early in case finally block overwrites it @@ -503,11 +739,11 @@ namespace Js mov eax, handlerAddr #if 0 && defined(_CONTROL_FLOW_GUARD) - // verify that the call target is valid - mov ebx, eax ; save call target - mov ecx, eax - call [__guard_check_icall_fptr] - mov eax, ebx ; restore call target + // verify that the call target is valid + mov ebx, eax; save call target + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ebx; restore call target #endif // save the current frame ptr, and adjust the frame to access diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.h b/lib/Runtime/Language/JavascriptExceptionOperators.h index 6c83e8f2e2b..92c614b2c35 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.h +++ b/lib/Runtime/Language/JavascriptExceptionOperators.h @@ -53,13 +53,16 @@ namespace Js #ifdef _M_X64 static void *OP_TryCatch(void *try_, void *catch_, void *frame, size_t spillSize, size_t argsSize, int hasBailedOutOffset, ScriptContext *scriptContext); - static void *OP_TryFinally(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, ScriptContext *scriptContext); + static void *OP_TryFinally(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, int hasBailedOutOffset, ScriptContext *scriptContext); + static void *OP_TryFinallySimpleJit(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, ScriptContext *scriptContext); #elif defined(_M_ARM32_OR_ARM64) static void* OP_TryCatch(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, int hasBailedOutOffset, ScriptContext* scriptContext); - static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, ScriptContext* scriptContext); + static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, int hasBailedOutOffset, ScriptContext* scriptContext); + static void* OP_TryFinallySimpleJit(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, ScriptContext* scriptContext); #else static void* OP_TryCatch(void* continuationAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext* scriptContext); - static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, ScriptContext* scriptContext); + static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext* scriptContext); + static void* OP_TryFinallySimpleJit(void* continuationAddr, void* handlerAddr, void* framePtr, ScriptContext* scriptContext); #endif #if defined(DBG) && defined(_M_IX86) static void DbgCheckEHChain(); diff --git a/lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h b/lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h index 68e877eb977..a414b97a4e7 100644 --- a/lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h +++ b/lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h @@ -3116,7 +3116,7 @@ namespace Js /* 00006940 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x03, 0x00, 0x62, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, /* 00006950 */ 0x3E, 0x5D, 0x02, 0x2D, 0x0E, 0x00, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, /* 00006960 */ 0x00, 0x00, 0x00, 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, -/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7B, +/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7B, /* 00006980 */ 0x29, 0x3E, 0x0C, 0x7B, 0x25, 0x3E, 0x0D, 0x7B, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0xEE, 0x04, /* 00006990 */ 0xFF, 0x3D, 0x0E, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x05, 0x00, /* 000069A0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, @@ -4009,7 +4009,7 @@ namespace Js /* 0000A110 */ 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x62, 0x2B, /* 0000A120 */ 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5D, 0x02, 0x1E, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, /* 0000A130 */ 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x2D, -/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x65, +/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x66, /* 0000A150 */ 0x01, 0x2C, 0x2B, 0x7B, 0x0C, 0x2B, 0x02, 0x7B, 0x1B, 0x2B, 0x04, 0x7B, 0x0C, 0x2B, 0x03, 0x5C, /* 0000A160 */ 0x03, 0x2B, 0xEE, 0x04, 0xFF, 0x2A, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, /* 0000A170 */ 0x00, 0x2A, 0x05, 0x00, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, @@ -4400,7 +4400,7 @@ namespace Js /* 0000B980 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x62, 0x1E, 0x1E, 0x05, 0x5C, 0x01, /* 0000B990 */ 0x1E, 0x5D, 0x02, 0x13, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, /* 0000B9A0 */ 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, 0x01, -/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, 0x7B, +/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, 0x7B, /* 0000B9C0 */ 0x0C, 0x1E, 0x02, 0x7B, 0x10, 0x1E, 0x04, 0x7B, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0xEE, 0x04, /* 0000B9D0 */ 0xFF, 0x1D, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x05, 0x00, /* 0000B9E0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, @@ -5095,12 +5095,12 @@ namespace Js /* 0000E4F0 */ 0x03, 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x8E, 0x8F, 0x01, /* 0000E500 */ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09, /* 0000E510 */ 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, -/* 0000E520 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, -/* 0000E530 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, -/* 0000E540 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, -/* 0000E550 */ 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7B, -/* 0000E560 */ 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, -/* 0000E570 */ 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, 0x00, 0x00, 0xEE, +/* 0000E520 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, +/* 0000E530 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, +/* 0000E540 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, +/* 0000E550 */ 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7B, +/* 0000E560 */ 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, +/* 0000E570 */ 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, 0x00, 0x00, 0xEE, /* 0000E580 */ 0x03, 0x00, 0x0B, 0x00, 0x00, 0x09, 0x02, 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01, 0x20, 0x00, 0x00, /* 0000E590 */ 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D, /* 0000E5A0 */ 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x42, diff --git a/lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h b/lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h index cf476ea9937..a9448d09b07 100644 --- a/lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h +++ b/lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h @@ -3116,7 +3116,7 @@ namespace Js /* 00006940 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x03, 0x00, 0x62, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, /* 00006950 */ 0x3E, 0x5D, 0x02, 0x2D, 0x0E, 0x00, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, /* 00006960 */ 0x00, 0x00, 0x00, 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, -/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7B, +/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7B, /* 00006980 */ 0x29, 0x3E, 0x0C, 0x7B, 0x25, 0x3E, 0x0D, 0x7B, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0xEE, 0x04, /* 00006990 */ 0xFF, 0x3D, 0x0E, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x05, 0x00, /* 000069A0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, @@ -4009,7 +4009,7 @@ namespace Js /* 0000A110 */ 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x62, 0x2B, /* 0000A120 */ 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5D, 0x02, 0x1E, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, /* 0000A130 */ 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x2D, -/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x65, +/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x66, /* 0000A150 */ 0x01, 0x2C, 0x2B, 0x7B, 0x0C, 0x2B, 0x02, 0x7B, 0x1B, 0x2B, 0x04, 0x7B, 0x0C, 0x2B, 0x03, 0x5C, /* 0000A160 */ 0x03, 0x2B, 0xEE, 0x04, 0xFF, 0x2A, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, /* 0000A170 */ 0x00, 0x2A, 0x05, 0x00, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, @@ -4400,7 +4400,7 @@ namespace Js /* 0000B980 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x62, 0x1E, 0x1E, 0x05, 0x5C, 0x01, /* 0000B990 */ 0x1E, 0x5D, 0x02, 0x13, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E, /* 0000B9A0 */ 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, 0x01, -/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, 0x7B, +/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, 0x7B, /* 0000B9C0 */ 0x0C, 0x1E, 0x02, 0x7B, 0x10, 0x1E, 0x04, 0x7B, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0xEE, 0x04, /* 0000B9D0 */ 0xFF, 0x1D, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x05, 0x00, /* 0000B9E0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, @@ -5095,12 +5095,12 @@ namespace Js /* 0000E4F0 */ 0x01, 0xFE, 0x40, 0x03, 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, /* 0000E500 */ 0x8E, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x07, 0x03, 0x00, /* 0000E510 */ 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, -/* 0000E520 */ 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, -/* 0000E530 */ 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, -/* 0000E540 */ 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, -/* 0000E550 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, -/* 0000E560 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, -/* 0000E570 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, +/* 0000E520 */ 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, +/* 0000E530 */ 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, +/* 0000E540 */ 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, +/* 0000E550 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, +/* 0000E560 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, +/* 0000E570 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, /* 0000E580 */ 0x00, 0x00, 0xEE, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x09, 0x02, 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01, /* 0000E590 */ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03, /* 0000E5A0 */ 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02, diff --git a/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h b/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h index 8920b7de2c6..4801ab0961f 100644 --- a/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h +++ b/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h @@ -3091,7 +3091,7 @@ namespace Js /* 000067B0 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x61, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, 0x3E, /* 000067C0 */ 0x5C, 0x02, 0x2D, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, /* 000067D0 */ 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, 0x0D, 0x00, 0x00, -/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C, +/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C, /* 000067F0 */ 0x7A, 0x25, 0x3E, 0x0D, 0x7A, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0x1F, 0x04, 0xFF, 0x3D, 0x8E, /* 00006800 */ 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x90, /* 00006810 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x5C, 0x01, 0x3E, 0x5C, 0x02, 0x2E, 0xCC, @@ -3907,7 +3907,7 @@ namespace Js /* 00009AB0 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1E, 0xCC, /* 00009AC0 */ 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, /* 00009AD0 */ 0x01, 0x00, 0x00, 0x00, 0x2D, 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7A, -/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x65, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04, +/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x66, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04, /* 00009AF0 */ 0x7A, 0x0C, 0x2B, 0x03, 0x5C, 0x03, 0x2B, 0x1F, 0x04, 0xFF, 0x2A, 0x8E, 0x01, 0x00, 0x00, 0x00, /* 00009B00 */ 0x19, 0x00, 0x00, 0x00, 0x2A, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, /* 00009B10 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1F, 0xCC, @@ -4264,7 +4264,7 @@ namespace Js /* 0000B100 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E, /* 0000B110 */ 0x05, 0x5C, 0x01, 0x1E, 0x5C, 0x02, 0x13, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, /* 0000B120 */ 0x1E, 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, -/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, +/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, /* 0000B140 */ 0x7A, 0x0C, 0x1E, 0x02, 0x7A, 0x10, 0x1E, 0x04, 0x7A, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0x1F, /* 0000B150 */ 0x04, 0xFF, 0x1D, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x07, 0x04, 0x00, /* 0000B160 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E, @@ -4904,11 +4904,11 @@ namespace Js /* 0000D900 */ 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x88, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x08, /* 0000D910 */ 0x00, 0x00, 0x00, 0x0B, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0000D920 */ 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, -/* 0000D930 */ 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x01, -/* 0000D940 */ 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x02, 0x01, -/* 0000D950 */ 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x03, 0x01, 0x65, -/* 0000D960 */ 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01, -/* 0000D970 */ 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D, +/* 0000D930 */ 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x01, +/* 0000D940 */ 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x02, 0x01, +/* 0000D950 */ 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x03, 0x01, 0x66, +/* 0000D960 */ 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01, +/* 0000D970 */ 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D, /* 0000D980 */ 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02, 0x00, 0xA8, 0x00, /* 0000D990 */ 0x24, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0000D9A0 */ 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, diff --git a/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h b/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h index 2b11cf43e7a..b04bb745b92 100644 --- a/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h +++ b/lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h @@ -3091,7 +3091,7 @@ namespace Js /* 000067B0 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x61, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, 0x3E, /* 000067C0 */ 0x5C, 0x02, 0x2D, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, /* 000067D0 */ 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, 0x0D, 0x00, 0x00, -/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C, +/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C, /* 000067F0 */ 0x7A, 0x25, 0x3E, 0x0D, 0x7A, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0x1F, 0x04, 0xFF, 0x3D, 0x8E, /* 00006800 */ 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x90, /* 00006810 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x5C, 0x01, 0x3E, 0x5C, 0x02, 0x2E, 0xCC, @@ -3907,7 +3907,7 @@ namespace Js /* 00009AB0 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1E, 0xCC, /* 00009AC0 */ 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, /* 00009AD0 */ 0x01, 0x00, 0x00, 0x00, 0x2D, 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7A, -/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x65, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04, +/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x66, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04, /* 00009AF0 */ 0x7A, 0x0C, 0x2B, 0x03, 0x5C, 0x03, 0x2B, 0x1F, 0x04, 0xFF, 0x2A, 0x8E, 0x01, 0x00, 0x00, 0x00, /* 00009B00 */ 0x19, 0x00, 0x00, 0x00, 0x2A, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, /* 00009B10 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1F, 0xCC, @@ -4264,7 +4264,7 @@ namespace Js /* 0000B100 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E, /* 0000B110 */ 0x05, 0x5C, 0x01, 0x1E, 0x5C, 0x02, 0x13, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, /* 0000B120 */ 0x1E, 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, -/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, +/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, /* 0000B140 */ 0x7A, 0x0C, 0x1E, 0x02, 0x7A, 0x10, 0x1E, 0x04, 0x7A, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0x1F, /* 0000B150 */ 0x04, 0xFF, 0x1D, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x07, 0x04, 0x00, /* 0000B160 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E, @@ -4904,12 +4904,12 @@ namespace Js /* 0000D900 */ 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x88, 0x8E, 0x01, 0x00, /* 0000D910 */ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00, /* 0000D920 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, -/* 0000D930 */ 0x7A, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, -/* 0000D940 */ 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, -/* 0000D950 */ 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, -/* 0000D960 */ 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, -/* 0000D970 */ 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, -/* 0000D980 */ 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02, +/* 0000D930 */ 0x7A, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, +/* 0000D940 */ 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, +/* 0000D950 */ 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, +/* 0000D960 */ 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, +/* 0000D970 */ 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, +/* 0000D980 */ 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02, /* 0000D990 */ 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, /* 0000D9A0 */ 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, /* 0000D9B0 */ 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x42, 0x03, 0x00, 0x00, 0x00, 0xFE, 0x3D, 0x03, diff --git a/test/EH/early1.baseline b/test/EH/early1.baseline new file mode 100644 index 00000000000..7ff0a975f91 --- /dev/null +++ b/test/EH/early1.baseline @@ -0,0 +1,261 @@ +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +Done earlyReturn +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyBreak +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyContinue +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +Done earlyReturnFromFinally +catch 0 +Done earlyReturnFromCatch +inner finally 0 +outer finally 0 +Done earlyReturnFromNestedFinally +outer finally 0 +Done earlyReturnFromNestedTFTC +outer finally 0 +done +Done earlyBreakFromNestedTFTC +outer finally 0 +outer finally 1 +outer finally 2 +outer finally 3 +outer finally 4 +outer finally 5 +outer finally 6 +done +Done earlyContinueFromNestedTFTC +outer finally 0 +done +Done earlyReturnFromNestedTFTC +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +done +Done earlyReturnFromNestedTFTC +catch 0 +finally 0 +earlyReturnFromCatchInTryFinally +catch 0 +finally 0 +earlyReturnFromCatchInTryCatchTryFinally +try +inner finally 0 +outer finally 0 +Done earlyReturnFromFinallyInTryFinally +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +infinite loop catch +infinite loop finally +earlyReturnFromCatchInInfiniteLoop +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +Done earlyReturn +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyBreak +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyContinue +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +Done earlyReturnFromFinally +catch 0 +Done earlyReturnFromCatch +inner finally 0 +outer finally 0 +Done earlyReturnFromNestedFinally +outer finally 0 +Done earlyReturnFromNestedTFTC +outer finally 0 +done +Done earlyBreakFromNestedTFTC +outer finally 0 +outer finally 1 +outer finally 2 +outer finally 3 +outer finally 4 +outer finally 5 +outer finally 6 +done +Done earlyContinueFromNestedTFTC +outer finally 0 +done +Done earlyReturnFromNestedTFTC +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +done +Done earlyReturnFromNestedTFTC +catch 0 +finally 0 +earlyReturnFromCatchInTryFinally +catch 0 +finally 0 +earlyReturnFromCatchInTryCatchTryFinally +try +inner finally 0 +outer finally 0 +Done earlyReturnFromFinallyInTryFinally +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +infinite loop catch +infinite loop finally +earlyReturnFromCatchInInfiniteLoop +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +Done earlyReturn +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyBreak +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +done +Done earlyContinue +finally 0 +finally 1 +finally 2 +finally 3 +finally 4 +finally 5 +finally 6 +Done earlyReturnFromFinally +catch 0 +Done earlyReturnFromCatch +inner finally 0 +outer finally 0 +Done earlyReturnFromNestedFinally +outer finally 0 +Done earlyReturnFromNestedTFTC +outer finally 0 +done +Done earlyBreakFromNestedTFTC +outer finally 0 +outer finally 1 +outer finally 2 +outer finally 3 +outer finally 4 +outer finally 5 +outer finally 6 +done +Done earlyContinueFromNestedTFTC +outer finally 0 +done +Done earlyReturnFromNestedTFTC +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +outer finally 0 +done +Done earlyReturnFromNestedTFTC +catch 0 +finally 0 +earlyReturnFromCatchInTryFinally +catch 0 +finally 0 +earlyReturnFromCatchInTryCatchTryFinally +try +inner finally 0 +outer finally 0 +Done earlyReturnFromFinallyInTryFinally +catch 0 +catch 1 +catch 2 +catch 3 +catch 4 +catch 5 +catch 6 +done +infinite loop catch +infinite loop finally +earlyReturnFromCatchInInfiniteLoop diff --git a/test/EH/early1.js b/test/EH/early1.js new file mode 100644 index 00000000000..99ece5dbf81 --- /dev/null +++ b/test/EH/early1.js @@ -0,0 +1,315 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function oos() { + oos(); +} + +function earlyReturn(num) { +for (var i = 0; i < num; i++) { + try { + if (num > 5) { + oos(); + return; + } + } + catch(e) { + WScript.Echo("catch " + i); + } +} +WScript.Echo("done"); +} + +function earlyBreak(num) { +for (var i = 0; i < num; i++) { + try { + if (i > 5) { + break; + } + } + finally { + WScript.Echo("finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyContinue(num) { +for (var i = 0; i < num; i++) { + try { + if (i > 5) { + continue; + } + } + finally { + WScript.Echo("finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyReturnFromFinally(num) +{ + +for (var i = 0; i < num; i++) { + try { + } + finally { + WScript.Echo("finally " + i); + if (i > 5) { + return; + } + } +} +WScript.Echo("done"); +} + +function earlyReturnFromCatch(num) +{ +for (var i = 0; i < num; i++) { + try { + oos(); + } + catch(e) { + WScript.Echo("catch " + i); + if (num > 5) { + return; + } + } +} +WScript.Echo("done"); +} + +function earlyReturnFromNestedFinally(num) +{ +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) return; + } + finally{ + WScript.Echo("inner finally " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyReturnFromNestedTFTC(num) +{ +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) return; + } + catch (e){ + WScript.Echo("inner catch " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyBreakFromNestedTFTC(num) +{ +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) break; + } + catch (e){ + WScript.Echo("inner catch " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyContinueFromNestedTFTC(num) +{ +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) continue; + } + catch (e){ + WScript.Echo("inner catch " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +WScript.Echo("done"); +} + +function earlyBreakLabelFromNestedTFTC(num) +{ +outer:for (var x = 0; x < num; x++) { +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) break outer; + } + catch (e){ + WScript.Echo("inner catch " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +} +WScript.Echo("done"); +} + +function earlyContinueLabelFromNestedTFTC(num) +{ +outer:for (var x = 0; x < num; x++) { +for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) continue outer; + } + catch (e){ + WScript.Echo("inner catch " + i); + } + } + finally { + WScript.Echo("outer finally " + i); + } +} +} +WScript.Echo("done"); +} + +function earlyReturnFromCatchInTryFinally(num) +{ +for (var i = 0; i < num; i++) { +try { + try { + throw "Err"; + } + catch(e) { + WScript.Echo("catch " + i); + if (num > 5) { + return; + } + } +} +finally { +WScript.Echo("finally " + i); +} +} +WScript.Echo("done"); +} + +function earlyReturnFromCatchInTryCatchTryFinally(num) +{ +for (var i = 0; i < num; i++) { +try { + try { + throw "Err"; + } + catch(e) { + WScript.Echo("catch " + i); + if (num > 5) { + return; + } + } +} +finally { +WScript.Echo("finally " + i); +} +} +WScript.Echo("done"); +} + +function earlyReturnFromFinallyInTryFinally(num) +{ +for (var i = 0; i < num; i++) { +try { + try { + WScript.Echo("try"); + } + finally { + WScript.Echo("inner finally " + i); + return; + } +} +finally { +WScript.Echo("outer finally " + i); +} +} +WScript.Echo("done"); +} + +function earlyReturnFromCatchInInfiniteLoop(num) +{ +while (true) { +try { + try { + throw "Err"; + } + catch(e) { + WScript.Echo("infinite loop catch"); + if (num > 5) { + return; + } + } +} +finally { +WScript.Echo("infinite loop finally"); +} +} +WScript.Echo("done"); +} + +function test0() { + earlyReturn(7); + WScript.Echo("Done earlyReturn"); + earlyBreak(7); + WScript.Echo("Done earlyBreak"); + earlyContinue(7); + WScript.Echo("Done earlyContinue"); + earlyReturnFromFinally(7); + WScript.Echo("Done earlyReturnFromFinally"); + earlyReturnFromCatch(7); + WScript.Echo("Done earlyReturnFromCatch"); + earlyReturnFromNestedFinally(7); + WScript.Echo("Done earlyReturnFromNestedFinally"); + earlyReturnFromNestedTFTC(7); + WScript.Echo("Done earlyReturnFromNestedTFTC"); + earlyBreakFromNestedTFTC(7); + WScript.Echo("Done earlyBreakFromNestedTFTC"); + earlyContinueFromNestedTFTC(7); + WScript.Echo("Done earlyContinueFromNestedTFTC"); + earlyBreakLabelFromNestedTFTC(7); + WScript.Echo("Done earlyReturnFromNestedTFTC"); + earlyContinueLabelFromNestedTFTC(7); + WScript.Echo("Done earlyReturnFromNestedTFTC"); + earlyReturnFromCatchInTryFinally(7); + WScript.Echo("earlyReturnFromCatchInTryFinally"); + earlyReturnFromCatchInTryCatchTryFinally(7); + WScript.Echo("earlyReturnFromCatchInTryCatchTryFinally"); + earlyReturnFromFinallyInTryFinally(7); + WScript.Echo("Done earlyReturnFromFinallyInTryFinally"); + earlyReturn(7); + earlyReturnFromCatchInInfiniteLoop(7); + WScript.Echo("earlyReturnFromCatchInInfiniteLoop"); +} + +test0(); +test0(); +test0(); diff --git a/test/EH/early2.baseline b/test/EH/early2.baseline new file mode 100644 index 00000000000..89d7877d77b --- /dev/null +++ b/test/EH/early2.baseline @@ -0,0 +1,111 @@ +return outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 +outer finally +inner finally +outer finally +inner finally +outer finally +inner finally +continue outer finally 0 +inner finally +continue outer finally 1 +inner finally +continue outer finally 2 +inner finally +continue outer finally 3 +inner finally +continue outer finally 4 +inner finally +continue outer finally 5 +inner finally +continue outer finally 6 +outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 +return outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 +outer finally +inner finally +outer finally +inner finally +outer finally +inner finally +continue outer finally 0 +inner finally +continue outer finally 1 +inner finally +continue outer finally 2 +inner finally +continue outer finally 3 +inner finally +continue outer finally 4 +inner finally +continue outer finally 5 +inner finally +continue outer finally 6 +outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 +return outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 +outer finally +inner finally +outer finally +inner finally +outer finally +inner finally +continue outer finally 0 +inner finally +continue outer finally 1 +inner finally +continue outer finally 2 +inner finally +continue outer finally 3 +inner finally +continue outer finally 4 +inner finally +continue outer finally 5 +inner finally +continue outer finally 6 +outer finally +break outer finally +continue outer finally 0 +continue outer finally 1 +continue outer finally 2 +continue outer finally 3 +continue outer finally 4 +continue outer finally 5 +continue outer finally 6 diff --git a/test/EH/early2.js b/test/EH/early2.js new file mode 100644 index 00000000000..3b13c22e4d3 --- /dev/null +++ b/test/EH/early2.js @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function earlyReturnTF(num) { + for (var i = 0; i < num; i++) { + try { + if (num > 5) return; + }finally { + WScript.Echo("return outer finally"); + } + } +} + +function earlyBreakTF(num) { + for (var i = 0; i < num; i++) { + try { + if (num > 5) break; + }finally { + WScript.Echo("break outer finally"); + } + } +} + +function earlyContinueTF(num) { + for (var i = 0; i < num; i++) { + try { + if (i < 3) continue; + }finally { + WScript.Echo("continue outer finally " + i); + } + } +} + +function earlyReturnNestedTFTC(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) return; + } + catch(e) { + WScript.Echo("inner catch"); + } + }finally { + WScript.Echo("outer finally"); + } + } +} + +function earlyReturnNestedTFTF(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) return; + } + finally { + WScript.Echo("inner finally"); + } + }finally { + WScript.Echo("outer finally"); + } + } +} + +function earlyBreakNestedTFTF(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) break; + } + finally { + WScript.Echo("inner finally"); + } + }finally { + WScript.Echo("outer finally"); + } + } +} + +function earlyContinueNestedTFTF(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (i > 3) continue; + } + finally { + WScript.Echo("inner finally"); + } + }finally { + WScript.Echo("continue outer finally " + i); + } + } +} + +function earlyBreakNestedTFTC(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) break; + } + catch(e) { + WScript.Echo("inner catch"); + } + }finally { + WScript.Echo("break outer finally"); + } + } +} + +function earlyContinueNestedTFTC(num) { + for (var i = 0; i < num; i++) { + try { + try { + if (num > 5) continue; + } + catch(e) { + WScript.Echo("inner catch"); + } + }finally { + WScript.Echo("continue outer finally " + i); + } + } +} + +function test0() { + earlyReturnTF(7); + earlyBreakTF(7); + earlyContinueTF(7); + earlyReturnNestedTFTC(7); + earlyReturnNestedTFTF(7); + earlyBreakNestedTFTF(7); + earlyContinueNestedTFTF(7); + earlyReturnNestedTFTC(7); + earlyBreakNestedTFTC(7); + earlyContinueNestedTFTC(7); +} + +test0(); +test0(); +test0(); diff --git a/test/EH/rlexe.xml b/test/EH/rlexe.xml index 342b39d624b..1738b320f51 100644 --- a/test/EH/rlexe.xml +++ b/test/EH/rlexe.xml @@ -96,4 +96,16 @@ 101832.baseline + + + early1.js + early1.baseline + + + + + early2.js + early2.baseline + +