Skip to content

Commit dd4b91a

Browse files
author
Mark Lam
committed
Fold CallFrameClosure into CachedCall.
https://bugs.webkit.org/show_bug.cgi?id=253481 rdar://106341933 Reviewed by Yusuke Suzuki. CallFrameClosure is always embedded into and used with a CachedCall. This patch makes the following changes: 1. By folding it into CachedCall, we can remove many redundancies e.g. a protoCallFrame pointer, a JSFunction pointer, a VM pointer, and argumentCountIncludingThis. 2. CallFrameClosure::parameterCountIncludingThis was also never used. So, remove it. 3. Remove CachedCall::m_interpreter because it is always as an offset from CachedCall::m_vm. 4. Make use of Interpreter::vm() to compute VM&. This eliminates the need to pass globalObject to a few Interpreter::executeXXX functions where the sole purpose of the globalObject is for computing VM&. 5. Add an optimization in Interpreter::executeCachedCall where we can skip the call to ScriptExecutable::prepareForExecution if the addressForCall is unchanged. 6. Remove the now unneeded CallFrameClosure. 7. Rename Interpreter::prepareForRepeatCall to prepareForCachedCall to match what it's actually preparing for. This patch produces the following gains on microbenchmarks: base new cpp-to-js-call-as-first-entry 77.6193+-5.2323 74.1395+-0.7745 might be 1.0469x faster cpp-to-js-call 70.7867+-0.4565 70.5623+-0.3648 cpp-to-js-cached-call 80.8428+-0.2077 ^ 58.5465+-0.1468 ^ definitely 1.3808x faster * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp: (JSC::DebuggerCallFrame::evaluateWithScopeExtension): * Source/JavaScriptCore/interpreter/CachedCall.h: (JSC::CachedCall::CachedCall): (JSC::CachedCall::call): (JSC::CachedCall::function): (JSC::CachedCall::functionExecutable): (JSC::CachedCall::scope): * Source/JavaScriptCore/interpreter/CallFrameClosure.h: Removed. * Source/JavaScriptCore/interpreter/Interpreter.cpp: (JSC::eval): (JSC::Interpreter::executeProgram): (JSC::Interpreter::executeCall): (JSC::Interpreter::executeConstruct): (JSC::Interpreter::prepareForCachedCall): (JSC::Interpreter::executeEval): (JSC::Interpreter::executeModuleProgram): (JSC::Interpreter::prepareForRepeatCall): Deleted. * Source/JavaScriptCore/interpreter/Interpreter.h: * Source/JavaScriptCore/interpreter/InterpreterInlines.h: (JSC::Interpreter::executeCachedCall): * Source/JavaScriptCore/runtime/CallData.cpp: (JSC::call): * Source/JavaScriptCore/runtime/ConstructData.cpp: (JSC::construct): * Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp: (JSC::JSC_DEFINE_HOST_FUNCTION): * Source/JavaScriptCore/runtime/ShadowRealmPrototype.cpp: (JSC::JSC_DEFINE_HOST_FUNCTION): * Source/JavaScriptCore/runtime/VM.h: Canonical link: https://commits.webkit.org/261311@main
1 parent f2a535b commit dd4b91a

11 files changed

Lines changed: 87 additions & 123 deletions

File tree

