- a/Source/JavaScriptCore/ChangeLog +24 lines
Lines 1-3 a/Source/JavaScriptCore/ChangeLog_sec1
1
2015-11-17  Keith Miller  <keith_miller@apple.com>
2
3
        [JSC] TailCalls should not trigger assertion failures when running with the jsc profiler on.
4
        https://bugs.webkit.org/show_bug.cgi?id=151359
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        If we have the profiler on when parsing tail calls in the dfg we would emit a CountExecutions node
9
        following a TailCall node as our bytecode for tail calls always emits a op_ret following an op_tail_call.
10
        This trips assertions that no nodes follow a terminal in a basic block. This patch fixes this issue by
11
        not emiting a CountExecutions when we have a tail call that is terminal i.e. the tail caller is not
12
        inlined into another function or the tail caller is inlined but is in tail position itself.
13
14
        * dfg/DFGByteCodeParser.cpp:
15
        (JSC::DFG::ByteCodeParser::handleCall):
16
        (JSC::DFG::ByteCodeParser::handleVarargsCall):
17
        (JSC::DFG::ByteCodeParser::parseBlock):
18
        * tests/stress/tail-call-profiler.js: Added.
19
        (tail):
20
        (inlineTail):
21
        (inlineTailVarArgs):
22
        (inlineTailTernary):
23
        (body):
24
1
2015-11-17  Andreas Kling  <akling@apple.com>
25
2015-11-17  Andreas Kling  <akling@apple.com>
2
26
3
        [JSC] JSPropertyNameEnumerator could be destructorless.
27
        [JSC] JSPropertyNameEnumerator could be destructorless.
- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp -44 / +55 lines
Lines 176-193 private: a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec1
176
    // Helper for min and max.
176
    // Helper for min and max.
177
    template<typename ChecksFunctor>
177
    template<typename ChecksFunctor>
178
    bool handleMinMax(int resultOperand, NodeType op, int registerOffset, int argumentCountIncludingThis, const ChecksFunctor& insertChecks);
178
    bool handleMinMax(int resultOperand, NodeType op, int registerOffset, int argumentCountIncludingThis, const ChecksFunctor& insertChecks);
179
    
179
180
    // Handle calls. This resolves issues surrounding inlining and intrinsics.
180
    // Handle calls. This resolves issues surrounding inlining and intrinsics.
181
    void handleCall(
181
    enum Terminality { Terminal, NonTerminal };
182
    Terminality handleCall(
182
        int result, NodeType op, InlineCallFrame::Kind, unsigned instructionSize,
183
        int result, NodeType op, InlineCallFrame::Kind, unsigned instructionSize,
183
        Node* callTarget, int argCount, int registerOffset, CallLinkStatus,
184
        Node* callTarget, int argCount, int registerOffset, CallLinkStatus,
184
        SpeculatedType prediction);
185
        SpeculatedType prediction);
185
    void handleCall(
186
    Terminality handleCall(
186
        int result, NodeType op, CallMode, unsigned instructionSize,
187
        int result, NodeType op, CallMode, unsigned instructionSize,
187
        Node* callTarget, int argCount, int registerOffset, CallLinkStatus);
188
        Node* callTarget, int argCount, int registerOffset, CallLinkStatus);
188
    void handleCall(int result, NodeType op, CallMode, unsigned instructionSize, int callee, int argCount, int registerOffset);
189
    Terminality handleCall(int result, NodeType op, CallMode, unsigned instructionSize, int callee, int argCount, int registerOffset);
189
    void handleCall(Instruction* pc, NodeType op, CallMode);
190
    Terminality handleCall(Instruction* pc, NodeType op, CallMode);
190
    void handleVarargsCall(Instruction* pc, NodeType op, CallMode);
191
    Terminality handleVarargsCall(Instruction* pc, NodeType op, CallMode);
191
    void emitFunctionChecks(CallVariant, Node* callTarget, VirtualRegister thisArgumnt);
192
    void emitFunctionChecks(CallVariant, Node* callTarget, VirtualRegister thisArgumnt);
192
    void emitArgumentPhantoms(int registerOffset, int argumentCountIncludingThis);
