Ignore:
Timestamp:
Sep 15, 2017, 4:27:56 PM (8 years ago)
Author:
[email protected]
Message:

Arity fixup during inlining should do a 2 phase commit so it properly recovers the frame in case of exit
https://bugs.webkit.org/show_bug.cgi?id=176981

Reviewed by Yusuke Suzuki.

JSTests:

  • stress/exit-during-inlined-arity-fixup-recover-proper-frame.js: Added.

(assert):
(verify):
(func):
(const.bar.createBuiltin):

Source/JavaScriptCore:

This patch makes inline arity fixup happen in two phases:

  1. We get all the values we need and MovHint them to the expected locals.
  2. We SetLocal them inside the callee's CodeOrigin. This way, if we exit, the callee's frame is already set up. If any SetLocal exits, we have a valid exit state. This is required because if we didn't do this in two phases, we may exit in the middle of arity fixup from the caller's CodeOrigin. This is unsound because if we did the SetLocals in the caller's frame, the memcpy may clobber needed parts of the frame right before exiting. For example, consider if we need to pad two args: [arg3][arg2][arg1][arg0] [fix ][fix ][arg3][arg2][arg1][arg0] We memcpy starting from arg0 in the direction of arg3. If we were to exit at a type check for arg3's SetLocal in the caller's CodeOrigin, we'd exit with a frame like so: [arg3][arg2][arg1][arg2][arg1][arg0] And the caller would then just end up thinking its argument are: [arg3][arg2][arg1][arg2] which is incorrect.

This patch also fixes a couple of bugs in IdentitiyWithProfile:

  1. The bytecode generator for this bytecode intrinsic was written incorrectly. It needed to store the result of evaluating its argument in a temporary that it creates. Otherwise, it might try to simply overwrite a constant or a register that it didn't own.
  2. We weren't eliminating this node in CSE inside the DFG.
  • bytecompiler/NodesCodegen.cpp:

(JSC::BytecodeIntrinsicNode::emit_intrinsic_idWithProfile):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::inlineCall):

  • dfg/DFGCSEPhase.cpp:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r222060 r222115  
    15781578        calleeVariable->mergeShouldNeverUnbox(true);
    15791579    }
    1580    
     1580
     1581    Vector<std::pair<Node*, VirtualRegister>> delayedAritySets;
     1582
    15811583    if (arityFixupCount) {
     1584        // Note: we do arity fixup in two phases:
     1585        // 1. We get all the values we need and MovHint them to the expected locals.
     1586        // 2. We SetLocal them inside the callee's CodeOrigin. This way, if we exit, the callee's
     1587        //    frame is already set up. If any SetLocal exits, we have a valid exit state.
     1588        //    This is required because if we didn't do this in two phases, we may exit in
     1589        //    the middle of arity fixup from the caller's CodeOrigin. This is unsound because if
     1590        //    we did the SetLocals in the caller's frame, the memcpy may clobber needed parts
     1591        //    of the frame right before exiting. For example, consider if we need to pad two args:
     1592        //    [arg3][arg2][arg1][arg0]
     1593        //    [fix ][fix ][arg3][arg2][arg1][arg0]
     1594        //    We memcpy starting from arg0 in the direction of arg3. If we were to exit at a type check
     1595        //    for arg3's SetLocal in the caller's CodeOrigin, we'd exit with a frame like so:
     1596        //    [arg3][arg2][arg1][arg2][arg1][arg0]
     1597        //    And the caller would then just end up thinking its argument are:
     1598        //    [arg3][arg2][arg1][arg2]
     1599        //    which is incorrect.
     1600
    15821601        Node* undefined = addToGraph(JSConstant, OpInfo(m_constantUndefined));
    1583         auto fill = [&] (VirtualRegister reg, Node* value) {
    1584             // It's valid to exit here since we'll exit to the top of the
    1585             // call and re-setup the arguments.
    1586             m_exitOK = true;
    1587             addToGraph(ExitOK);
    1588 
    1589             set(reg, value, ImmediateNakedSet);
    1590         };
    1591 
    1592         // The stack needs to be aligned due to ABIs. Thus, we have a hole if the count of arguments is not aligned.
     1602        // The stack needs to be aligned due to the JS calling convention. Thus, we have a hole if the count of arguments is not aligned.
    15931603        // We call this hole "extra slot". Consider the following case, the number of arguments is 2. If this argument
    15941604        // count does not fulfill the stack alignment requirement, we already inserted extra slots.
     
    16041614        // In such cases, we do not need to move frames.
    16051615        if (registerOffsetAfterFixup != registerOffset) {
    1606             for (int index = 0; index < argumentCountIncludingThis; ++index)
    1607                 fill(virtualRegisterForArgument(index, registerOffsetAfterFixup), get(virtualRegisterForArgument(index, registerOffset)));
    1608         }
    1609         for (int index = 0; index < arityFixupCount; ++index)
    1610             fill(virtualRegisterForArgument(argumentCountIncludingThis + index, registerOffsetAfterFixup), undefined);
     1616            for (int index = 0; index < argumentCountIncludingThis; ++index) {
     1617                Node* value = get(virtualRegisterForArgument(index, registerOffset));
     1618                VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(index, registerOffsetAfterFixup));
     1619                addToGraph(MovHint, OpInfo(argumentToSet.offset()), value);
     1620                m_setLocalQueue.append(DelayedSetLocal { currentCodeOrigin(), argumentToSet, value, ImmediateNakedSet });
     1621            }
     1622        }
     1623        for (int index = 0; index < arityFixupCount; ++index) {
     1624            VirtualRegister argumentToSet = m_inlineStackTop->remapOperand(virtualRegisterForArgument(argumentCountIncludingThis + index, registerOffsetAfterFixup));
     1625            addToGraph(MovHint, OpInfo(argumentToSet.offset()), undefined);
     1626            m_setLocalQueue.append(DelayedSetLocal { currentCodeOrigin(), argumentToSet, undefined, ImmediateNakedSet });
     1627        }
     1628
     1629        // At this point, it's OK to OSR exit because we finished setting up
     1630        // our callee's frame. We emit an ExitOK below from the callee's CodeOrigin.
    16111631    }
    16121632
     
    16211641    // At this point, it's again OK to OSR exit.
    16221642    m_exitOK = true;
     1643    addToGraph(ExitOK);
     1644
     1645    processSetLocalQueue();
    16231646
    16241647    InlineVariableData inlineVariableData;
Note: See TracChangeset for help on using the changeset viewer.