diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/jit/JITOperations.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/jit/JITOperations.cpp')
-rw-r--r-- | Source/JavaScriptCore/jit/JITOperations.cpp | 1731 |
1 files changed, 677 insertions, 1054 deletions
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp index 868eed755..578d15dac 100644 --- a/Source/JavaScriptCore/jit/JITOperations.cpp +++ b/Source/JavaScriptCore/jit/JITOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,43 +24,32 @@ */ #include "config.h" -#include "JITOperations.h" - #if ENABLE(JIT) +#include "JITOperations.h" +#include "Arguments.h" #include "ArrayConstructor.h" +#include "CallFrameInlines.h" #include "CommonSlowPaths.h" #include "DFGCompilationMode.h" #include "DFGDriver.h" #include "DFGOSREntry.h" -#include "DFGThunks.h" #include "DFGWorklist.h" -#include "Debugger.h" -#include "DirectArguments.h" #include "Error.h" -#include "ErrorHandlingScope.h" -#include "ExceptionFuzz.h" #include "GetterSetter.h" #include "HostCallReturnValue.h" #include "JIT.h" -#include "JITExceptions.h" +#include "JITOperationWrappers.h" #include "JITToDFGDeferredCompilationCallback.h" -#include "JSCInlines.h" -#include "JSGeneratorFunction.h" #include "JSGlobalObjectFunctions.h" -#include "JSLexicalEnvironment.h" -#include "JSPropertyNameEnumerator.h" +#include "JSNameScope.h" +#include "JSPropertyNameIterator.h" #include "JSStackInlines.h" #include "JSWithScope.h" -#include "LegacyProfiler.h" #include "ObjectConstructor.h" -#include "PropertyName.h" +#include "Operations.h" #include "Repatch.h" -#include "ScopedArguments.h" -#include "TestRunnerUtils.h" -#include "TypeProfilerLog.h" -#include "VMInlines.h" -#include <wtf/InlineASM.h> +#include "RepatchBuffer.h" namespace JSC { @@ -82,56 +71,33 @@ void * _ReturnAddress(void); #endif -void JIT_OPERATION operationThrowStackOverflowError(ExecState* exec, CodeBlock* codeBlock) +void JIT_OPERATION operationStackCheck(ExecState* exec, CodeBlock* codeBlock) { // We pass in our own code block, because the callframe hasn't been populated. VM* vm = codeBlock->vm(); - - VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame; - CallFrame* callerFrame = exec->callerFrame(vmEntryFrame); + CallFrame* callerFrame = exec->callerFrameSkippingVMEntrySentinel(); if (!callerFrame) callerFrame = exec; - NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame); - throwStackOverflowError(callerFrame); -} + NativeCallFrameTracer tracer(vm, callerFrame); -#if ENABLE(WEBASSEMBLY) -void JIT_OPERATION operationThrowDivideError(ExecState* exec) -{ - VM* vm = &exec->vm(); - VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame; - CallFrame* callerFrame = exec->callerFrame(vmEntryFrame); - - NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame); - ErrorHandlingScope errorScope(*vm); - vm->throwException(callerFrame, createError(callerFrame, ASCIILiteral("Division by zero or division overflow."))); -} - -void JIT_OPERATION operationThrowOutOfBoundsAccessError(ExecState* exec) -{ - VM* vm = &exec->vm(); - VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame; - CallFrame* callerFrame = exec->callerFrame(vmEntryFrame); + JSStack& stack = vm->interpreter->stack(); - NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame); - ErrorHandlingScope errorScope(*vm); - vm->throwException(callerFrame, createError(callerFrame, ASCIILiteral("Out-of-bounds access."))); + if (UNLIKELY(!stack.grow(&exec->registers()[virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset()]))) + vm->throwException(callerFrame, createStackOverflowError(callerFrame)); } -#endif int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec) { VM* vm = &exec->vm(); + CallFrame* callerFrame = exec->callerFrameSkippingVMEntrySentinel(); + NativeCallFrameTracer tracer(vm, callerFrame); + JSStack& stack = vm->interpreter->stack(); int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, &stack, CodeForCall); - if (missingArgCount < 0) { - VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame; - CallFrame* callerFrame = exec->callerFrame(vmEntryFrame); - NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame); - throwStackOverflowError(callerFrame); - } + if (missingArgCount < 0) + vm->throwException(callerFrame, createStackOverflowError(callerFrame)); return missingArgCount; } @@ -139,96 +105,105 @@ int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec) int32_t JIT_OPERATION operationConstructArityCheck(ExecState* exec) { VM* vm = &exec->vm(); + CallFrame* callerFrame = exec->callerFrameSkippingVMEntrySentinel(); + NativeCallFrameTracer tracer(vm, callerFrame); + JSStack& stack = vm->interpreter->stack(); int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, &stack, CodeForConstruct); - if (missingArgCount < 0) { - VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame; - CallFrame* callerFrame = exec->callerFrame(vmEntryFrame); - NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame); - throwStackOverflowError(callerFrame); - } + if (missingArgCount < 0) + vm->throwException(callerFrame, createStackOverflowError(callerFrame)); return missingArgCount; } -EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid) +EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo*, EncodedJSValue base, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - stubInfo->tookSlowPath = true; - JSValue baseValue = JSValue::decode(base); - PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); - Identifier ident = Identifier::fromUid(vm, uid); + PropertySlot slot(baseValue); + Identifier ident(vm, uid); return JSValue::encode(baseValue.get(exec, ident, slot)); } -EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid) +EncodedJSValue JIT_OPERATION operationGetByIdBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - + + Identifier ident(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); + JSValue baseValue = JSValue::decode(base); - PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); - Identifier ident = Identifier::fromUid(vm, uid); - return JSValue::encode(baseValue.get(exec, ident, slot)); + PropertySlot slot(baseValue); + JSValue result = baseValue.get(exec, ident, slot); + + if (accessType == static_cast<AccessType>(stubInfo->accessType)) + buildGetByIDList(exec, baseValue, ident, slot, *stubInfo); + + return JSValue::encode(result); } -EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid) +EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident = uid->isEmptyUnique() ? Identifier::from(PrivateName(uid)) : Identifier(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); JSValue baseValue = JSValue::decode(base); - PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); - - bool hasResult = baseValue.getPropertySlot(exec, ident, slot); - if (stubInfo->considerCaching()) - repatchGetByID(exec, baseValue, ident, slot, *stubInfo); + PropertySlot slot(baseValue); + JSValue result = baseValue.get(exec, ident, slot); - return JSValue::encode(hasResult? slot.getValue(exec, ident) : jsUndefined()); + if (accessType == static_cast<AccessType>(stubInfo->accessType)) { + if (stubInfo->seen) + repatchGetByID(exec, baseValue, ident, slot, *stubInfo); + else + stubInfo->seen = true; + } + + return JSValue::encode(result); } -EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key) +EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, StringImpl* key) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); if (!base->isObject()) { - vm->throwException(exec, createInvalidInParameterError(exec, base)); + vm->throwException(exec, createInvalidParameterError(exec, "in", base)); return JSValue::encode(jsUndefined()); } AccessType accessType = static_cast<AccessType>(stubInfo->accessType); - Identifier ident = Identifier::fromUid(vm, key); - PropertySlot slot(base, PropertySlot::InternalMethodType::HasProperty); + Identifier ident(vm, key); + PropertySlot slot(base); bool result = asObject(base)->getPropertySlot(exec, ident, slot); RELEASE_ASSERT(accessType == stubInfo->accessType); - if (stubInfo->considerCaching()) + if (stubInfo->seen) repatchIn(exec, base, ident, result, slot, *stubInfo); + else + stubInfo->seen = true; return JSValue::encode(jsBoolean(result)); } -EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key) +EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo*, JSCell* base, StringImpl* key) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - - stubInfo->tookSlowPath = true; if (!base->isObject()) { - vm->throwException(exec, createInvalidInParameterError(exec, base)); + vm->throwException(exec, createInvalidParameterError(exec, "in", base)); return JSValue::encode(jsUndefined()); } - Identifier ident = Identifier::fromUid(vm, key); + Identifier ident(vm, key); return JSValue::encode(jsBoolean(asObject(base)->hasProperty(exec, ident))); } @@ -240,140 +215,234 @@ EncodedJSValue JIT_OPERATION operationGenericIn(ExecState* exec, JSCell* base, E return JSValue::encode(jsBoolean(CommonSlowPaths::opIn(exec, JSValue::decode(key), base))); } -void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +EncodedJSValue JIT_OPERATION operationCallCustomGetter(ExecState* exec, JSCell* base, PropertySlot::GetValueFunc function, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - stubInfo->tookSlowPath = true; + Identifier ident(vm, uid); - Identifier ident = Identifier::fromUid(vm, uid); - PutPropertySlot slot(JSValue::decode(encodedBase), true, exec->codeBlock()->putByIdContext()); - JSValue::decode(encodedBase).putInline(exec, ident, JSValue::decode(encodedValue), slot); + return function(exec, JSValue::encode(base), JSValue::encode(base), ident); +} + +EncodedJSValue JIT_OPERATION operationCallGetter(ExecState* exec, JSCell* base, JSCell* getterSetter) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + return JSValue::encode(callGetter(exec, base, getterSetter)); } -void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - stubInfo->tookSlowPath = true; + Identifier ident(vm, uid); + PutPropertySlot slot(JSValue::decode(encodedBase), true, exec->codeBlock()->putByIdContext()); + JSValue::decode(encodedBase).put(exec, ident, JSValue::decode(encodedValue), slot); +} + +void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); PutPropertySlot slot(JSValue::decode(encodedBase), false, exec->codeBlock()->putByIdContext()); - JSValue::decode(encodedBase).putInline(exec, ident, JSValue::decode(encodedValue), slot); + JSValue::decode(encodedBase).put(exec, ident, JSValue::decode(encodedValue), slot); } -void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - stubInfo->tookSlowPath = true; - - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); PutPropertySlot slot(JSValue::decode(encodedBase), true, exec->codeBlock()->putByIdContext()); asObject(JSValue::decode(encodedBase))->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot); } -void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - stubInfo->tookSlowPath = true; - - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); PutPropertySlot slot(JSValue::decode(encodedBase), false, exec->codeBlock()->putByIdContext()); asObject(JSValue::decode(encodedBase))->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot); } -void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); AccessType accessType = static_cast<AccessType>(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext()); - - Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr; - baseValue.putInline(exec, ident, value, slot); + + baseValue.put(exec, ident, value, slot); if (accessType != static_cast<AccessType>(stubInfo->accessType)) return; - if (stubInfo->considerCaching()) - repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect); + if (stubInfo->seen) + repatchPutByID(exec, baseValue, ident, slot, *stubInfo, NotDirect); + else + stubInfo->seen = true; } -void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); AccessType accessType = static_cast<AccessType>(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext()); - - Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr; - baseValue.putInline(exec, ident, value, slot); + + baseValue.put(exec, ident, value, slot); if (accessType != static_cast<AccessType>(stubInfo->accessType)) return; - if (stubInfo->considerCaching()) - repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect); + if (stubInfo->seen) + repatchPutByID(exec, baseValue, ident, slot, *stubInfo, NotDirect); + else + stubInfo->seen = true; } -void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); AccessType accessType = static_cast<AccessType>(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSObject* baseObject = asObject(JSValue::decode(encodedBase)); PutPropertySlot slot(baseObject, true, exec->codeBlock()->putByIdContext()); - Structure* structure = baseObject->structure(*vm); baseObject->putDirect(exec->vm(), ident, value, slot); if (accessType != static_cast<AccessType>(stubInfo->accessType)) return; - if (stubInfo->considerCaching()) - repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct); + if (stubInfo->seen) + repatchPutByID(exec, baseObject, ident, slot, *stubInfo, Direct); + else + stubInfo->seen = true; } -void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid) +void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - Identifier ident = Identifier::fromUid(vm, uid); + Identifier ident(vm, uid); AccessType accessType = static_cast<AccessType>(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSObject* baseObject = asObject(JSValue::decode(encodedBase)); PutPropertySlot slot(baseObject, false, exec->codeBlock()->putByIdContext()); - Structure* structure = baseObject->structure(*vm); baseObject->putDirect(exec->vm(), ident, value, slot); if (accessType != static_cast<AccessType>(stubInfo->accessType)) return; - if (stubInfo->considerCaching()) - repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct); + if (stubInfo->seen) + repatchPutByID(exec, baseObject, ident, slot, *stubInfo, Direct); + else + stubInfo->seen = true; +} + +void JIT_OPERATION operationPutByIdStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + Identifier ident(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext()); + + baseValue.put(exec, ident, value, slot); + + if (accessType != static_cast<AccessType>(stubInfo->accessType)) + return; + + buildPutByIdList(exec, baseValue, ident, slot, *stubInfo, NotDirect); +} + +void JIT_OPERATION operationPutByIdNonStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + Identifier ident(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSValue baseValue = JSValue::decode(encodedBase); + PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext()); + + baseValue.put(exec, ident, value, slot); + + if (accessType != static_cast<AccessType>(stubInfo->accessType)) + return; + + buildPutByIdList(exec, baseValue, ident, slot, *stubInfo, NotDirect); +} + +void JIT_OPERATION operationPutByIdDirectStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + Identifier ident(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSObject* baseObject = asObject(JSValue::decode(encodedBase)); + PutPropertySlot slot(baseObject, true, exec->codeBlock()->putByIdContext()); + + baseObject->putDirect(exec->vm(), ident, value, slot); + + if (accessType != static_cast<AccessType>(stubInfo->accessType)) + return; + + buildPutByIdList(exec, baseObject, ident, slot, *stubInfo, Direct); +} + +void JIT_OPERATION operationPutByIdDirectNonStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid) +{ + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + Identifier ident(vm, uid); + AccessType accessType = static_cast<AccessType>(stubInfo->accessType); + + JSValue value = JSValue::decode(encodedValue); + JSObject* baseObject = asObject(JSValue::decode(encodedBase)); + PutPropertySlot slot(baseObject, false, exec->codeBlock()->putByIdContext()); + + baseObject ->putDirect(exec->vm(), ident, value, slot); + + if (accessType != static_cast<AccessType>(stubInfo->accessType)) + return; + + buildPutByIdList(exec, baseObject, ident, slot, *stubInfo, Direct); } void JIT_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObject* base, Structure* structure, PropertyOffset offset, EncodedJSValue value) @@ -381,273 +450,144 @@ void JIT_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObj VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - ASSERT(structure->outOfLineCapacity() > base->structure(vm)->outOfLineCapacity()); + ASSERT(structure->outOfLineCapacity() > base->structure()->outOfLineCapacity()); ASSERT(!vm.heap.storageAllocator().fastPathShouldSucceed(structure->outOfLineCapacity() * sizeof(JSValue))); base->setStructureAndReallocateStorageIfNecessary(vm, structure); base->putDirect(vm, offset, JSValue::decode(value)); } -ALWAYS_INLINE static bool isStringOrSymbol(JSValue value) -{ - return value.isString() || value.isSymbol(); -} - -static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value, ByValInfo* byValInfo) +static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value) { - VM& vm = callFrame->vm(); if (LIKELY(subscript.isUInt32())) { - byValInfo->tookSlowPath = true; uint32_t i = subscript.asUInt32(); if (baseValue.isObject()) { JSObject* object = asObject(baseValue); if (object->canSetIndexQuickly(i)) object->setIndexQuickly(callFrame->vm(), i, value); - else { - // FIXME: This will make us think that in-bounds typed array accesses are actually - // out-of-bounds. - // https://bugs.webkit.org/show_bug.cgi?id=149886 - byValInfo->arrayProfile->setOutOfBounds(); - object->methodTable(vm)->putByIndex(object, callFrame, i, value, callFrame->codeBlock()->isStrictMode()); - } + else + object->methodTable()->putByIndex(object, callFrame, i, value, callFrame->codeBlock()->isStrictMode()); } else baseValue.putByIndex(callFrame, i, value, callFrame->codeBlock()->isStrictMode()); - return; + } else if (isName(subscript)) { + PutPropertySlot slot(baseValue, callFrame->codeBlock()->isStrictMode()); + baseValue.put(callFrame, jsCast<NameInstance*>(subscript.asCell())->privateName(), value, slot); + } else { + Identifier property(callFrame, subscript.toString(callFrame)->value(callFrame)); + if (!callFrame->vm().exception()) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot(baseValue, callFrame->codeBlock()->isStrictMode()); + baseValue.put(callFrame, property, value, slot); + } } - - auto property = subscript.toPropertyKey(callFrame); - // Don't put to an object if toString threw an exception. - if (callFrame->vm().exception()) - return; - - if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property)) - byValInfo->tookSlowPath = true; - - PutPropertySlot slot(baseValue, callFrame->codeBlock()->isStrictMode()); - baseValue.putInline(callFrame, property, value, slot); } -static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value, ByValInfo* byValInfo) +static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value) { - bool isStrictMode = callFrame->codeBlock()->isStrictMode(); if (LIKELY(subscript.isUInt32())) { - // Despite its name, JSValue::isUInt32 will return true only for positive boxed int32_t; all those values are valid array indices. - byValInfo->tookSlowPath = true; - uint32_t index = subscript.asUInt32(); - ASSERT(isIndex(index)); - if (baseObject->canSetIndexQuicklyForPutDirect(index)) { - baseObject->setIndexQuickly(callFrame->vm(), index, value); - return; - } - - // FIXME: This will make us think that in-bounds typed array accesses are actually - // out-of-bounds. - // https://bugs.webkit.org/show_bug.cgi?id=149886 - byValInfo->arrayProfile->setOutOfBounds(); - baseObject->putDirectIndex(callFrame, index, value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); - return; - } - - if (subscript.isDouble()) { - double subscriptAsDouble = subscript.asDouble(); - uint32_t subscriptAsUInt32 = static_cast<uint32_t>(subscriptAsDouble); - if (subscriptAsDouble == subscriptAsUInt32 && isIndex(subscriptAsUInt32)) { - byValInfo->tookSlowPath = true; - baseObject->putDirectIndex(callFrame, subscriptAsUInt32, value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); - return; + uint32_t i = subscript.asUInt32(); + baseObject->putDirectIndex(callFrame, i, value); + } else if (isName(subscript)) { + PutPropertySlot slot(baseObject, callFrame->codeBlock()->isStrictMode()); + baseObject->putDirect(callFrame->vm(), jsCast<NameInstance*>(subscript.asCell())->privateName(), value, slot); + } else { + Identifier property(callFrame, subscript.toString(callFrame)->value(callFrame)); + if (!callFrame->vm().exception()) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot(baseObject, callFrame->codeBlock()->isStrictMode()); + baseObject->putDirect(callFrame->vm(), property, value, slot); } } - - // Don't put to an object if toString threw an exception. - auto property = subscript.toPropertyKey(callFrame); - if (callFrame->vm().exception()) - return; - - if (Optional<uint32_t> index = parseIndex(property)) { - byValInfo->tookSlowPath = true; - baseObject->putDirectIndex(callFrame, index.value(), value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); - return; - } - - if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property)) - byValInfo->tookSlowPath = true; - - PutPropertySlot slot(baseObject, isStrictMode); - baseObject->putDirect(callFrame->vm(), property, value, slot); } - -enum class OptimizationResult { - NotOptimized, - SeenOnce, - Optimized, - GiveUp, -}; - -static OptimizationResult tryPutByValOptimize(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress) +void JIT_OPERATION operationPutByVal(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue) { - // See if it's worth optimizing at all. - OptimizationResult optimizationResult = OptimizationResult::NotOptimized; - VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + JSValue baseValue = JSValue::decode(encodedBaseValue); + JSValue subscript = JSValue::decode(encodedSubscript); + JSValue value = JSValue::decode(encodedValue); if (baseValue.isObject() && subscript.isInt32()) { + // See if it's worth optimizing at all. JSObject* object = asObject(baseValue); + bool didOptimize = false; - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); + unsigned bytecodeOffset = exec->locationAsBytecodeOffset(); + ASSERT(bytecodeOffset); + ByValInfo& byValInfo = exec->codeBlock()->getByValInfo(bytecodeOffset - 1); + ASSERT(!byValInfo.stubRoutine); - Structure* structure = object->structure(vm); - if (hasOptimizableIndexing(structure)) { + if (hasOptimizableIndexing(object->structure())) { // Attempt to optimize. - JITArrayMode arrayMode = jitArrayModeForStructure(structure); - if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo->arrayMode) { - CodeBlock* codeBlock = exec->codeBlock(); - ConcurrentJITLocker locker(codeBlock->m_lock); - byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure); - - JIT::compilePutByVal(&vm, exec->codeBlock(), byValInfo, returnAddress, arrayMode); - optimizationResult = OptimizationResult::Optimized; + JITArrayMode arrayMode = jitArrayModeForStructure(object->structure()); + if (arrayMode != byValInfo.arrayMode) { + JIT::compilePutByVal(&vm, exec->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode); + didOptimize = true; } } - // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times. - if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) - optimizationResult = OptimizationResult::GiveUp; - } - - if (baseValue.isObject() && isStringOrSymbol(subscript)) { - const Identifier propertyName = subscript.toPropertyKey(exec); - if (!subscript.isString() || !parseIndex(propertyName)) { - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - if (byValInfo->seen) { - if (byValInfo->cachedId == propertyName) { - JIT::compilePutByValWithCachedId(&vm, exec->codeBlock(), byValInfo, returnAddress, NotDirect, propertyName); - optimizationResult = OptimizationResult::Optimized; - } else { - // Seem like a generic property access site. - optimizationResult = OptimizationResult::GiveUp; - } - } else { - byValInfo->seen = true; - byValInfo->cachedId = propertyName; - optimizationResult = OptimizationResult::SeenOnce; + if (!didOptimize) { + // If we take slow path more than 10 times without patching then make sure we + // never make that mistake again. Or, if we failed to patch and we have some object + // that intercepts indexed get, then don't even wait until 10 times. For cases + // where we see non-index-intercepting objects, this gives 10 iterations worth of + // opportunity for us to observe that the get_by_val may be polymorphic. + if (++byValInfo.slowPathCount >= 10 + || object->structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) { + // Don't ever try to optimize. + RepatchBuffer repatchBuffer(exec->codeBlock()); + repatchBuffer.relinkCallerToFunction(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationPutByValGeneric)); } } } - if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) { - // If we take slow path more than 10 times without patching then make sure we - // never make that mistake again. For cases where we see non-index-intercepting - // objects, this gives 10 iterations worth of opportunity for us to observe - // that the put_by_val may be polymorphic. We count up slowPathCount even if - // the result is GiveUp. - if (++byValInfo->slowPathCount >= 10) - optimizationResult = OptimizationResult::GiveUp; - } - - return optimizationResult; + putByVal(exec, baseValue, subscript, value); } -void JIT_OPERATION operationPutByValOptimize(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo) +void JIT_OPERATION operationDirectPutByVal(ExecState* callFrame, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - + VM& vm = callFrame->vm(); + NativeCallFrameTracer tracer(&vm, callFrame); + JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); - if (tryPutByValOptimize(exec, baseValue, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS)) == OptimizationResult::GiveUp) { - // Don't ever try to optimize. - byValInfo->tookSlowPath = true; - ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationPutByValGeneric)); - } - putByVal(exec, baseValue, subscript, value, byValInfo); -} - -static OptimizationResult tryDirectPutByValOptimize(ExecState* exec, JSObject* object, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress) -{ - // See if it's worth optimizing at all. - OptimizationResult optimizationResult = OptimizationResult::NotOptimized; - - VM& vm = exec->vm(); - + RELEASE_ASSERT(baseValue.isObject()); + JSObject* object = asObject(baseValue); if (subscript.isInt32()) { - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - - Structure* structure = object->structure(vm); - if (hasOptimizableIndexing(structure)) { + // See if it's worth optimizing at all. + bool didOptimize = false; + + unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); + ASSERT(bytecodeOffset); + ByValInfo& byValInfo = callFrame->codeBlock()->getByValInfo(bytecodeOffset - 1); + ASSERT(!byValInfo.stubRoutine); + + if (hasOptimizableIndexing(object->structure())) { // Attempt to optimize. - JITArrayMode arrayMode = jitArrayModeForStructure(structure); - if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo->arrayMode) { - CodeBlock* codeBlock = exec->codeBlock(); - ConcurrentJITLocker locker(codeBlock->m_lock); - byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure); - - JIT::compileDirectPutByVal(&vm, exec->codeBlock(), byValInfo, returnAddress, arrayMode); - optimizationResult = OptimizationResult::Optimized; + JITArrayMode arrayMode = jitArrayModeForStructure(object->structure()); + if (arrayMode != byValInfo.arrayMode) { + JIT::compileDirectPutByVal(&vm, callFrame->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode); + didOptimize = true; } } - - // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times. - if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) - optimizationResult = OptimizationResult::GiveUp; - } else if (isStringOrSymbol(subscript)) { - const Identifier propertyName = subscript.toPropertyKey(exec); - Optional<uint32_t> index = parseIndex(propertyName); - - if (!subscript.isString() || !index) { - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - if (byValInfo->seen) { - if (byValInfo->cachedId == propertyName) { - JIT::compilePutByValWithCachedId(&vm, exec->codeBlock(), byValInfo, returnAddress, Direct, propertyName); - optimizationResult = OptimizationResult::Optimized; - } else { - // Seem like a generic property access site. - optimizationResult = OptimizationResult::GiveUp; - } - } else { - byValInfo->seen = true; - byValInfo->cachedId = propertyName; - optimizationResult = OptimizationResult::SeenOnce; + + if (!didOptimize) { + // If we take slow path more than 10 times without patching then make sure we + // never make that mistake again. Or, if we failed to patch and we have some object + // that intercepts indexed get, then don't even wait until 10 times. For cases + // where we see non-index-intercepting objects, this gives 10 iterations worth of + // opportunity for us to observe that the get_by_val may be polymorphic. + if (++byValInfo.slowPathCount >= 10 + || object->structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) { + // Don't ever try to optimize. + RepatchBuffer repatchBuffer(callFrame->codeBlock()); + repatchBuffer.relinkCallerToFunction(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationDirectPutByValGeneric)); } } } - - if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) { - // If we take slow path more than 10 times without patching then make sure we - // never make that mistake again. For cases where we see non-index-intercepting - // objects, this gives 10 iterations worth of opportunity for us to observe - // that the get_by_val may be polymorphic. We count up slowPathCount even if - // the result is GiveUp. - if (++byValInfo->slowPathCount >= 10) - optimizationResult = OptimizationResult::GiveUp; - } - - return optimizationResult; -} - -void JIT_OPERATION operationDirectPutByValOptimize(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - JSValue baseValue = JSValue::decode(encodedBaseValue); - JSValue subscript = JSValue::decode(encodedSubscript); - JSValue value = JSValue::decode(encodedValue); - RELEASE_ASSERT(baseValue.isObject()); - JSObject* object = asObject(baseValue); - if (tryDirectPutByValOptimize(exec, object, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS)) == OptimizationResult::GiveUp) { - // Don't ever try to optimize. - byValInfo->tookSlowPath = true; - ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationDirectPutByValGeneric)); - } - - directPutByVal(exec, object, subscript, value, byValInfo); + directPutByVal(callFrame, object, subscript, value); } -void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo) +void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); @@ -656,11 +596,11 @@ void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue enco JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); - putByVal(exec, baseValue, subscript, value, byValInfo); + putByVal(exec, baseValue, subscript, value); } -void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo) +void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); @@ -669,13 +609,18 @@ void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValu JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); RELEASE_ASSERT(baseValue.isObject()); - directPutByVal(exec, asObject(baseValue), subscript, value, byValInfo); + directPutByVal(exec, asObject(baseValue), subscript, value); } -EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execCallee) +EncodedJSValue JIT_OPERATION operationCallEval(ExecState* execCallee) { - UNUSED_PARAM(exec); + CallFrame* callerFrame = execCallee->callerFrame(); + ASSERT(execCallee->callerFrame()->codeBlock()->codeType() != FunctionCode + || !execCallee->callerFrame()->codeBlock()->needsFullScopeChain() + || execCallee->callerFrame()->uncheckedR(execCallee->callerFrame()->codeBlock()->activationRegister().offset()).jsValue()); + execCallee->setScope(callerFrame->scope()); + execCallee->setReturnPC(static_cast<Instruction*>(OUR_RETURN_ADDRESS)); execCallee->setCodeBlock(0); if (!isHostFunction(execCallee->calleeAsValue(), globalFuncEval)) @@ -689,14 +634,15 @@ EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execC return JSValue::encode(result); } -static SlowPathReturnType handleHostCall(ExecState* execCallee, JSValue callee, CallLinkInfo* callLinkInfo) +static void* handleHostCall(ExecState* execCallee, JSValue callee, CodeSpecializationKind kind) { ExecState* exec = execCallee->callerFrame(); VM* vm = &exec->vm(); + execCallee->setScope(exec->scope()); execCallee->setCodeBlock(0); - if (callLinkInfo->specializationKind() == CodeForCall) { + if (kind == CodeForCall) { CallData callData; CallType callType = getCallData(callee, callData); @@ -706,25 +652,18 @@ static SlowPathReturnType handleHostCall(ExecState* execCallee, JSValue callee, NativeCallFrameTracer tracer(vm, execCallee); execCallee->setCallee(asObject(callee)); vm->hostCallReturnValue = JSValue::decode(callData.native.function(execCallee)); - if (vm->exception()) { - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } + if (vm->exception()) + return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(); - return encodeResult( - bitwise_cast<void*>(getHostCallReturnValue), - reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame)); + return reinterpret_cast<void*>(getHostCallReturnValue); } ASSERT(callType == CallTypeNone); exec->vm().throwException(exec, createNotAFunctionError(exec, callee)); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); + return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(); } - ASSERT(callLinkInfo->specializationKind() == CodeForConstruct); + ASSERT(kind == CodeForConstruct); ConstructData constructData; ConstructType constructType = getConstructData(callee, constructData); @@ -735,166 +674,153 @@ static SlowPathReturnType handleHostCall(ExecState* execCallee, JSValue callee, NativeCallFrameTracer tracer(vm, execCallee); execCallee->setCallee(asObject(callee)); vm->hostCallReturnValue = JSValue::decode(constructData.native.function(execCallee)); - if (vm->exception()) { - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } + if (vm->exception()) + return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(); - return encodeResult(bitwise_cast<void*>(getHostCallReturnValue), reinterpret_cast<void*>(KeepTheFrame)); + return reinterpret_cast<void*>(getHostCallReturnValue); } ASSERT(constructType == ConstructTypeNone); exec->vm().throwException(exec, createNotAConstructorError(exec, callee)); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); + return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(); } -SlowPathReturnType JIT_OPERATION operationLinkCall(ExecState* execCallee, CallLinkInfo* callLinkInfo) +inline char* linkFor(ExecState* execCallee, CodeSpecializationKind kind) { ExecState* exec = execCallee->callerFrame(); VM* vm = &exec->vm(); - CodeSpecializationKind kind = callLinkInfo->specializationKind(); NativeCallFrameTracer tracer(vm, exec); JSValue calleeAsValue = execCallee->calleeAsValue(); JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue); - if (!calleeAsFunctionCell) { - // FIXME: We should cache these kinds of calls. They can be common and currently they are - // expensive. - // https://bugs.webkit.org/show_bug.cgi?id=144458 - return handleHostCall(execCallee, calleeAsValue, callLinkInfo); - } + if (!calleeAsFunctionCell) + return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); - JSScope* scope = callee->scopeUnchecked(); + execCallee->setScope(callee->scopeUnchecked()); ExecutableBase* executable = callee->executable(); MacroAssemblerCodePtr codePtr; CodeBlock* codeBlock = 0; - if (executable->isHostFunction()) { - codePtr = executable->entrypointFor(kind, MustCheckArity); -#if ENABLE(WEBASSEMBLY) - } else if (executable->isWebAssemblyExecutable()) { - WebAssemblyExecutable* webAssemblyExecutable = static_cast<WebAssemblyExecutable*>(executable); - webAssemblyExecutable->prepareForExecution(execCallee); - codeBlock = webAssemblyExecutable->codeBlockForCall(); - ASSERT(codeBlock); - ArityCheckMode arity; - if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters())) - arity = MustCheckArity; - else - arity = ArityCheckNotRequired; - codePtr = webAssemblyExecutable->entrypointFor(kind, arity); -#endif - } else { + CallLinkInfo& callLinkInfo = exec->codeBlock()->getCallLinkInfo(execCallee->returnPC()); + if (executable->isHostFunction()) + codePtr = executable->generatedJITCodeFor(kind)->addressForCall(); + else { FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable); - - if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) { - exec->vm().throwException(exec, createNotAConstructorError(exec, callee)); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } - - JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, scope, kind); + JSObject* error = functionExecutable->prepareForExecution(execCallee, callee->scope(), kind); if (error) { - exec->vm().throwException(exec, error); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); + vm->throwException(exec, createStackOverflowError(exec)); + return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress()); } codeBlock = functionExecutable->codeBlockFor(kind); - ArityCheckMode arity; - if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs()) - arity = MustCheckArity; + if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo.callType == CallLinkInfo::CallVarargs) + codePtr = functionExecutable->generatedJITCodeWithArityCheckFor(kind); else - arity = ArityCheckNotRequired; - codePtr = functionExecutable->entrypointFor(kind, arity); + codePtr = functionExecutable->generatedJITCodeFor(kind)->addressForCall(); } - if (!callLinkInfo->seenOnce()) - callLinkInfo->setSeen(); + if (!callLinkInfo.seenOnce()) + callLinkInfo.setSeen(); else - linkFor(execCallee, *callLinkInfo, codeBlock, callee, codePtr); - - return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame)); + linkFor(execCallee, callLinkInfo, codeBlock, callee, codePtr, kind); + return reinterpret_cast<char*>(codePtr.executableAddress()); +} + +char* JIT_OPERATION operationLinkCall(ExecState* execCallee) +{ + return linkFor(execCallee, CodeForCall); } -inline SlowPathReturnType virtualForWithFunction( - ExecState* execCallee, CallLinkInfo* callLinkInfo, JSCell*& calleeAsFunctionCell) +char* JIT_OPERATION operationLinkConstruct(ExecState* execCallee) +{ + return linkFor(execCallee, CodeForConstruct); +} + +inline char* virtualForWithFunction(ExecState* execCallee, CodeSpecializationKind kind, JSCell*& calleeAsFunctionCell) { ExecState* exec = execCallee->callerFrame(); VM* vm = &exec->vm(); - CodeSpecializationKind kind = callLinkInfo->specializationKind(); NativeCallFrameTracer tracer(vm, exec); JSValue calleeAsValue = execCallee->calleeAsValue(); calleeAsFunctionCell = getJSFunction(calleeAsValue); if (UNLIKELY(!calleeAsFunctionCell)) - return handleHostCall(execCallee, calleeAsValue, callLinkInfo); + return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell); - JSScope* scope = function->scopeUnchecked(); + execCallee->setScope(function->scopeUnchecked()); ExecutableBase* executable = function->executable(); if (UNLIKELY(!executable->hasJITCodeFor(kind))) { - bool isWebAssemblyExecutable = false; -#if ENABLE(WEBASSEMBLY) - isWebAssemblyExecutable = executable->isWebAssemblyExecutable(); -#endif - if (!isWebAssemblyExecutable) { - FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable); - - if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) { - exec->vm().throwException(exec, createNotAConstructorError(exec, function)); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } + FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable); + JSObject* error = functionExecutable->prepareForExecution(execCallee, function->scope(), kind); + if (error) { + exec->vm().throwException(execCallee, error); + return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress()); + } + } + return reinterpret_cast<char*>(executable->generatedJITCodeWithArityCheckFor(kind).executableAddress()); +} - JSObject* error = functionExecutable->prepareForExecution(execCallee, function, scope, kind); - if (error) { - exec->vm().throwException(exec, error); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } - } else { -#if ENABLE(WEBASSEMBLY) - if (!isCall(kind)) { - exec->vm().throwException(exec, createNotAConstructorError(exec, function)); - return encodeResult( - vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(), - reinterpret_cast<void*>(KeepTheFrame)); - } +inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) +{ + JSCell* calleeAsFunctionCellIgnored; + return virtualForWithFunction(execCallee, kind, calleeAsFunctionCellIgnored); +} - WebAssemblyExecutable* webAssemblyExecutable = static_cast<WebAssemblyExecutable*>(executable); - webAssemblyExecutable->prepareForExecution(execCallee); -#endif - } +static bool attemptToOptimizeClosureCall(ExecState* execCallee, JSCell* calleeAsFunctionCell, CallLinkInfo& callLinkInfo) +{ + if (!calleeAsFunctionCell) + return false; + + JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); + JSFunction* oldCallee = callLinkInfo.callee.get(); + + if (!oldCallee + || oldCallee->structure() != callee->structure() + || oldCallee->executable() != callee->executable()) + return false; + + ASSERT(callee->executable()->hasJITCodeForCall()); + MacroAssemblerCodePtr codePtr = callee->executable()->generatedJITCodeForCall()->addressForCall(); + + CodeBlock* codeBlock; + if (callee->executable()->isHostFunction()) + codeBlock = 0; + else { + codeBlock = jsCast<FunctionExecutable*>(callee->executable())->codeBlockForCall(); + if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters())) + return false; } - return encodeResult(executable->entrypointFor( - kind, MustCheckArity).executableAddress(), - reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame)); + + linkClosureCall( + execCallee, callLinkInfo, codeBlock, + callee->structure(), callee->executable(), codePtr); + + return true; } -SlowPathReturnType JIT_OPERATION operationLinkPolymorphicCall(ExecState* execCallee, CallLinkInfo* callLinkInfo) +char* JIT_OPERATION operationLinkClosureCall(ExecState* execCallee) { - ASSERT(callLinkInfo->specializationKind() == CodeForCall); JSCell* calleeAsFunctionCell; - SlowPathReturnType result = virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCell); + char* result = virtualForWithFunction(execCallee, CodeForCall, calleeAsFunctionCell); + CallLinkInfo& callLinkInfo = execCallee->callerFrame()->codeBlock()->getCallLinkInfo(execCallee->returnPC()); - linkPolymorphicCall(execCallee, *callLinkInfo, CallVariant(calleeAsFunctionCell)); + if (!attemptToOptimizeClosureCall(execCallee, calleeAsFunctionCell, callLinkInfo)) + linkSlowFor(execCallee, callLinkInfo, CodeForCall); return result; } -SlowPathReturnType JIT_OPERATION operationVirtualCall(ExecState* execCallee, CallLinkInfo* callLinkInfo) +char* JIT_OPERATION operationVirtualCall(ExecState* execCallee) +{ + return virtualFor(execCallee, CodeForCall); +} + +char* JIT_OPERATION operationVirtualConstruct(ExecState* execCallee) { - JSCell* calleeAsFunctionCellIgnored; - return virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCellIgnored); + return virtualFor(execCallee, CodeForConstruct); } + size_t JIT_OPERATION operationCompareLess(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) { VM* vm = &exec->vm(); @@ -952,7 +878,7 @@ size_t JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSC VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - bool result = WTF::equal(*asString(left)->value(exec).impl(), *asString(right)->value(exec).impl()); + bool result = asString(left)->value(exec) == asString(right)->value(exec); #if USE(JSVALUE64) return JSValue::encode(jsBoolean(result)); #else @@ -962,7 +888,7 @@ size_t JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSC size_t JIT_OPERATION operationHasProperty(ExecState* exec, JSObject* base, JSString* property) { - int result = base->hasProperty(exec, property->toIdentifier(exec)); + int result = base->hasProperty(exec, Identifier(exec, property->value(exec))); return result; } @@ -989,46 +915,19 @@ EncodedJSValue JIT_OPERATION operationNewArrayWithSizeAndProfile(ExecState* exec return JSValue::encode(constructArrayWithSizeQuirk(exec, profile, exec->lexicalGlobalObject(), sizeValue)); } -} - -template<typename FunctionType> -static EncodedJSValue operationNewFunctionCommon(ExecState* exec, JSScope* scope, JSCell* functionExecutable, bool isInvalidated) +EncodedJSValue JIT_OPERATION operationNewFunction(ExecState* exec, JSCell* functionExecutable) { ASSERT(functionExecutable->inherits(FunctionExecutable::info())); VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - if (isInvalidated) - return JSValue::encode(FunctionType::createWithInvalidatedReallocationWatchpoint(vm, static_cast<FunctionExecutable*>(functionExecutable), scope)); - return JSValue::encode(FunctionType::create(vm, static_cast<FunctionExecutable*>(functionExecutable), scope)); -} - -extern "C" { - -EncodedJSValue JIT_OPERATION operationNewFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable) -{ - return operationNewFunctionCommon<JSFunction>(exec, scope, functionExecutable, false); -} - -EncodedJSValue JIT_OPERATION operationNewFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable) -{ - return operationNewFunctionCommon<JSFunction>(exec, scope, functionExecutable, true); -} - -EncodedJSValue JIT_OPERATION operationNewGeneratorFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable) -{ - return operationNewFunctionCommon<JSGeneratorFunction>(exec, scope, functionExecutable, false); -} - -EncodedJSValue JIT_OPERATION operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable) -{ - return operationNewFunctionCommon<JSGeneratorFunction>(exec, scope, functionExecutable, true); + return JSValue::encode(JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), exec->scope())); } JSCell* JIT_OPERATION operationNewObject(ExecState* exec, Structure* structure) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - + return constructEmptyObject(exec, structure); } @@ -1038,39 +937,32 @@ EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState* exec, void* regexpPtr NativeCallFrameTracer tracer(&vm, exec); RegExp* regexp = static_cast<RegExp*>(regexpPtr); if (!regexp->isValid()) { - vm.throwException(exec, createSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor."))); + vm.throwException(exec, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor.")); return JSValue::encode(jsUndefined()); } return JSValue::encode(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp)); } -// The only reason for returning an UnusedPtr (instead of void) is so that we can reuse the -// existing DFG slow path generator machinery when creating the slow path for CheckWatchdogTimer -// in the DFG. If a DFG slow path generator that supports a void return type is added in the -// future, we can switch to using that then. -UnusedPtr JIT_OPERATION operationHandleWatchdogTimer(ExecState* exec) +void JIT_OPERATION operationHandleWatchdogTimer(ExecState* exec) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - if (UNLIKELY(vm.shouldTriggerTermination(exec))) + if (UNLIKELY(vm.watchdog.didFire(exec))) vm.throwException(exec, createTerminatedExecutionException(&vm)); - - return nullptr; } void JIT_OPERATION operationThrowStaticError(ExecState* exec, EncodedJSValue encodedValue, int32_t referenceErrorFlag) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - JSValue errorMessageValue = JSValue::decode(encodedValue); - RELEASE_ASSERT(errorMessageValue.isString()); - String errorMessage = asString(errorMessageValue)->value(exec); + + String message = errorDescriptionForValue(exec, JSValue::decode(encodedValue))->value(exec); if (referenceErrorFlag) - vm.throwException(exec, createReferenceError(exec, errorMessage)); + vm.throwException(exec, createReferenceError(exec, message)); else - vm.throwException(exec, createTypeError(exec, errorMessage)); + vm.throwException(exec, createTypeError(exec, message)); } void JIT_OPERATION operationDebug(ExecState* exec, int32_t debugHookID) @@ -1082,13 +974,7 @@ void JIT_OPERATION operationDebug(ExecState* exec, int32_t debugHookID) } #if ENABLE(DFG_JIT) -static void updateAllPredictionsAndOptimizeAfterWarmUp(CodeBlock* codeBlock) -{ - codeBlock->updateAllPredictions(); - codeBlock->optimizeAfterWarmUp(); -} - -SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex) +char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); @@ -1110,11 +996,7 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte DeferGCForAWhile deferGC(vm.heap); CodeBlock* codeBlock = exec->codeBlock(); - if (codeBlock->jitType() != JITCode::BaselineJIT) { - dataLog("Unexpected code block in Baseline->DFG tier-up: ", *codeBlock, "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - + if (bytecodeIndex) { // If we're attempting to OSR from a loop, assume that this should be // separately optimized. @@ -1138,37 +1020,26 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte codeBlock->updateAllPredictions(); if (Options::verboseOSR()) dataLog("Choosing not to optimize ", *codeBlock, " yet, because the threshold hasn't been reached.\n"); - return encodeResult(0, 0); + return 0; } - if (vm.enabledProfiler()) { - updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); - return encodeResult(0, 0); - } - - Debugger* debugger = codeBlock->globalObject()->debugger(); - if (debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests())) { - updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); - return encodeResult(0, 0); - } - if (codeBlock->m_shouldAlwaysBeInlined) { - updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); + codeBlock->updateAllPredictions(); + codeBlock->optimizeAfterWarmUp(); if (Options::verboseOSR()) dataLog("Choosing not to optimize ", *codeBlock, " yet, because m_shouldAlwaysBeInlined == true.\n"); - return encodeResult(0, 0); + return 0; } // We cannot be in the process of asynchronous compilation and also have an optimized // replacement. - DFG::Worklist* worklist = DFG::existingGlobalDFGWorklistOrNull(); ASSERT( - !worklist - || !(worklist->compilationState(DFG::CompilationKey(codeBlock, DFG::DFGMode)) != DFG::Worklist::NotKnown + !vm.worklist + || !(vm.worklist->compilationState(DFG::CompilationKey(codeBlock, DFG::DFGMode)) != DFG::Worklist::NotKnown && codeBlock->hasOptimizedReplacement())); DFG::Worklist::State worklistState; - if (worklist) { + if (vm.worklist) { // The call to DFG::Worklist::completeAllReadyPlansForVM() will complete all ready // (i.e. compiled) code blocks. But if it completes ours, we also need to know // what the result was so that we don't plow ahead and attempt OSR or immediate @@ -1187,7 +1058,7 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte // probably a waste of memory. Our goal here is to complete code blocks as soon as // possible in order to minimize the chances of us executing baseline code after // optimized code is already available. - worklistState = worklist->completeAllReadyPlansForVM( + worklistState = vm.worklist->completeAllReadyPlansForVM( vm, DFG::CompilationKey(codeBlock, DFG::DFGMode)); } else worklistState = DFG::Worklist::NotKnown; @@ -1197,7 +1068,7 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte // replacement. RELEASE_ASSERT(!codeBlock->hasOptimizedReplacement()); codeBlock->setOptimizationThresholdBasedOnCompilationResult(CompilationDeferred); - return encodeResult(0, 0); + return 0; } if (worklistState == DFG::Worklist::Compiled) { @@ -1210,7 +1081,7 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte codeBlock->updateAllPredictions(); if (Options::verboseOSR()) dataLog("Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement.\n"); - return encodeResult(0, 0); + return 0; } } else if (codeBlock->hasOptimizedReplacement()) { if (Options::verboseOSR()) @@ -1234,8 +1105,8 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte "Triggering reoptimization of ", *codeBlock, "(", *codeBlock->replacement(), ") (in loop).\n"); } - codeBlock->replacement()->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTrigger, CountReoptimization); - return encodeResult(0, 0); + codeBlock->replacement()->jettison(CountReoptimization); + return 0; } } else { if (!codeBlock->shouldOptimizeNow()) { @@ -1244,7 +1115,7 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte "Delaying optimization for ", *codeBlock, " because of insufficient profiling.\n"); } - return encodeResult(0, 0); + return 0; } if (Options::verboseOSR()) @@ -1256,34 +1127,42 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte else numVarsWithValues = 0; Operands<JSValue> mustHandleValues(codeBlock->numParameters(), numVarsWithValues); - int localsUsedForCalleeSaves = static_cast<int>(CodeBlock::llintBaselineCalleeSaveSpaceAsVirtualRegisters()); for (size_t i = 0; i < mustHandleValues.size(); ++i) { int operand = mustHandleValues.operandForIndex(i); - if (operandIsLocal(operand) && VirtualRegister(operand).toLocal() < localsUsedForCalleeSaves) - continue; - mustHandleValues[i] = exec->uncheckedR(operand).jsValue(); + if (operandIsArgument(operand) + && !VirtualRegister(operand).toArgument() + && codeBlock->codeType() == FunctionCode + && codeBlock->specializationKind() == CodeForConstruct) { + // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will + // also never be used. It doesn't matter what we put into the value for this, + // but it has to be an actual value that can be grokked by subsequent DFG passes, + // so we sanitize it here by turning it into Undefined. + mustHandleValues[i] = jsUndefined(); + } else + mustHandleValues[i] = exec->uncheckedR(operand).jsValue(); } - CodeBlock* replacementCodeBlock = codeBlock->newReplacement(); CompilationResult result = DFG::compile( - vm, replacementCodeBlock, nullptr, DFG::DFGMode, bytecodeIndex, - mustHandleValues, JITToDFGDeferredCompilationCallback::create()); + vm, codeBlock->newReplacement().get(), DFG::DFGMode, bytecodeIndex, + mustHandleValues, JITToDFGDeferredCompilationCallback::create(), + vm.ensureWorklist()); if (result != CompilationSuccessful) - return encodeResult(0, 0); + return 0; } CodeBlock* optimizedCodeBlock = codeBlock->replacement(); ASSERT(JITCode::isOptimizingJIT(optimizedCodeBlock->jitType())); - if (void* dataBuffer = DFG::prepareOSREntry(exec, optimizedCodeBlock, bytecodeIndex)) { + if (void* address = DFG::prepareOSREntry(exec, optimizedCodeBlock, bytecodeIndex)) { if (Options::verboseOSR()) { dataLog( - "Performing OSR ", *codeBlock, " -> ", *optimizedCodeBlock, ".\n"); + "Performing OSR ", *codeBlock, " -> ", *optimizedCodeBlock, ", address ", + RawPointer(OUR_RETURN_ADDRESS), " -> ", RawPointer(address), ".\n"); } codeBlock->optimizeSoon(); - return encodeResult(vm.getCTIStub(DFG::osrEntryThunkGenerator).code().executableAddress(), dataBuffer); + return static_cast<char*>(address); } if (Options::verboseOSR()) { @@ -1311,15 +1190,15 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t byte "Triggering reoptimization of ", *codeBlock, " -> ", *codeBlock->replacement(), " (after OSR fail).\n"); } - optimizedCodeBlock->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail, CountReoptimization); - return encodeResult(0, 0); + optimizedCodeBlock->jettison(CountReoptimization); + return 0; } // OSR failed this time, but it might succeed next time! Let the code run a bit // longer and then try again. codeBlock->optimizeAfterWarmUp(); - return encodeResult(0, 0); + return 0; } #endif @@ -1333,73 +1212,16 @@ void JIT_OPERATION operationPutByIndex(ExecState* exec, EncodedJSValue encodedAr asArray(arrayValue)->putDirectIndex(exec, index, JSValue::decode(encodedValue)); } -enum class AccessorType { - Getter, - Setter -}; - -static void putAccessorByVal(ExecState* exec, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType) -{ - auto propertyKey = subscript.toPropertyKey(exec); - if (exec->hadException()) - return; - - if (accessorType == AccessorType::Getter) - base->putGetter(exec, propertyKey, accessor, attribute); - else - base->putSetter(exec, propertyKey, accessor, attribute); -} - -void JIT_OPERATION operationPutGetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* getter) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - ASSERT(object && object->isObject()); - JSObject* baseObj = object->getObject(); - - ASSERT(getter->isObject()); - baseObj->putGetter(exec, uid, getter, options); -} - -void JIT_OPERATION operationPutSetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* setter) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - ASSERT(object && object->isObject()); - JSObject* baseObj = object->getObject(); - - ASSERT(setter->isObject()); - baseObj->putSetter(exec, uid, setter, options); -} - -void JIT_OPERATION operationPutGetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter); -} - -void JIT_OPERATION operationPutSetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter); -} - #if USE(JSVALUE64) -void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue) +void JIT_OPERATION operationPutGetterSetter(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - ASSERT(object && object->isObject()); - JSObject* baseObj = asObject(object); + ASSERT(JSValue::decode(encodedObjectValue).isObject()); + JSObject* baseObj = asObject(JSValue::decode(encodedObjectValue)); - GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + GetterSetter* accessor = GetterSetter::create(vm); JSValue getter = JSValue::decode(encodedGetterValue); JSValue setter = JSValue::decode(encodedSetterValue); @@ -1408,42 +1230,62 @@ void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Uni ASSERT(getter.isObject() || setter.isObject()); if (!getter.isUndefined()) - accessor->setGetter(vm, exec->lexicalGlobalObject(), asObject(getter)); + accessor->setGetter(vm, asObject(getter)); if (!setter.isUndefined()) - accessor->setSetter(vm, exec->lexicalGlobalObject(), asObject(setter)); - baseObj->putDirectAccessor(exec, uid, accessor, attribute); + accessor->setSetter(vm, asObject(setter)); + baseObj->putDirectAccessor(exec, *identifier, accessor, Accessor); } - #else -void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, JSCell* getter, JSCell* setter) +void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Identifier* identifier, JSCell* getter, JSCell* setter) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); ASSERT(object && object->isObject()); - JSObject* baseObj = asObject(object); + JSObject* baseObj = object->getObject(); - GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + GetterSetter* accessor = GetterSetter::create(vm); ASSERT(!getter || getter->isObject()); ASSERT(!setter || setter->isObject()); ASSERT(getter || setter); if (getter) - accessor->setGetter(vm, exec->lexicalGlobalObject(), getter->getObject()); + accessor->setGetter(vm, getter->getObject()); if (setter) - accessor->setSetter(vm, exec->lexicalGlobalObject(), setter->getObject()); - baseObj->putDirectAccessor(exec, uid, accessor, attribute); + accessor->setSetter(vm, setter->getObject()); + baseObj->putDirectAccessor(exec, *identifier, accessor, Accessor); } #endif -void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg) +void JIT_OPERATION operationPushNameScope(ExecState* exec, Identifier* identifier, EncodedJSValue encodedValue, int32_t attibutes) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + JSNameScope* scope = JSNameScope::create(exec, *identifier, JSValue::decode(encodedValue), attibutes); + + exec->setScope(scope); +} + +void JIT_OPERATION operationPushWithScope(ExecState* exec, EncodedJSValue encodedValue) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + JSObject* o = JSValue::decode(encodedValue).toObject(exec); + if (vm.exception()) + return; + + exec->setScope(JSWithScope::create(exec, o)); +} + +void JIT_OPERATION operationPopScope(ExecState* exec) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - JSScope* scope = exec->uncheckedR(scopeReg).Register::scope(); - exec->uncheckedR(scopeReg) = scope->next(); + exec->setScope(exec->scope()->next()); } void JIT_OPERATION operationProfileDidCall(ExecState* exec, EncodedJSValue encodedValue) @@ -1464,277 +1306,143 @@ void JIT_OPERATION operationProfileWillCall(ExecState* exec, EncodedJSValue enco profiler->willExecute(exec, JSValue::decode(encodedValue)); } -int32_t JIT_OPERATION operationInstanceOfCustom(ExecState* exec, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance) +EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBaseVal) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); JSValue value = JSValue::decode(encodedValue); - JSValue hasInstanceValue = JSValue::decode(encodedHasInstance); + JSValue baseVal = JSValue::decode(encodedBaseVal); + + if (baseVal.isObject()) { + JSObject* baseObject = asObject(baseVal); + ASSERT(!baseObject->structure()->typeInfo().implementsDefaultHasInstance()); + if (baseObject->structure()->typeInfo().implementsHasInstance()) { + bool result = baseObject->methodTable()->customHasInstance(baseObject, exec, value); + return JSValue::encode(jsBoolean(result)); + } + } - ASSERT(hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction() || !constructor->structure()->typeInfo().implementsDefaultHasInstance()); + vm->throwException(exec, createInvalidParameterError(exec, "instanceof", baseVal)); + return JSValue::encode(JSValue()); +} - if (constructor->hasInstance(exec, value, hasInstanceValue)) - return 1; - return 0; +JSCell* JIT_OPERATION operationCreateActivation(ExecState* exec, int32_t offset) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + JSActivation* activation = JSActivation::create(vm, exec, exec->registers() + offset, exec->codeBlock()); + exec->setScope(activation); + return activation; } +JSCell* JIT_OPERATION operationCreateArguments(ExecState* exec) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + // NB: This needs to be exceedingly careful with top call frame tracking, since it + // may be called from OSR exit, while the state of the call stack is bizarre. + Arguments* result = Arguments::create(vm, exec); + ASSERT(!vm.exception()); + return result; } -static bool canAccessArgumentIndexQuickly(JSObject& object, uint32_t index) +EncodedJSValue JIT_OPERATION operationGetArgumentsLength(ExecState* exec, int32_t argumentsRegister) { - switch (object.structure()->typeInfo().type()) { - case DirectArgumentsType: { - DirectArguments* directArguments = jsCast<DirectArguments*>(&object); - if (directArguments->canAccessArgumentIndexQuicklyInDFG(index)) - return true; - break; - } - case ScopedArgumentsType: { - ScopedArguments* scopedArguments = jsCast<ScopedArguments*>(&object); - if (scopedArguments->canAccessArgumentIndexQuicklyInDFG(index)) - return true; - break; - } - default: - break; - } - return false; + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + // Here we can assume that the argumernts were created. Because otherwise the JIT code would + // have not made this call. + Identifier ident(&vm, "length"); + JSValue baseValue = exec->uncheckedR(argumentsRegister).jsValue(); + PropertySlot slot(baseValue); + return JSValue::encode(baseValue.get(exec, ident, slot)); +} + } -static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress) +static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ReturnAddressPtr returnAddress) { if (LIKELY(baseValue.isCell() && subscript.isString())) { - VM& vm = exec->vm(); - Structure& structure = *baseValue.asCell()->structure(vm); - if (JSCell::canUseFastGetOwnProperty(structure)) { - if (RefPtr<AtomicStringImpl> existingAtomicString = asString(subscript)->toExistingAtomicString(exec)) { - if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomicString.get())) { - ASSERT(exec->bytecodeOffset()); - if (byValInfo->stubInfo && byValInfo->cachedId.impl() != existingAtomicString) - byValInfo->tookSlowPath = true; - return result; - } - } - } + if (JSValue result = baseValue.asCell()->fastGetOwnProperty(exec, asString(subscript)->value(exec))) + return result; } if (subscript.isUInt32()) { - ASSERT(exec->bytecodeOffset()); - byValInfo->tookSlowPath = true; - uint32_t i = subscript.asUInt32(); - if (isJSString(baseValue)) { - if (asString(baseValue)->canGetIndex(i)) { - ctiPatchCallByReturnAddress(returnAddress, FunctionPtr(operationGetByValString)); - return asString(baseValue)->getIndex(exec, i); - } - byValInfo->arrayProfile->setOutOfBounds(); - } else if (baseValue.isObject()) { - JSObject* object = asObject(baseValue); - if (object->canGetIndexQuickly(i)) - return object->getIndexQuickly(i); - - if (!canAccessArgumentIndexQuickly(*object, i)) { - // FIXME: This will make us think that in-bounds typed array accesses are actually - // out-of-bounds. - // https://bugs.webkit.org/show_bug.cgi?id=149886 - byValInfo->arrayProfile->setOutOfBounds(); - } + if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) { + ctiPatchCallByReturnAddress(exec->codeBlock(), returnAddress, FunctionPtr(operationGetByValString)); + return asString(baseValue)->getIndex(exec, i); } - return baseValue.get(exec, i); } - baseValue.requireObjectCoercible(exec); - if (exec->hadException()) - return jsUndefined(); - auto property = subscript.toPropertyKey(exec); - if (exec->hadException()) - return jsUndefined(); - - ASSERT(exec->bytecodeOffset()); - if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property)) - byValInfo->tookSlowPath = true; + if (isName(subscript)) + return baseValue.get(exec, jsCast<NameInstance*>(subscript.asCell())->privateName()); + Identifier property(exec, subscript.toString(exec)->value(exec)); return baseValue.get(exec, property); } -static OptimizationResult tryGetByValOptimize(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress) -{ - // See if it's worth optimizing this at all. - OptimizationResult optimizationResult = OptimizationResult::NotOptimized; - - VM& vm = exec->vm(); - - if (baseValue.isObject() && subscript.isInt32()) { - JSObject* object = asObject(baseValue); - - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - - if (hasOptimizableIndexing(object->structure(vm))) { - // Attempt to optimize. - Structure* structure = object->structure(vm); - JITArrayMode arrayMode = jitArrayModeForStructure(structure); - if (arrayMode != byValInfo->arrayMode) { - // If we reached this case, we got an interesting array mode we did not expect when we compiled. - // Let's update the profile to do better next time. - CodeBlock* codeBlock = exec->codeBlock(); - ConcurrentJITLocker locker(codeBlock->m_lock); - byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure); - - JIT::compileGetByVal(&vm, exec->codeBlock(), byValInfo, returnAddress, arrayMode); - optimizationResult = OptimizationResult::Optimized; - } - } - - // If we failed to patch and we have some object that intercepts indexed get, then don't even wait until 10 times. - if (optimizationResult != OptimizationResult::Optimized && object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) - optimizationResult = OptimizationResult::GiveUp; - } - - if (baseValue.isObject() && isStringOrSymbol(subscript)) { - const Identifier propertyName = subscript.toPropertyKey(exec); - if (!subscript.isString() || !parseIndex(propertyName)) { - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - if (byValInfo->seen) { - if (byValInfo->cachedId == propertyName) { - JIT::compileGetByValWithCachedId(&vm, exec->codeBlock(), byValInfo, returnAddress, propertyName); - optimizationResult = OptimizationResult::Optimized; - } else { - // Seem like a generic property access site. - optimizationResult = OptimizationResult::GiveUp; - } - } else { - byValInfo->seen = true; - byValInfo->cachedId = propertyName; - optimizationResult = OptimizationResult::SeenOnce; - } - - } - } - - if (optimizationResult != OptimizationResult::Optimized && optimizationResult != OptimizationResult::SeenOnce) { - // If we take slow path more than 10 times without patching then make sure we - // never make that mistake again. For cases where we see non-index-intercepting - // objects, this gives 10 iterations worth of opportunity for us to observe - // that the get_by_val may be polymorphic. We count up slowPathCount even if - // the result is GiveUp. - if (++byValInfo->slowPathCount >= 10) - optimizationResult = OptimizationResult::GiveUp; - } - - return optimizationResult; -} - extern "C" { - -EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo) + +EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); - JSValue result = getByVal(exec, baseValue, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS)); + JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS)); return JSValue::encode(result); } -EncodedJSValue JIT_OPERATION operationGetByValOptimize(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - JSValue baseValue = JSValue::decode(encodedBase); - JSValue subscript = JSValue::decode(encodedSubscript); - ReturnAddressPtr returnAddress = ReturnAddressPtr(OUR_RETURN_ADDRESS); - if (tryGetByValOptimize(exec, baseValue, subscript, byValInfo, returnAddress) == OptimizationResult::GiveUp) { - // Don't ever try to optimize. - byValInfo->tookSlowPath = true; - ctiPatchCallByReturnAddress(returnAddress, FunctionPtr(operationGetByValGeneric)); - } - - return JSValue::encode(getByVal(exec, baseValue, subscript, byValInfo, returnAddress)); -} - -EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo) +EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); - ASSERT(baseValue.isObject()); - ASSERT(subscript.isUInt32()); - - JSObject* object = asObject(baseValue); - bool didOptimize = false; + if (baseValue.isObject() && subscript.isInt32()) { + // See if it's worth optimizing this at all. + JSObject* object = asObject(baseValue); + bool didOptimize = false; - ASSERT(exec->bytecodeOffset()); - ASSERT(!byValInfo->stubRoutine); - - if (hasOptimizableIndexing(object->structure(vm))) { - // Attempt to optimize. - JITArrayMode arrayMode = jitArrayModeForStructure(object->structure(vm)); - if (arrayMode != byValInfo->arrayMode) { - JIT::compileHasIndexedProperty(&vm, exec->codeBlock(), byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode); - didOptimize = true; + unsigned bytecodeOffset = exec->locationAsBytecodeOffset(); + ASSERT(bytecodeOffset); + ByValInfo& byValInfo = exec->codeBlock()->getByValInfo(bytecodeOffset - 1); + ASSERT(!byValInfo.stubRoutine); + + if (hasOptimizableIndexing(object->structure())) { + // Attempt to optimize. + JITArrayMode arrayMode = jitArrayModeForStructure(object->structure()); + if (arrayMode != byValInfo.arrayMode) { + JIT::compileGetByVal(&vm, exec->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode); + didOptimize = true; + } } - } - - if (!didOptimize) { - // If we take slow path more than 10 times without patching then make sure we - // never make that mistake again. Or, if we failed to patch and we have some object - // that intercepts indexed get, then don't even wait until 10 times. For cases - // where we see non-index-intercepting objects, this gives 10 iterations worth of - // opportunity for us to observe that the get_by_val may be polymorphic. - if (++byValInfo->slowPathCount >= 10 - || object->structure(vm)->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) { - // Don't ever try to optimize. - ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationHasIndexedPropertyGeneric)); + + if (!didOptimize) { + // If we take slow path more than 10 times without patching then make sure we + // never make that mistake again. Or, if we failed to patch and we have some object + // that intercepts indexed get, then don't even wait until 10 times. For cases + // where we see non-index-intercepting objects, this gives 10 iterations worth of + // opportunity for us to observe that the get_by_val may be polymorphic. + if (++byValInfo.slowPathCount >= 10 + || object->structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero()) { + // Don't ever try to optimize. + RepatchBuffer repatchBuffer(exec->codeBlock()); + repatchBuffer.relinkCallerToFunction(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationGetByValGeneric)); + } } } - - uint32_t index = subscript.asUInt32(); - if (object->canGetIndexQuickly(index)) - return JSValue::encode(JSValue(JSValue::JSTrue)); - - if (!canAccessArgumentIndexQuickly(*object, index)) { - // FIXME: This will make us think that in-bounds typed array accesses are actually - // out-of-bounds. - // https://bugs.webkit.org/show_bug.cgi?id=149886 - byValInfo->arrayProfile->setOutOfBounds(); - } - return JSValue::encode(jsBoolean(object->hasProperty(exec, index))); -} - -EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - JSValue baseValue = JSValue::decode(encodedBase); - JSValue subscript = JSValue::decode(encodedSubscript); - ASSERT(baseValue.isObject()); - ASSERT(subscript.isUInt32()); - - JSObject* object = asObject(baseValue); - uint32_t index = subscript.asUInt32(); - if (object->canGetIndexQuickly(index)) - return JSValue::encode(JSValue(JSValue::JSTrue)); - - if (!canAccessArgumentIndexQuickly(*object, index)) { - // FIXME: This will make us think that in-bounds typed array accesses are actually - // out-of-bounds. - // https://bugs.webkit.org/show_bug.cgi?id=149886 - byValInfo->arrayProfile->setOutOfBounds(); - } - return JSValue::encode(jsBoolean(object->hasProperty(exec, subscript.asUInt32()))); + JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS)); + return JSValue::encode(result); } -EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo) +EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); @@ -1748,23 +1456,37 @@ EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSV result = asString(baseValue)->getIndex(exec, i); else { result = baseValue.get(exec, i); - if (!isJSString(baseValue)) { - ASSERT(exec->bytecodeOffset()); - ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(byValInfo->stubRoutine ? operationGetByValGeneric : operationGetByValOptimize)); - } + if (!isJSString(baseValue)) + ctiPatchCallByReturnAddress(exec->codeBlock(), ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationGetByValDefault)); } - } else { - baseValue.requireObjectCoercible(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - auto property = subscript.toPropertyKey(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); + } else if (isName(subscript)) + result = baseValue.get(exec, jsCast<NameInstance*>(subscript.asCell())->privateName()); + else { + Identifier property(exec, subscript.toString(exec)->value(exec)); result = baseValue.get(exec, property); } return JSValue::encode(result); } + +void JIT_OPERATION operationTearOffActivation(ExecState* exec, JSCell* activationCell) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + ASSERT(exec->codeBlock()->needsFullScopeChain()); + jsCast<JSActivation*>(activationCell)->tearOff(vm); +} + +void JIT_OPERATION operationTearOffArguments(ExecState* exec, JSCell* argumentsCell, JSCell* activationCell) +{ + ASSERT(exec->codeBlock()->usesArguments()); + if (activationCell) { + jsCast<Arguments*>(argumentsCell)->didTearOffActivation(exec, jsCast<JSActivation*>(activationCell)); + return; + } + jsCast<Arguments*>(argumentsCell)->tearOff(exec); +} EncodedJSValue JIT_OPERATION operationDeleteById(ExecState* exec, EncodedJSValue encodedBase, const Identifier* identifier) { @@ -1772,13 +1494,25 @@ EncodedJSValue JIT_OPERATION operationDeleteById(ExecState* exec, EncodedJSValue NativeCallFrameTracer tracer(&vm, exec); JSObject* baseObj = JSValue::decode(encodedBase).toObject(exec); - bool couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, exec, *identifier); + bool couldDelete = baseObj->methodTable()->deleteProperty(baseObj, exec, *identifier); JSValue result = jsBoolean(couldDelete); if (!couldDelete && exec->codeBlock()->isStrictMode()) - vm.throwException(exec, createTypeError(exec, ASCIILiteral("Unable to delete property."))); + vm.throwException(exec, createTypeError(exec, "Unable to delete property.")); return JSValue::encode(result); } +JSCell* JIT_OPERATION operationGetPNames(ExecState* exec, JSObject* obj) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + Structure* structure = obj->structure(); + JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache(); + if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(exec)) + jsPropertyNameIterator = JSPropertyNameIterator::create(exec, obj); + return jsPropertyNameIterator; +} + EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedProto) { VM& vm = exec->vm(); @@ -1792,21 +1526,23 @@ EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState* exec, EncodedJSValue return JSValue::encode(jsBoolean(result)); } -int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset) +CallFrame* JIT_OPERATION operationSizeAndAllocFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t firstFreeRegister) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); JSStack* stack = &exec->interpreter()->stack(); JSValue arguments = JSValue::decode(encodedArguments); - return sizeFrameForVarargs(exec, stack, arguments, numUsedStackSlots, firstVarArgOffset); + CallFrame* newCallFrame = sizeAndAllocFrameForVarargs(exec, stack, arguments, firstFreeRegister); + return newCallFrame; } -CallFrame* JIT_OPERATION operationSetupVarargsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length) +CallFrame* JIT_OPERATION operationLoadVarargs(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedThis, EncodedJSValue encodedArguments) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); + JSValue thisValue = JSValue::decode(encodedThis); JSValue arguments = JSValue::decode(encodedArguments); - setupVarargsFrame(exec, newCallFrame, arguments, firstVarArgOffset, length); + loadVarargs(exec, newCallFrame, thisValue, arguments); return newCallFrame; } @@ -1873,6 +1609,14 @@ char* JIT_OPERATION operationSwitchStringWithUnknownKeyType(ExecState* exec, Enc return reinterpret_cast<char*>(result); } +EncodedJSValue JIT_OPERATION operationResolveScope(ExecState* exec, int32_t identifierIndex) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + const Identifier& ident = exec->codeBlock()->identifier(identifierIndex); + return JSValue::encode(JSScope::resolve(exec, exec->scope(), ident)); +} + EncodedJSValue JIT_OPERATION operationGetFromScope(ExecState* exec, Instruction* bytecodePC) { VM& vm = exec->vm(); @@ -1882,33 +1626,25 @@ EncodedJSValue JIT_OPERATION operationGetFromScope(ExecState* exec, Instruction* const Identifier& ident = codeBlock->identifier(pc[3].u.operand); JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(pc[2].u.operand).jsValue()); - GetPutInfo getPutInfo(pc[4].u.operand); + ResolveModeAndType modeAndType(pc[4].u.operand); - // ModuleVar is always converted to ClosureVar for get_from_scope. - ASSERT(getPutInfo.resolveType() != ModuleVar); - - PropertySlot slot(scope, PropertySlot::InternalMethodType::Get); + PropertySlot slot(scope); if (!scope->getPropertySlot(exec, ident, slot)) { - if (getPutInfo.resolveMode() == ThrowIfNotFound) + if (modeAndType.mode() == ThrowIfNotFound) vm.throwException(exec, createUndefinedVariableError(exec, ident)); return JSValue::encode(jsUndefined()); } - JSValue result = JSValue(); - if (jsDynamicCast<JSGlobalLexicalEnvironment*>(scope)) { - // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. - result = slot.getValue(exec, ident); - if (result == jsTDZValue()) { - exec->vm().throwException(exec, createTDZError(exec)); - return JSValue::encode(jsUndefined()); + // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time. + if (slot.isCacheableValue() && slot.slotBase() == scope && scope->structure()->propertyAccessesAreCacheable()) { + if (modeAndType.type() == GlobalProperty || modeAndType.type() == GlobalPropertyWithVarInjectionChecks) { + ConcurrentJITLocker locker(codeBlock->m_lock); + pc[5].u.structure.set(exec->vm(), codeBlock->ownerExecutable(), scope->structure()); + pc[6].u.operand = slot.cachedOffset(); } } - CommonSlowPaths::tryCacheGetFromScopeGlobal(exec, vm, pc, scope, slot, ident); - - if (!result) - result = slot.getValue(exec, ident); - return JSValue::encode(result); + return JSValue::encode(slot.getValue(exec, ident)); } void JIT_OPERATION operationPutToScope(ExecState* exec, Instruction* bytecodePC) @@ -1921,44 +1657,27 @@ void JIT_OPERATION operationPutToScope(ExecState* exec, Instruction* bytecodePC) const Identifier& ident = codeBlock->identifier(pc[2].u.operand); JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(pc[1].u.operand).jsValue()); JSValue value = exec->r(pc[3].u.operand).jsValue(); - GetPutInfo getPutInfo = GetPutInfo(pc[4].u.operand); - - // ModuleVar does not keep the scope register value alive in DFG. - ASSERT(getPutInfo.resolveType() != ModuleVar); - - if (getPutInfo.resolveType() == LocalClosureVar) { - JSLexicalEnvironment* environment = jsCast<JSLexicalEnvironment*>(scope); - environment->variableAt(ScopeOffset(pc[6].u.operand)).set(vm, environment, value); - if (WatchpointSet* set = pc[5].u.watchpointSet) - set->touch("Executed op_put_scope<LocalClosureVar>"); - return; - } + ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand); - bool hasProperty = scope->hasProperty(exec, ident); - if (hasProperty - && jsDynamicCast<JSGlobalLexicalEnvironment*>(scope) - && getPutInfo.initializationMode() != Initialization) { - // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. - PropertySlot slot(scope, PropertySlot::InternalMethodType::Get); - JSGlobalLexicalEnvironment::getOwnPropertySlot(scope, exec, ident, slot); - if (slot.getValue(exec, ident) == jsTDZValue()) { - exec->vm().throwException(exec, createTDZError(exec)); - return; - } - } - - if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) { + if (modeAndType.mode() == ThrowIfNotFound && !scope->hasProperty(exec, ident)) { exec->vm().throwException(exec, createUndefinedVariableError(exec, ident)); return; } - PutPropertySlot slot(scope, codeBlock->isStrictMode(), PutPropertySlot::UnknownContext, getPutInfo.initializationMode() == Initialization); + PutPropertySlot slot(scope, codeBlock->isStrictMode()); scope->methodTable()->put(scope, exec, ident, value, slot); if (exec->vm().exception()) return; - CommonSlowPaths::tryCachePutToScopeGlobal(exec, codeBlock, pc, scope, getPutInfo, slot, ident); + // Covers implicit globals. Since they don't exist until they first execute, we didn't know how to cache them at compile time. + if (modeAndType.type() == GlobalProperty || modeAndType.type() == GlobalPropertyWithVarInjectionChecks) { + if (slot.isCacheable() && slot.base() == scope && scope->structure()->propertyAccessesAreCacheable()) { + ConcurrentJITLocker locker(codeBlock->m_lock); + pc[5].u.structure.set(exec->vm(), codeBlock->ownerExecutable(), scope->structure()); + pc[6].u.operand = slot.cachedOffset(); + } + } } void JIT_OPERATION operationThrow(ExecState* exec, EncodedJSValue encodedExceptionValue) @@ -1969,8 +1688,8 @@ void JIT_OPERATION operationThrow(ExecState* exec, EncodedJSValue encodedExcepti JSValue exceptionValue = JSValue::decode(encodedExceptionValue); vm->throwException(exec, exceptionValue); - // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForCatch - genericUnwind(vm, exec); + // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForThrow + genericUnwind(vm, exec, exceptionValue); } void JIT_OPERATION operationFlushWriteBarrierBuffer(ExecState* exec, JSCell* cell) @@ -1984,7 +1703,7 @@ void JIT_OPERATION operationOSRWriteBarrier(ExecState* exec, JSCell* cell) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - vm->heap.writeBarrier(cell); + exec->heap()->writeBarrier(cell); } // NB: We don't include the value as part of the barrier because the write barrier elision @@ -1994,7 +1713,7 @@ void JIT_OPERATION operationUnconditionalWriteBarrier(ExecState* exec, JSCell* c { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - vm->heap.writeBarrier(cell); + Heap::writeBarrier(cell); } void JIT_OPERATION operationInitGlobalConst(ExecState* exec, Instruction* pc) @@ -2003,20 +1722,18 @@ void JIT_OPERATION operationInitGlobalConst(ExecState* exec, Instruction* pc) NativeCallFrameTracer tracer(vm, exec); JSValue value = exec->r(pc[2].u.operand).jsValue(); - pc[1].u.variablePointer->set(*vm, exec->codeBlock()->globalObject(), value); + pc[1].u.registerPointer->set(*vm, exec->codeBlock()->globalObject(), value); } -void JIT_OPERATION lookupExceptionHandler(VM* vm, ExecState* exec) +void JIT_OPERATION lookupExceptionHandler(ExecState* exec) { + VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - genericUnwind(vm, exec); - ASSERT(vm->targetMachinePCForThrow); -} -void JIT_OPERATION lookupExceptionHandlerFromCallerFrame(VM* vm, ExecState* exec) -{ - NativeCallFrameTracer tracer(vm, exec); - genericUnwind(vm, exec, UnwindFromCallerFrame); + JSValue exceptionValue = exec->exception(); + ASSERT(exceptionValue); + + genericUnwind(vm, exec, exceptionValue); ASSERT(vm->targetMachinePCForThrow); } @@ -2024,89 +1741,9 @@ void JIT_OPERATION operationVMHandleException(ExecState* exec) { VM* vm = &exec->vm(); NativeCallFrameTracer tracer(vm, exec); - genericUnwind(vm, exec); -} -// This function "should" just take the ExecState*, but doing so would make it more difficult -// to call from exception check sites. So, unlike all of our other functions, we allow -// ourselves to play some gnarly ABI tricks just to simplify the calling convention. This is -// particularly safe here since this is never called on the critical path - it's only for -// testing. -void JIT_OPERATION operationExceptionFuzz(ExecState* exec) -{ - VM* vm = &exec->vm(); - NativeCallFrameTracer tracer(vm, exec); -#if COMPILER(GCC_OR_CLANG) - void* returnPC = __builtin_return_address(0); - doExceptionFuzzing(exec, "JITOperations", returnPC); -#endif // COMPILER(GCC_OR_CLANG) -} - -EncodedJSValue JIT_OPERATION operationHasGenericProperty(ExecState* exec, EncodedJSValue encodedBaseValue, JSCell* propertyName) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - JSValue baseValue = JSValue::decode(encodedBaseValue); - if (baseValue.isUndefinedOrNull()) - return JSValue::encode(jsBoolean(false)); - - JSObject* base = baseValue.toObject(exec); - return JSValue::encode(jsBoolean(base->hasProperty(exec, asString(propertyName)->toIdentifier(exec)))); -} - -EncodedJSValue JIT_OPERATION operationHasIndexedProperty(ExecState* exec, JSCell* baseCell, int32_t subscript) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - JSObject* object = baseCell->toObject(exec, exec->lexicalGlobalObject()); - return JSValue::encode(jsBoolean(object->hasProperty(exec, subscript))); -} - -JSCell* JIT_OPERATION operationGetPropertyEnumerator(ExecState* exec, JSCell* cell) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - JSObject* base = cell->toObject(exec, exec->lexicalGlobalObject()); - - return propertyNameEnumerator(exec, base); -} - -EncodedJSValue JIT_OPERATION operationNextEnumeratorPname(ExecState* exec, JSCell* enumeratorCell, int32_t index) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(enumeratorCell); - JSString* propertyName = enumerator->propertyNameAtIndex(index); - return JSValue::encode(propertyName ? propertyName : jsNull()); -} - -JSCell* JIT_OPERATION operationToIndexString(ExecState* exec, int32_t index) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - return jsString(exec, Identifier::from(exec, index).string()); -} - -void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec) -{ - exec->vm().typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside baseline JIT")); -} - -int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState* exec) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - RELEASE_ASSERT(!!vm.exception()); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->exceptionUnwind(exec); - - if (isTerminatedExecutionException(vm.exception())) { - genericUnwind(&vm, exec); - return 1; - } else - return 0; + ASSERT(!exec->isVMEntrySentinel()); + genericUnwind(vm, exec, vm->exception()); } } // extern "C" @@ -2121,32 +1758,28 @@ extern "C" EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValueWi return JSValue::encode(exec->vm().hostCallReturnValue); } -#if COMPILER(GCC_OR_CLANG) && CPU(X86_64) +#if COMPILER(GCC) && CPU(X86_64) asm ( ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "lea -8(%rsp), %rdi\n" + "mov 0(%rbp), %rbp\n" // CallerFrameAndPC::callerFrame + "mov %rbp, %rdi\n" "jmp " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); -#elif COMPILER(GCC_OR_CLANG) && CPU(X86) +#elif COMPILER(GCC) && CPU(X86) asm ( ".text" "\n" \ ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "push %ebp\n" - "mov %esp, %eax\n" - "leal -4(%esp), %esp\n" - "push %eax\n" - "call " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" - "leal 8(%esp), %esp\n" - "pop %ebp\n" - "ret\n" + "mov 0(%ebp), %ebp\n" // CallerFrameAndPC::callerFrame + "mov %ebp, 4(%esp)\n" + "jmp " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); -#elif COMPILER(GCC_OR_CLANG) && CPU(ARM_THUMB2) +#elif COMPILER(GCC) && CPU(ARM_THUMB2) asm ( ".text" "\n" ".align 2" "\n" @@ -2155,18 +1788,20 @@ HIDE_SYMBOL(getHostCallReturnValue) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "sub r0, sp, #8" "\n" + "ldr r7, [r7, #0]" "\n" // CallerFrameAndPC::callerFrame + "mov r0, r7" "\n" "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); -#elif COMPILER(GCC_OR_CLANG) && CPU(ARM_TRADITIONAL) +#elif COMPILER(GCC) && CPU(ARM_TRADITIONAL) asm ( ".text" "\n" ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" INLINE_ARM_FUNCTION(getHostCallReturnValue) SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "sub r0, sp, #8" "\n" + "ldr r11, [r11, #0]" "\n" // CallerFrameAndPC::callerFrame + "mov r0, r11" "\n" "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); @@ -2177,43 +1812,31 @@ asm ( ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "sub x0, sp, #16" "\n" + "ldur x29, [x29, #0]" "\n" + "mov x0, x29" "\n" "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); -#elif COMPILER(GCC_OR_CLANG) && CPU(MIPS) - -#if WTF_MIPS_PIC -#define LOAD_FUNCTION_TO_T9(function) \ - ".set noreorder" "\n" \ - ".cpload $25" "\n" \ - ".set reorder" "\n" \ - "la $t9, " LOCAL_REFERENCE(function) "\n" -#else -#define LOAD_FUNCTION_TO_T9(function) "" "\n" -#endif - +#elif COMPILER(GCC) && CPU(MIPS) asm ( ".text" "\n" ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" LOAD_FUNCTION_TO_T9(getHostCallReturnValueWithExecState) - "addi $a0, $sp, -8" "\n" + "lw $fp, 0($fp)" "\n" // CallerFrameAndPC::callerFrame + "move $a0, $fp" "\n" "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); -#elif COMPILER(GCC_OR_CLANG) && CPU(SH4) - -#define SH4_SCRATCH_REGISTER "r11" - +#elif COMPILER(GCC) && CPU(SH4) asm ( ".text" "\n" ".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" HIDE_SYMBOL(getHostCallReturnValue) "\n" SYMBOL_STRING(getHostCallReturnValue) ":" "\n" - "mov r15, r4" "\n" - "add -8, r4" "\n" + "mov.l @r14, r14" "\n" // CallerFrameAndPC::callerFrame + "mov r14, r4" "\n" "mov.l 2f, " SH4_SCRATCH_REGISTER "\n" "braf " SH4_SCRATCH_REGISTER "\n" "nop" "\n" @@ -2225,8 +1848,8 @@ SYMBOL_STRING(getHostCallReturnValue) ":" "\n" extern "C" { __declspec(naked) EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValue() { - __asm lea eax, [esp - 4] - __asm mov [esp + 4], eax; + __asm mov ebp, [ebp + 0]; // CallerFrameAndPC::callerFrame + __asm mov [esp + 4], ebp; __asm jmp getHostCallReturnValueWithExecState } } |