193
    void emitArgumentPhantoms(int registerOffset, int argumentCountIncludingThis);
193
    unsigned inliningCost(CallVariant, int argumentCountIncludingThis, CallMode); // Return UINT_MAX if it's not an inlining candidate. By convention, intrinsics have a cost of 1.
194
    unsigned inliningCost(CallVariant, int argumentCountIncludingThis, CallMode); // Return UINT_MAX if it's not an inlining candidate. By convention, intrinsics have a cost of 1.
Lines 1131-1146 private: a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec2
1131
    m_exitOK = false; \
1132
    m_exitOK = false; \
1132
    return shouldContinueParsing
1133
    return shouldContinueParsing
1133
1134
1134
void ByteCodeParser::handleCall(Instruction* pc, NodeType op, CallMode callMode)
1135
ByteCodeParser::Terminality ByteCodeParser::handleCall(Instruction* pc, NodeType op, CallMode callMode)
1135
{
1136
{
1136
    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
1137
    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
1137
    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call));
1138
    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call));
1138
    handleCall(
1139
    return handleCall(
1139
        pc[1].u.operand, op, callMode, OPCODE_LENGTH(op_call),
1140
        pc[1].u.operand, op, callMode, OPCODE_LENGTH(op_call),
1140
        pc[2].u.operand, pc[3].u.operand, -pc[4].u.operand);
1141
        pc[2].u.operand, pc[3].u.operand, -pc[4].u.operand);
1141
}
1142
}
1142
1143
1143
void ByteCodeParser::handleCall(
1144
ByteCodeParser::Terminality ByteCodeParser::handleCall(
1144
    int result, NodeType op, CallMode callMode, unsigned instructionSize,
1145
    int result, NodeType op, CallMode callMode, unsigned instructionSize,
1145
    int callee, int argumentCountIncludingThis, int registerOffset)
1146
    int callee, int argumentCountIncludingThis, int registerOffset)
1146
{
1147
{
Lines 1150-1171 void ByteCodeParser::handleCall( a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec3
1150
        m_inlineStackTop->m_profiledBlock, currentCodeOrigin(),
1151
        m_inlineStackTop->m_profiledBlock, currentCodeOrigin(),
1151
        m_inlineStackTop->m_callLinkInfos, m_callContextMap);
1152
        m_inlineStackTop->m_callLinkInfos, m_callContextMap);
1152
    
1153
    
1153
    handleCall(
1154
    return handleCall(
1154
        result, op, callMode, instructionSize, callTarget,
1155
        result, op, callMode, instructionSize, callTarget,
1155
        argumentCountIncludingThis, registerOffset, callLinkStatus);
1156
        argumentCountIncludingThis, registerOffset, callLinkStatus);
1156
}
1157
}
1157
    
1158
    
1158
void ByteCodeParser::handleCall(
1159
ByteCodeParser::Terminality ByteCodeParser::handleCall(
1159
    int result, NodeType op, CallMode callMode, unsigned instructionSize,
1160
    int result, NodeType op, CallMode callMode, unsigned instructionSize,
1160
    Node* callTarget, int argumentCountIncludingThis, int registerOffset,
1161
    Node* callTarget, int argumentCountIncludingThis, int registerOffset,
1161
    CallLinkStatus callLinkStatus)
1162
    CallLinkStatus callLinkStatus)
1162
{
1163
{
1163
    handleCall(
1164
    return handleCall(
1164
        result, op, InlineCallFrame::kindFor(callMode), instructionSize, callTarget, argumentCountIncludingThis,
1165
        result, op, InlineCallFrame::kindFor(callMode), instructionSize, callTarget, argumentCountIncludingThis,
1165
        registerOffset, callLinkStatus, getPrediction());
1166
        registerOffset, callLinkStatus, getPrediction());
1166
}
1167
}
1167
1168
1168
void ByteCodeParser::handleCall(
1169
ByteCodeParser::Terminality ByteCodeParser::handleCall(
1169
    int result, NodeType op, InlineCallFrame::Kind kind, unsigned instructionSize,
1170
    int result, NodeType op, InlineCallFrame::Kind kind, unsigned instructionSize,
1170
    Node* callTarget, int argumentCountIncludingThis, int registerOffset,
1171
    Node* callTarget, int argumentCountIncludingThis, int registerOffset,
1171
    CallLinkStatus callLinkStatus, SpeculatedType prediction)
1172
    CallLinkStatus callLinkStatus, SpeculatedType prediction)
