Changeset 209952 in webkit
- Timestamp:
- Dec 16, 2016, 5:06:49 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r209928 r209952 1 2016-12-16 Mark Lam <mark.lam@apple.com> 2 3 De-duplicate finally blocks. 4 https://bugs.webkit.org/show_bug.cgi?id=160168 5 6 Reviewed by Keith Miller. 7 8 * stress/deeply-nested-finallys.js: Added. 9 - Tests many levels of finally nesting. This causes the old code to hang (and 10 crashes eventually) while trying to generate bytecode for the exponentially 11 duplicated finally blocks. The new code completes this test almost instantly. 12 13 * stress/test-finally.js: Added. 14 - Tests control flow through various permutations of finally blocks. 15 1 16 2016-12-16 Saam Barati <sbarati@apple.com> 2 17 -
trunk/Source/JavaScriptCore/ChangeLog
r209934 r209952 1 2016-12-16 Mark Lam <mark.lam@apple.com> 2 3 De-duplicate finally blocks. 4 https://bugs.webkit.org/show_bug.cgi?id=160168 5 6 Reviewed by Keith Miller. 7 8 JS execution can arrive at a finally block when there are abrupt completions from 9 its try or catch block. The abrupt completion types include Break, 10 Continue, Return, and Throw. The non-abrupt completion type is called Normal 11 (i.e. the case of a try block falling through to the finally block). 12 13 Previously, we enable each of these paths for abrupt completion (except for Throw) 14 to run the finally block code by duplicating the finally block code at each of 15 the sites that trigger those completions. This patch fixes the implementation so 16 that each of these abrupt completions will set a finallyActionRegister (plus a 17 finallyReturnValueRegister for CompletionType::Return) and then jump to the 18 relevant finally blocks, and continue to thread through subsequent outer finally 19 blocks until execution reaches the outermost finally block that the completion 20 type dictates. We no longer duplicate the finally block code. 21 22 The implementation details: 23 1. We allocate a pair of finallyActionRegister and finallyReturnValueRegister 24 just before entering the outermost try-catch-finally scope. 25 26 On allocating the registers, we set them to the empty JSValue. This serves 27 to set the completion type to CompletionType::Normal (see (2) below). 28 29 2. The finallyActionRegister serves 2 purpose: 30 a. indicates the CompletionType that triggered entry into the finally block. 31 32 This is how we encode the completion type in the finallyActionRegister: 33 1. CompletionType::Normal 34 - finallyActionRegister is set to the empty JSValue. 35 2. CompletionType::Break 36 - finallyActionRegister is set to the int jumpID for the site of the break statement. 37 3. CompletionType::Continue 38 - finallyActionRegister is set to the int jumpID for the site of the continue statement. 39 4. CompletionType::Return 40 - finallyActionRegister is set to CompletionType::Return as an int JSValue. 41 - finallyReturnValueRegister is set to the value to be returned. 42 5. CompletionType::Throw 43 - finallyActionRegister is set to the exception object that was caught by the finally block. 44 45 Hence, if the finallyActionRegister can either be: 46 1. empty i.e. we're handling CompletionType::Normal. 47 2. an int JSValue i.e. we're handling CompletionType::Break, Continue, or Return. 48 3. an object i.e. we're handling CompletionType::Throw. 49 50 b. stores the exception caught in the finally block if we're handing 51 CompletionType::Throw. 52 53 3. Each finally block will have 2 entries: 54 a. the entry via throw. 55 b. the normal entry. 56 57 The entry via throw is recorded in the codeBlock's exception table, and can 58 only be jumped to by the VM's exception handling mechanism. 59 60 The normal entry is recorded in a FinallyContext (at bytecode generation time 61 only) and is jumped to when we want enter the finally block due any of the 62 other CompletionTypes. 63 64 4. CompletionType::Normal 65 ====================== 66 We encounter this when falling through from a try or catch block to the finally block. 67 68 For the try block case, since finallyActionRegister is set to Normal by default, 69 there's nothing more that needs to be done. 70 71 For the catch block case, since we entered the catch block with an exception, 72 finallyActionRegister may be set to Throw. We'll need to set it to Normal 73 before jumping to the finally block's normal entry. 74 75 CompletionType::Break 76 ===================== 77 When we emit bytecode for the BreakNode, we check if we have any FinallyContexts 78 that we need to service before jumping to the breakTarget. If we do, then: 79 a. we'll register a jumpID along with the breakTarget with the outermost FinallyContext. 80 b. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext 81 from the innermost to the outermost. 82 c. instead of emitting bytecode to jump to the breakTarget, we: 83 1. emit bytecode to set finallyActionRegister to the jumpID. 84 b. emit bytecode to jump to the normal entry of the innermost finally block. 85 86 Each finally block will take care of cascading to the next outer finally block 87 as needed (see (5) below). 88 89 CompletionType::Continue 90 ======================== 91 Since continues and breaks work the same way (i.e. with a jump), we handle this 92 exactly the same way as CompletionType::Break, except that we use the 93 continueTarget instead of the breakTarget. 94 95 CompletionType::Return 96 ====================== 97 When we emit bytecode for the ReturnNode, we check if we have any FinallyContexts 98 at all on the m_controlFlowScopeStack. 99 100 If so, then instead of emitting op_ret, we: 101 1. emit bytecode to set finallyActionRegister to the CompletionType::Return. 102 1. emit bytecode to move the return value into finallyReturnValueRegister. 103 2. emit bytecode to jump to the normal entry of the innermost finally block. 104 105 Each finally block will take care of cascading to the next outer finally block 106 as needed (see (5) below). 107 108 CompletionType::Throw 109 ====================== 110 The op_catch of a finally block will always store the caught exception object 111 in the finallyActionRegister. This means we're handling CompletionType::Throw 112 (see (2) above). 113 114 5. What happens in each finally block? 115 ================================== 116 Only the finally block's entry via throw will have an op_catch that catches the 117 pending exception (and stores it in the finallyActionRegister). This throw 118 entry then falls through to the normal entry. 119 120 The finally block's normal entry will restore the scope of the finally block 121 and proceed to execute its code. 122 123 At the end of the finally block (see emitFinallyCompletion()), the finally 124 block will check the finallyActionRegister for each completion type in the 125 following order: 126 127 a. CompletionType::Normal: jump to the code after the finally block as 128 designated by a normalCompletion label. 129 130 b. CompletionType::Break and Continue: 131 If the FinallyContext for this block has registered FinallyJumps, we'll 132 check for the jumpIDs against the finallyActionRegister. If the jumpID 133 matches, jump to the corresponding jumpTarget. 134 135 If no jumpIDs match but the FinallyContext's numberOfBreaksOrContinues is 136 greater than the number of registered FinallyJumps, then this means that 137 we have a Break or Continue that needs to be handled by an outer finally 138 block. In that case, jump to the outer finally block's normal entry. 139 140 c. CompletionType::Return: 141 If this finally block is not the outermost and finallyActionRegister contains 142 CompletionType::Return, then jump to the outer finally block's normal entry. 143 144 Otherwise, if this finally block is the outermost and finallyActionRegister 145 contains CompletionType::Return, then execute op_ret and return the value 146 in finallyReturnValueRegister. 147 148 d. CompletionType::Throw: 149 If we're not handling any of the above cases, then just throw the 150 finallyActionRegister which contains the exception to re-throw. 151 152 6. restoreScopeRegister() 153 154 Since the needed scope objects are always stored in a local, we can restore 155 the scope register by simply moving from that local instead of going through 156 op_get_parent_scope. 157 158 7. m_controlFlowScopeStack needs to be a SegmentedVector instead of a Vector. 159 This makes it easier to keep a pointer to the FinallyContext on that stack, 160 and not have to worry about the vector being realloc'ed due to resizing. 161 162 Performance appears to be neutral both on ES6SampleBench (run via cli) and the 163 JSC benchmarks. 164 165 Relevant spec references: 166 https://tc39.github.io/ecma262/#sec-completion-record-specification-type 167 https://tc39.github.io/ecma262/#sec-try-statement-runtime-semantics-evaluation 168 169 * bytecode/HandlerInfo.h: 170 (JSC::HandlerInfoBase::typeName): 171 * bytecompiler/BytecodeGenerator.cpp: 172 (JSC::BytecodeGenerator::generate): 173 (JSC::BytecodeGenerator::BytecodeGenerator): 174 (JSC::BytecodeGenerator::emitReturn): 175 (JSC::BytecodeGenerator::pushFinallyControlFlowScope): 176 (JSC::BytecodeGenerator::popFinallyControlFlowScope): 177 (JSC::BytecodeGenerator::allocateAndEmitScope): 178 (JSC::BytecodeGenerator::pushTry): 179 (JSC::BytecodeGenerator::popTry): 180 (JSC::BytecodeGenerator::emitCatch): 181 (JSC::BytecodeGenerator::restoreScopeRegister): 182 (JSC::BytecodeGenerator::labelScopeDepthToLexicalScopeIndex): 183 (JSC::BytecodeGenerator::labelScopeDepth): 184 (JSC::BytecodeGenerator::pushLocalControlFlowScope): 185 (JSC::BytecodeGenerator::popLocalControlFlowScope): 186 (JSC::BytecodeGenerator::emitEnumeration): 187 (JSC::BytecodeGenerator::emitIsNumber): 188 (JSC::BytecodeGenerator::emitYield): 189 (JSC::BytecodeGenerator::emitDelegateYield): 190 (JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded): 191 (JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded): 192 (JSC::BytecodeGenerator::emitFinallyCompletion): 193 (JSC::BytecodeGenerator::allocateFinallyRegisters): 194 (JSC::BytecodeGenerator::releaseFinallyRegisters): 195 (JSC::BytecodeGenerator::emitCompareFinallyActionAndJumpIf): 196 (JSC::BytecodeGenerator::pushIteratorCloseControlFlowScope): Deleted. 197 (JSC::BytecodeGenerator::popIteratorCloseControlFlowScope): Deleted. 198 (JSC::BytecodeGenerator::emitComplexPopScopes): Deleted. 199 (JSC::BytecodeGenerator::emitPopScopes): Deleted. 200 (JSC::BytecodeGenerator::popTryAndEmitCatch): Deleted. 201 * bytecompiler/BytecodeGenerator.h: 202 (JSC::FinallyJump::FinallyJump): 203 (JSC::FinallyContext::FinallyContext): 204 (JSC::FinallyContext::outerContext): 205 (JSC::FinallyContext::finallyLabel): 206 (JSC::FinallyContext::depth): 207 (JSC::FinallyContext::numberOfBreaksOrContinues): 208 (JSC::FinallyContext::incNumberOfBreaksOrContinues): 209 (JSC::FinallyContext::handlesReturns): 210 (JSC::FinallyContext::setHandlesReturns): 211 (JSC::FinallyContext::registerJump): 212 (JSC::FinallyContext::numberOfJumps): 213 (JSC::FinallyContext::jumps): 214 (JSC::ControlFlowScope::ControlFlowScope): 215 (JSC::ControlFlowScope::isLabelScope): 216 (JSC::ControlFlowScope::isFinallyScope): 217 (JSC::BytecodeGenerator::currentLexicalScopeIndex): 218 (JSC::BytecodeGenerator::FinallyRegistersScope::FinallyRegistersScope): 219 (JSC::BytecodeGenerator::FinallyRegistersScope::~FinallyRegistersScope): 220 (JSC::BytecodeGenerator::finallyActionRegister): 221 (JSC::BytecodeGenerator::finallyReturnValueRegister): 222 (JSC::BytecodeGenerator::emitSetFinallyActionToNormalCompletion): 223 (JSC::BytecodeGenerator::emitSetFinallyActionToReturnCompletion): 224 (JSC::BytecodeGenerator::emitSetFinallyActionToJumpID): 225 (JSC::BytecodeGenerator::emitSetFinallyReturnValueRegister): 226 (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNormalCompletion): 227 (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotJump): 228 (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsReturnCompletion): 229 (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotReturnCompletion): 230 (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotThrowCompletion): 231 (JSC::BytecodeGenerator::emitJumpIfCompletionTypeIsThrow): 232 (JSC::BytecodeGenerator::bytecodeOffsetToJumpID): 233 (JSC::BytecodeGenerator::isInFinallyBlock): Deleted. 234 * bytecompiler/NodesCodegen.cpp: 235 (JSC::ContinueNode::emitBytecode): 236 (JSC::BreakNode::emitBytecode): 237 (JSC::ReturnNode::emitBytecode): 238 (JSC::TryNode::emitBytecode): 239 1 240 2016-12-16 Keith Miller <keith_miller@apple.com> 2 241 -
trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h
r206525 r209952 32 32 33 33 enum class HandlerType { 34 Illegal= 0,35 Catch= 1,36 Finally= 2,34 Catch = 0, 35 Finally = 1, 36 SynthesizedCatch = 2, 37 37 SynthesizedFinally = 3 38 38 }; … … 54 54 case HandlerType::Finally: 55 55 return "finally"; 56 case HandlerType::SynthesizedCatch: 57 return "synthesized catch"; 56 58 case HandlerType::SynthesizedFinally: 57 59 return "synthesized finally"; -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r209847 r209952 157 157 continue; 158 158 159 ASSERT(range.tryData->handlerType != HandlerType::Illegal);160 159 UnlinkedHandlerInfo info(static_cast<uint32_t>(start), static_cast<uint32_t>(end), 161 160 static_cast<uint32_t>(range.tryData->target->bind()), range.tryData->handlerType); … … 681 680 pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ); 682 681 682 RefPtr<Label> catchLabel = newLabel(); 683 683 TryData* tryFormalParametersData = nullptr; 684 if (isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) { 684 bool needTryCatch = isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList; 685 if (needTryCatch) { 685 686 RefPtr<Label> tryFormalParametersStart = emitLabel(newLabel().get()); 686 tryFormalParametersData = pushTry(tryFormalParametersStart.get() );687 tryFormalParametersData = pushTry(tryFormalParametersStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch); 687 688 } 688 689 689 690 initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope); 690 691 691 if ( isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) {692 if (needTryCatch) { 692 693 RefPtr<Label> didNotThrow = newLabel(); 693 694 emitJump(didNotThrow.get()); 694 RefPtr<RegisterID> exception = newTemporary(); 695 emitLabel(catchLabel.get()); 696 popTry(tryFormalParametersData, catchLabel.get()); 697 695 698 RefPtr<RegisterID> thrownValue = newTemporary(); 696 Re fPtr<Label> catchHere = emitLabel(newLabel().get());697 popTryAndEmitCatch(tryFormalParametersData, exception.get(), thrownValue.get(), catchHere.get(), HandlerType::Catch);699 RegisterID* unused = newTemporary(); 700 emitCatch(unused, thrownValue.get()); 698 701 699 702 // return promiseCapability.@reject(thrownValue) … … 3494 3497 } 3495 3498 3496 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src )3499 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src, ReturnFrom from) 3497 3500 { 3498 3501 if (isConstructor()) { … … 3500 3503 bool srcIsThis = src->index() == m_thisRegister.index(); 3501 3504 3502 if (mightBeDerived && srcIsThis)3505 if (mightBeDerived && (srcIsThis || from == ReturnFrom::Finally)) 3503 3506 emitTDZCheck(src); 3504 3507 3505 if (!srcIsThis ) {3508 if (!srcIsThis || from == ReturnFrom::Finally) { 3506 3509 RefPtr<Label> isObjectLabel = newLabel(); 3507 3510 emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get()); … … 3681 3684 } 3682 3685 3683 void BytecodeGenerator::pushFinallyControlFlowScope(StatementNode* finallyBlock)3686 FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label* finallyLabel) 3684 3687 { 3685 3688 // Reclaim free label scopes. … … 3687 3690 m_labelScopes.removeLast(); 3688 3691 3689 ControlFlowScope scope; 3690 scope.isFinallyBlock = true; 3691 FinallyContext context = { 3692 finallyBlock, 3693 nullptr, 3694 nullptr, 3695 static_cast<unsigned>(m_controlFlowScopeStack.size()), 3696 static_cast<unsigned>(m_switchContextStack.size()), 3697 static_cast<unsigned>(m_forInContextStack.size()), 3698 static_cast<unsigned>(m_tryContextStack.size()), 3699 static_cast<unsigned>(m_labelScopes.size()), 3700 static_cast<unsigned>(m_lexicalScopeStack.size()), 3701 m_finallyDepth, 3702 m_localScopeDepth 3703 }; 3704 scope.finallyContext = context; 3705 m_controlFlowScopeStack.append(scope); 3692 ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel, m_finallyDepth)); 3693 m_controlFlowScopeStack.append(WTFMove(scope)); 3694 3706 3695 m_finallyDepth++; 3707 } 3708 3709 void BytecodeGenerator::pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* node) 3710 { 3711 // Reclaim free label scopes. 3712 while (m_labelScopes.size() && !m_labelScopes.last().refCount()) 3713 m_labelScopes.removeLast(); 3714 3715 ControlFlowScope scope; 3716 scope.isFinallyBlock = true; 3717 FinallyContext context = { 3718 nullptr, 3719 iterator, 3720 node, 3721 static_cast<unsigned>(m_controlFlowScopeStack.size()), 3722 static_cast<unsigned>(m_switchContextStack.size()), 3723 static_cast<unsigned>(m_forInContextStack.size()), 3724 static_cast<unsigned>(m_tryContextStack.size()), 3725 static_cast<unsigned>(m_labelScopes.size()), 3726 static_cast<unsigned>(m_lexicalScopeStack.size()), 3727 m_finallyDepth, 3728 m_localScopeDepth 3729 }; 3730 scope.finallyContext = context; 3731 m_controlFlowScopeStack.append(scope); 3732 m_finallyDepth++; 3733 } 3734 3735 void BytecodeGenerator::popFinallyControlFlowScope() 3696 m_currentFinallyContext = &m_controlFlowScopeStack.last().finallyContext; 3697 return m_currentFinallyContext; 3698 } 3699 3700 FinallyContext BytecodeGenerator::popFinallyControlFlowScope() 3736 3701 { 3737 3702 ASSERT(m_controlFlowScopeStack.size()); 3738 ASSERT(m_controlFlowScopeStack.last().isFinallyBlock); 3739 ASSERT(m_controlFlowScopeStack.last().finallyContext.finallyBlock); 3740 ASSERT(!m_controlFlowScopeStack.last().finallyContext.iterator); 3741 ASSERT(!m_controlFlowScopeStack.last().finallyContext.enumerationNode); 3703 ASSERT(m_controlFlowScopeStack.last().isFinallyScope()); 3742 3704 ASSERT(m_finallyDepth > 0); 3743 m_controlFlowScopeStack.removeLast(); 3705 ASSERT(m_currentFinallyContext); 3706 m_currentFinallyContext = m_currentFinallyContext->outerContext(); 3744 3707 m_finallyDepth--; 3745 } 3746 3747 void BytecodeGenerator::popIteratorCloseControlFlowScope() 3748 { 3749 ASSERT(m_controlFlowScopeStack.size()); 3750 ASSERT(m_controlFlowScopeStack.last().isFinallyBlock); 3751 ASSERT(!m_controlFlowScopeStack.last().finallyContext.finallyBlock); 3752 ASSERT(m_controlFlowScopeStack.last().finallyContext.iterator); 3753 ASSERT(m_controlFlowScopeStack.last().finallyContext.enumerationNode); 3754 ASSERT(m_finallyDepth > 0); 3755 m_controlFlowScopeStack.removeLast(); 3756 m_finallyDepth--; 3708 return m_controlFlowScopeStack.takeLast().finallyContext; 3757 3709 } 3758 3710 … … 3854 3806 emitMove(m_topMostScope, scopeRegister()); 3855 3807 } 3856 3857 void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowScope* topScope, ControlFlowScope* bottomScope) 3858 { 3859 while (topScope > bottomScope) { 3860 // First we count the number of dynamic scopes we need to remove to get 3861 // to a finally block. 3862 int numberOfNormalScopes = 0; 3863 while (topScope > bottomScope) { 3864 if (topScope->isFinallyBlock) 3865 break; 3866 ++numberOfNormalScopes; 3867 --topScope; 3868 } 3869 3870 if (numberOfNormalScopes) { 3871 // We need to remove a number of dynamic scopes to get to the next 3872 // finally block 3873 RefPtr<RegisterID> parentScope = newTemporary(); 3874 while (numberOfNormalScopes--) { 3875 parentScope = emitGetParentScope(parentScope.get(), scope); 3876 emitMove(scope, parentScope.get()); 3877 } 3878 3879 // If topScope == bottomScope then there isn't a finally block left to emit. 3880 if (topScope == bottomScope) 3881 return; 3882 } 3883 3884 Vector<ControlFlowScope> savedControlFlowScopeStack; 3885 Vector<SwitchInfo> savedSwitchContextStack; 3886 Vector<RefPtr<ForInContext>> savedForInContextStack; 3887 Vector<TryContext> poppedTryContexts; 3888 Vector<LexicalScopeStackEntry> savedLexicalScopeStack; 3889 LabelScopeStore savedLabelScopes; 3890 while (topScope > bottomScope && topScope->isFinallyBlock) { 3891 RefPtr<Label> beforeFinally = emitLabel(newLabel().get()); 3892 3893 // Save the current state of the world while instating the state of the world 3894 // for the finally block. 3895 FinallyContext finallyContext = topScope->finallyContext; 3896 bool flipScopes = finallyContext.controlFlowScopeStackSize != m_controlFlowScopeStack.size(); 3897 bool flipSwitches = finallyContext.switchContextStackSize != m_switchContextStack.size(); 3898 bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size(); 3899 bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size(); 3900 bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size(); 3901 bool flipLexicalScopeStack = finallyContext.lexicalScopeStackSize != m_lexicalScopeStack.size(); 3902 int topScopeIndex = -1; 3903 int bottomScopeIndex = -1; 3904 if (flipScopes) { 3905 topScopeIndex = topScope - m_controlFlowScopeStack.begin(); 3906 bottomScopeIndex = bottomScope - m_controlFlowScopeStack.begin(); 3907 savedControlFlowScopeStack = m_controlFlowScopeStack; 3908 m_controlFlowScopeStack.shrink(finallyContext.controlFlowScopeStackSize); 3909 } 3910 if (flipSwitches) { 3911 savedSwitchContextStack = m_switchContextStack; 3912 m_switchContextStack.shrink(finallyContext.switchContextStackSize); 3913 } 3914 if (flipForIns) { 3915 savedForInContextStack = m_forInContextStack; 3916 m_forInContextStack.shrink(finallyContext.forInContextStackSize); 3917 } 3918 if (flipTries) { 3919 while (m_tryContextStack.size() != finallyContext.tryContextStackSize) { 3920 ASSERT(m_tryContextStack.size() > finallyContext.tryContextStackSize); 3921 TryContext context = m_tryContextStack.takeLast(); 3922 TryRange range; 3923 range.start = context.start; 3924 range.end = beforeFinally; 3925 range.tryData = context.tryData; 3926 m_tryRanges.append(range); 3927 poppedTryContexts.append(context); 3928 } 3929 } 3930 if (flipLabelScopes) { 3931 savedLabelScopes = m_labelScopes; 3932 while (m_labelScopes.size() > finallyContext.labelScopesSize) 3933 m_labelScopes.removeLast(); 3934 } 3935 if (flipLexicalScopeStack) { 3936 savedLexicalScopeStack = m_lexicalScopeStack; 3937 m_lexicalScopeStack.shrink(finallyContext.lexicalScopeStackSize); 3938 } 3939 int savedFinallyDepth = m_finallyDepth; 3940 m_finallyDepth = finallyContext.finallyDepth; 3941 int savedDynamicScopeDepth = m_localScopeDepth; 3942 m_localScopeDepth = finallyContext.dynamicScopeDepth; 3943 3944 if (finallyContext.finallyBlock) { 3945 // Emit the finally block. 3946 emitNode(finallyContext.finallyBlock); 3947 } else { 3948 // Emit the IteratorClose block. 3949 ASSERT(finallyContext.iterator); 3950 emitIteratorClose(finallyContext.iterator, finallyContext.enumerationNode); 3951 } 3952 3953 RefPtr<Label> afterFinally = emitLabel(newLabel().get()); 3954 3955 // Restore the state of the world. 3956 if (flipScopes) { 3957 m_controlFlowScopeStack = savedControlFlowScopeStack; 3958 topScope = &m_controlFlowScopeStack[topScopeIndex]; // assert it's within bounds 3959 bottomScope = m_controlFlowScopeStack.begin() + bottomScopeIndex; // don't assert, since it the index might be -1. 3960 } 3961 if (flipSwitches) 3962 m_switchContextStack = savedSwitchContextStack; 3963 if (flipForIns) 3964 m_forInContextStack = savedForInContextStack; 3965 if (flipTries) { 3966 ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize); 3967 for (unsigned i = poppedTryContexts.size(); i--;) { 3968 TryContext context = poppedTryContexts[i]; 3969 context.start = afterFinally; 3970 m_tryContextStack.append(context); 3971 } 3972 poppedTryContexts.clear(); 3973 } 3974 if (flipLabelScopes) 3975 m_labelScopes = savedLabelScopes; 3976 if (flipLexicalScopeStack) 3977 m_lexicalScopeStack = savedLexicalScopeStack; 3978 m_finallyDepth = savedFinallyDepth; 3979 m_localScopeDepth = savedDynamicScopeDepth; 3980 3981 --topScope; 3982 } 3983 } 3984 } 3985 3986 void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth) 3987 { 3988 ASSERT(labelScopeDepth() - targetScopeDepth >= 0); 3989 3990 size_t scopeDelta = labelScopeDepth() - targetScopeDepth; 3991 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 3992 if (!scopeDelta) 3993 return; 3994 3995 if (!m_finallyDepth) { 3996 RefPtr<RegisterID> parentScope = newTemporary(); 3997 while (scopeDelta--) { 3998 parentScope = emitGetParentScope(parentScope.get(), scope); 3999 emitMove(scope, parentScope.get()); 4000 } 4001 return; 4002 } 4003 4004 emitComplexPopScopes(scope, &m_controlFlowScopeStack.last(), &m_controlFlowScopeStack.last() - scopeDelta); 4005 } 4006 4007 TryData* BytecodeGenerator::pushTry(Label* start) 3808 3809 TryData* BytecodeGenerator::pushTry(Label* start, Label* handlerLabel, HandlerType handlerType) 4008 3810 { 4009 3811 TryData tryData; 4010 tryData.target = newLabel();4011 tryData.handlerType = HandlerType::Illegal;3812 tryData.target = handlerLabel; 3813 tryData.handlerType = handlerType; 4012 3814 m_tryData.append(tryData); 4013 3815 TryData* result = &m_tryData.last(); … … 4022 3824 } 4023 3825 4024 void BytecodeGenerator::popTry AndEmitCatch(TryData* tryData, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType handlerType)3826 void BytecodeGenerator::popTry(TryData* tryData, Label* end) 4025 3827 { 4026 3828 m_usesExceptions = true; … … 4034 3836 m_tryRanges.append(tryRange); 4035 3837 m_tryContextStack.removeLast(); 4036 4037 emitLabel(tryRange.tryData->target.get()); 4038 tryRange.tryData->handlerType = handlerType; 4039 3838 } 3839 3840 void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister) 3841 { 4040 3842 emitOpcode(op_catch); 4041 3843 instructions().append(exceptionRegister->index()); 4042 3844 instructions().append(thrownValueRegister->index()); 4043 4044 bool foundLocalScope = false; 4045 for (unsigned i = m_lexicalScopeStack.size(); i--; ) { 4046 // Note that if we don't find a local scope in the current function/program, 4047 // we must grab the outer-most scope of this bytecode generation. 4048 if (m_lexicalScopeStack[i].m_scope) { 4049 foundLocalScope = true; 4050 emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope); 4051 break; 4052 } 4053 } 4054 if (!foundLocalScope) 4055 emitMove(scopeRegister(), m_topMostScope); 3845 } 3846 3847 void BytecodeGenerator::restoreScopeRegister(int lexicalScopeIndex) 3848 { 3849 if (lexicalScopeIndex == CurrentLexicalScopeIndex) 3850 return; // No change needed. 3851 3852 if (lexicalScopeIndex != OutermostLexicalScopeIndex) { 3853 ASSERT(lexicalScopeIndex < static_cast<int>(m_lexicalScopeStack.size())); 3854 int endIndex = lexicalScopeIndex + 1; 3855 for (size_t i = endIndex; i--; ) { 3856 if (m_lexicalScopeStack[i].m_scope) { 3857 emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope); 3858 return; 3859 } 3860 } 3861 } 3862 // Note that if we don't find a local scope in the current function/program, 3863 // we must grab the outer-most scope of this bytecode generation. 3864 emitMove(scopeRegister(), m_topMostScope); 3865 } 3866 3867 void BytecodeGenerator::restoreScopeRegister() 3868 { 3869 restoreScopeRegister(currentLexicalScopeIndex()); 3870 } 3871 3872 int BytecodeGenerator::labelScopeDepthToLexicalScopeIndex(int targetLabelScopeDepth) 3873 { 3874 ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0); 3875 size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth; 3876 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 3877 if (!scopeDelta) 3878 return CurrentLexicalScopeIndex; 3879 3880 ControlFlowScope& targetScope = m_controlFlowScopeStack[targetLabelScopeDepth]; 3881 return targetScope.lexicalScopeIndex; 4056 3882 } 4057 3883 … … 4062 3888 4063 3889 int BytecodeGenerator::labelScopeDepth() const 4064 { 4065 return localScopeDepth() + m_finallyDepth; 3890 { 3891 int depth = localScopeDepth() + m_finallyDepth; 3892 ASSERT(depth == static_cast<int>(m_controlFlowScopeStack.size())); 3893 return depth; 4066 3894 } 4067 3895 … … 4133 3961 void BytecodeGenerator::pushLocalControlFlowScope() 4134 3962 { 4135 ControlFlowScope scope; 4136 scope.isFinallyBlock = false; 4137 m_controlFlowScopeStack.append(scope); 3963 ControlFlowScope scope(ControlFlowScope::Label, currentLexicalScopeIndex()); 3964 m_controlFlowScopeStack.append(WTFMove(scope)); 4138 3965 m_localScopeDepth++; 4139 3966 } … … 4142 3969 { 4143 3970 ASSERT(m_controlFlowScopeStack.size()); 4144 ASSERT(!m_controlFlowScopeStack.last().isFinally Block);3971 ASSERT(!m_controlFlowScopeStack.last().isFinallyScope()); 4145 3972 m_controlFlowScopeStack.removeLast(); 4146 3973 m_localScopeDepth--; … … 4297 4124 return false; 4298 4125 } 4299 4126 4300 4127 void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* forLoopNode, RegisterID* forLoopSymbolTable) 4301 4128 { 4129 FinallyRegistersScope finallyRegistersScope(*this); 4130 4302 4131 RefPtr<RegisterID> subject = newTemporary(); 4303 4132 emitNode(subject.get(), subjectNode); … … 4310 4139 4311 4140 RefPtr<Label> loopDone = newLabel(); 4141 RefPtr<Label> tryStartLabel = newLabel(); 4142 RefPtr<Label> finallyViaThrowLabel = newLabel(); 4143 RefPtr<Label> finallyLabel = newLabel(); 4144 RefPtr<Label> catchLabel = newLabel(); 4145 RefPtr<Label> endCatchLabel = newLabel(); 4146 4312 4147 // RefPtr<Register> iterator's lifetime must be longer than IteratorCloseContext. 4313 pushIteratorCloseControlFlowScope(iterator.get(), node); 4148 FinallyContext* finallyContext = pushFinallyControlFlowScope(finallyLabel.get()); 4149 4314 4150 { 4315 4151 LabelScopePtr scope = newLabelScope(LabelScope::Loop); … … 4323 4159 emitLoopHint(); 4324 4160 4325 RefPtr<Label> tryStartLabel = newLabel();4326 4161 emitLabel(tryStartLabel.get()); 4327 TryData* tryData = pushTry(tryStartLabel.get() );4162 TryData* tryData = pushTry(tryStartLabel.get(), finallyViaThrowLabel.get(), HandlerType::SynthesizedFinally); 4328 4163 callBack(*this, value.get()); 4329 4164 emitJump(scope->continueTarget()); 4330 4165 4331 // IteratorClose sequence for throw-ed control flow.4166 // IteratorClose sequence for abrupt completions. 4332 4167 { 4333 RefPtr<Label> catchHere = emitLabel(newLabel().get()); 4334 RefPtr<RegisterID> exceptionRegister = newTemporary(); 4335 RefPtr<RegisterID> thrownValueRegister = newTemporary(); 4336 popTryAndEmitCatch(tryData, exceptionRegister.get(), 4337 thrownValueRegister.get(), catchHere.get(), HandlerType::SynthesizedFinally); 4338 4339 RefPtr<Label> catchDone = newLabel(); 4168 // Finally block for the enumeration. 4169 emitLabel(finallyViaThrowLabel.get()); 4170 popTry(tryData, finallyViaThrowLabel.get()); 4171 4172 RegisterID* unused = newTemporary(); 4173 emitCatch(finallyActionRegister(), unused); 4174 // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw. 4175 4176 emitLabel(finallyLabel.get()); 4177 restoreScopeRegister(); 4178 4179 RefPtr<Label> finallyDone = newLabel(); 4340 4180 4341 4181 RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword); 4342 emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), catchDone.get()); 4182 emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), finallyDone.get()); 4183 4184 RefPtr<RegisterID> originalFinallyActionRegister = newTemporary(); 4185 emitMove(originalFinallyActionRegister.get(), finallyActionRegister()); 4343 4186 4344 4187 RefPtr<Label> returnCallTryStart = newLabel(); 4345 4188 emitLabel(returnCallTryStart.get()); 4346 TryData* returnCallTryData = pushTry(returnCallTryStart.get() );4189 TryData* returnCallTryData = pushTry(returnCallTryStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch); 4347 4190 4348 4191 CallArguments returnArguments(*this, nullptr); 4349 4192 emitMove(returnArguments.thisRegister(), iterator.get()); 4350 4193 emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd(), DebuggableCall::No); 4351 4352 emitLabel(catchDone.get()); 4353 emitThrow(exceptionRegister.get()); 4354 4355 // Absorb exception. 4356 popTryAndEmitCatch(returnCallTryData, newTemporary(), 4357 newTemporary(), catchDone.get(), HandlerType::SynthesizedFinally); 4358 emitThrow(exceptionRegister.get()); 4194 emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), finallyDone.get()); 4195 emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); 4196 4197 emitLabel(finallyDone.get()); 4198 emitFinallyCompletion(*finallyContext, endCatchLabel.get()); 4199 4200 popTry(returnCallTryData, finallyDone.get()); 4201 4202 // Catch block for exceptions that may be thrown while calling the return 4203 // handler in the enumeration finally block. The only reason we need this 4204 // catch block is because if entered the above finally block due to a thrown 4205 // exception, then we want to re-throw the original exception on exiting 4206 // the finally block. Otherwise, we'll let any new exception pass through. 4207 { 4208 emitLabel(catchLabel.get()); 4209 RefPtr<RegisterID> exceptionRegister = newTemporary(); 4210 RegisterID* unused = newTemporary(); 4211 emitCatch(exceptionRegister.get(), unused); 4212 restoreScopeRegister(); 4213 4214 RefPtr<Label> throwLabel = newLabel(); 4215 emitJumpIfCompletionTypeIsThrow(originalFinallyActionRegister.get(), throwLabel.get()); 4216 emitMove(originalFinallyActionRegister.get(), exceptionRegister.get()); 4217 4218 emitLabel(throwLabel.get()); 4219 emitThrow(originalFinallyActionRegister.get()); 4220 4221 emitLabel(endCatchLabel.get()); 4222 } 4359 4223 } 4360 4224 … … 4377 4241 4378 4242 // IteratorClose sequence for break-ed control flow. 4379 pop IteratorCloseControlFlowScope();4243 popFinallyControlFlowScope(); 4380 4244 emitIteratorClose(iterator.get(), node); 4381 4245 emitLabel(loopDone.get()); … … 4493 4357 { 4494 4358 emitOpcode(op_is_object); 4359 instructions().append(dst->index()); 4360 instructions().append(src->index()); 4361 return dst; 4362 } 4363 4364 RegisterID* BytecodeGenerator::emitIsNumber(RegisterID* dst, RegisterID* src) 4365 { 4366 emitOpcode(op_is_number); 4495 4367 instructions().append(dst->index()); 4496 4368 instructions().append(src->index()); … … 4785 4657 { 4786 4658 RefPtr<RegisterID> returnRegister = generatorValueRegister(); 4787 if (isInFinallyBlock()) { 4788 returnRegister = emitMove(newTemporary(), returnRegister.get()); 4789 emitPopScopes(scopeRegister(), 0); 4790 } 4791 emitReturn(returnRegister.get()); 4659 bool hasFinally = emitReturnViaFinallyIfNeeded(returnRegister.get()); 4660 if (!hasFinally) 4661 emitReturn(returnRegister.get()); 4792 4662 } 4793 4663 … … 4892 4762 4893 4763 emitLabel(returnSequence.get()); 4894 if (isInFinallyBlock())4895 emitPopScopes(scopeRegister(), 0);4896 emitReturn(value.get());4764 bool hasFinally = emitReturnViaFinallyIfNeeded(value.get()); 4765 if (!hasFinally) 4766 emitReturn(value.get()); 4897 4767 } 4898 4768 … … 4922 4792 RegisterID* completedState = emitLoad(nullptr, jsNumber(state)); 4923 4793 emitPutById(generatorRegister(), propertyNames().builtinNames().generatorStatePrivateName(), completedState); 4794 } 4795 4796 bool BytecodeGenerator::emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget) 4797 { 4798 ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0); 4799 size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth; 4800 ASSERT(scopeDelta <= m_controlFlowScopeStack.size()); 4801 if (!scopeDelta) 4802 return nullptr; // No finallys to thread through. 4803 4804 ControlFlowScope* topScope = &m_controlFlowScopeStack.last(); 4805 ControlFlowScope* bottomScope = &m_controlFlowScopeStack.last() - scopeDelta; 4806 4807 FinallyContext* innermostFinallyContext = nullptr; 4808 FinallyContext* outermostFinallyContext = nullptr; 4809 while (topScope > bottomScope) { 4810 if (topScope->isFinallyScope()) { 4811 FinallyContext* finallyContext = &topScope->finallyContext; 4812 if (!innermostFinallyContext) 4813 innermostFinallyContext = finallyContext; 4814 outermostFinallyContext = finallyContext; 4815 finallyContext->incNumberOfBreaksOrContinues(); 4816 } 4817 --topScope; 4818 } 4819 if (!outermostFinallyContext) 4820 return false; // No finallys to thread through. 4821 4822 int jumpID = bytecodeOffsetToJumpID(instructions().size()); 4823 int lexicalScopeIndex = labelScopeDepthToLexicalScopeIndex(targetLabelScopeDepth); 4824 outermostFinallyContext->registerJump(jumpID, lexicalScopeIndex, jumpTarget); 4825 4826 emitSetFinallyActionToJumpID(jumpID); 4827 emitJump(innermostFinallyContext->finallyLabel()); 4828 return true; // We'll be jumping to a finally block. 4829 } 4830 4831 bool BytecodeGenerator::emitReturnViaFinallyIfNeeded(RegisterID* returnRegister) 4832 { 4833 if (!m_controlFlowScopeStack.size()) 4834 return false; // No finallys to thread through. 4835 4836 ControlFlowScope* topScope = &m_controlFlowScopeStack.last(); 4837 ControlFlowScope* bottomScope = &m_controlFlowScopeStack.first(); 4838 4839 FinallyContext* innermostFinallyContext = nullptr; 4840 while (topScope >= bottomScope) { 4841 if (topScope->isFinallyScope()) { 4842 FinallyContext* finallyContext = &topScope->finallyContext; 4843 if (!innermostFinallyContext) 4844 innermostFinallyContext = finallyContext; 4845 finallyContext->setHandlesReturns(); 4846 } 4847 --topScope; 4848 } 4849 if (!innermostFinallyContext) 4850 return false; // No finallys to thread through. 4851 4852 emitSetFinallyActionToReturnCompletion(); 4853 emitSetFinallyReturnValueRegister(returnRegister); 4854 emitJump(innermostFinallyContext->finallyLabel()); 4855 return true; // We'll be jumping to a finally block. 4856 } 4857 4858 void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, Label* normalCompletionLabel) 4859 { 4860 // FIXME: switch the finallyActionRegister to only store int values for all CompletionTypes. This is more optimal for JIT type speculation. 4861 // https://bugs.webkit.org/show_bug.cgi?id=165979 4862 emitJumpIfFinallyActionIsNormalCompletion(normalCompletionLabel); 4863 4864 if (context.numberOfBreaksOrContinues() || context.handlesReturns()) { 4865 FinallyContext* outerContext = context.outerContext(); 4866 if (outerContext) { 4867 // We are not the outermost finally. 4868 size_t numberOfJumps = context.numberOfJumps(); 4869 for (size_t i = 0; i < numberOfJumps; i++) { 4870 RefPtr<Label> nextLabel = newLabel(); 4871 auto& jump = context.jumps(i); 4872 emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get()); 4873 4874 restoreScopeRegister(jump.targetLexicalScopeIndex); 4875 emitSetFinallyActionToNormalCompletion(); 4876 emitJump(jump.targetLabel.get()); 4877 4878 emitLabel(nextLabel.get()); 4879 } 4880 4881 bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() > numberOfJumps; 4882 if (hasBreaksOrContinuesNotCoveredByJumps || context.handlesReturns()) 4883 emitJumpIfFinallyActionIsNotThrowCompletion(outerContext->finallyLabel()); 4884 4885 } else { 4886 // We are the outermost finally. 4887 size_t numberOfJumps = context.numberOfJumps(); 4888 ASSERT(numberOfJumps == context.numberOfBreaksOrContinues()); 4889 4890 for (size_t i = 0; i < numberOfJumps; i++) { 4891 RefPtr<Label> nextLabel = newLabel(); 4892 auto& jump = context.jumps(i); 4893 emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get()); 4894 4895 restoreScopeRegister(jump.targetLexicalScopeIndex); 4896 emitSetFinallyActionToNormalCompletion(); 4897 emitJump(jump.targetLabel.get()); 4898 4899 emitLabel(nextLabel.get()); 4900 } 4901 4902 if (context.handlesReturns()) { 4903 RefPtr<Label> notReturnLabel = newLabel(); 4904 emitJumpIfFinallyActionIsNotReturnCompletion(notReturnLabel.get()); 4905 4906 emitWillLeaveCallFrameDebugHook(); 4907 emitReturn(finallyReturnValueRegister(), ReturnFrom::Finally); 4908 4909 emitLabel(notReturnLabel.get()); 4910 } 4911 } 4912 } 4913 emitThrow(finallyActionRegister()); 4914 } 4915 4916 bool BytecodeGenerator::allocateFinallyRegisters() 4917 { 4918 if (m_finallyActionRegister) 4919 return false; 4920 4921 ASSERT(!m_finallyReturnValueRegister); 4922 m_finallyActionRegister = newTemporary(); 4923 m_finallyReturnValueRegister = newTemporary(); 4924 4925 emitSetFinallyActionToNormalCompletion(); 4926 emitMoveEmptyValue(m_finallyReturnValueRegister.get()); 4927 return true; 4928 } 4929 4930 void BytecodeGenerator::releaseFinallyRegisters() 4931 { 4932 ASSERT(m_finallyActionRegister && m_finallyReturnValueRegister); 4933 m_finallyActionRegister = nullptr; 4934 m_finallyReturnValueRegister = nullptr; 4935 } 4936 4937 void BytecodeGenerator::emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget) 4938 { 4939 RefPtr<RegisterID> tempRegister = newTemporary(); 4940 RegisterID* valueConstant = addConstantValue(JSValue(value)); 4941 OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType()); 4942 4943 auto equivalenceResult = emitBinaryOp(compareOpcode, tempRegister.get(), valueConstant, finallyActionRegister(), operandTypes); 4944 emitJumpIfTrue(equivalenceResult, jumpTarget); 4924 4945 } 4925 4946 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r209871 r209952 81 81 }; 82 82 83 struct FinallyJump { 84 FinallyJump(int jumpID, int targetLexicalScopeIndex, Label* targetLabel) 85 : jumpID(jumpID) 86 , targetLexicalScopeIndex(targetLexicalScopeIndex) 87 , targetLabel(targetLabel) 88 { } 89 90 int jumpID { 0 }; 91 int targetLexicalScopeIndex { 0 }; 92 RefPtr<Label> targetLabel; 93 }; 94 83 95 struct FinallyContext { 84 StatementNode* finallyBlock; 85 RegisterID* iterator; 86 ThrowableExpressionData* enumerationNode; 87 unsigned controlFlowScopeStackSize; 88 unsigned switchContextStackSize; 89 unsigned forInContextStackSize; 90 unsigned tryContextStackSize; 91 unsigned labelScopesSize; 92 unsigned lexicalScopeStackSize; 93 int finallyDepth; 94 int dynamicScopeDepth; 96 FinallyContext() { } 97 FinallyContext(FinallyContext* outerContext, Label* finallyLabel, int finallyDepth) 98 : m_outerContext(outerContext) 99 , m_finallyLabel(finallyLabel) 100 , m_finallyDepth(finallyDepth) 101 { 102 ASSERT(!m_jumps || m_jumps->isEmpty()); 103 } 104 105 FinallyContext* outerContext() const { return m_outerContext; } 106 Label* finallyLabel() const { return m_finallyLabel; } 107 int depth() const { return m_finallyDepth; } 108 109 unsigned numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues; } 110 void incNumberOfBreaksOrContinues() 111 { 112 ASSERT(m_numberOfBreaksOrContinues < INT_MAX); 113 m_numberOfBreaksOrContinues++; 114 } 115 116 bool handlesReturns() const { return m_handlesReturns; } 117 void setHandlesReturns() { m_handlesReturns = true; } 118 119 void registerJump(int jumpID, int lexicalScopeIndex, Label* targetLabel) 120 { 121 if (!m_jumps) 122 m_jumps = std::make_unique<Vector<FinallyJump>>(); 123 m_jumps->append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel)); 124 } 125 126 size_t numberOfJumps() const { return m_jumps ? m_jumps->size() : 0; } 127 FinallyJump& jumps(size_t i) { return (*m_jumps)[i]; } 128 129 private: 130 FinallyContext* m_outerContext { nullptr }; 131 Label* m_finallyLabel { nullptr }; 132 int m_finallyDepth { 0 }; 133 unsigned m_numberOfBreaksOrContinues { 0 }; 134 bool m_handlesReturns { false }; 135 std::unique_ptr<Vector<FinallyJump>> m_jumps; 95 136 }; 96 137 97 138 struct ControlFlowScope { 98 bool isFinallyBlock; 139 typedef uint8_t Type; 140 enum { 141 Label, 142 Finally 143 }; 144 ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&& finallyContext = FinallyContext()) 145 : type(type) 146 , lexicalScopeIndex(lexicalScopeIndex) 147 , finallyContext(std::forward<FinallyContext>(finallyContext)) 148 { } 149 150 bool isLabelScope() const { return type == Label; } 151 bool isFinallyScope() const { return type == Finally; } 152 153 Type type; 154 int lexicalScopeIndex; 99 155 FinallyContext finallyContext; 100 156 }; … … 606 662 RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*); 607 663 608 RegisterID* emitReturn(RegisterID* src); 664 enum class ReturnFrom { Normal, Finally }; 665 RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal); 609 666 RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } 610 667 … … 627 684 PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); 628 685 PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); 629 void emitPopScopes(RegisterID* srcDst, int targetScopeDepth);630 686 631 687 void emitEnter(); … … 650 706 RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); } 651 707 RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); 708 RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src); 652 709 RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); 653 710 RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src); … … 664 721 665 722 // Start a try block. 'start' must have been emitted. 666 TryData* pushTry(Label* start );723 TryData* pushTry(Label* start, Label* handlerLabel, HandlerType); 667 724 // End a try block. 'end' must have been emitted. 668 void popTryAndEmitCatch(TryData*, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType); 725 void popTry(TryData*, Label* end); 726 void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister); 727 728 private: 729 static const int CurrentLexicalScopeIndex = -2; 730 static const int OutermostLexicalScopeIndex = -1; 731 732 public: 733 void restoreScopeRegister(); 734 void restoreScopeRegister(int lexicalScopeIndex); 735 736 int currentLexicalScopeIndex() const 737 { 738 int size = static_cast<int>(m_lexicalScopeStack.size()); 739 ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size()); 740 ASSERT(size >= 0); 741 int index = size - 1; 742 return index; 743 } 744 745 int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth); 669 746 670 747 void emitThrow(RegisterID* exc) … … 699 776 void emitWillLeaveCallFrameDebugHook(); 700 777 701 bool isInFinallyBlock() { return m_finallyDepth > 0; } 702 703 void pushFinallyControlFlowScope(StatementNode* finallyBlock); 704 void popFinallyControlFlowScope(); 705 void pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* enumerationNode); 706 void popIteratorCloseControlFlowScope(); 778 class FinallyRegistersScope { 779 public: 780 FinallyRegistersScope(BytecodeGenerator& generator, bool needFinallyRegisters = true) 781 : m_generator(generator) 782 { 783 if (needFinallyRegisters && m_generator.allocateFinallyRegisters()) 784 m_needToReleaseOnDestruction = true; 785 } 786 ~FinallyRegistersScope() 787 { 788 if (m_needToReleaseOnDestruction) 789 m_generator.releaseFinallyRegisters(); 790 } 791 792 private: 793 BytecodeGenerator& m_generator; 794 bool m_needToReleaseOnDestruction { false }; 795 }; 796 797 RegisterID* finallyActionRegister() const 798 { 799 ASSERT(m_finallyActionRegister); 800 return m_finallyActionRegister.get(); 801 } 802 RegisterID* finallyReturnValueRegister() const 803 { 804 ASSERT(m_finallyReturnValueRegister); 805 return m_finallyReturnValueRegister.get(); 806 } 807 808 void emitSetFinallyActionToNormalCompletion() 809 { 810 emitMoveEmptyValue(m_finallyActionRegister.get()); 811 } 812 void emitSetFinallyActionToReturnCompletion() 813 { 814 emitLoad(finallyActionRegister(), JSValue(static_cast<int>(CompletionType::Return))); 815 } 816 void emitSetFinallyActionToJumpID(int jumpID) 817 { 818 emitLoad(finallyActionRegister(), JSValue(jumpID)); 819 } 820 void emitSetFinallyReturnValueRegister(RegisterID* reg) 821 { 822 emitMove(finallyReturnValueRegister(), reg); 823 } 824 825 void emitJumpIfFinallyActionIsNormalCompletion(Label* jumpTarget) 826 { 827 emitJumpIfTrue(emitIsEmpty(newTemporary(), finallyActionRegister()), jumpTarget); 828 } 829 830 void emitJumpIfFinallyActionIsNotJump(int jumpID, Label* jumpTarget) 831 { 832 emitCompareFinallyActionAndJumpIf(op_nstricteq, jumpID, jumpTarget); 833 } 834 835 void emitJumpIfFinallyActionIsReturnCompletion(Label* jumpTarget) 836 { 837 emitCompareFinallyActionAndJumpIf(op_stricteq, static_cast<int>(CompletionType::Return), jumpTarget); 838 } 839 void emitJumpIfFinallyActionIsNotReturnCompletion(Label* jumpTarget) 840 { 841 emitCompareFinallyActionAndJumpIf(op_nstricteq, static_cast<int>(CompletionType::Return), jumpTarget); 842 } 843 844 void emitJumpIfFinallyActionIsNotThrowCompletion(Label* jumpTarget) 845 { 846 emitJumpIfTrue(emitIsNumber(newTemporary(), finallyActionRegister()), jumpTarget); 847 } 848 void emitJumpIfCompletionTypeIsThrow(RegisterID* reg, Label* jumpTarget) 849 { 850 emitJumpIfFalse(emitIsNumber(newTemporary(), reg), jumpTarget); 851 } 852 853 bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget); 854 bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister); 855 void emitFinallyCompletion(FinallyContext&, Label* normalCompletionLabel); 856 857 private: 858 void emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget); 859 860 int bytecodeOffsetToJumpID(unsigned offset) 861 { 862 int jumpID = offset + static_cast<int>(CompletionType::NumberOfTypes); 863 ASSERT(jumpID >= static_cast<int>(CompletionType::NumberOfTypes)); 864 return jumpID; 865 } 866 867 bool allocateFinallyRegisters(); 868 void releaseFinallyRegisters(); 869 870 public: 871 FinallyContext* pushFinallyControlFlowScope(Label* finallyLabel); 872 FinallyContext popFinallyControlFlowScope(); 707 873 708 874 void pushIndexedForInScope(RegisterID* local, RegisterID* index); … … 798 964 void allocateCalleeSaveSpace(); 799 965 void allocateAndEmitScope(); 800 void emitComplexPopScopes(RegisterID*, ControlFlowScope* topScope, ControlFlowScope* bottomScope);801 966 802 967 typedef HashMap<double, JSValue> NumberMap; … … 936 1101 RegisterID* m_promiseCapabilityRegister { nullptr }; 937 1102 1103 // The spec at https://tc39.github.io/ecma262/#sec-completion-record-specification-type says 1104 // that there are 5 types of completions. Conceptually, we'll set m_finallyActionRegister 1105 // to one of these completion types. However, to optimize our implementation, we'll encode 1106 // these type info as follows: 1107 // 1108 // CompletionType::Normal - m_finallyActionRegister is empty. 1109 // CompletionType::Break - m_finallyActionRegister is an int JSValue jumpID. 1110 // CompletionType::Continue - m_finallyActionRegister is an int JSValue jumpID. 1111 // CompletionType::Return - m_finallyActionRegister is the Return enum as an int JSValue. 1112 // CompletionType::Throw - m_finallyActionRegister is the Exception object to rethrow. 1113 // 1114 // Hence, of the 5 completion types, only the CompletionType::Return enum value is used in 1115 // our implementation. The rest are just provided for completeness. 1116 1117 enum class CompletionType : int { 1118 Normal, 1119 Break, 1120 Continue, 1121 Return, 1122 Throw, 1123 1124 NumberOfTypes 1125 }; 1126 1127 RefPtr<RegisterID> m_finallyActionRegister; 1128 RefPtr<RegisterID> m_finallyReturnValueRegister; 1129 1130 FinallyContext* m_currentFinallyContext { nullptr }; 1131 938 1132 SegmentedVector<RegisterID*, 16> m_localRegistersForCalleeSaveRegisters; 939 1133 SegmentedVector<RegisterID, 32> m_constantPoolRegisters; … … 950 1144 void popLocalControlFlowScope(); 951 1145 952 Vector<ControlFlowScope, 0, UnsafeVectorOverflow> m_controlFlowScopeStack; 1146 // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it. 1147 // https://bugs.webkit.org/show_bug.cgi?id=165980 1148 SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack; 953 1149 Vector<SwitchInfo> m_switchContextStack; 954 1150 Vector<RefPtr<ForInContext>> m_forInContextStack; -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r209728 r209952 2999 2999 ASSERT(scope); 3000 3000 3001 generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); 3002 generator.emitJump(scope->continueTarget()); 3001 bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->continueTarget()); 3002 if (!hasFinally) { 3003 int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth()); 3004 generator.restoreScopeRegister(lexicalScopeIndex); 3005 generator.emitJump(scope->continueTarget()); 3006 } 3003 3007 3004 3008 generator.emitProfileControlFlow(endOffset()); … … 3026 3030 ASSERT(scope); 3027 3031 3028 generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); 3029 generator.emitJump(scope->breakTarget()); 3032 bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->breakTarget()); 3033 if (!hasFinally) { 3034 int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth()); 3035 generator.restoreScopeRegister(lexicalScopeIndex); 3036 generator.emitJump(scope->breakTarget()); 3037 } 3030 3038 3031 3039 generator.emitProfileControlFlow(endOffset()); … … 3044 3052 3045 3053 generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, divotStart(), divotEnd()); 3046 if (generator.isInFinallyBlock()) { 3047 returnRegister = generator.emitMove(generator.newTemporary(),returnRegister.get());3048 generator.emitPopScopes(generator.scopeRegister(), 0);3049 }3050 3051 generator.emitWillLeaveCallFrameDebugHook();3052 generator.emitReturn(returnRegister.get()); 3053 generator.emitProfileControlFlow(endOffset()); 3054 3055 bool hasFinally = generator.emitReturnViaFinallyIfNeeded(returnRegister.get()); 3056 if (!hasFinally) { 3057 generator.emitWillLeaveCallFrameDebugHook(); 3058 generator.emitReturn(returnRegister.get()); 3059 } 3060 3061 generator.emitProfileControlFlow(endOffset()); 3054 3062 // Emitting an unreachable return here is needed in case this op_profile_control_flow is the 3055 3063 // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode. … … 3280 3288 3281 3289 ASSERT(m_catchBlock || m_finallyBlock); 3290 BytecodeGenerator::FinallyRegistersScope finallyRegistersScope(generator, m_finallyBlock); 3291 3292 RefPtr<Label> catchLabel; 3293 RefPtr<Label> catchEndLabel; 3294 RefPtr<Label> finallyViaThrowLabel; 3295 RefPtr<Label> finallyLabel; 3296 RefPtr<Label> finallyEndLabel; 3282 3297 3283 3298 RefPtr<Label> tryStartLabel = generator.newLabel(); 3284 3299 generator.emitLabel(tryStartLabel.get()); 3285 3300 3301 if (m_finallyBlock) { 3302 finallyViaThrowLabel = generator.newLabel(); 3303 finallyLabel = generator.newLabel(); 3304 finallyEndLabel = generator.newLabel(); 3305 3306 generator.pushFinallyControlFlowScope(finallyLabel.get()); 3307 } 3308 if (m_catchBlock) { 3309 catchLabel = generator.newLabel(); 3310 catchEndLabel = generator.newLabel(); 3311 } 3312 3313 Label* tryHandlerLabel = m_catchBlock ? catchLabel.get() : finallyViaThrowLabel.get(); 3314 HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally; 3315 TryData* tryData = generator.pushTry(tryStartLabel.get(), tryHandlerLabel, tryHandlerType); 3316 3317 generator.emitNode(dst, m_tryBlock); 3318 3319 // The finallyActionRegister is an empty value by default, which implies CompletionType::Normal. 3286 3320 if (m_finallyBlock) 3287 generator.pushFinallyControlFlowScope(m_finallyBlock); 3288 TryData* tryData = generator.pushTry(tryStartLabel.get()); 3289 3290 generator.emitNode(dst, m_tryBlock); 3321 generator.emitJump(finallyLabel.get()); 3322 else 3323 generator.emitJump(catchEndLabel.get()); 3324 3325 RefPtr<Label> endTryLabel = generator.emitLabel(generator.newLabel().get()); 3326 generator.popTry(tryData, endTryLabel.get()); 3291 3327 3292 3328 if (m_catchBlock) { 3293 RefPtr<Label> catchEndLabel = generator.newLabel();3294 3295 // Normal path: jump over the catch block.3296 generator.emitJump(catchEndLabel.get());3297 3298 3329 // Uncaught exception path: the catch block. 3299 RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); 3300 RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); 3330 generator.emitLabel(catchLabel.get()); 3301 3331 RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); 3302 generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), here.get(), HandlerType::Catch); 3303 3332 RegisterID* unused = generator.newTemporary(); 3333 generator.emitCatch(unused, thrownValueRegister.get()); 3334 generator.restoreScopeRegister(); 3335 3336 TryData* tryData = nullptr; 3304 3337 if (m_finallyBlock) { 3305 3338 // If the catch block throws an exception and we have a finally block, then the finally 3306 3339 // block should "catch" that exception. 3307 tryData = generator.pushTry( here.get());3340 tryData = generator.pushTry(catchLabel.get(), finallyViaThrowLabel.get(), HandlerType::Finally); 3308 3341 } 3309 3342 … … 3317 3350 generator.emitLoad(thrownValueRegister.get(), jsUndefined()); 3318 3351 generator.emitPopCatchScope(m_lexicalVariables); 3352 3353 if (m_finallyBlock) { 3354 generator.emitSetFinallyActionToNormalCompletion(); 3355 generator.emitJump(finallyLabel.get()); 3356 generator.popTry(tryData, finallyViaThrowLabel.get()); 3357 } 3358 3319 3359 generator.emitLabel(catchEndLabel.get()); 3360 generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1); 3320 3361 } 3321 3362 3322 3363 if (m_finallyBlock) { 3323 RefPtr<Label> preFinallyLabel = generator.emitLabel(generator.newLabel().get()); 3324 3325 generator.popFinallyControlFlowScope(); 3326 3327 RefPtr<Label> finallyEndLabel = generator.newLabel(); 3364 FinallyContext finallyContext = generator.popFinallyControlFlowScope(); 3365 3366 // Entry to the finally block for CompletionType::Throw. 3367 generator.emitLabel(finallyViaThrowLabel.get()); 3368 RegisterID* unused = generator.newTemporary(); 3369 generator.emitCatch(generator.finallyActionRegister(), unused); 3370 // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw. 3371 3372 // Entry to the finally block for CompletionTypes other than Throw. 3373 generator.emitLabel(finallyLabel.get()); 3374 generator.restoreScopeRegister(); 3328 3375 3329 3376 int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1; 3330 3331 // Normal path: run the finally code, and jump to the end.3332 3377 generator.emitProfileControlFlow(finallyStartOffset); 3333 3378 generator.emitNodeInTailPosition(dst, m_finallyBlock); 3334 generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); 3335 generator.emitJump(finallyEndLabel.get()); 3336 3337 // Uncaught exception path: invoke the finally block, then re-throw the exception. 3338 RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); 3339 RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); 3340 generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), preFinallyLabel.get(), HandlerType::Finally); 3341 generator.emitProfileControlFlow(finallyStartOffset); 3342 generator.emitNodeInTailPosition(dst, m_finallyBlock); 3343 generator.emitThrow(exceptionRegister.get()); 3344 3379 3380 generator.emitFinallyCompletion(finallyContext, finallyEndLabel.get()); 3345 3381 generator.emitLabel(finallyEndLabel.get()); 3346 3382 generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); 3347 } else 3348 generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1); 3349 3383 } 3350 3384 } 3351 3385 -
trunk/Source/WTF/ChangeLog
r209938 r209952 1 2016-12-16 Mark Lam <mark.lam@apple.com> 2 3 Add predecessor info to dumps from JSC_dumpBytecodeLivenessResults=true. 4 https://bugs.webkit.org/show_bug.cgi?id=165958 5 6 Reviewed by Keith Miller. 7 8 Added some methods to bring SegmentedVector closer to parity with Vector. 9 10 * wtf/SegmentedVector.h: 11 (WTF::SegmentedVector::first): 12 (WTF::SegmentedVector::last): 13 (WTF::SegmentedVector::takeLast): 14 1 15 2016-12-16 Michael Saboff <msaboff@apple.com> 2 16 -
trunk/Source/WTF/wtf/SegmentedVector.h
r195339 r209952 128 128 } 129 129 130 T& last() 131 { 132 return at(size() - 1); 130 T& first() { return at(0); } 131 const T& first() const { return at(0); } 132 T& last() { return at(size() - 1); } 133 const T& last() const { return at(size() - 1); } 134 135 T takeLast() 136 { 137 ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty()); 138 T result = WTFMove(last()); 139 --m_size; 140 return result; 133 141 } 134 142
Note:
See TracChangeset
for help on using the changeset viewer.