diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 76f0c0977b840..0cfe96f55375a 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -18856,14 +18856,29 @@ void SpeculativeJIT::compilePerformPromiseThenOneHandler(Node* node) #if USE(JSVALUE64) GPRTemporary packed(this); GPRReg packedGPR = packed.gpr(); +#if USE(BUN_JSC_ADDITIONS) + GPRTemporary asyncContext(this); + GPRReg asyncContextGPR = asyncContext.gpr(); +#endif constexpr unsigned pointerBits = CompactPointerTuple::maxNumberOfBitsInPointer; constexpr uint64_t pointerMask = (1ULL << pointerBits) - 1; constexpr uint64_t flagMask = static_cast(JSPromise::stateMask | JSPromise::inlineReactionKindMask) << pointerBits; constexpr uint64_t mask = pointerMask | flagMask; + JumpList slowCases; load64(Address(inputPromiseGPR, JSPromise::offsetOfPacked()), packedGPR); - Jump slowPath = branchTest64(NonZero, packedGPR, TrustedImm64(mask)); + slowCases.append(branchTest64(NonZero, packedGPR, TrustedImm64(mask))); + +#if USE(BUN_JSC_ADDITIONS) + // Inline reactions cannot carry an async context; when one is active, + // take the slow path so performPromiseThen captures it (see JSPromise.cpp). + loadLinkableConstant(LinkableConstant::globalObject(*this, node), asyncContextGPR); + loadPtr(Address(asyncContextGPR, JSGlobalObject::offsetOfAsyncContextData()), asyncContextGPR); + Jump noAsyncContextData = branchTestPtr(Zero, asyncContextGPR); + slowCases.append(branch64(NotEqual, Address(asyncContextGPR, JSInternalFieldObjectImpl<>::offsetOfInternalField(0)), TrustedImm64(JSValue::encode(jsUndefined())))); + noAsyncContextData.link(this); +#endif uint64_t orBits = static_cast(JSPromise::isHandledFlag | (static_cast(kind) << JSPromise::inlineReactionKindShift)) << pointerBits; or64(TrustedImm64(orBits), packedGPR); @@ -18872,7 +18887,7 @@ void SpeculativeJIT::compilePerformPromiseThenOneHandler(Node* node) store64(handlerGPR, Address(inputPromiseGPR, JSPromise::offsetOfSlot())); store64(packedGPR, Address(inputPromiseGPR, JSPromise::offsetOfPacked())); - addSlowPathGenerator(slowPathCall(slowPath, this, operationPerformPromiseThenOneHandler, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded, NoResult, LinkableConstant::globalObject(*this, node), inputPromiseGPR, handlerGPR, resultPromiseGPR, TrustedImm32(static_cast(kind)))); + addSlowPathGenerator(slowPathCall(slowCases, this, operationPerformPromiseThenOneHandler, NeedToSpill, ExceptionCheckRequirement::CheckNotNeeded, NoResult, LinkableConstant::globalObject(*this, node), inputPromiseGPR, handlerGPR, resultPromiseGPR, TrustedImm32(static_cast(kind)))); #else flushRegisters(); callOperationWithoutExceptionCheck(operationPerformPromiseThenOneHandler, LinkableConstant::globalObject(*this, node), inputPromiseGPR, handlerGPR, resultPromiseGPR, TrustedImm32(static_cast(kind))); diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index 0f8ee575a4e98..6204924103ad0 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp +++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp @@ -21974,11 +21974,31 @@ IGNORE_CLANG_WARNINGS_END LBasicBlock fastPath = m_out.newBlock(); LBasicBlock slowPath = m_out.newBlock(); LBasicBlock continuation = m_out.newBlock(); +#if USE(BUN_JSC_ADDITIONS) + LBasicBlock asyncContextDataCase = m_out.newBlock(); + LBasicBlock asyncContextCheck = m_out.newBlock(); +#endif LValue packed = m_out.load64(inputPromise, m_heaps.JSPromise_packed); +#if USE(BUN_JSC_ADDITIONS) + // Inline reactions cannot carry an async context; when one is active, + // take the slow path so performPromiseThen captures it (see JSPromise.cpp). + m_out.branch(m_out.notZero64(m_out.bitAnd(packed, m_out.constInt64(mask))), rarely(slowPath), usually(asyncContextDataCase)); + + LBasicBlock lastNext = m_out.appendTo(asyncContextDataCase, asyncContextCheck); + LValue asyncContextData = m_out.loadPtr(m_out.absolute(reinterpret_cast(globalObject) + JSGlobalObject::offsetOfAsyncContextData())); + m_out.branch(m_out.isNull(asyncContextData), unsure(fastPath), unsure(asyncContextCheck)); + + m_out.appendTo(asyncContextCheck, fastPath); + LValue asyncContext = m_out.load64(asyncContextData, m_heaps.JSInternalFieldObjectImpl_internalFields[0]); + m_out.branch(m_out.equal(asyncContext, m_out.constInt64(JSValue::ValueUndefined)), usually(fastPath), rarely(slowPath)); + + m_out.appendTo(fastPath, slowPath); +#else m_out.branch(m_out.notZero64(m_out.bitAnd(packed, m_out.constInt64(mask))), rarely(slowPath), usually(fastPath)); LBasicBlock lastNext = m_out.appendTo(fastPath, slowPath); +#endif LValue newPacked = m_out.bitOr(m_out.bitOr(packed, m_out.constInt64(orBits)), resultPromise); m_out.store64(handler, inputPromise, m_heaps.JSPromise_slot); m_out.store64(newPacked, inputPromise, m_heaps.JSPromise_packed); diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h index 386591fa5eefd..486e6b583a625 100644 --- a/Source/JavaScriptCore/runtime/JSGlobalObject.h +++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h @@ -753,6 +753,7 @@ class JSGlobalObject : public JSSegmentedVariableObject { #if USE(BUN_JSC_ADDITIONS) bool isAsyncContextTrackingEnabled() const { return m_isAsyncContextTrackingEnabled; } void setAsyncContextTrackingEnabled(bool isEnabled) { m_isAsyncContextTrackingEnabled = isEnabled; } + static constexpr ptrdiff_t offsetOfAsyncContextData() { return OBJECT_OFFSETOF(JSGlobalObject, m_asyncContextData); } #endif bool hasDebugger() const { return m_debugger; }