+bool ByteCodeParser::handleInlining(
+ Node* callTargetNode, int resultOperand, const CallLinkStatus& callLinkStatus,
+ int registerOffsetOrFirstFreeReg, VirtualRegister thisArgument,
+ VirtualRegister argumentsArgument, unsigned argumentsOffset, int argumentCountIncludingThis,
+ unsigned nextOffset, NodeType callOp, InlineCallFrame::Kind kind, SpeculatedType prediction)
+{
+ if (verbose) {
+ dataLog("Handling inlining...\n");
+ dataLog("Stack: ", currentCodeOrigin(), "\n");
+ }
+ CodeSpecializationKind specializationKind = InlineCallFrame::specializationKindFor(kind);
+
+ if (!callLinkStatus.size()) {
+ if (verbose)
+ dataLog("Bailing inlining.\n");
+ return false;
+ }
+
+ if (InlineCallFrame::isVarargs(kind)
+ && callLinkStatus.maxNumArguments() > Options::maximumVarargsForInlining()) {
+ if (verbose)
+ dataLog("Bailing inlining because of varargs.\n");
+ return false;
+ }
+
+ unsigned inliningBalance = Options::maximumFunctionForCallInlineCandidateInstructionCount();
+ if (specializationKind == CodeForConstruct)
+ inliningBalance = std::min(inliningBalance, Options::maximumFunctionForConstructInlineCandidateInstructionCount());
+ if (callLinkStatus.isClosureCall())
+ inliningBalance = std::min(inliningBalance, Options::maximumFunctionForClosureCallInlineCandidateInstructionCount());
+
+ // First check if we can avoid creating control flow. Our inliner does some CFG
+ // simplification on the fly and this helps reduce compile times, but we can only leverage
+ // this in cases where we don't need control flow diamonds to check the callee.
+ if (!callLinkStatus.couldTakeSlowPath() && callLinkStatus.size() == 1) {
+ int registerOffset;
+
+ // Only used for varargs calls.
+ unsigned mandatoryMinimum = 0;
+ unsigned maxNumArguments = 0;
+
+ if (InlineCallFrame::isVarargs(kind)) {
+ if (FunctionExecutable* functionExecutable = callLinkStatus[0].functionExecutable())
+ mandatoryMinimum = functionExecutable->parameterCount();
+ else
+ mandatoryMinimum = 0;
+
+ // includes "this"
+ maxNumArguments = std::max(
+ callLinkStatus.maxNumArguments(),
+ mandatoryMinimum + 1);
+
+ // We sort of pretend that this *is* the number of arguments that were passed.
+ argumentCountIncludingThis = maxNumArguments;
+
+ registerOffset = registerOffsetOrFirstFreeReg + 1;
+ registerOffset -= maxNumArguments; // includes "this"
+ registerOffset -= JSStack::CallFrameHeaderSize;
+ registerOffset = -WTF::roundUpToMultipleOf(
+ stackAlignmentRegisters(),
+ -registerOffset);
+ } else
+ registerOffset = registerOffsetOrFirstFreeReg;
+
+ bool result = attemptToInlineCall(
+ callTargetNode, resultOperand, callLinkStatus[0], registerOffset,
+ argumentCountIncludingThis, nextOffset, kind, CallerDoesNormalLinking, prediction,
+ inliningBalance, [&] (CodeBlock* codeBlock) {
+ emitFunctionChecks(callLinkStatus[0], callTargetNode, thisArgument);
+
+ // If we have a varargs call, we want to extract the arguments right now.
+ if (InlineCallFrame::isVarargs(kind)) {
+ int remappedRegisterOffset =
+ m_inlineStackTop->remapOperand(VirtualRegister(registerOffset)).offset();
+
+ ensureLocals(VirtualRegister(remappedRegisterOffset).toLocal());
+
+ int argumentStart = registerOffset + JSStack::CallFrameHeaderSize;
+ int remappedArgumentStart =
+ m_inlineStackTop->remapOperand(VirtualRegister(argumentStart)).offset();
+
+ LoadVarargsData* data = m_graph.m_loadVarargsData.add();
+ data->start = VirtualRegister(remappedArgumentStart + 1);
+ data->count = VirtualRegister(remappedRegisterOffset + JSStack::ArgumentCount);
+ data->offset = argumentsOffset;
+ data->limit = maxNumArguments;
+ data->mandatoryMinimum = mandatoryMinimum;
+
+ addToGraph(LoadVarargs, OpInfo(data), get(argumentsArgument));
+
+ // LoadVarargs may OSR exit. Hence, we need to keep alive callTargetNode, thisArgument
+ // and argumentsArgument for the baseline JIT. However, we only need a Phantom for
+ // callTargetNode because the other 2 are still in use and alive at this point.
+ addToGraph(Phantom, callTargetNode);
+
+ // In DFG IR before SSA, we cannot insert control flow between after the
+ // LoadVarargs and the last SetArgument. This isn't a problem once we get to DFG
+ // SSA. Fortunately, we also have other reasons for not inserting control flow
+ // before SSA.
+
+ VariableAccessData* countVariable = newVariableAccessData(
+ VirtualRegister(remappedRegisterOffset + JSStack::ArgumentCount));
+ // This is pretty lame, but it will force the count to be flushed as an int. This doesn't
+ // matter very much, since our use of a SetArgument and Flushes for this local slot is
+ // mostly just a formality.
+ countVariable->predict(SpecInt32);
+ countVariable->mergeIsProfitableToUnbox(true);
+ Node* setArgumentCount = addToGraph(SetArgument, OpInfo(countVariable));
+ m_currentBlock->variablesAtTail.setOperand(countVariable->local(), setArgumentCount);
+
+ set(VirtualRegister(argumentStart), get(thisArgument), ImmediateNakedSet);
+ for (unsigned argument = 1; argument < maxNumArguments; ++argument) {
+ VariableAccessData* variable = newVariableAccessData(
+ VirtualRegister(remappedArgumentStart + argument));
+ variable->mergeShouldNeverUnbox(true); // We currently have nowhere to put the type check on the LoadVarargs. LoadVarargs is effectful, so after it finishes, we cannot exit.
+
+ // For a while it had been my intention to do things like this inside the
+ // prediction injection phase. But in this case it's really best to do it here,
+ // because it's here that we have access to the variable access datas for the
+ // inlining we're about to do.
+ //
+ // Something else that's interesting here is that we'd really love to get
+ // predictions from the arguments loaded at the callsite, rather than the
+ // arguments received inside the callee. But that probably won't matter for most
+ // calls.
+ if (codeBlock && argument < static_cast<unsigned>(codeBlock->numParameters())) {
+ ConcurrentJITLocker locker(codeBlock->m_lock);
+ if (ValueProfile* profile = codeBlock->valueProfileForArgument(argument))
+ variable->predict(profile->computeUpdatedPrediction(locker));
+ }
+
+ Node* setArgument = addToGraph(SetArgument, OpInfo(variable));
+ m_currentBlock->variablesAtTail.setOperand(variable->local(), setArgument);
+ }
+ }
+ });
+ if (verbose) {
+ dataLog("Done inlining (simple).\n");
+ dataLog("Stack: ", currentCodeOrigin(), "\n");
+ dataLog("Result: ", result, "\n");
+ }
+ return result;
+ }
+
+ // We need to create some kind of switch over callee. For now we only do this if we believe that
+ // we're in the top tier. We have two reasons for this: first, it provides us an opportunity to
+ // do more detailed polyvariant/polymorphic profiling; and second, it reduces compile times in
+ // the DFG. And by polyvariant profiling we mean polyvariant profiling of *this* call. Note that
+ // we could improve that aspect of this by doing polymorphic inlining but having the profiling
+ // also.
+ if (!isFTL(m_graph.m_plan.mode) || !Options::enablePolymorphicCallInlining()
+ || InlineCallFrame::isVarargs(kind)) {
+ if (verbose) {
+ dataLog("Bailing inlining (hard).\n");
+ dataLog("Stack: ", currentCodeOrigin(), "\n");