Changeset 244864 in webkit
- Timestamp:
- May 1, 2019 7:40:44 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r244862 r244864 1 2019-05-01 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Inlining Getter/Setter should care availability of ad-hocly constructed frame 4 https://bugs.webkit.org/show_bug.cgi?id=197405 5 6 Reviewed by Saam Barati. 7 8 * stress/getter-setter-inlining-should-emit-movhint.js: Added. 9 (foo): 10 (test): 11 (i.o.get f): 12 (i.o.set f): 13 1 14 2019-05-01 Michael Saboff <msaboff@apple.com> 2 15 -
trunk/Source/JavaScriptCore/ChangeLog
r244862 r244864 1 2019-05-01 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Inlining Getter/Setter should care availability of ad-hocly constructed frame 4 https://bugs.webkit.org/show_bug.cgi?id=197405 5 6 Reviewed by Saam Barati. 7 8 When inlining getter and setter calls, we setup a stack frame which does not appear in the bytecode. 9 Because Inlining can switch on executable, we could have a graph like this. 10 11 BB#0 12 ... 13 30: GetSetter 14 31: MovHint(loc10) 15 32: SetLocal(loc10) 16 33: MovHint(loc9) 17 34: SetLocal(loc9) 18 ... 19 37: GetExecutable(@30) 20 ... 21 41: Switch(@37) 22 23 BB#2 24 42: GetLocal(loc12, bc#7 of caller) 25 ... 26 --> callee: loc9 and loc10 are arguments of callee. 27 ... 28 <HERE, exit to callee, loc9 and loc10 are required in the bytecode> 29 30 When we prune OSR availability at the beginning of BB#2 (bc#7 in the caller), we prune loc9 and loc10's liveness because the caller does not actually have loc9 and loc10. 31 However, when we begin executing the callee, we need OSR exit to be aware of where it can recover the arguments to the setter, loc9 and loc10. 32 33 This patch inserts MovHint at the beginning of callee for a getter / setter stack frame to make arguments (loc9 and loc10 in the above example) recoverable from OSR exit. 34 We also move arity fixup DFG nodes from the caller to the callee, since moved arguments are not live in the caller too. 35 36 Interestingly, this fix also reveals the existing issue in LiveCatchVariablePreservationPhase. We emitted Flush for |this| of InlineCallFrame blindly if we saw InlineCallFrame 37 inside a block which is covered by catch handler. But this is wrong because inlined function can finish its execution within the block, and |this| is completely unrelated to 38 the catch handler if the catch handler is in the outer callee. We already collect all the live locals at the catch handler. And this locals must include arguments too if the 39 catch handler is in inlined function. So, we should not emit Flush for each |this| of seen InlineCallFrame. This emitted Flush may connect unrelated locals in the catch handler 40 to the locals that is only defined and used in the inlined function, and it leads to the results like DFG says the local is live while the bytecode says the local is dead. 41 This results in reading and using garbage in OSR entry because DFG OSR entry needs to fill live DFG values from the stack. 42 43 * dfg/DFGByteCodeParser.cpp: 44 (JSC::DFG::ByteCodeParser::inlineCall): 45 (JSC::DFG::ByteCodeParser::handleGetById): 46 (JSC::DFG::ByteCodeParser::handlePutById): 47 * dfg/DFGLiveCatchVariablePreservationPhase.cpp: 48 (JSC::DFG::LiveCatchVariablePreservationPhase::handleBlockForTryCatch): 49 1 50 2019-05-01 Michael Saboff <msaboff@apple.com> 2 51 -
trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
r244811 r244864 1609 1609 } 1610 1610 1611 InlineStackEntry* callerStackTop = m_inlineStackTop; 1612 InlineStackEntry inlineStackEntry(this, codeBlock, codeBlock, callee.function(), result, 1613 (VirtualRegister)inlineCallFrameStart, argumentCountIncludingThis, kind, continuationBlock); 1614 1615 // This is where the actual inlining really happens. 1616 unsigned oldIndex = m_currentIndex; 1617 m_currentIndex = 0; 1618 1619 switch (kind) { 1620 case InlineCallFrame::GetterCall: 1621 case InlineCallFrame::SetterCall: { 1622 // When inlining getter and setter calls, we setup a stack frame which does not appear in the bytecode. 1623 // Because Inlining can switch on executable, we could have a graph like this. 1624 // 1625 // BB#0 1626 // ... 1627 // 30: GetSetter 1628 // 31: MovHint(loc10) 1629 // 32: SetLocal(loc10) 1630 // 33: MovHint(loc9) 1631 // 34: SetLocal(loc9) 1632 // ... 1633 // 37: GetExecutable(@30) 1634 // ... 1635 // 41: Switch(@37) 1636 // 1637 // BB#2 1638 // 42: GetLocal(loc12, bc#7 of caller) 1639 // ... 1640 // --> callee: loc9 and loc10 are arguments of callee. 1641 // ... 1642 // <HERE, exit to callee, loc9 and loc10 are required in the bytecode> 1643 // 1644 // When we prune OSR availability at the beginning of BB#2 (bc#7 in the caller), we prune loc9 and loc10's liveness because the caller does not actually have loc9 and loc10. 1645 // However, when we begin executing the callee, we need OSR exit to be aware of where it can recover the arguments to the setter, loc9 and loc10. The MovHints in the inlined 1646 // callee make it so that if we exit at <HERE>, we can recover loc9 and loc10. 1647 for (int index = 0; index < argumentCountIncludingThis; ++index) { 1648 VirtualRegister argumentToGet = callerStackTop->remapOperand(virtualRegisterForArgument(index, registerOffset)); 1649 addToGraph(MovHint, OpInfo(argumentToGet.offset()), getDirect(argumentToGet)); 1650 } 1651 break; 1652 } 1653 default: 1654 break; 1655 } 1656 1611 1657 if (arityFixupCount) { 1612 1658 // Note: we do arity fixup in two phases: 1613 1659 // 1. We get all the values we need and MovHint them to the expected locals. 1614 // 2. We SetLocal them inside the callee's CodeOrigin. This way, if we exit, the callee's1660 // 2. We SetLocal them after that. This way, if we exit, the callee's 1615 1661 // frame is already set up. If any SetLocal exits, we have a valid exit state. 1616 1662 // This is required because if we didn't do this in two phases, we may exit in 1617 // the middle of arity fixup from the calle r's CodeOrigin. This is unsound because if1618 // we did the SetLocals in the caller's frame, the memcpy may clobber needed parts1619 // of the frame right before exiting.For example, consider if we need to pad two args:1663 // the middle of arity fixup from the callee's CodeOrigin. This is unsound because exited 1664 // code does not have arity fixup so that remaining necessary fixups are not executed. 1665 // For example, consider if we need to pad two args: 1620 1666 // [arg3][arg2][arg1][arg0] 1621 1667 // [fix ][fix ][arg3][arg2][arg1][arg0] 1622 1668 // We memcpy starting from arg0 in the direction of arg3. If we were to exit at a type check 1623 // for arg3's SetLocal in the calle r's CodeOrigin, we'd exit with a frame like so:1669 // for arg3's SetLocal in the callee's CodeOrigin, we'd exit with a frame like so: 1624 1670 // [arg3][arg2][arg1][arg2][arg1][arg0] 1625 // And the caller would then just end up thinking its argument are: 1626 // [arg3][arg2][arg1][arg2] 1671 // Since we do not perform arity fixup in the callee, this is the frame used by the callee. 1672 // And the callee would then just end up thinking its argument are: 1673 // [fix ][fix ][arg3][arg2][arg1][arg0] 1627 1674 // which is incorrect. 1628 1675 … … 1643 1690 if (registerOffsetAfterFixup != registerOffset) { 1644 1691 for (int index = 0; index < argumentCountIncludingThis; ++index) { 1645 Node* value = get(virtualRegisterForArgument(index, registerOffset)); 1646 VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(index, registerOffsetAfterFixup)); 1692 VirtualRegister argumentToGet = callerStackTop->remapOperand(virtualRegisterForArgument(index, registerOffset)); 1693 Node* value = getDirect(argumentToGet); 1694 VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(index)); 1647 1695 addToGraph(MovHint, OpInfo(argumentToSet.offset()), value); 1648 1696 m_setLocalQueue.append(DelayedSetLocal { currentCodeOrigin(), argumentToSet, value, ImmediateNakedSet }); … … 1650 1698 } 1651 1699 for (int index = 0; index < arityFixupCount; ++index) { 1652 VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(argumentCountIncludingThis + index , registerOffsetAfterFixup));1700 VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(argumentCountIncludingThis + index)); 1653 1701 addToGraph(MovHint, OpInfo(argumentToSet.offset()), undefined); 1654 1702 m_setLocalQueue.append(DelayedSetLocal { currentCodeOrigin(), argumentToSet, undefined, ImmediateNakedSet }); … … 1656 1704 1657 1705 // At this point, it's OK to OSR exit because we finished setting up 1658 // our callee's frame. We emit an ExitOK below from the callee's CodeOrigin. 1659 } 1660 1661 InlineStackEntry inlineStackEntry(this, codeBlock, codeBlock, callee.function(), result, 1662 (VirtualRegister)inlineCallFrameStart, argumentCountIncludingThis, kind, continuationBlock); 1663 1664 // This is where the actual inlining really happens. 1665 unsigned oldIndex = m_currentIndex; 1666 m_currentIndex = 0; 1706 // our callee's frame. We emit an ExitOK below. 1707 } 1667 1708 1668 1709 // At this point, it's again OK to OSR exit. … … 4372 4413 // since we only really care about 'this' in this case. But we're not going to take that 4373 4414 // shortcut. 4374 int nextRegister = registerOffset + CallFrame::headerSizeInRegisters; 4375 set(VirtualRegister(nextRegister++), base, ImmediateNakedSet); 4415 set(virtualRegisterForArgument(0, registerOffset), base, ImmediateNakedSet); 4376 4416 4377 4417 // We've set some locals, but they are not user-visible. It's still OK to exit from here. … … 4556 4596 VirtualRegister(registerOffset)).toLocal()); 4557 4597 4558 int nextRegister = registerOffset + CallFrame::headerSizeInRegisters; 4559 set(VirtualRegister(nextRegister++), base, ImmediateNakedSet); 4560 set(VirtualRegister(nextRegister++), value, ImmediateNakedSet); 4598 set(virtualRegisterForArgument(0, registerOffset), base, ImmediateNakedSet); 4599 set(virtualRegisterForArgument(1, registerOffset), value, ImmediateNakedSet); 4561 4600 4562 4601 // We've set some locals, but they are not user-visible. It's still OK to exit from here. -
trunk/Source/JavaScriptCore/dfg/DFGLiveCatchVariablePreservationPhase.cpp
r244324 r244864 158 158 159 159 Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr); 160 HashSet<InlineCallFrame*> seenInlineCallFrames;161 160 162 161 auto flushEverything = [&] (NodeOrigin origin, unsigned index) { 163 162 RELEASE_ASSERT(currentExceptionHandler); 164 auto flush = [&] (VirtualRegister operand, bool alwaysInsert) { 165 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) 166 || operand.isArgument() 167 || alwaysInsert) { 163 auto flush = [&] (VirtualRegister operand) { 164 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) { 168 165 169 166 ASSERT(isValidFlushLocation(block, index, operand)); … … 181 178 182 179 for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++) 183 flush(virtualRegisterForLocal(local), false); 184 for (InlineCallFrame* inlineCallFrame : seenInlineCallFrames) 185 flush(VirtualRegister(inlineCallFrame->stackOffset + CallFrame::thisArgumentOffset()), true); 186 flush(VirtualRegister(CallFrame::thisArgumentOffset()), true); 187 188 seenInlineCallFrames.clear(); 180 flush(virtualRegisterForLocal(local)); 181 flush(VirtualRegister(CallFrame::thisArgumentOffset())); 189 182 }; 190 183 … … 200 193 201 194 if (currentExceptionHandler && (node->op() == SetLocal || node->op() == SetArgumentDefinitely || node->op() == SetArgumentMaybe)) { 202 InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame();203 if (inlineCallFrame)204 seenInlineCallFrames.add(inlineCallFrame);205 195 VirtualRegister operand = node->local(); 206 207 int stackOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; 208 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) 209 || operand.isArgument() 210 || (operand.offset() == stackOffset + CallFrame::thisArgumentOffset())) { 196 if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) { 211 197 212 198 ASSERT(isValidFlushLocation(block, nodeIndex, operand));
Note: See TracChangeset
for help on using the changeset viewer.