Lines 1182-1189 void ByteCodeParser::handleCall( a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec4
1182
        // Oddly, this conflates calls that haven't executed with calls that behaved sufficiently polymorphically
1183
        // Oddly, this conflates calls that haven't executed with calls that behaved sufficiently polymorphically
1183
        // that we cannot optimize them.
1184
        // that we cannot optimize them.
1184
1185
1185
        addCall(result, op, OpInfo(), callTarget, argumentCountIncludingThis, registerOffset, prediction);
1186
        Node* callNode = addCall(result, op, OpInfo(), callTarget, argumentCountIncludingThis, registerOffset, prediction);
1186
        return;
1187
        if (callNode->op() == TailCall)
1188
            return Terminal;
1189
        ASSERT(callNode->op() != TailCallVarargs);
1190
        return NonTerminal;
1187
    }
1191
    }
1188
    
1192
    
1189
    unsigned nextOffset = m_currentIndex + instructionSize;
1193
    unsigned nextOffset = m_currentIndex + instructionSize;
Lines 1193-1205 void ByteCodeParser::handleCall( a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec5
1193
    if (handleInlining(callTarget, result, callLinkStatus, registerOffset, virtualRegisterForArgument(0, registerOffset), VirtualRegister(), 0, argumentCountIncludingThis, nextOffset, op, kind, prediction)) {
1197
    if (handleInlining(callTarget, result, callLinkStatus, registerOffset, virtualRegisterForArgument(0, registerOffset), VirtualRegister(), 0, argumentCountIncludingThis, nextOffset, op, kind, prediction)) {
1194
        if (m_graph.compilation())
1198
        if (m_graph.compilation())
1195
            m_graph.compilation()->noticeInlinedCall();
1199
            m_graph.compilation()->noticeInlinedCall();
1196
        return;
1200
        return NonTerminal;
1197
    }
1201
    }
1198
    
1202
    
1199
    addCall(result, op, callOpInfo, callTarget, argumentCountIncludingThis, registerOffset, prediction);
1203
    Node* callNode = addCall(result, op, callOpInfo, callTarget, argumentCountIncludingThis, registerOffset, prediction);
1204
    if (callNode->op() == TailCall)
1205
        return Terminal;
1206
    ASSERT(callNode->op() != TailCallVarargs);
1207
    return NonTerminal;
1200
}
1208
}
1201
1209
1202
void ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode callMode)
1210
ByteCodeParser::Terminality ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode callMode)
1203
{
1211
{
1204
    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_construct_varargs));
1212
    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_construct_varargs));
1205
    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_tail_call_varargs));
1213
    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_tail_call_varargs));
Lines 1228-1234 void ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode ca a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec6
1228
        && handleInlining(callTarget, result, callLinkStatus, firstFreeReg, VirtualRegister(thisReg), VirtualRegister(arguments), firstVarArgOffset, 0, m_currentIndex + OPCODE_LENGTH(op_call_varargs), op, InlineCallFrame::varargsKindFor(callMode), prediction)) {
1236
        && handleInlining(callTarget, result, callLinkStatus, firstFreeReg, VirtualRegister(thisReg), VirtualRegister(arguments), firstVarArgOffset, 0, m_currentIndex + OPCODE_LENGTH(op_call_varargs), op, InlineCallFrame::varargsKindFor(callMode), prediction)) {
1229
        if (m_graph.compilation())
1237
        if (m_graph.compilation())
1230
            m_graph.compilation()->noticeInlinedCall();
1238
            m_graph.compilation()->noticeInlinedCall();
1231
        return;
1239
        return NonTerminal;
1232
    }
1240
    }
1233
    
1241
    
1234
    CallVarargsData* data = m_graph.m_callVarargsData.add();
