blob: d9b8463c7e8acc0a7a833d318130b3a16a0c61f0 [file]
/*
* Copyright (C) 2014-2022, 2026 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InlineCacheHandler.h"
#if ENABLE(JIT)
#include "CacheableIdentifierInlines.h"
#include "CodeBlock.h"
#include "GetterSetterAccessCase.h"
#include "InlineCacheCompiler.h"
#include "InstanceOfAccessCase.h"
#include "JSModuleEnvironment.h"
#include "ModuleNamespaceAccessCase.h"
#include "PropertyInlineCache.h"
#include "SharedJITStubSet.h"
#include "StructureInlines.h"
namespace JSC {
WTF_MAKE_TZONE_ALLOCATED_IMPL(InlineCacheHandler);
WTF_MAKE_TZONE_ALLOCATED_IMPL(InlineCacheHandlerWithJSCall);
void InlineCacheHandler::dump(PrintStream& out) const
{
if (m_callTarget)
out.print(m_callTarget);
}
InlineCacheHandler::InlineCacheHandler()
{
disableThreadingChecks();
}
InlineCacheHandler::InlineCacheHandler(bool makesJSCalls, Ref<InlineCacheHandler>&& previous, Ref<PolymorphicAccessJITStubRoutine>&& stubRoutine, std::unique_ptr<PropertyInlineCacheClearingWatchpoint>&& watchpoint, CacheType cacheType)
: m_callTarget(stubRoutine->code().code().template retagged<JITStubRoutinePtrTag>())
, m_jumpTarget(CodePtr<NoPtrTag> { m_callTarget.retagged<NoPtrTag>().dataLocation<uint8_t*>() + prologueSizeInBytesDataIC }.template retagged<JITStubRoutinePtrTag>())
, m_cacheType(cacheType)
, m_makesJSCalls(makesJSCalls)
, m_next(WTF::move(previous))
, m_stubRoutine(WTF::move(stubRoutine))
, m_watchpoint(WTF::move(watchpoint))
{
disableThreadingChecks();
}
InlineCacheHandlerWithJSCall::InlineCacheHandlerWithJSCall(Ref<InlineCacheHandler>&& previous, Ref<PolymorphicAccessJITStubRoutine>&& stubRoutine, std::unique_ptr<PropertyInlineCacheClearingWatchpoint>&& watchpoint, CacheType cacheType)
: InlineCacheHandler(true, WTF::move(previous), WTF::move(stubRoutine), WTF::move(watchpoint), cacheType)
{
}
void InlineCacheHandler::operator delete(InlineCacheHandler* handler, std::destroying_delete_t)
{
if (auto* withJSCall = dynamicDowncast<InlineCacheHandlerWithJSCall>(handler)) {
std::destroy_at(withJSCall);
InlineCacheHandlerWithJSCall::freeAfterDestruction(withJSCall);
} else {
std::destroy_at(handler);
InlineCacheHandler::freeAfterDestruction(handler);
}
}
Ref<InlineCacheHandler> InlineCacheHandler::create(Ref<InlineCacheHandler>&& previous, CodeBlock* codeBlock, PropertyInlineCache& propertyCache, Ref<PolymorphicAccessJITStubRoutine>&& stubRoutine, std::unique_ptr<PropertyInlineCacheClearingWatchpoint>&& watchpoint, unsigned callLinkInfoCount)
{
VM& vm = codeBlock->vm();
if (callLinkInfoCount) {
auto result = adoptRef(*new InlineCacheHandlerWithJSCall(WTF::move(previous), WTF::move(stubRoutine), WTF::move(watchpoint), CacheType::Unset));
result->m_callLinkInfo.initialize(vm, codeBlock, CallLinkInfo::CallType::Call, propertyCache.codeOrigin);
result->m_uid = propertyCache.identifier().uid();
return result;
}
auto result = adoptRef(*new InlineCacheHandler(false, WTF::move(previous), WTF::move(stubRoutine), WTF::move(watchpoint), CacheType::Unset));
result->m_uid = propertyCache.identifier().uid();
return result;
}
Ref<InlineCacheHandler> InlineCacheHandler::createPreCompiled(Ref<InlineCacheHandler>&& previous, CodeBlock* codeBlock, PropertyInlineCache& propertyCache, Ref<PolymorphicAccessJITStubRoutine>&& stubRoutine, std::unique_ptr<PropertyInlineCacheClearingWatchpoint>&& watchpoint, AccessCase& accessCase, CacheType cacheType)
{
bool makesJSCalls = JSC::doesJSCalls(accessCase.m_type);
VM& vm = codeBlock->vm();
Ref<InlineCacheHandler> result = [&]() -> Ref<InlineCacheHandler> {
if (makesJSCalls) {
auto handler = adoptRef(*new InlineCacheHandlerWithJSCall(WTF::move(previous), WTF::move(stubRoutine), WTF::move(watchpoint), cacheType));
handler->m_callLinkInfo.initialize(vm, codeBlock, CallLinkInfo::CallType::Call, propertyCache.codeOrigin);
return handler;
}
return adoptRef(*new InlineCacheHandler(false, WTF::move(previous), WTF::move(stubRoutine), WTF::move(watchpoint), cacheType));
}();
result->m_structureID = accessCase.structureID();
result->m_offset = accessCase.offset();
result->m_uid = propertyCache.identifier().uid();
if (!result->m_uid)
result->m_uid = accessCase.uid();
switch (accessCase.m_type) {
case AccessCase::Load:
case AccessCase::GetGetter:
case AccessCase::Getter:
case AccessCase::Setter: {
result->u.s1.m_holder = nullptr;
if (auto* holder = accessCase.tryGetAlternateBase())
result->u.s1.m_holder = holder;
break;
}
case AccessCase::ProxyObjectLoad: {
result->u.s1.m_holder = accessCase.identifier().cell();
break;
}
case AccessCase::Delete:
case AccessCase::SetPrivateBrand: {
result->u.s2.m_newStructureID = accessCase.newStructureID();
break;
}
case AccessCase::Transition: {
result->u.s2.m_newStructureID = accessCase.newStructureID();
result->u.s2.m_newSize = accessCase.newStructure()->outOfLineCapacity() * sizeof(JSValue);
result->u.s2.m_oldSize = accessCase.structure()->outOfLineCapacity() * sizeof(JSValue);
break;
}
case AccessCase::CustomAccessorGetter:
case AccessCase::CustomAccessorSetter:
case AccessCase::CustomValueGetter:
case AccessCase::CustomValueSetter: {
result->u.s1.m_holder = nullptr;
Structure* currStructure = accessCase.structure();
if (auto* holder = accessCase.tryGetAlternateBase()) {
currStructure = holder->structure();
result->u.s1.m_holder = holder;
}
result->u.s1.m_globalObject = currStructure->realm();
result->u.s1.m_customAccessor = accessCase.as<GetterSetterAccessCase>().customAccessor().taggedPtr();
break;
}
case AccessCase::InstanceOfHit:
case AccessCase::InstanceOfMiss: {
result->u.s1.m_holder = accessCase.as<InstanceOfAccessCase>().prototype();
break;
}
case AccessCase::ModuleNamespaceLoad: {
auto& derived = accessCase.as<ModuleNamespaceAccessCase>();
result->u.s3.m_moduleNamespaceObject = derived.moduleNamespaceObject();
result->u.s3.m_moduleVariableSlot = &derived.moduleEnvironment()->variableAt(derived.scopeOffset());
break;
}
case AccessCase::CheckPrivateBrand: {
break;
}
default:
break;
}
return result;
}
Ref<InlineCacheHandler> InlineCacheHandler::createNonHandlerSlowPath(CodePtr<JITStubRoutinePtrTag> slowPath)
{
auto result = adoptRef(*new InlineCacheHandler);
result->m_callTarget = slowPath;
result->m_jumpTarget = slowPath;
return result;
}
Ref<InlineCacheHandler> InlineCacheHandler::createSlowPath(VM& vm, AccessType accessType)
{
auto result = adoptRef(*new InlineCacheHandler);
auto codeRef = InlineCacheCompiler::generateSlowPathCode(vm, accessType);
result->m_callTarget = codeRef.code().template retagged<JITStubRoutinePtrTag>();
result->m_jumpTarget = CodePtr<NoPtrTag> { codeRef.retaggedCode<NoPtrTag>().dataLocation<uint8_t*>() + prologueSizeInBytesDataIC }.template retagged<JITStubRoutinePtrTag>();
return result;
}
Ref<InlineCacheHandler> InlineCacheCompiler::generateSlowPathHandler(VM& vm, AccessType accessType)
{
ASSERT(!isCompilationThread());
if (auto handler = vm.m_sharedJITStubs->getSlowPathHandler(accessType))
return handler.releaseNonNull();
auto handler = InlineCacheHandler::createSlowPath(vm, accessType);
vm.m_sharedJITStubs->setSlowPathHandler(accessType, handler);
return handler;
}
template<typename Visitor>
void InlineCacheHandler::propagateTransitions(Visitor& visitor) const
{
if (m_accessCase)
m_accessCase->propagateTransitions(visitor);
}
template void InlineCacheHandler::propagateTransitions(AbstractSlotVisitor&) const;
template void InlineCacheHandler::propagateTransitions(SlotVisitor&) const;
template<typename Visitor>
void InlineCacheHandler::visitAggregateImpl(Visitor& visitor)
{
if (m_accessCase)
m_accessCase->visitAggregate(visitor);
}
DEFINE_VISIT_AGGREGATE(InlineCacheHandler);
void InlineCacheHandler::aboutToDie()
{
if (m_stubRoutine)
m_stubRoutine->aboutToDie();
// A reference to InlineCacheHandler may keep it alive later than the CodeBlock that "owns" this
// watchpoint but the watchpoint must not fire after the CodeBlock has finished destruction,
// so clear the watchpoint eagerly.
m_watchpoint.reset();
}
bool InlineCacheHandler::visitWeak(VM& vm)
{
bool isValid = true;
if (auto* withJSCall = dynamicDowncast<InlineCacheHandlerWithJSCall>(*this))
withJSCall->m_callLinkInfo.visitWeak(vm);
if (m_accessCase)
isValid &= m_accessCase->visitWeak(vm);
if (m_stubRoutine)
isValid &= m_stubRoutine->visitWeak(vm);
return isValid;
}
void InlineCacheHandler::addOwner(CodeBlock* codeBlock)
{
if (!m_stubRoutine)
return;
m_stubRoutine->addOwner(codeBlock);
}
void InlineCacheHandler::removeOwner(CodeBlock* codeBlock)
{
if (!m_stubRoutine)
return;
m_stubRoutine->removeOwner(codeBlock);
}
} // namespace JSC
#endif // ENABLE(JIT)