Skip to content

Commit

Permalink
[JSC] Do not fallback to slow C++ JSBoundFunction code
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=266311
rdar://119582604

Reviewed by Justin Michaud.

Let's not fallback to slow C++ JSBoundFunction code, which invokes function through interpreter again.
We can just materialize JIT code from JSBoundFunction thunk and continue running from JS world instead of jumping to C++ and jumping back to JS.

* Source/JavaScriptCore/jit/JITOperations.cpp:
(JSC::materializeTargetCode):
(JSC::JSC_DEFINE_JIT_OPERATION):
* Source/JavaScriptCore/jit/JITOperations.h:
* Source/JavaScriptCore/jit/ThunkGenerators.cpp:
(JSC::boundFunctionCallGenerator):
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):

Canonical link: https://commits.webkit.org/272004@main
  • Loading branch information
Constellation committed Dec 13, 2023
1 parent 424d67c commit f4e684d
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 22 deletions.
31 changes: 22 additions & 9 deletions Source/JavaScriptCore/jit/JITOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,18 +181,10 @@ JSC_DEFINE_JIT_OPERATION(operationGetWrappedValueForCaller, EncodedJSValue, (JSR
RELEASE_AND_RETURN(scope, JSValue::encode(getWrappedValue(globalObject, globalObject, JSValue::decode(encodedValue))));
}

JSC_DEFINE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, UGPRPair, (JSRemoteFunction* callee))
static inline UGPRPair materializeTargetCode(VM& vm, JSFunction* targetFunction)
{
JSGlobalObject* globalObject = callee->globalObject();
VM& vm = globalObject->vm();

CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
auto throwScope = DECLARE_THROW_SCOPE(vm);

ASSERT(isRemoteFunction(callee));

auto* targetFunction = jsCast<JSFunction*>(callee->targetFunction()); // We call this function only when JSRemoteFunction's target is JSFunction.
ExecutableBase* executable = targetFunction->executable();

// Force the executable to cache its arity entrypoint.
Expand All @@ -209,6 +201,27 @@ JSC_DEFINE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, UGPRPair,
}
}

JSC_DEFINE_JIT_OPERATION(operationMaterializeBoundFunctionTargetCode, UGPRPair, (JSBoundFunction* callee))
{
JSGlobalObject* globalObject = callee->globalObject();
VM& vm = globalObject->vm();
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
auto* targetFunction = jsCast<JSFunction*>(callee->targetFunction()); // We call this function only when JSBoundFunction's target is JSFunction.
return materializeTargetCode(vm, targetFunction);
}

JSC_DEFINE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, UGPRPair, (JSRemoteFunction* callee))
{
JSGlobalObject* globalObject = callee->globalObject();
VM& vm = globalObject->vm();
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
ASSERT(isRemoteFunction(callee));
auto* targetFunction = jsCast<JSFunction*>(callee->targetFunction()); // We call this function only when JSRemoteFunction's target is JSFunction.
return materializeTargetCode(vm, targetFunction);
}