1242
    CallVarargsData* data = m_graph.m_callVarargsData.add();
Lines 1239-1245 void ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode ca a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec7
1239
    if (op == TailCallVarargs) {
1247
    if (op == TailCallVarargs) {
1240
        if (allInlineFramesAreTailCalls()) {
1248
        if (allInlineFramesAreTailCalls()) {
1241
            addToGraph(op, OpInfo(data), OpInfo(), callTarget, get(VirtualRegister(arguments)), thisChild);
1249
            addToGraph(op, OpInfo(data), OpInfo(), callTarget, get(VirtualRegister(arguments)), thisChild);
1242
            return;
1250
            return Terminal;
1243
        }
1251
        }
1244
        op = TailCallVarargsInlinedCaller;
1252
        op = TailCallVarargsInlinedCaller;
1245
    }
1253
    }
Lines 1248-1253 void ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode ca a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec8
1248
    VirtualRegister resultReg(result);
1256
    VirtualRegister resultReg(result);
1249
    if (resultReg.isValid())
1257
    if (resultReg.isValid())
1250
        set(resultReg, call);
1258
        set(resultReg, call);
1259
    return NonTerminal;
1251
}
1260
}
1252
1261
1253
void ByteCodeParser::emitFunctionChecks(CallVariant callee, Node* callTarget, VirtualRegister thisArgumentReg)
1262
void ByteCodeParser::emitFunctionChecks(CallVariant callee, Node* callTarget, VirtualRegister thisArgumentReg)
Lines 3180-3188 bool ByteCodeParser::parseBlock(unsigned limit) a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec9
3180
            // to be true.
3189
            // to be true.
3181
            // We also don't insert a jump if the block already has a terminal,
3190
            // We also don't insert a jump if the block already has a terminal,
3182
            // which could happen after a tail call.
3191
            // which could happen after a tail call.
3183
            ASSERT(m_currentBlock->isEmpty() || !m_currentBlock->terminal()
3192
            ASSERT(m_currentBlock->isEmpty() || !m_currentBlock->terminal());
3184
                || m_currentBlock->terminal()->op() == TailCall || m_currentBlock->terminal()->op() == TailCallVarargs);
3193
            if (!m_currentBlock->isEmpty())
3185
            if (!m_currentBlock->isEmpty() && !m_currentBlock->terminal())
3186
                addToGraph(Jump, OpInfo(m_currentIndex));
3194
                addToGraph(Jump, OpInfo(m_currentIndex));
3187
            return shouldContinueParsing;
3195
            return shouldContinueParsing;
3188
        }
3196
        }
Lines 3796-3807 bool ByteCodeParser::parseBlock(unsigned limit) a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec10
3796
        // === Block terminators. ===
3804
        // === Block terminators. ===
3797
3805
3798
        case op_jmp: {
3806
        case op_jmp: {
3799
            if (m_currentBlock->terminal()) {
3807
            ASSERT(!m_currentBlock->terminal());
3800
                // We could be the dummy jump to a return after a non-inlined, non-emulated tail call in a ternary operator
3801
                Node* terminal = m_currentBlock->terminal();
3802
                ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
3803
                LAST_OPCODE(op_jmp);
3804
            }
3805
            int relativeOffset = currentInstruction[1].u.operand;
3808
            int relativeOffset = currentInstruction[1].u.operand;
3806
            addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
3809
            addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
3807
            if (relativeOffset <= 0)
3810
            if (relativeOffset <= 0)
Lines 3973-3984 bool ByteCodeParser::parseBlock(unsigned limit) a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec11
3973
        }
3976
        }
3974
3977
3975
        case op_ret:
3978
        case op_ret:
3976
            if (m_currentBlock->terminal()) {
3979
            ASSERT(!m_currentBlock->terminal());
3977
                // We could be the dummy return after a non-inlined, non-emulated tail call
3978
                Node* terminal = m_currentBlock->terminal();
3979
                ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs);
3980
                LAST_OPCODE(op_ret);
3981
            }