‎Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5041,7 +5041,6 @@
50415041
A7F2996917A0BB670010417A /* FTLFail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLFail.cpp; path = ftl/FTLFail.cpp; sourceTree = "<group>"; };
50425042
A7F2996A17A0BB670010417A /* FTLFail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLFail.h; path = ftl/FTLFail.h; sourceTree = "<group>"; };
50435043
A7F8690E0F9584A100558697 /* CachedCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedCall.h; sourceTree = "<group>"; };
5044-
A7F869EC0F95C2EC00558697 /* CallFrameClosure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallFrameClosure.h; sourceTree = "<group>"; };
50455044
A7F9935D0FD7325100A0B2D0 /* JSONObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONObject.h; sourceTree = "<group>"; };
50465045
A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSONObject.cpp; sourceTree = "<group>"; };
50475046
A7FB604B103F5EAB0017A286 /* PropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyDescriptor.h; sourceTree = "<group>"; };
@@ -6613,7 +6612,6 @@
66136612
796DAA2A1E89CCD6005DF24A /* CalleeBits.h */,
66146613
1429D8DB0ED2205B00B89619 /* CallFrame.cpp */,
66156614
1429D8DC0ED2205B00B89619 /* CallFrame.h */,
6616-
A7F869EC0F95C2EC00558697 /* CallFrameClosure.h */,
66176615
FEA3BBA7212B655800E93AD1 /* CallFrameInlines.h */,
66186616
5338E2A62396EFEC00C61BAD /* CheckpointOSRExitSideState.h */,
66196617
1429D85B0ED218E900B89619 /* CLoopStack.cpp */,
@@ -9823,7 +9821,6 @@
98239821
isa = PBXHeadersBuildPhase;
98249822
buildActionMask = 2147483647;
98259823
files = (
9826-
BC18C4140E16F5CD00B34460 /* JavaScriptCore.h in Headers */,
98279824
0FFA549816B8835300B3A982 /* A64DOpcode.h in Headers */,
98289825
0F1FE51C1922A3BC006987C5 /* AbortReason.h in Headers */,
98299826
860161E30F3A83C100F84710 /* AbstractMacroAssembler.h in Headers */,
@@ -10739,6 +10736,7 @@
1073910736
70DC3E0A1B2DF2C700054299 /* IteratorPrototype.h in Headers */,
1074010737
BC18C4130E16F5CD00B34460 /* JavaScript.h in Headers */,
1074110738
A503FA1A188E0FB000110F14 /* JavaScriptCallFrame.h in Headers */,
10739+
BC18C4140E16F5CD00B34460 /* JavaScriptCore.h in Headers */,
1074210740
BC18C4150E16F5CD00B34460 /* JavaScriptCorePrefix.h in Headers */,
1074310741
1429D9300ED22D7000B89619 /* JIT.h in Headers */,
1074410742
FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */,

‎Source/JavaScriptCore/debugger/DebuggerCallFrame.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ JSValue DebuggerCallFrame::evaluateWithScopeExtension(VM& vm, const String& scri
263263
globalObject->setGlobalScopeExtension(JSWithScope::create(vm, globalObject, ignoredPreviousScope, scopeExtensionObject));
264264
}
265265

266-
JSValue result = vm.interpreter.executeEval(eval, globalObject, debuggerCallFrame->thisValue(vm), debuggerCallFrame->scope(vm)->jsScope());
266+
JSValue result = vm.interpreter.executeEval(eval, debuggerCallFrame->thisValue(vm), debuggerCallFrame->scope(vm)->jsScope());
267267
if (UNLIKELY(catchScope.exception())) {
268268
exception = catchScope.exception();
269269
catchScope.clearException();

‎Source/JavaScriptCore/interpreter/CachedCall.h‎

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525

2626
#pragma once
2727

28-
#include "CallFrameClosure.h"
2928
#include "ExceptionHelpers.h"
3029
#include "JSFunction.h"
3130
#include "Interpreter.h"
3231
#include "ProtoCallFrame.h"
3332
#include "VMEntryScope.h"
3433
#include "VMInlines.h"
3534
#include <wtf/ForbidHeapAllocation.h>
35+
#include <wtf/Scope.h>
3636

3737
namespace JSC {
3838

@@ -42,48 +42,68 @@ class CachedCall {
4242
public:
4343
CachedCall(JSGlobalObject* globalObject, JSFunction* function, int argumentCount)
4444
: m_vm(globalObject->vm())
45-
, m_interpreter(m_vm.interpreter)
4645
, m_entryScope(m_vm, function->scope()->globalObject())
46+
, m_functionExecutable(function->jsExecutable())
47+
, m_scope(function->scope())
4748
{
48-
VM& vm = m_entryScope.vm();
49+
VM& vm = m_vm;
4950
auto scope = DECLARE_THROW_SCOPE(vm);
50-
51-
ASSERT(!function->isHostFunctionNonInline());
52-
if (LIKELY(vm.isSafeToRecurseSoft())) {
53-
m_arguments.ensureCapacity(argumentCount);
54-
if (LIKELY(!m_arguments.hasOverflowed()))
55-
m_closure = m_interpreter.prepareForRepeatCall(function->jsExecutable(), &m_protoCallFrame, function, argumentCount + 1, function->scope(), m_arguments);
56-
else
57-
throwOutOfMemoryError(globalObject, scope);
58-
} else
59-
throwStackOverflowError(globalObject, scope);
6051
#if ASSERT_ENABLED
61-
m_valid = !scope.exception();
52+
auto updateValidStatus = makeScopeExit([&] {
53+
m_valid = !scope.exception();
54+
});
6255
#endif
56+
ASSERT(!function->isHostFunctionNonInline());
57+
if (UNLIKELY(!vm.isSafeToRecurseSoft())) {
58+
throwStackOverflowError(globalObject, scope);
59+
return;
60+
}
61+
62+
m_arguments.ensureCapacity(argumentCount);
63+
if (UNLIKELY(m_arguments.hasOverflowed())) {
64+
throwOutOfMemoryError(globalObject, scope);
65+
return;
66+
}
67+
68+
scope.release();
69+
m_vm.interpreter.prepareForCachedCall(*this, function, argumentCount + 1, m_arguments);
6370
}
6471

6572
ALWAYS_INLINE JSValue call()
6673
{
6774
ASSERT(m_valid);
6875
ASSERT(m_arguments.size() == static_cast<size_t>(m_protoCallFrame.argumentCount()));
69-
return m_interpreter.executeCachedCall(m_closure);
76+
return m_vm.interpreter.executeCachedCall(*this);
77+
}
78+
79+
JSFunction* function()
80+
{
81+
ASSERT(m_valid);
82+
return jsCast<JSFunction*>(m_protoCallFrame.calleeValue.unboxedCell());
7083
}
84+
FunctionExecutable* functionExecutable() { return m_functionExecutable; }
85+
JSScope* scope() { return m_scope; }
86+
7187
void setThis(JSValue v) { m_protoCallFrame.setThisValue(v); }
7288

7389
void clearArguments() { m_arguments.clear(); }
7490
void appendArgument(JSValue v) { m_arguments.append(v); }
7591
bool hasOverflowedArguments() { return m_arguments.hasOverflowed(); }
7692

7793
private:
78-
#if ASSERT_ENABLED
79-
bool m_valid { false };
80-
#endif
8194
VM& m_vm;
82-
Interpreter& m_interpreter;
8395
VMEntryScope m_entryScope;
8496
ProtoCallFrame m_protoCallFrame;
8597
MarkedArgumentBuffer m_arguments;
86-
CallFrameClosure m_closure;
98+
99+
FunctionExecutable* m_functionExecutable;
100+
JSScope* m_scope;
101+
void* m_addressForCall;
102+
#if ASSERT_ENABLED
103+
bool m_valid { false };
104+
#endif
105+
106+
friend class Interpreter;
87107
};
88108

89109
} // namespace JSC

‎Source/JavaScriptCore/interpreter/CallFrameClosure.h‎

Lines changed: 0 additions & 52 deletions
This file was deleted.

‎Source/JavaScriptCore/interpreter/Interpreter.cpp‎

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
#include "ArgList.h"
3535
#include "BatchedTransitionOptimizer.h"
3636
#include "Bytecodes.h"
37-
#include "CallFrameClosure.h"
3837
#include "CatchScope.h"
3938
#include "CheckpointOSRExitSideState.h"
4039
#include "CodeBlock.h"
@@ -88,11 +87,6 @@
8887

8988
namespace JSC {
9089

91-
VM& Interpreter::vm()
92-
{
93-
return *bitwise_cast<VM*>(bitwise_cast<uintptr_t>(this) - OBJECT_OFFSETOF(VM, interpreter));
94-
}
95-
9690
JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain, ECMAMode ecmaMode)
9791
{
9892
CallFrame* callerFrame = callFrame->callerFrame();
@@ -183,7 +177,7 @@ JSValue eval(CallFrame* callFrame, JSValue thisValue, JSScope* callerScopeChain,
183177
callerBaselineCodeBlock->directEvalCodeCache().set(globalObject, callerBaselineCodeBlock, programSource, bytecodeIndex, eval);
184178
}
185179

186-
RELEASE_AND_RETURN(scope, vm.interpreter.executeEval(eval, globalObject, thisValue, callerScopeChain));
180+
RELEASE_AND_RETURN(scope, vm.interpreter.executeEval(eval, thisValue, callerScopeChain));
187181
}
188182

189183
unsigned sizeOfVarargs(JSGlobalObject* globalObject, JSValue arguments, uint32_t firstVarArgOffset)
@@ -828,9 +822,9 @@ void Interpreter::notifyDebuggerOfExceptionToBeThrown(VM& vm, JSGlobalObject* gl
828822

829823
JSValue Interpreter::executeProgram(const SourceCode& source, JSGlobalObject*, JSObject* thisObj)
830824
{
831-
JSScope* scope = thisObj->globalObject()->globalScope();
832-
VM& vm = scope->vm();
825+
VM& vm = this->vm();
833826
auto throwScope = DECLARE_THROW_SCOPE(vm);
827+
JSScope* scope = thisObj->globalObject()->globalScope();
834828
JSGlobalObject* globalObject = scope->globalObject();
835829
JSCallee* globalCallee = globalObject->globalCallee();
836830

@@ -1125,9 +1119,9 @@ ALWAYS_INLINE JSValue Interpreter::executeCallImpl(VM& vm, JSObject* function, c
11251119
return JSValue::decode(vmEntryToNative(nativeFunction.taggedPtr(), &vm, &protoCallFrame));
11261120
}
11271121

1128-
JSValue Interpreter::executeCall(JSGlobalObject* lexicalGlobalObject, JSObject* function, const CallData& callData, JSValue thisValue, const ArgList& args)
1122+
JSValue Interpreter::executeCall(JSObject* function, const CallData& callData, JSValue thisValue, const ArgList& args)
11291123
{
1130-
VM& vm = lexicalGlobalObject->vm();
1124+
VM& vm = this->vm();
11311125
if (callData.type == CallData::Type::JS || !callData.native.isBoundFunction)
11321126
return executeCallImpl(vm, function, callData, thisValue, args);
11331127

@@ -1145,9 +1139,9 @@ JSValue Interpreter::executeCall(JSGlobalObject* lexicalGlobalObject, JSObject*
11451139
return executeBoundCall(vm, boundFunction, args);
11461140
}
11471141

1148-
JSObject* Interpreter::executeConstruct(JSGlobalObject* lexicalGlobalObject, JSObject* constructor, const CallData& constructData, const ArgList& args, JSValue newTarget)
1142+
JSObject* Interpreter::executeConstruct(JSObject* constructor, const CallData& constructData, const ArgList& args, JSValue newTarget)
11491143
{
1150-
VM& vm = lexicalGlobalObject->vm();
1144+
VM& vm = this->vm();
11511145
auto throwScope = DECLARE_THROW_SCOPE(vm);
11521146

11531147
auto clobberizeValidator = makeScopeExit([&] {
@@ -1218,38 +1212,34 @@ JSObject* Interpreter::executeConstruct(JSGlobalObject* lexicalGlobalObject, JSO
12181212
return asObject(JSValue::decode(result));
12191213
}
12201214

1221-
CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, const ArgList& args)
1215+
void Interpreter::prepareForCachedCall(CachedCall& cachedCall, JSFunction* function, int argumentCountIncludingThis, const ArgList& args)
12221216
{
1223-
VM& vm = scope->vm();
1217+
VM& vm = this->vm();
12241218
auto throwScope = DECLARE_THROW_SCOPE(vm);
12251219
throwScope.assertNoException();
12261220

12271221
// Compile the callee:
12281222
CodeBlock* newCodeBlock;
1229-
functionExecutable->prepareForExecution<FunctionExecutable>(vm, function, scope, CodeForCall, newCodeBlock);
1230-
RETURN_IF_EXCEPTION(throwScope, { });
1223+
cachedCall.functionExecutable()->prepareForExecution<FunctionExecutable>(vm, function, cachedCall.scope(), CodeForCall, newCodeBlock);
1224+
RETURN_IF_EXCEPTION(throwScope, void());
12311225

12321226
ASSERT(newCodeBlock);
12331227
newCodeBlock->m_shouldAlwaysBeInlined = false;
12341228

1235-
size_t argsCount = argumentCountIncludingThis;
1236-
1237-
protoCallFrame->init(newCodeBlock, function->globalObject(), function, jsUndefined(), argsCount, args.data());
1238-
// Return the successful closure:
1239-
CallFrameClosure result = { protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis };
1240-
return result;
1229+
cachedCall.m_addressForCall = newCodeBlock->jitCode()->addressForCall();
1230+
cachedCall.m_protoCallFrame.init(newCodeBlock, function->globalObject(), function, jsUndefined(), argumentCountIncludingThis, args.data());
12411231
}
12421232

1243-
JSValue Interpreter::executeEval(EvalExecutable* eval, JSGlobalObject* lexicalGlobalObject, JSValue thisValue, JSScope* scope)
1233+
JSValue Interpreter::executeEval(EvalExecutable* eval, JSValue thisValue, JSScope* scope)
12441234
{
1245-
VM& vm = scope->vm();
1235+
VM& vm = this->vm();
12461236
auto throwScope = DECLARE_THROW_SCOPE(vm);
12471237

12481238
auto clobberizeValidator = makeScopeExit([&] {
12491239
vm.didEnterVM = true;
12501240
});
12511241

1252-
ASSERT_UNUSED(lexicalGlobalObject, &vm == &lexicalGlobalObject->vm());
1242+
ASSERT(&vm == &scope->vm());
12531243
throwScope.assertNoException();
12541244
ASSERT(!vm.isCollectorBusyOnCurrentThread());
12551245
RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
@@ -1413,7 +1403,7 @@ JSValue Interpreter::executeEval(EvalExecutable* eval, JSGlobalObject* lexicalGl
14131403

14141404
JSValue Interpreter::executeModuleProgram(JSModuleRecord* record, ModuleProgramExecutable* executable, JSGlobalObject* lexicalGlobalObject, JSModuleEnvironment* scope, JSValue sentValue, JSValue resumeMode)
14151405
{
1416-
VM& vm = scope->vm();
1406+
VM& vm = this->vm();
14171407
auto throwScope = DECLARE_THROW_SCOPE(vm);
14181408

14191409
auto clobberizeValidator = makeScopeExit([&] {

‎Source/JavaScriptCore/interpreter/Interpreter.h‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ using WasmInstruction = BaseInstruction<WasmOpcodeTraits>;
5959
using JSOrWasmInstruction = std::variant<const JSInstruction*, const WasmInstruction*>;
6060

6161
class ArgList;
62+
class CachedCall;
6263
class CodeBlock;
6364
class EvalExecutable;
6465
class Exception;
@@ -78,7 +79,6 @@ using JSOrWasmInstruction = std::variant<const JSInstruction*, const WasmInstruc
7879
class SourceCode;
7980
class StackFrame;
8081
enum class HandlerType : uint8_t;
81-
struct CallFrameClosure;
8282
struct HandlerInfo;
8383
struct ProtoCallFrame;
8484

@@ -127,7 +127,7 @@ using JSOrWasmInstruction = std::variant<const JSInstruction*, const WasmInstruc
127127
public:
128128
Interpreter();
129129
~Interpreter();
130-
130+
131131
#if ENABLE(C_LOOP)
132132
CLoopStack& cloopStack() { return m_cloopStack; }
133133
const CLoopStack& cloopStack() const { return m_cloopStack; }
@@ -143,9 +143,9 @@ using JSOrWasmInstruction = std::variant<const JSInstruction*, const WasmInstruc
143143

144144
JSValue executeProgram(const SourceCode&, JSGlobalObject*, JSObject* thisObj);
145145
JSValue executeModuleProgram(JSModuleRecord*, ModuleProgramExecutable*, JSGlobalObject*, JSModuleEnvironment*, JSValue sentValue, JSValue resumeMode);
146-
JSValue executeCall(JSGlobalObject*, JSObject* function, const CallData&, JSValue thisValue, const ArgList&);
147-
JSObject* executeConstruct(JSGlobalObject*, JSObject* function, const CallData&, const ArgList&, JSValue newTarget);
148-
JSValue executeEval(EvalExecutable*, JSGlobalObject*, JSValue thisValue, JSScope*);
146+
JSValue executeCall(JSObject* function, const CallData&, JSValue thisValue, const ArgList&);
147+
JSObject* executeConstruct(JSObject* function, const CallData&, const ArgList&, JSValue newTarget);
148+
JSValue executeEval(EvalExecutable*, JSValue thisValue, JSScope*);
149149

150150
void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc);
151151

@@ -159,9 +159,9 @@ using JSOrWasmInstruction = std::variant<const JSInstruction*, const WasmInstruc
159159
private:
160160
enum ExecutionFlag { Normal, InitializeAndReturn };
161161

162-
CallFrameClosure prepareForRepeatCall(FunctionExecutable*, ProtoCallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*, const ArgList&);
162+
void prepareForCachedCall(CachedCall&, JSFunction*, int argumentCountIncludingThis, const ArgList&);
163163

164-
JSValue executeCachedCall(CallFrameClosure&);
164+
JSValue executeCachedCall(CachedCall&);
165165
JSValue executeBoundCall(VM&, JSBoundFunction*, const ArgList&);
166166
JSValue executeCallImpl(VM&, JSObject*, const CallData&, JSValue, const ArgList&);
167167

0 commit comments

Comments
 (0)