JSC_DEFINE_JIT_OPERATION(operationThrowRemoteFunctionException, EncodedJSValue, (JSRemoteFunction* callee))
{
JSGlobalObject* globalObject = callee->globalObject();
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/jit/JITOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CallFrame;
class CallLinkInfo;
class CodeBlock;
class JSArray;
class JSBoundFunction;
class JSCell;
class JSFunction;
class JSGlobalObject;
Expand Down Expand Up @@ -158,6 +159,7 @@ JSC_DECLARE_JIT_OPERATION(operationThrowIteratorResultIsNotObject, void, (JSGlob
JSC_DECLARE_JIT_OPERATION(operationGetWrappedValueForCaller, EncodedJSValue, (JSRemoteFunction*, EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationGetWrappedValueForTarget, EncodedJSValue, (JSRemoteFunction*, EncodedJSValue));
JSC_DECLARE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, UGPRPair, (JSRemoteFunction*));
JSC_DECLARE_JIT_OPERATION(operationMaterializeBoundFunctionTargetCode, UGPRPair, (JSBoundFunction*));
JSC_DECLARE_JIT_OPERATION(operationThrowRemoteFunctionException, EncodedJSValue, (JSRemoteFunction*));

JSC_DECLARE_JIT_OPERATION(operationThrowStackOverflowError, void, (CodeBlock*));
Expand Down
54 changes: 41 additions & 13 deletions Source/JavaScriptCore/jit/ThunkGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1446,32 +1446,60 @@ MacroAssemblerCodeRef<JITThunkPtrTag> boundFunctionCallGenerator(VM& vm)
jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, JSBoundFunction::offsetOfTargetFunction()), GPRInfo::regT2);
jit.storeCell(GPRInfo::regT2, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee));
jit.loadPtr(CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutableOrRareData()), GPRInfo::regT0);
auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT0, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
jit.loadPtr(CCallHelpers::Address(GPRInfo::regT0, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), GPRInfo::regT0);
jit.loadPtr(CCallHelpers::Address(GPRInfo::regT2, JSFunction::offsetOfExecutableOrRareData()), GPRInfo::regT1);
auto hasExecutable = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT1, CCallHelpers::TrustedImm32(JSFunction::rareDataTag));
jit.loadPtr(CCallHelpers::Address(GPRInfo::regT1, FunctionRareData::offsetOfExecutable() - JSFunction::rareDataTag), GPRInfo::regT1);
hasExecutable.link(&jit);
jit.loadPtr(
CCallHelpers::Address(
GPRInfo::regT0, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)),
GPRInfo::regT1);
jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT1).linkThunk(CodeLocationLabel<JITThunkPtrTag> { vm.jitStubs->ctiNativeTailCallWithoutSavedTags(vm) }, &jit);
GPRInfo::regT1, ExecutableBase::offsetOfJITCodeWithArityCheckFor(CodeForCall)),
GPRInfo::regT2);
auto codeNotExists = jit.branchTestPtr(CCallHelpers::Zero, GPRInfo::regT2);
auto isNative = jit.branchIfNotType(GPRInfo::regT0, FunctionExecutableType);
auto isNative = jit.branchIfNotType(GPRInfo::regT1, FunctionExecutableType);
jit.loadPtr(
CCallHelpers::Address(
GPRInfo::regT0, FunctionExecutable::offsetOfCodeBlockForCall()),
GPRInfo::regT2);
jit.storePtr(GPRInfo::regT2, CCallHelpers::calleeFrameCodeBlockBeforeCall());
GPRInfo::regT1, FunctionExecutable::offsetOfCodeBlockForCall()),
GPRInfo::regT3);
jit.storePtr(GPRInfo::regT3, CCallHelpers::calleeFrameCodeBlockBeforeCall());
isNative.link(&jit);
auto dispatch = jit.label();
emitPointerValidation(jit, GPRInfo::regT1, JSEntryPtrTag);
jit.call(GPRInfo::regT1, JSEntryPtrTag);
emitPointerValidation(jit, GPRInfo::regT2, JSEntryPtrTag);
jit.call(GPRInfo::regT2, JSEntryPtrTag);
jit.emitFunctionEpilogue();
jit.ret();
codeNotExists.link(&jit);
CCallHelpers::JumpList exceptionChecks;
// If we find that the JIT code is null (i.e. has been jettisoned), then we need to re-materialize it for the call below. Note that we know
// that operationMaterializeBoundFunctionTargetCode should be able to re-materialize the JIT code (except for any OOME) because we only
// went down this code path after we found a non-null JIT code (in the noCode check) above i.e. it should be possible to materialize the JIT code.
// FIXME: Windows x64 is not supported since operationMaterializeBoundFunctionTargetCode returns UGPRPair.
jit.setupArguments<decltype(operationMaterializeBoundFunctionTargetCode)>(GPRInfo::regT0);
jit.prepareCallOperation(vm);
jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationMaterializeBoundFunctionTargetCode)), GPRInfo::nonArgGPR0);
emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
jit.call(GPRInfo::nonArgGPR0, OperationPtrTag);
exceptionChecks.append(jit.emitJumpIfException(vm));
jit.storePtr(GPRInfo::returnValueGPR2, CCallHelpers::calleeFrameCodeBlockBeforeCall());
jit.move(GPRInfo::returnValueGPR, GPRInfo::regT2);
jit.jump().linkTo(dispatch, &jit);
exceptionChecks.link(&jit);
jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame, GPRInfo::argumentGPR0);
jit.setupArguments<decltype(operationLookupExceptionHandler)>(CCallHelpers::TrustedImmPtr(&vm));
jit.prepareCallOperation(vm);
jit.move(CCallHelpers::TrustedImmPtr(tagCFunction<OperationPtrTag>(operationLookupExceptionHandler)), GPRInfo::nonArgGPR0);
emitPointerValidation(jit, GPRInfo::nonArgGPR0, OperationPtrTag);
jit.call(GPRInfo::nonArgGPR0, OperationPtrTag);
jit.jumpToExceptionHandler(vm);
LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::BoundFunctionThunk);
return FINALIZE_THUNK(linkBuffer, JITThunkPtrTag, "Specialized thunk for bound function calls with no arguments");
}
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,10 +669,12 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
return imulThunkGenerator;
case RandomIntrinsic:
return randomThunkGenerator;
#if !OS(WINDOWS)
case BoundFunctionCallIntrinsic:
return boundFunctionCallGenerator;
case RemoteFunctionCallIntrinsic:
return remoteFunctionCallGenerator;
#endif
case NumberConstructorIntrinsic:
return numberConstructorCallThunkGenerator;
case StringConstructorIntrinsic:
Expand Down

0 comments on commit f4e684d

Please sign in to comment.