3982
            if (inlineCallFrame()) {
3980
            if (inlineCallFrame()) {
3983
                flushForReturn();
3981
                flushForReturn();
3984
                if (m_inlineStackTop->m_returnValue.isValid())
3982
                if (m_inlineStackTop->m_returnValue.isValid())
Lines 4031-4048 bool ByteCodeParser::parseBlock(unsigned limit) a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec12
4031
            
4029
            
4032
        case op_call:
4030
        case op_call:
4033
            handleCall(currentInstruction, Call, CallMode::Regular);
4031
            handleCall(currentInstruction, Call, CallMode::Regular);
4034
            // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction
4032
            // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction.
4035
            ASSERT(m_currentInstruction == currentInstruction);
4033
            ASSERT(m_currentInstruction == currentInstruction);
4036
            NEXT_OPCODE(op_call);
4034
            NEXT_OPCODE(op_call);
4037
4035
4038
        case op_tail_call:
4036
        case op_tail_call: {
4039
            flushForReturn();
4037
            flushForReturn();
4040
            handleCall(currentInstruction, TailCall, CallMode::Tail);
4038
            Terminality terminality = handleCall(currentInstruction, TailCall, CallMode::Tail);
4041
            // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction
4039
            // Verify that handleCall(), which could have inlined the callee, didn't trash m_currentInstruction.
4042
            ASSERT(m_currentInstruction == currentInstruction);
4040
            ASSERT(m_currentInstruction == currentInstruction);
4043
            // We let the following op_ret handle cases related to
4041
            // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
4044
            // inlining to keep things simple.
4042
            // If the call is not terminal, however, then we want the subsequent op_ret/op_jumpt to update metadata and clean
4045
            NEXT_OPCODE(op_tail_call);
4043
            // things up.
4044
            if (terminality == NonTerminal) {
4045
                NEXT_OPCODE(op_tail_call);
4046
            } else {
4047
                LAST_OPCODE(op_tail_call);
4048
            }
4049
        }
4046
4050
4047
        case op_construct:
4051
        case op_construct:
4048
            handleCall(currentInstruction, Construct, CallMode::Construct);
4052
            handleCall(currentInstruction, Construct, CallMode::Construct);
Lines 4055-4062 bool ByteCodeParser::parseBlock(unsigned limit) a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp_sec13
4055
4059
4056
        case op_tail_call_varargs: {
4060
        case op_tail_call_varargs: {
4057
            flushForReturn();
4061
            flushForReturn();
4058
            handleVarargsCall(currentInstruction, TailCallVarargs, CallMode::Tail);
4062
            Terminality terminality = handleVarargsCall(currentInstruction, TailCallVarargs, CallMode::Tail);
4059
            NEXT_OPCODE(op_tail_call_varargs);
4063
            // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
4064
            // If the call is not terminal, however, then we want the subsequent op_ret/op_jumpt to update metadata and clean
4065
            // things up.
4066
            if (terminality == NonTerminal) {
4067
                NEXT_OPCODE(op_tail_call_varargs);
4068
            } else {
4069
                LAST_OPCODE(op_tail_call_varargs);
4070
            }
4060
        }
4071
        }
4061
            
4072
            
4062
        case op_construct_varargs: {
4073
        case op_construct_varargs: {
- a/Source/JavaScriptCore/tests/stress/tail-call-profiler.js +29 lines
Line 0 a/Source/JavaScriptCore/tests/stress/tail-call-profiler.js_sec1
1
//@ runProfiler
2
// This tests the profiler DFG node, CountExecution, plays nicely with TailCalls.
3
4
"use strict";
5
6
function tail(a) { return 1; }
7
noInline(tail);
8
9
function inlineTail(a) {
10
    return tail(a);
11
}
12
13
function inlineTailVarArgs(a) {
14
    return tail(...[a])
15
}
16
17
function inlineTailTernary(a) {
18
    return true ? tail(a) : null;
19
}
20
21
function body() {
22
    for (var i = 0; i < 10000; i++) {
23
        inlineTail(1);
24
        inlineTailVarArgs(1);
25
        inlineTailTernary(1)
26
    }
27
}
28
29
body();

Return to Bug 151359