summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/jit/JITOperations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/jit/JITOperations.cpp')
-rw-r--r--Source/JavaScriptCore/jit/JITOperations.cpp2494
1 files changed, 1729 insertions, 765 deletions
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 578d15dac..a1d4e7351 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 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,32 +24,53 @@
*/
#include "config.h"
-#if ENABLE(JIT)
#include "JITOperations.h"
-#include "Arguments.h"
+#if ENABLE(JIT)
+
+#include "ArithProfile.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 "EvalCodeBlock.h"
+#include "ExceptionFuzz.h"
+#include "FunctionCodeBlock.h"
#include "GetterSetter.h"
#include "HostCallReturnValue.h"
+#include "ICStats.h"
+#include "Interpreter.h"
#include "JIT.h"
-#include "JITOperationWrappers.h"
+#include "JITExceptions.h"
#include "JITToDFGDeferredCompilationCallback.h"
+#include "JSAsyncFunction.h"
+#include "JSCInlines.h"
+#include "JSGeneratorFunction.h"
#include "JSGlobalObjectFunctions.h"
-#include "JSNameScope.h"
-#include "JSPropertyNameIterator.h"
-#include "JSStackInlines.h"
-#include "JSWithScope.h"
+#include "JSLexicalEnvironment.h"
+#include "JSPropertyNameEnumerator.h"
+#include "ModuleProgramCodeBlock.h"
#include "ObjectConstructor.h"
-#include "Operations.h"
+#include "PolymorphicAccess.h"
+#include "ProgramCodeBlock.h"
+#include "PropertyName.h"
+#include "RegExpObject.h"
#include "Repatch.h"
-#include "RepatchBuffer.h"
+#include "ScopedArguments.h"
+#include "ShadowChicken.h"
+#include "StructureStubInfo.h"
+#include "SuperSampler.h"
+#include "TestRunnerUtils.h"
+#include "TypeProfilerLog.h"
+#include "VMInlines.h"
+#include <wtf/InlineASM.h>
namespace JSC {
@@ -71,33 +92,63 @@ void * _ReturnAddress(void);
#endif
-void JIT_OPERATION operationStackCheck(ExecState* exec, CodeBlock* codeBlock)
+void JIT_OPERATION operationThrowStackOverflowError(ExecState* exec, CodeBlock* codeBlock)
{
// We pass in our own code block, because the callframe hasn't been populated.
VM* vm = codeBlock->vm();
- CallFrame* callerFrame = exec->callerFrameSkippingVMEntrySentinel();
- if (!callerFrame)
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+ CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
+ if (!callerFrame) {
callerFrame = exec;
+ vmEntryFrame = vm->topVMEntryFrame;
+ }
+
+ NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+ throwStackOverflowError(callerFrame, scope);
+}
- NativeCallFrameTracer tracer(vm, callerFrame);
+#if ENABLE(WEBASSEMBLY)
+void JIT_OPERATION operationThrowDivideError(ExecState* exec)
+{
+ VM* vm = &exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(*vm);
- JSStack& stack = vm->interpreter->stack();
+ VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+ CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
- if (UNLIKELY(!stack.grow(&exec->registers()[virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset()])))
- vm->throwException(callerFrame, createStackOverflowError(callerFrame));
+ NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+ ErrorHandlingScope errorScope(*vm);
+ throwException(callerFrame, scope, createError(callerFrame, ASCIILiteral("Division by zero or division overflow.")));
}
-int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
+void JIT_OPERATION operationThrowOutOfBoundsAccessError(ExecState* exec)
{
VM* vm = &exec->vm();
- CallFrame* callerFrame = exec->callerFrameSkippingVMEntrySentinel();
- NativeCallFrameTracer tracer(vm, callerFrame);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
- JSStack& stack = vm->interpreter->stack();
+ VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+ CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
- int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, &stack, CodeForCall);
- if (missingArgCount < 0)
- vm->throwException(callerFrame, createStackOverflowError(callerFrame));
+ NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+ ErrorHandlingScope errorScope(*vm);
+ throwException(callerFrame, scope, createError(callerFrame, ASCIILiteral("Out-of-bounds access.")));
+}
+#endif
+
+int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
+{
+ VM* vm = &exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, *vm, CodeForCall);
+ if (missingArgCount < 0) {
+ VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+ CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
+ NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+ throwStackOverflowError(callerFrame, scope);
+ }
return missingArgCount;
}
@@ -105,489 +156,616 @@ 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)
- vm->throwException(callerFrame, createStackOverflowError(callerFrame));
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, *vm, CodeForConstruct);
+ if (missingArgCount < 0) {
+ VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+ CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
+ NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
+ throwStackOverflowError(callerFrame, scope);
+ }
return missingArgCount;
}
-EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo*, EncodedJSValue base, StringImpl* uid)
+EncodedJSValue JIT_OPERATION operationTryGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
-
+ Identifier ident = Identifier::fromUid(vm, uid);
+ stubInfo->tookSlowPath = true;
+
JSValue baseValue = JSValue::decode(base);
- PropertySlot slot(baseValue);
- Identifier ident(vm, uid);
- return JSValue::encode(baseValue.get(exec, ident, slot));
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+ baseValue.getPropertySlot(exec, ident, slot);
+
+ return JSValue::encode(slot.getPureResult());
}
-EncodedJSValue JIT_OPERATION operationGetByIdBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, StringImpl* uid)
+
+EncodedJSValue JIT_OPERATION operationTryGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* 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 baseValue = JSValue::decode(base);
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
+ baseValue.getPropertySlot(exec, ident, slot);
+
+ return JSValue::encode(slot.getPureResult());
+}
+
+EncodedJSValue JIT_OPERATION operationTryGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+ Identifier ident = Identifier::fromUid(vm, uid);
JSValue baseValue = JSValue::decode(base);
- PropertySlot slot(baseValue);
- JSValue result = baseValue.get(exec, ident, slot);
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry);
- if (accessType == static_cast<AccessType>(stubInfo->accessType))
- buildGetByIDList(exec, baseValue, ident, slot, *stubInfo);
+ baseValue.getPropertySlot(exec, ident, slot);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
- return JSValue::encode(result);
+ if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()) && !slot.isTaintedByOpaqueObject() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset()))
+ repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Try);
+
+ return JSValue::encode(slot.getPureResult());
}
-EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, StringImpl* uid)
+EncodedJSValue JIT_OPERATION operationGetById(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Identifier ident = uid->isEmptyUnique() ? Identifier::from(PrivateName(uid)) : Identifier(vm, uid);
- AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
-
+
+ stubInfo->tookSlowPath = true;
+
JSValue baseValue = JSValue::decode(base);
- PropertySlot slot(baseValue);
- JSValue result = baseValue.get(exec, ident, slot);
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
+ Identifier ident = Identifier::fromUid(vm, uid);
- if (accessType == static_cast<AccessType>(stubInfo->accessType)) {
- if (stubInfo->seen)
- repatchGetByID(exec, baseValue, ident, slot, *stubInfo);
- else
- stubInfo->seen = true;
- }
+ LOG_IC((ICEvent::OperationGetById, baseValue.classInfoOrNull(*vm), ident));
+ return JSValue::encode(baseValue.get(exec, ident, slot));
+}
- return JSValue::encode(result);
+EncodedJSValue JIT_OPERATION operationGetByIdGeneric(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
+{
+ SuperSamplerScope superSamplerScope(false);
+
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue baseValue = JSValue::decode(base);
+ PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
+ Identifier ident = Identifier::fromUid(vm, uid);
+ LOG_IC((ICEvent::OperationGetByIdGeneric, baseValue.classInfoOrNull(*vm), ident));
+ return JSValue::encode(baseValue.get(exec, ident, slot));
}
-EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, StringImpl* key)
+EncodedJSValue JIT_OPERATION operationGetByIdOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue base, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
+ Identifier ident = Identifier::fromUid(vm, uid);
+
+ JSValue baseValue = JSValue::decode(base);
+ LOG_IC((ICEvent::OperationGetByIdOptimize, baseValue.classInfoOrNull(*vm), ident));
+
+ return JSValue::encode(baseValue.getPropertySlot(exec, ident, [&] (bool found, PropertySlot& slot) -> JSValue {
+ if (stubInfo->considerCaching(exec->codeBlock(), baseValue.structureOrNull()))
+ repatchGetByID(exec, baseValue, ident, slot, *stubInfo, GetByIDKind::Normal);
+ return found ? slot.getValue(exec, ident) : jsUndefined();
+ }));
+}
+
+EncodedJSValue JIT_OPERATION operationInOptimize(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key)
+{
+ SuperSamplerScope superSamplerScope(false);
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
if (!base->isObject()) {
- vm->throwException(exec, createInvalidParameterError(exec, "in", base));
+ throwException(exec, scope, createInvalidInParameterError(exec, base));
return JSValue::encode(jsUndefined());
}
AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
- Identifier ident(vm, key);
- PropertySlot slot(base);
+ Identifier ident = Identifier::fromUid(vm, key);
+ LOG_IC((ICEvent::OperationInOptimize, base->classInfo(*vm), ident));
+ PropertySlot slot(base, PropertySlot::InternalMethodType::HasProperty);
bool result = asObject(base)->getPropertySlot(exec, ident, slot);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
RELEASE_ASSERT(accessType == stubInfo->accessType);
- if (stubInfo->seen)
+ if (stubInfo->considerCaching(exec->codeBlock(), asObject(base)->structure()))
repatchIn(exec, base, ident, result, slot, *stubInfo);
- else
- stubInfo->seen = true;
return JSValue::encode(jsBoolean(result));
}
-EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo*, JSCell* base, StringImpl* key)
+EncodedJSValue JIT_OPERATION operationIn(ExecState* exec, StructureStubInfo* stubInfo, JSCell* base, UniquedStringImpl* key)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ stubInfo->tookSlowPath = true;
if (!base->isObject()) {
- vm->throwException(exec, createInvalidParameterError(exec, "in", base));
+ throwException(exec, scope, createInvalidInParameterError(exec, base));
return JSValue::encode(jsUndefined());
}
- Identifier ident(vm, key);
+ Identifier ident = Identifier::fromUid(vm, key);
+ LOG_IC((ICEvent::OperationIn, base->classInfo(*vm), ident));
return JSValue::encode(jsBoolean(asObject(base)->hasProperty(exec, ident)));
}
EncodedJSValue JIT_OPERATION operationGenericIn(ExecState* exec, JSCell* base, EncodedJSValue key)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- return JSValue::encode(jsBoolean(CommonSlowPaths::opIn(exec, JSValue::decode(key), base)));
+ return JSValue::encode(jsBoolean(CommonSlowPaths::opIn(exec, base, JSValue::decode(key))));
}
-EncodedJSValue JIT_OPERATION operationCallCustomGetter(ExecState* exec, JSCell* base, PropertySlot::GetValueFunc function, StringImpl* uid)
+void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Identifier ident(vm, uid);
+ stubInfo->tookSlowPath = true;
- 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);
+ JSValue baseValue = JSValue::decode(encodedBase);
+ Identifier ident = Identifier::fromUid(vm, uid);
+ LOG_IC((ICEvent::OperationPutByIdStrict, baseValue.classInfoOrNull(*vm), ident));
- return JSValue::encode(callGetter(exec, base, getterSetter));
+ PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext());
+ baseValue.putInline(exec, ident, JSValue::decode(encodedValue), slot);
}
-void JIT_OPERATION operationPutByIdStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
- VM* vm = &exec->vm();
- NativeCallFrameTracer tracer(vm, exec);
+ SuperSamplerScope superSamplerScope(false);
- 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(vm, uid);
- PutPropertySlot slot(JSValue::decode(encodedBase), false, exec->codeBlock()->putByIdContext());
- JSValue::decode(encodedBase).put(exec, ident, JSValue::decode(encodedValue), slot);
-}
-
-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(vm, uid);
- PutPropertySlot slot(JSValue::decode(encodedBase), true, exec->codeBlock()->putByIdContext());
- asObject(JSValue::decode(encodedBase))->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot);
+ JSValue baseValue = JSValue::decode(encodedBase);
+ Identifier ident = Identifier::fromUid(vm, uid);
+ LOG_IC((ICEvent::OperationPutByIdNonStrict, baseValue.classInfoOrNull(*vm), ident));
+ PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext());
+ baseValue.putInline(exec, ident, JSValue::decode(encodedValue), slot);
}
-void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdDirectStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
- VM* vm = &exec->vm();
- NativeCallFrameTracer tracer(vm, exec);
+ SuperSamplerScope superSamplerScope(false);
- 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, 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);
+ stubInfo->tookSlowPath = true;
+
JSValue baseValue = JSValue::decode(encodedBase);
+ Identifier ident = Identifier::fromUid(vm, uid);
+ LOG_IC((ICEvent::OperationPutByIdDirectStrict, baseValue.classInfoOrNull(*vm), ident));
PutPropertySlot slot(baseValue, true, exec->codeBlock()->putByIdContext());
-
- baseValue.put(exec, ident, value, slot);
-
- if (accessType != static_cast<AccessType>(stubInfo->accessType))
- return;
-
- if (stubInfo->seen)
- repatchPutByID(exec, baseValue, ident, slot, *stubInfo, NotDirect);
- else
- stubInfo->seen = true;
+ asObject(baseValue)->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot);
}
-void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Identifier ident(vm, uid);
- AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
-
- JSValue value = JSValue::decode(encodedValue);
+ stubInfo->tookSlowPath = true;
+
JSValue baseValue = JSValue::decode(encodedBase);
+ Identifier ident = Identifier::fromUid(vm, uid);
+ LOG_IC((ICEvent::OperationPutByIdDirectNonStrict, baseValue.classInfoOrNull(*vm), ident));
PutPropertySlot slot(baseValue, false, exec->codeBlock()->putByIdContext());
-
- baseValue.put(exec, ident, value, slot);
-
- if (accessType != static_cast<AccessType>(stubInfo->accessType))
- return;
-
- if (stubInfo->seen)
- repatchPutByID(exec, baseValue, ident, slot, *stubInfo, NotDirect);
- else
- stubInfo->seen = true;
+ asObject(baseValue)->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot);
}
-void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
-
- Identifier ident(vm, uid);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ Identifier ident = Identifier::fromUid(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;
-
- if (stubInfo->seen)
- repatchPutByID(exec, baseObject, ident, slot, *stubInfo, Direct);
- else
- stubInfo->seen = true;
-}
+ JSValue baseValue = JSValue::decode(encodedBase);
+ LOG_IC((ICEvent::OperationPutByIdStrictOptimize, baseValue.classInfoOrNull(*vm), ident));
+ CodeBlock* codeBlock = exec->codeBlock();
+ PutPropertySlot slot(baseValue, true, codeBlock->putByIdContext());
-void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(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);
+ Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr;
+ baseValue.putInline(exec, ident, value, slot);
+ RETURN_IF_EXCEPTION(scope, void());
- 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;
- if (stubInfo->seen)
- repatchPutByID(exec, baseObject, ident, slot, *stubInfo, Direct);
- else
- stubInfo->seen = true;
+ if (stubInfo->considerCaching(codeBlock, structure))
+ repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect);
}
-void JIT_OPERATION operationPutByIdStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
-
- Identifier ident(vm, uid);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ Identifier ident = Identifier::fromUid(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);
-}
+ LOG_IC((ICEvent::OperationPutByIdNonStrictOptimize, baseValue.classInfoOrNull(*vm), ident));
+ CodeBlock* codeBlock = exec->codeBlock();
+ PutPropertySlot slot(baseValue, false, codeBlock->putByIdContext());
-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);
+ Structure* structure = baseValue.isCell() ? baseValue.asCell()->structure(*vm) : nullptr;
+ baseValue.putInline(exec, ident, value, slot);
+ RETURN_IF_EXCEPTION(scope, void());
- 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);
+ if (stubInfo->considerCaching(codeBlock, structure))
+ repatchPutByID(exec, baseValue, structure, ident, slot, *stubInfo, NotDirect);
}
-void JIT_OPERATION operationPutByIdDirectStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdDirectStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Identifier ident(vm, uid);
+ Identifier ident = Identifier::fromUid(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());
+ LOG_IC((ICEvent::OperationPutByIdDirectStrictOptimize, baseObject->classInfo(*vm), ident));
+ CodeBlock* codeBlock = exec->codeBlock();
+ PutPropertySlot slot(baseObject, true, codeBlock->putByIdContext());
+ Structure* structure = baseObject->structure(*vm);
baseObject->putDirect(exec->vm(), ident, value, slot);
if (accessType != static_cast<AccessType>(stubInfo->accessType))
return;
- buildPutByIdList(exec, baseObject, ident, slot, *stubInfo, Direct);
+ if (stubInfo->considerCaching(codeBlock, structure))
+ repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct);
}
-void JIT_OPERATION operationPutByIdDirectNonStrictBuildList(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, StringImpl* uid)
+void JIT_OPERATION operationPutByIdDirectNonStrictOptimize(ExecState* exec, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
+ SuperSamplerScope superSamplerScope(false);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Identifier ident(vm, uid);
+ Identifier ident = Identifier::fromUid(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());
+ LOG_IC((ICEvent::OperationPutByIdDirectNonStrictOptimize, baseObject->classInfo(*vm), ident));
+ CodeBlock* codeBlock = exec->codeBlock();
+ PutPropertySlot slot(baseObject, false, codeBlock->putByIdContext());
- baseObject ->putDirect(exec->vm(), ident, value, slot);
+ Structure* structure = baseObject->structure(*vm);
+ baseObject->putDirect(exec->vm(), ident, value, slot);
if (accessType != static_cast<AccessType>(stubInfo->accessType))
return;
- buildPutByIdList(exec, baseObject, ident, slot, *stubInfo, Direct);
+ if (stubInfo->considerCaching(codeBlock, structure))
+ repatchPutByID(exec, baseObject, structure, ident, slot, *stubInfo, Direct);
}
-void JIT_OPERATION operationReallocateStorageAndFinishPut(ExecState* exec, JSObject* base, Structure* structure, PropertyOffset offset, EncodedJSValue value)
+ALWAYS_INLINE static bool isStringOrSymbol(JSValue value)
{
- VM& vm = exec->vm();
- NativeCallFrameTracer tracer(&vm, exec);
-
- 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));
+ return value.isString() || value.isSymbol();
}
-static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value)
+static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value, ByValInfo* byValInfo)
{
+ VM& vm = callFrame->vm();
+ auto scope = DECLARE_THROW_SCOPE(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
- object->methodTable()->putByIndex(object, callFrame, i, value, callFrame->codeBlock()->isStrictMode());
+ 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
baseValue.putByIndex(callFrame, i, value, callFrame->codeBlock()->isStrictMode());
- } 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);
- }
+ return;
}
+
+ auto property = subscript.toPropertyKey(callFrame);
+ // Don't put to an object if toString threw an exception.
+ RETURN_IF_EXCEPTION(scope, void());
+
+ if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property))
+ byValInfo->tookSlowPath = true;
+
+ scope.release();
+ PutPropertySlot slot(baseValue, callFrame->codeBlock()->isStrictMode());
+ baseValue.putInline(callFrame, property, value, slot);
}
-static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value)
+static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value, ByValInfo* byValInfo)
{
+ VM& vm = callFrame->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ bool isStrictMode = callFrame->codeBlock()->isStrictMode();
if (LIKELY(subscript.isUInt32())) {
- 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);
+ // 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;
+ }
+ }
+
+ // Don't put to an object if toString threw an exception.
+ auto property = subscript.toPropertyKey(callFrame);
+ RETURN_IF_EXCEPTION(scope, void());
+
+ if (std::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);
}
-void JIT_OPERATION operationPutByVal(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+
+enum class OptimizationResult {
+ NotOptimized,
+ SeenOnce,
+ Optimized,
+ GiveUp,
+};
+
+static OptimizationResult tryPutByValOptimize(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
{
- VM& vm = exec->vm();
- NativeCallFrameTracer tracer(&vm, exec);
+ // See if it's worth optimizing at all.
+ OptimizationResult optimizationResult = OptimizationResult::NotOptimized;
- JSValue baseValue = JSValue::decode(encodedBaseValue);
- JSValue subscript = JSValue::decode(encodedSubscript);
- JSValue value = JSValue::decode(encodedValue);
+ VM& vm = exec->vm();
if (baseValue.isObject() && subscript.isInt32()) {
- // See if it's worth optimizing at all.
JSObject* object = asObject(baseValue);
- bool didOptimize = false;
- unsigned bytecodeOffset = exec->locationAsBytecodeOffset();
- ASSERT(bytecodeOffset);
- ByValInfo& byValInfo = exec->codeBlock()->getByValInfo(bytecodeOffset - 1);
- ASSERT(!byValInfo.stubRoutine);
+ ASSERT(exec->bytecodeOffset());
+ ASSERT(!byValInfo->stubRoutine);
- if (hasOptimizableIndexing(object->structure())) {
+ Structure* structure = object->structure(vm);
+ if (hasOptimizableIndexing(structure)) {
// Attempt to optimize.
- JITArrayMode arrayMode = jitArrayModeForStructure(object->structure());
- if (arrayMode != byValInfo.arrayMode) {
- JIT::compilePutByVal(&vm, exec->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
- didOptimize = true;
+ JITArrayMode arrayMode = jitArrayModeForStructure(structure);
+ if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo->arrayMode) {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJSLocker locker(codeBlock->m_lock);
+ byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
+
+ JIT::compilePutByVal(&vm, codeBlock, byValInfo, returnAddress, arrayMode);
+ optimizationResult = OptimizationResult::Optimized;
}
}
- 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 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.isSymbol() || !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 {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJSLocker locker(codeBlock->m_lock);
+ byValInfo->seen = true;
+ byValInfo->cachedId = propertyName;
+ if (subscript.isSymbol())
+ byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
+ optimizationResult = OptimizationResult::SeenOnce;
}
}
}
- putByVal(exec, baseValue, subscript, value);
+ 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;
}
-void JIT_OPERATION operationDirectPutByVal(ExecState* callFrame, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationPutByValOptimize(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
{
- VM& vm = callFrame->vm();
- NativeCallFrameTracer tracer(&vm, callFrame);
-
+ 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 (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();
+
if (subscript.isInt32()) {
- // 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())) {
+ ASSERT(exec->bytecodeOffset());
+ ASSERT(!byValInfo->stubRoutine);
+
+ Structure* structure = object->structure(vm);
+ if (hasOptimizableIndexing(structure)) {
// Attempt to optimize.
- JITArrayMode arrayMode = jitArrayModeForStructure(object->structure());
- if (arrayMode != byValInfo.arrayMode) {
- JIT::compileDirectPutByVal(&vm, callFrame->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
- didOptimize = true;
+ JITArrayMode arrayMode = jitArrayModeForStructure(structure);
+ if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo->arrayMode) {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJSLocker locker(codeBlock->m_lock);
+ byValInfo->arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
+
+ JIT::compileDirectPutByVal(&vm, codeBlock, byValInfo, returnAddress, arrayMode);
+ optimizationResult = OptimizationResult::Optimized;
}
}
-
- 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 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);
+ if (subscript.isSymbol() || !parseIndex(propertyName)) {
+ 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 {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJSLocker locker(codeBlock->m_lock);
+ byValInfo->seen = true;
+ byValInfo->cachedId = propertyName;
+ if (subscript.isSymbol())
+ byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
+ optimizationResult = OptimizationResult::SeenOnce;
}
}
}
- directPutByVal(callFrame, object, subscript, value);
+
+ 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 operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+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);
+}
+
+void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -596,11 +774,11 @@ void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue enco
JSValue subscript = JSValue::decode(encodedSubscript);
JSValue value = JSValue::decode(encodedValue);
- putByVal(exec, baseValue, subscript, value);
+ putByVal(exec, baseValue, subscript, value, byValInfo);
}
-void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ByValInfo* byValInfo)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -609,218 +787,265 @@ 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);
+ directPutByVal(exec, asObject(baseValue), subscript, value, byValInfo);
}
-EncodedJSValue JIT_OPERATION operationCallEval(ExecState* execCallee)
+EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execCallee)
{
- CallFrame* callerFrame = execCallee->callerFrame();
- ASSERT(execCallee->callerFrame()->codeBlock()->codeType() != FunctionCode
- || !execCallee->callerFrame()->codeBlock()->needsFullScopeChain()
- || execCallee->callerFrame()->uncheckedR(execCallee->callerFrame()->codeBlock()->activationRegister().offset()).jsValue());
+ VM* vm = &exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(*vm);
- execCallee->setScope(callerFrame->scope());
- execCallee->setReturnPC(static_cast<Instruction*>(OUR_RETURN_ADDRESS));
execCallee->setCodeBlock(0);
-
+
if (!isHostFunction(execCallee->calleeAsValue(), globalFuncEval))
return JSValue::encode(JSValue());
- VM* vm = &execCallee->vm();
JSValue result = eval(execCallee);
- if (vm->exception())
- return EncodedJSValue();
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
return JSValue::encode(result);
}
-static void* handleHostCall(ExecState* execCallee, JSValue callee, CodeSpecializationKind kind)
+static SlowPathReturnType handleHostCall(ExecState* execCallee, JSValue callee, CallLinkInfo* callLinkInfo)
{
ExecState* exec = execCallee->callerFrame();
VM* vm = &exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(*vm);
- execCallee->setScope(exec->scope());
execCallee->setCodeBlock(0);
- if (kind == CodeForCall) {
+ if (callLinkInfo->specializationKind() == CodeForCall) {
CallData callData;
CallType callType = getCallData(callee, callData);
- ASSERT(callType != CallTypeJS);
+ ASSERT(callType != CallType::JS);
- if (callType == CallTypeHost) {
+ if (callType == CallType::Host) {
NativeCallFrameTracer tracer(vm, execCallee);
execCallee->setCallee(asObject(callee));
vm->hostCallReturnValue = JSValue::decode(callData.native.function(execCallee));
- if (vm->exception())
- return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress();
+ if (UNLIKELY(scope.exception())) {
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
+ }
- return reinterpret_cast<void*>(getHostCallReturnValue);
+ return encodeResult(
+ bitwise_cast<void*>(getHostCallReturnValue),
+ reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
- ASSERT(callType == CallTypeNone);
- exec->vm().throwException(exec, createNotAFunctionError(exec, callee));
- return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress();
+ ASSERT(callType == CallType::None);
+ throwException(exec, scope, createNotAFunctionError(exec, callee));
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
}
- ASSERT(kind == CodeForConstruct);
+ ASSERT(callLinkInfo->specializationKind() == CodeForConstruct);
ConstructData constructData;
ConstructType constructType = getConstructData(callee, constructData);
- ASSERT(constructType != ConstructTypeJS);
+ ASSERT(constructType != ConstructType::JS);
- if (constructType == ConstructTypeHost) {
+ if (constructType == ConstructType::Host) {
NativeCallFrameTracer tracer(vm, execCallee);
execCallee->setCallee(asObject(callee));
vm->hostCallReturnValue = JSValue::decode(constructData.native.function(execCallee));
- if (vm->exception())
- return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress();
+ if (UNLIKELY(scope.exception())) {
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
+ }
- return reinterpret_cast<void*>(getHostCallReturnValue);
+ return encodeResult(bitwise_cast<void*>(getHostCallReturnValue), reinterpret_cast<void*>(KeepTheFrame));
}
- ASSERT(constructType == ConstructTypeNone);
- exec->vm().throwException(exec, createNotAConstructorError(exec, callee));
- return vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress();
+ ASSERT(constructType == ConstructType::None);
+ throwException(exec, scope, createNotAConstructorError(exec, callee));
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
}
-inline char* linkFor(ExecState* execCallee, CodeSpecializationKind kind)
+SlowPathReturnType JIT_OPERATION operationLinkCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
{
ExecState* exec = execCallee->callerFrame();
VM* vm = &exec->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(*vm);
+
+ CodeSpecializationKind kind = callLinkInfo->specializationKind();
NativeCallFrameTracer tracer(vm, exec);
+ RELEASE_ASSERT(!callLinkInfo->isDirect());
+
JSValue calleeAsValue = execCallee->calleeAsValue();
JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue);
- if (!calleeAsFunctionCell)
- return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind));
+ 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
+ throwScope.release();
+ return handleHostCall(execCallee, calleeAsValue, callLinkInfo);
+ }
JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
- execCallee->setScope(callee->scopeUnchecked());
+ JSScope* scope = callee->scopeUnchecked();
ExecutableBase* executable = callee->executable();
MacroAssemblerCodePtr codePtr;
CodeBlock* codeBlock = 0;
- CallLinkInfo& callLinkInfo = exec->codeBlock()->getCallLinkInfo(execCallee->returnPC());
- if (executable->isHostFunction())
- codePtr = executable->generatedJITCodeFor(kind)->addressForCall();
- else {
+ if (executable->isHostFunction()) {
+ codePtr = executable->entrypointFor(kind, MustCheckArity);
+ } else {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
- JSObject* error = functionExecutable->prepareForExecution(execCallee, callee->scope(), kind);
+
+ if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
+ throwException(exec, throwScope, createNotAConstructorError(exec, callee));
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
+ }
+
+ CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
+ JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, callee, scope, kind, *codeBlockSlot);
+ ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(error));
if (error) {
- vm->throwException(exec, createStackOverflowError(exec));
- return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress());
+ throwException(exec, throwScope, error);
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
}
- codeBlock = functionExecutable->codeBlockFor(kind);
- if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo.callType == CallLinkInfo::CallVarargs)
- codePtr = functionExecutable->generatedJITCodeWithArityCheckFor(kind);
+ codeBlock = *codeBlockSlot;
+ ArityCheckMode arity;
+ if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()) || callLinkInfo->isVarargs())
+ arity = MustCheckArity;
else
- codePtr = functionExecutable->generatedJITCodeFor(kind)->addressForCall();
+ arity = ArityCheckNotRequired;
+ codePtr = functionExecutable->entrypointFor(kind, arity);
}
- if (!callLinkInfo.seenOnce())
- callLinkInfo.setSeen();
+ if (!callLinkInfo->seenOnce())
+ callLinkInfo->setSeen();
else
- linkFor(execCallee, callLinkInfo, codeBlock, callee, codePtr, kind);
- return reinterpret_cast<char*>(codePtr.executableAddress());
+ linkFor(execCallee, *callLinkInfo, codeBlock, callee, codePtr);
+
+ return encodeResult(codePtr.executableAddress(), reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
-char* JIT_OPERATION operationLinkCall(ExecState* execCallee)
+void JIT_OPERATION operationLinkDirectCall(ExecState* exec, CallLinkInfo* callLinkInfo, JSFunction* callee)
{
- return linkFor(execCallee, CodeForCall);
-}
+ VM* vm = &exec->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(*vm);
-char* JIT_OPERATION operationLinkConstruct(ExecState* execCallee)
-{
- return linkFor(execCallee, CodeForConstruct);
+ CodeSpecializationKind kind = callLinkInfo->specializationKind();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ RELEASE_ASSERT(callLinkInfo->isDirect());
+
+ // This would happen if the executable died during GC but the CodeBlock did not die. That should
+ // not happen because the CodeBlock should have a weak reference to any executable it uses for
+ // this purpose.
+ RELEASE_ASSERT(callLinkInfo->executable());
+
+ // Having a CodeBlock indicates that this is linked. We shouldn't be taking this path if it's
+ // linked.
+ RELEASE_ASSERT(!callLinkInfo->codeBlock());
+
+ // We just don't support this yet.
+ RELEASE_ASSERT(!callLinkInfo->isVarargs());
+
+ ExecutableBase* executable = callLinkInfo->executable();
+ RELEASE_ASSERT(callee->executable() == callLinkInfo->executable());
+
+ JSScope* scope = callee->scopeUnchecked();
+
+ MacroAssemblerCodePtr codePtr;
+ CodeBlock* codeBlock = nullptr;
+ if (executable->isHostFunction())
+ codePtr = executable->entrypointFor(kind, MustCheckArity);
+ else {
+ FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
+
+ RELEASE_ASSERT(isCall(kind) || functionExecutable->constructAbility() != ConstructAbility::CannotConstruct);
+
+ JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, callee, scope, kind, codeBlock);
+ ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(error));
+ if (error) {
+ throwException(exec, throwScope, error);
+ return;
+ }
+ ArityCheckMode arity;
+ unsigned argumentStackSlots = callLinkInfo->maxNumArguments();
+ if (argumentStackSlots < static_cast<size_t>(codeBlock->numParameters()))
+ arity = MustCheckArity;
+ else
+ arity = ArityCheckNotRequired;
+ codePtr = functionExecutable->entrypointFor(kind, arity);
+ }
+
+ linkDirectFor(exec, *callLinkInfo, codeBlock, codePtr);
}
-inline char* virtualForWithFunction(ExecState* execCallee, CodeSpecializationKind kind, JSCell*& calleeAsFunctionCell)
+inline SlowPathReturnType virtualForWithFunction(
+ ExecState* execCallee, CallLinkInfo* callLinkInfo, JSCell*& calleeAsFunctionCell)
{
ExecState* exec = execCallee->callerFrame();
VM* vm = &exec->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(*vm);
+
+ CodeSpecializationKind kind = callLinkInfo->specializationKind();
NativeCallFrameTracer tracer(vm, exec);
JSValue calleeAsValue = execCallee->calleeAsValue();
calleeAsFunctionCell = getJSFunction(calleeAsValue);
if (UNLIKELY(!calleeAsFunctionCell))
- return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind));
+ return handleHostCall(execCallee, calleeAsValue, callLinkInfo);
JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell);
- execCallee->setScope(function->scopeUnchecked());
+ JSScope* scope = function->scopeUnchecked();
ExecutableBase* executable = function->executable();
if (UNLIKELY(!executable->hasJITCodeFor(kind))) {
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());
-}
-inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind)
-{
- JSCell* calleeAsFunctionCellIgnored;
- return virtualForWithFunction(execCallee, kind, calleeAsFunctionCellIgnored);
-}
+ if (!isCall(kind) && functionExecutable->constructAbility() == ConstructAbility::CannotConstruct) {
+ throwException(exec, throwScope, createNotAConstructorError(exec, function));
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
+ }
-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;
+ CodeBlock** codeBlockSlot = execCallee->addressOfCodeBlock();
+ JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(*vm, function, scope, kind, *codeBlockSlot);
+ if (error) {
+ throwException(exec, throwScope, error);
+ return encodeResult(
+ vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress(),
+ reinterpret_cast<void*>(KeepTheFrame));
+ }
}
-
- linkClosureCall(
- execCallee, callLinkInfo, codeBlock,
- callee->structure(), callee->executable(), codePtr);
-
- return true;
+ return encodeResult(executable->entrypointFor(
+ kind, MustCheckArity).executableAddress(),
+ reinterpret_cast<void*>(callLinkInfo->callMode() == CallMode::Tail ? ReuseTheFrame : KeepTheFrame));
}
-char* JIT_OPERATION operationLinkClosureCall(ExecState* execCallee)
+SlowPathReturnType JIT_OPERATION operationLinkPolymorphicCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
{
+ ASSERT(callLinkInfo->specializationKind() == CodeForCall);
JSCell* calleeAsFunctionCell;
- char* result = virtualForWithFunction(execCallee, CodeForCall, calleeAsFunctionCell);
- CallLinkInfo& callLinkInfo = execCallee->callerFrame()->codeBlock()->getCallLinkInfo(execCallee->returnPC());
+ SlowPathReturnType result = virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCell);
- if (!attemptToOptimizeClosureCall(execCallee, calleeAsFunctionCell, callLinkInfo))
- linkSlowFor(execCallee, callLinkInfo, CodeForCall);
+ linkPolymorphicCall(execCallee, *callLinkInfo, CallVariant(calleeAsFunctionCell));
return result;
}
-char* JIT_OPERATION operationVirtualCall(ExecState* execCallee)
-{
- return virtualFor(execCallee, CodeForCall);
-}
-
-char* JIT_OPERATION operationVirtualConstruct(ExecState* execCallee)
+SlowPathReturnType JIT_OPERATION operationVirtualCall(ExecState* execCallee, CallLinkInfo* callLinkInfo)
{
- return virtualFor(execCallee, CodeForConstruct);
+ JSCell* calleeAsFunctionCellIgnored;
+ return virtualForWithFunction(execCallee, callLinkInfo, calleeAsFunctionCellIgnored);
}
-
size_t JIT_OPERATION operationCompareLess(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
{
VM* vm = &exec->vm();
@@ -853,14 +1078,6 @@ size_t JIT_OPERATION operationCompareGreaterEq(ExecState* exec, EncodedJSValue e
return jsLessEq<false>(exec, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1));
}
-size_t JIT_OPERATION operationConvertJSValueToBoolean(ExecState* exec, EncodedJSValue encodedOp)
-{
- VM* vm = &exec->vm();
- NativeCallFrameTracer tracer(vm, exec);
-
- return JSValue::decode(encodedOp).toBoolean(exec);
-}
-
size_t JIT_OPERATION operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
{
VM* vm = &exec->vm();
@@ -878,7 +1095,7 @@ size_t JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSC
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- bool result = asString(left)->value(exec) == asString(right)->value(exec);
+ bool result = asString(left)->equal(exec, asString(right));
#if USE(JSVALUE64)
return JSValue::encode(jsBoolean(result));
#else
@@ -886,13 +1103,6 @@ size_t JIT_OPERATION operationCompareStringEq(ExecState* exec, JSCell* left, JSC
#endif
}
-size_t JIT_OPERATION operationHasProperty(ExecState* exec, JSObject* base, JSString* property)
-{
- int result = base->hasProperty(exec, Identifier(exec, property->value(exec)));
- return result;
-}
-
-
EncodedJSValue JIT_OPERATION operationNewArrayWithProfile(ExecState* exec, ArrayAllocationProfile* profile, const JSValue* values, int size)
{
VM* vm = &exec->vm();
@@ -915,66 +1125,117 @@ EncodedJSValue JIT_OPERATION operationNewArrayWithSizeAndProfile(ExecState* exec
return JSValue::encode(constructArrayWithSizeQuirk(exec, profile, exec->lexicalGlobalObject(), sizeValue));
}
-EncodedJSValue JIT_OPERATION operationNewFunction(ExecState* exec, JSCell* functionExecutable)
+}
+
+template<typename FunctionType>
+static EncodedJSValue operationNewFunctionCommon(ExecState* exec, JSScope* scope, JSCell* functionExecutable, bool isInvalidated)
{
- ASSERT(functionExecutable->inherits(FunctionExecutable::info()));
VM& vm = exec->vm();
+ ASSERT(functionExecutable->inherits(vm, FunctionExecutable::info()));
NativeCallFrameTracer tracer(&vm, exec);
- return JSValue::encode(JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), exec->scope()));
+ 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);
+}
+
+EncodedJSValue JIT_OPERATION operationNewAsyncFunction(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
+{
+ return operationNewFunctionCommon<JSAsyncFunction>(exec, scope, functionExecutable, false);
+}
+
+EncodedJSValue JIT_OPERATION operationNewAsyncFunctionWithInvalidatedReallocationWatchpoint(ExecState* exec, JSScope* scope, JSCell* functionExecutable)
+{
+ return operationNewFunctionCommon<JSAsyncFunction>(exec, scope, functionExecutable, true);
+}
+
+void JIT_OPERATION operationSetFunctionName(ExecState* exec, JSCell* funcCell, EncodedJSValue encodedName)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSFunction* func = jsCast<JSFunction*>(funcCell);
+ JSValue name = JSValue::decode(encodedName);
+ func->setFunctionName(exec, name);
}
JSCell* JIT_OPERATION operationNewObject(ExecState* exec, Structure* structure)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
-
+
return constructEmptyObject(exec, structure);
}
EncodedJSValue JIT_OPERATION operationNewRegexp(ExecState* exec, void* regexpPtr)
{
+ SuperSamplerScope superSamplerScope(false);
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
RegExp* regexp = static_cast<RegExp*>(regexpPtr);
if (!regexp->isValid()) {
- vm.throwException(exec, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor."));
+ throwException(exec, scope, createSyntaxError(exec, regexp->errorMessage()));
return JSValue::encode(jsUndefined());
}
return JSValue::encode(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regexp));
}
-void JIT_OPERATION operationHandleWatchdogTimer(ExecState* exec)
+// 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)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (UNLIKELY(vm.shouldTriggerTermination(exec)))
+ throwException(exec, scope, createTerminatedExecutionException(&vm));
- if (UNLIKELY(vm.watchdog.didFire(exec)))
- vm.throwException(exec, createTerminatedExecutionException(&vm));
+ return nullptr;
}
-void JIT_OPERATION operationThrowStaticError(ExecState* exec, EncodedJSValue encodedValue, int32_t referenceErrorFlag)
+void JIT_OPERATION operationDebug(ExecState* exec, int32_t debugHookType)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- String message = errorDescriptionForValue(exec, JSValue::decode(encodedValue))->value(exec);
- if (referenceErrorFlag)
- vm.throwException(exec, createReferenceError(exec, message));
- else
- vm.throwException(exec, createTypeError(exec, message));
+ vm.interpreter->debug(exec, static_cast<DebugHookType>(debugHookType));
}
-void JIT_OPERATION operationDebug(ExecState* exec, int32_t debugHookID)
+#if ENABLE(DFG_JIT)
+static void updateAllPredictionsAndOptimizeAfterWarmUp(CodeBlock* codeBlock)
{
- VM& vm = exec->vm();
- NativeCallFrameTracer tracer(&vm, exec);
-
- vm.interpreter->debug(exec, static_cast<DebugHookID>(debugHookID));
+ codeBlock->updateAllPredictions();
+ codeBlock->optimizeAfterWarmUp();
}
-#if ENABLE(DFG_JIT)
-char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
+SlowPathReturnType JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -996,7 +1257,11 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
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.
@@ -1017,29 +1282,38 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
}
if (!codeBlock->checkIfOptimizationThresholdReached()) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("counter = ", codeBlock->jitExecuteCounter()));
codeBlock->updateAllPredictions();
if (Options::verboseOSR())
dataLog("Choosing not to optimize ", *codeBlock, " yet, because the threshold hasn't been reached.\n");
- return 0;
+ return encodeResult(0, 0);
}
+ Debugger* debugger = codeBlock->globalObject()->debugger();
+ if (debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests())) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("debugger is stepping or has requests"));
+ updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock);
+ return encodeResult(0, 0);
+ }
+
if (codeBlock->m_shouldAlwaysBeInlined) {
- codeBlock->updateAllPredictions();
- codeBlock->optimizeAfterWarmUp();
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should always be inlined"));
+ updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock);
if (Options::verboseOSR())
dataLog("Choosing not to optimize ", *codeBlock, " yet, because m_shouldAlwaysBeInlined == true.\n");
- return 0;
+ return encodeResult(0, 0);
}
// We cannot be in the process of asynchronous compilation and also have an optimized
// replacement.
+ DFG::Worklist* worklist = DFG::existingGlobalDFGWorklistOrNull();
ASSERT(
- !vm.worklist
- || !(vm.worklist->compilationState(DFG::CompilationKey(codeBlock, DFG::DFGMode)) != DFG::Worklist::NotKnown
+ !worklist
+ || !(worklist->compilationState(DFG::CompilationKey(codeBlock, DFG::DFGMode)) != DFG::Worklist::NotKnown
&& codeBlock->hasOptimizedReplacement()));
DFG::Worklist::State worklistState;
- if (vm.worklist) {
+ if (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
@@ -1058,17 +1332,18 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
// 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 = vm.worklist->completeAllReadyPlansForVM(
+ worklistState = worklist->completeAllReadyPlansForVM(
vm, DFG::CompilationKey(codeBlock, DFG::DFGMode));
} else
worklistState = DFG::Worklist::NotKnown;
if (worklistState == DFG::Worklist::Compiling) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiling"));
// We cannot be in the process of asynchronous compilation and also have an optimized
// replacement.
RELEASE_ASSERT(!codeBlock->hasOptimizedReplacement());
codeBlock->setOptimizationThresholdBasedOnCompilationResult(CompilationDeferred);
- return 0;
+ return encodeResult(0, 0);
}
if (worklistState == DFG::Worklist::Compiled) {
@@ -1078,10 +1353,11 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
// CodeBlock::setOptimizationThresholdBasedOnCompilationResult() and we have
// nothing left to do.
if (!codeBlock->hasOptimizedReplacement()) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiled and failed"));
codeBlock->updateAllPredictions();
if (Options::verboseOSR())
dataLog("Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement.\n");
- return 0;
+ return encodeResult(0, 0);
}
} else if (codeBlock->hasOptimizedReplacement()) {
if (Options::verboseOSR())
@@ -1100,22 +1376,24 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
// shouldReoptimizeFromLoopNow() to always return true. But we make it do some
// additional checking anyway, to reduce the amount of recompilation thrashing.
if (codeBlock->replacement()->shouldReoptimizeFromLoopNow()) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize from loop now"));
if (Options::verboseOSR()) {
dataLog(
"Triggering reoptimization of ", *codeBlock,
"(", *codeBlock->replacement(), ") (in loop).\n");
}
- codeBlock->replacement()->jettison(CountReoptimization);
- return 0;
+ codeBlock->replacement()->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTrigger, CountReoptimization);
+ return encodeResult(0, 0);
}
} else {
if (!codeBlock->shouldOptimizeNow()) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("insufficient profiling"));
if (Options::verboseOSR()) {
dataLog(
"Delaying optimization for ", *codeBlock,
" because of insufficient profiling.\n");
}
- return 0;
+ return encodeResult(0, 0);
}
if (Options::verboseOSR())
@@ -1123,46 +1401,42 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
unsigned numVarsWithValues;
if (bytecodeIndex)
- numVarsWithValues = codeBlock->m_numVars;
+ numVarsWithValues = codeBlock->m_numCalleeLocals;
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 (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();
+ if (operandIsLocal(operand) && VirtualRegister(operand).toLocal() < localsUsedForCalleeSaves)
+ continue;
+ mustHandleValues[i] = exec->uncheckedR(operand).jsValue();
}
+ CodeBlock* replacementCodeBlock = codeBlock->newReplacement();
CompilationResult result = DFG::compile(
- vm, codeBlock->newReplacement().get(), DFG::DFGMode, bytecodeIndex,
- mustHandleValues, JITToDFGDeferredCompilationCallback::create(),
- vm.ensureWorklist());
+ vm, replacementCodeBlock, nullptr, DFG::DFGMode, bytecodeIndex,
+ mustHandleValues, JITToDFGDeferredCompilationCallback::create());
- if (result != CompilationSuccessful)
- return 0;
+ if (result != CompilationSuccessful) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compilation failed"));
+ return encodeResult(0, 0);
+ }
}
CodeBlock* optimizedCodeBlock = codeBlock->replacement();
ASSERT(JITCode::isOptimizingJIT(optimizedCodeBlock->jitType()));
- if (void* address = DFG::prepareOSREntry(exec, optimizedCodeBlock, bytecodeIndex)) {
+ if (void* dataBuffer = DFG::prepareOSREntry(exec, optimizedCodeBlock, bytecodeIndex)) {
+ CODEBLOCK_LOG_EVENT(optimizedCodeBlock, "osrEntry", ("at bc#", bytecodeIndex));
if (Options::verboseOSR()) {
dataLog(
- "Performing OSR ", *codeBlock, " -> ", *optimizedCodeBlock, ", address ",
- RawPointer(OUR_RETURN_ADDRESS), " -> ", RawPointer(address), ".\n");
+ "Performing OSR ", *codeBlock, " -> ", *optimizedCodeBlock, ".\n");
}
codeBlock->optimizeSoon();
- return static_cast<char*>(address);
+ codeBlock->unlinkedCodeBlock()->setDidOptimize(TrueTriState);
+ return encodeResult(vm.getCTIStub(DFG::osrEntryThunkGenerator).code().executableAddress(), dataBuffer);
}
if (Options::verboseOSR()) {
@@ -1185,20 +1459,22 @@ char* JIT_OPERATION operationOptimize(ExecState* exec, int32_t bytecodeIndex)
// right now. So, we only trigger reoptimization only upon the more conservative (non-loop)
// reoptimization trigger.
if (optimizedCodeBlock->shouldReoptimizeNow()) {
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize now"));
if (Options::verboseOSR()) {
dataLog(
"Triggering reoptimization of ", *codeBlock, " -> ",
*codeBlock->replacement(), " (after OSR fail).\n");
}
- optimizedCodeBlock->jettison(CountReoptimization);
- return 0;
+ optimizedCodeBlock->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail, CountReoptimization);
+ return encodeResult(0, 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 0;
+ CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("OSR failed"));
+ return encodeResult(0, 0);
}
#endif
@@ -1212,31 +1488,25 @@ void JIT_OPERATION operationPutByIndex(ExecState* exec, EncodedJSValue encodedAr
asArray(arrayValue)->putDirectIndex(exec, index, JSValue::decode(encodedValue));
}
-#if USE(JSVALUE64)
-void JIT_OPERATION operationPutGetterSetter(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)
+enum class AccessorType {
+ Getter,
+ Setter
+};
+
+static void putAccessorByVal(ExecState* exec, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType)
{
VM& vm = exec->vm();
- NativeCallFrameTracer tracer(&vm, exec);
-
- ASSERT(JSValue::decode(encodedObjectValue).isObject());
- JSObject* baseObj = asObject(JSValue::decode(encodedObjectValue));
-
- GetterSetter* accessor = GetterSetter::create(vm);
-
- JSValue getter = JSValue::decode(encodedGetterValue);
- JSValue setter = JSValue::decode(encodedSetterValue);
- ASSERT(getter.isObject() || getter.isUndefined());
- ASSERT(setter.isObject() || setter.isUndefined());
- ASSERT(getter.isObject() || setter.isObject());
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ auto propertyKey = subscript.toPropertyKey(exec);
+ RETURN_IF_EXCEPTION(scope, void());
- if (!getter.isUndefined())
- accessor->setGetter(vm, asObject(getter));
- if (!setter.isUndefined())
- accessor->setSetter(vm, asObject(setter));
- baseObj->putDirectAccessor(exec, *identifier, accessor, Accessor);
+ if (accessorType == AccessorType::Getter)
+ base->putGetter(exec, propertyKey, accessor, attribute);
+ else
+ base->putSetter(exec, propertyKey, accessor, attribute);
}
-#else
-void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Identifier* identifier, JSCell* getter, JSCell* setter)
+
+void JIT_OPERATION operationPutGetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* getter)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -1244,208 +1514,372 @@ void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Ide
ASSERT(object && object->isObject());
JSObject* baseObj = object->getObject();
- GetterSetter* accessor = GetterSetter::create(vm);
-
- ASSERT(!getter || getter->isObject());
- ASSERT(!setter || setter->isObject());
- ASSERT(getter || setter);
-
- if (getter)
- accessor->setGetter(vm, getter->getObject());
- if (setter)
- accessor->setSetter(vm, setter->getObject());
- baseObj->putDirectAccessor(exec, *identifier, accessor, Accessor);
+ ASSERT(getter->isObject());
+ baseObj->putGetter(exec, uid, getter, options);
}
-#endif
-void JIT_OPERATION operationPushNameScope(ExecState* exec, Identifier* identifier, EncodedJSValue encodedValue, int32_t attibutes)
+void JIT_OPERATION operationPutSetterById(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* setter)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- JSNameScope* scope = JSNameScope::create(exec, *identifier, JSValue::decode(encodedValue), attibutes);
+ ASSERT(object && object->isObject());
+ JSObject* baseObj = object->getObject();
- exec->setScope(scope);
+ ASSERT(setter->isObject());
+ baseObj->putSetter(exec, uid, setter, options);
}
-void JIT_OPERATION operationPushWithScope(ExecState* exec, EncodedJSValue encodedValue)
+void JIT_OPERATION operationPutGetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter)
{
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));
+ putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter);
}
-void JIT_OPERATION operationPopScope(ExecState* exec)
+void JIT_OPERATION operationPutSetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- exec->setScope(exec->scope()->next());
+ putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter);
}
-void JIT_OPERATION operationProfileDidCall(ExecState* exec, EncodedJSValue encodedValue)
+#if USE(JSVALUE64)
+void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->didExecute(exec, JSValue::decode(encodedValue));
+ ASSERT(object && object->isObject());
+ JSObject* baseObj = asObject(object);
+
+ GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
+
+ JSValue getter = JSValue::decode(encodedGetterValue);
+ JSValue setter = JSValue::decode(encodedSetterValue);
+ ASSERT(getter.isObject() || getter.isUndefined());
+ ASSERT(setter.isObject() || setter.isUndefined());
+ ASSERT(getter.isObject() || setter.isObject());
+
+ if (!getter.isUndefined())
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), asObject(getter));
+ if (!setter.isUndefined())
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), asObject(setter));
+ baseObj->putDirectAccessor(exec, uid, accessor, attribute);
}
-void JIT_OPERATION operationProfileWillCall(ExecState* exec, EncodedJSValue encodedValue)
+#else
+void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, UniquedStringImpl* uid, int32_t attribute, JSCell* getter, JSCell* setter)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->willExecute(exec, JSValue::decode(encodedValue));
-}
+ ASSERT(object && object->isObject());
+ JSObject* baseObj = asObject(object);
-EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBaseVal)
-{
- VM* vm = &exec->vm();
- NativeCallFrameTracer tracer(vm, exec);
+ GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
- JSValue value = JSValue::decode(encodedValue);
- 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(!getter || getter->isObject());
+ ASSERT(!setter || setter->isObject());
+ ASSERT(getter || setter);
- vm->throwException(exec, createInvalidParameterError(exec, "instanceof", baseVal));
- return JSValue::encode(JSValue());
+ if (getter)
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), getter->getObject());
+ if (setter)
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), setter->getObject());
+ baseObj->putDirectAccessor(exec, uid, accessor, attribute);
}
+#endif
-JSCell* JIT_OPERATION operationCreateActivation(ExecState* exec, int32_t offset)
+void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- JSActivation* activation = JSActivation::create(vm, exec, exec->registers() + offset, exec->codeBlock());
- exec->setScope(activation);
- return activation;
+
+ JSScope* scope = exec->uncheckedR(scopeReg).Register::scope();
+ exec->uncheckedR(scopeReg) = scope->next();
}
-JSCell* JIT_OPERATION operationCreateArguments(ExecState* exec)
+int32_t JIT_OPERATION operationInstanceOfCustom(ExecState* exec, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance)
{
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;
+
+ JSValue value = JSValue::decode(encodedValue);
+ JSValue hasInstanceValue = JSValue::decode(encodedHasInstance);
+
+ ASSERT(hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction() || !constructor->structure()->typeInfo().implementsDefaultHasInstance());
+
+ if (constructor->hasInstance(exec, value, hasInstanceValue))
+ return 1;
+ return 0;
}
-EncodedJSValue JIT_OPERATION operationGetArgumentsLength(ExecState* exec, int32_t argumentsRegister)
-{
- 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 bool canAccessArgumentIndexQuickly(JSObject& object, uint32_t index)
+{
+ switch (object.structure()->typeInfo().type()) {
+ case DirectArgumentsType: {
+ DirectArguments* directArguments = jsCast<DirectArguments*>(&object);
+ if (directArguments->isMappedArgumentInDFG(index))
+ return true;
+ break;
+ }
+ case ScopedArgumentsType: {
+ ScopedArguments* scopedArguments = jsCast<ScopedArguments*>(&object);
+ if (scopedArguments->isMappedArgumentInDFG(index))
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
}
-static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ReturnAddressPtr returnAddress)
+static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ByValInfo* byValInfo, ReturnAddressPtr returnAddress)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
if (LIKELY(baseValue.isCell() && subscript.isString())) {
- if (JSValue result = baseValue.asCell()->fastGetOwnProperty(exec, asString(subscript)->value(exec)))
- return result;
+ 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 (subscript.isUInt32()) {
+ ASSERT(exec->bytecodeOffset());
+ byValInfo->tookSlowPath = true;
+
uint32_t i = subscript.asUInt32();
- if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) {
- ctiPatchCallByReturnAddress(exec->codeBlock(), returnAddress, FunctionPtr(operationGetByValString));
- return asString(baseValue)->getIndex(exec, i);
+ 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();
+ }
}
+
return baseValue.get(exec, i);
}
- if (isName(subscript))
- return baseValue.get(exec, jsCast<NameInstance*>(subscript.asCell())->privateName());
+ baseValue.requireObjectCoercible(exec);
+ RETURN_IF_EXCEPTION(scope, JSValue());
+ auto property = subscript.toPropertyKey(exec);
+ RETURN_IF_EXCEPTION(scope, JSValue());
+
+ ASSERT(exec->bytecodeOffset());
+ if (byValInfo->stubInfo && (!isStringOrSymbol(subscript) || byValInfo->cachedId != property))
+ byValInfo->tookSlowPath = true;
- 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();
+ ConcurrentJSLocker 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.isSymbol() || !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 {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJSLocker locker(codeBlock->m_lock);
+ byValInfo->seen = true;
+ byValInfo->cachedId = propertyName;
+ if (subscript.isSymbol())
+ byValInfo->cachedSymbol.set(vm, codeBlock, asSymbol(subscript));
+ 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)
+
+EncodedJSValue JIT_OPERATION operationGetByValGeneric(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);
- JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS));
+ JSValue result = getByVal(exec, baseValue, subscript, byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS));
return JSValue::encode(result);
}
-EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+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)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
JSValue baseValue = JSValue::decode(encodedBase);
JSValue subscript = JSValue::decode(encodedSubscript);
- if (baseValue.isObject() && subscript.isInt32()) {
- // See if it's worth optimizing this at all.
- JSObject* object = asObject(baseValue);
- bool didOptimize = false;
+ ASSERT(baseValue.isObject());
+ ASSERT(subscript.isUInt32());
- 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;
- }
+ 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;
}
-
- 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));
- }
+ }
+
+ 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));
}
}
+
+ 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->hasPropertyGeneric(exec, index, PropertySlot::InternalMethodType::GetOwnProperty)));
+}
- JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS));
- return JSValue::encode(result);
+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->hasPropertyGeneric(exec, subscript.asUInt32(), PropertySlot::InternalMethodType::GetOwnProperty)));
}
-EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
JSValue baseValue = JSValue::decode(encodedBase);
JSValue subscript = JSValue::decode(encodedSubscript);
@@ -1456,61 +1890,71 @@ EncodedJSValue JIT_OPERATION operationGetByValString(ExecState* exec, EncodedJSV
result = asString(baseValue)->getIndex(exec, i);
else {
result = baseValue.get(exec, i);
- if (!isJSString(baseValue))
- ctiPatchCallByReturnAddress(exec->codeBlock(), ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationGetByValDefault));
+ if (!isJSString(baseValue)) {
+ ASSERT(exec->bytecodeOffset());
+ ctiPatchCallByReturnAddress(ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(byValInfo->stubRoutine ? operationGetByValGeneric : operationGetByValOptimize));
+ }
}
- } else if (isName(subscript))
- result = baseValue.get(exec, jsCast<NameInstance*>(subscript.asCell())->privateName());
- else {
- Identifier property(exec, subscript.toString(exec)->value(exec));
+ } else {
+ baseValue.requireObjectCoercible(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ auto property = subscript.toPropertyKey(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
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)
+EncodedJSValue JIT_OPERATION operationDeleteByIdJSResult(ExecState* exec, EncodedJSValue base, UniquedStringImpl* uid)
{
- ASSERT(exec->codeBlock()->usesArguments());
- if (activationCell) {
- jsCast<Arguments*>(argumentsCell)->didTearOffActivation(exec, jsCast<JSActivation*>(activationCell));
- return;
- }
- jsCast<Arguments*>(argumentsCell)->tearOff(exec);
+ return JSValue::encode(jsBoolean(operationDeleteById(exec, base, uid)));
}
-EncodedJSValue JIT_OPERATION operationDeleteById(ExecState* exec, EncodedJSValue encodedBase, const Identifier* identifier)
+size_t JIT_OPERATION operationDeleteById(ExecState* exec, EncodedJSValue encodedBase, UniquedStringImpl* uid)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* baseObj = JSValue::decode(encodedBase).toObject(exec);
- bool couldDelete = baseObj->methodTable()->deleteProperty(baseObj, exec, *identifier);
- JSValue result = jsBoolean(couldDelete);
+ if (!baseObj)
+ return false;
+ bool couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, exec, Identifier::fromUid(&vm, uid));
if (!couldDelete && exec->codeBlock()->isStrictMode())
- vm.throwException(exec, createTypeError(exec, "Unable to delete property."));
- return JSValue::encode(result);
+ throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+ return couldDelete;
+}
+
+EncodedJSValue JIT_OPERATION operationDeleteByValJSResult(ExecState* exec, EncodedJSValue base, EncodedJSValue key)
+{
+ return JSValue::encode(jsBoolean(operationDeleteByVal(exec, base, key)));
}
-JSCell* JIT_OPERATION operationGetPNames(ExecState* exec, JSObject* obj)
+size_t JIT_OPERATION operationDeleteByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedKey)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
- Structure* structure = obj->structure();
- JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache();
- if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(exec))
- jsPropertyNameIterator = JSPropertyNameIterator::create(exec, obj);
- return jsPropertyNameIterator;
+ JSObject* baseObj = JSValue::decode(encodedBase).toObject(exec);
+ JSValue key = JSValue::decode(encodedKey);
+ if (!baseObj)
+ return false;
+
+ bool couldDelete;
+ uint32_t index;
+ if (key.getUInt32(index))
+ couldDelete = baseObj->methodTable(vm)->deletePropertyByIndex(baseObj, exec, index);
+ else {
+ RETURN_IF_EXCEPTION(scope, false);
+ Identifier property = key.toPropertyKey(exec);
+ RETURN_IF_EXCEPTION(scope, false);
+ couldDelete = baseObj->methodTable(vm)->deleteProperty(baseObj, exec, property);
+ }
+ if (!couldDelete && exec->codeBlock()->isStrictMode())
+ throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+ return couldDelete;
}
EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedProto)
@@ -1520,29 +1964,39 @@ EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState* exec, EncodedJSValue
JSValue value = JSValue::decode(encodedValue);
JSValue proto = JSValue::decode(encodedProto);
- ASSERT(!value.isObject() || !proto.isObject());
-
bool result = JSObject::defaultHasInstance(exec, value, proto);
return JSValue::encode(jsBoolean(result));
}
-CallFrame* JIT_OPERATION operationSizeAndAllocFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t firstFreeRegister)
+int32_t JIT_OPERATION operationSizeFrameForForwardArguments(ExecState* exec, EncodedJSValue, int32_t numUsedStackSlots, int32_t)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ return sizeFrameForForwardArguments(exec, vm, numUsedStackSlots);
+}
+
+int32_t JIT_OPERATION operationSizeFrameForVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- JSStack* stack = &exec->interpreter()->stack();
JSValue arguments = JSValue::decode(encodedArguments);
- CallFrame* newCallFrame = sizeAndAllocFrameForVarargs(exec, stack, arguments, firstFreeRegister);
+ return sizeFrameForVarargs(exec, vm, arguments, numUsedStackSlots, firstVarArgOffset);
+}
+
+CallFrame* JIT_OPERATION operationSetupForwardArgumentsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue, int32_t, int32_t length)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ setupForwardArgumentsFrame(exec, newCallFrame, length);
return newCallFrame;
}
-CallFrame* JIT_OPERATION operationLoadVarargs(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedThis, EncodedJSValue encodedArguments)
+CallFrame* JIT_OPERATION operationSetupVarargsFrame(ExecState* exec, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- JSValue thisValue = JSValue::decode(encodedThis);
JSValue arguments = JSValue::decode(encodedArguments);
- loadVarargs(exec, newCallFrame, thisValue, arguments);
+ setupVarargsFrame(exec, newCallFrame, arguments, firstVarArgOffset, length);
return newCallFrame;
}
@@ -1550,7 +2004,10 @@ EncodedJSValue JIT_OPERATION operationToObject(ExecState* exec, EncodedJSValue v
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
- return JSValue::encode(JSValue::decode(value).toObject(exec));
+ JSObject* obj = JSValue::decode(value).toObject(exec);
+ if (!obj)
+ return JSValue::encode(JSValue());
+ return JSValue::encode(obj);
}
char* JIT_OPERATION operationSwitchCharWithUnknownKeyType(ExecState* exec, EncodedJSValue encodedKey, size_t tableIndex)
@@ -1609,141 +2066,652 @@ 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();
NativeCallFrameTracer tracer(&vm, exec);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
CodeBlock* codeBlock = exec->codeBlock();
Instruction* pc = bytecodePC;
const Identifier& ident = codeBlock->identifier(pc[3].u.operand);
JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(pc[2].u.operand).jsValue());
- ResolveModeAndType modeAndType(pc[4].u.operand);
+ GetPutInfo getPutInfo(pc[4].u.operand);
- PropertySlot slot(scope);
- if (!scope->getPropertySlot(exec, ident, slot)) {
- if (modeAndType.mode() == ThrowIfNotFound)
- vm.throwException(exec, createUndefinedVariableError(exec, ident));
- return JSValue::encode(jsUndefined());
- }
+ // ModuleVar is always converted to ClosureVar for get_from_scope.
+ ASSERT(getPutInfo.resolveType() != ModuleVar);
- // 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();
+ return JSValue::encode(scope->getPropertySlot(exec, ident, [&] (bool found, PropertySlot& slot) -> JSValue {
+ if (!found) {
+ if (getPutInfo.resolveMode() == ThrowIfNotFound)
+ throwException(exec, throwScope, createUndefinedVariableError(exec, ident));
+ return jsUndefined();
}
- }
- return JSValue::encode(slot.getValue(exec, ident));
+ JSValue result = JSValue();
+ if (scope->isGlobalLexicalEnvironment()) {
+ // 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()) {
+ throwException(exec, throwScope, createTDZError(exec));
+ return jsUndefined();
+ }
+ }
+
+ CommonSlowPaths::tryCacheGetFromScopeGlobal(exec, vm, pc, scope, slot, ident);
+
+ if (!result)
+ return slot.getValue(exec, ident);
+ return result;
+ }));
}
void JIT_OPERATION operationPutToScope(ExecState* exec, Instruction* bytecodePC)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
Instruction* pc = bytecodePC;
CodeBlock* codeBlock = exec->codeBlock();
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();
- ResolveModeAndType modeAndType = ResolveModeAndType(pc[4].u.operand);
+ GetPutInfo getPutInfo = GetPutInfo(pc[4].u.operand);
- if (modeAndType.mode() == ThrowIfNotFound && !scope->hasProperty(exec, ident)) {
- exec->vm().throwException(exec, createUndefinedVariableError(exec, ident));
+ // 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(vm, "Executed op_put_scope<LocalClosureVar>");
return;
}
- PutPropertySlot slot(scope, codeBlock->isStrictMode());
+ bool hasProperty = scope->hasProperty(exec, ident);
+ if (hasProperty
+ && scope->isGlobalLexicalEnvironment()
+ && !isInitialization(getPutInfo.initializationMode())) {
+ // 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()) {
+ throwException(exec, throwScope, createTDZError(exec));
+ return;
+ }
+ }
+
+ if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) {
+ throwException(exec, throwScope, createUndefinedVariableError(exec, ident));
+ return;
+ }
+
+ PutPropertySlot slot(scope, codeBlock->isStrictMode(), PutPropertySlot::UnknownContext, isInitialization(getPutInfo.initializationMode()));
scope->methodTable()->put(scope, exec, ident, value, slot);
- if (exec->vm().exception())
- return;
+ RETURN_IF_EXCEPTION(throwScope, void());
- // 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();
- }
- }
+ CommonSlowPaths::tryCachePutToScopeGlobal(exec, codeBlock, pc, scope, getPutInfo, slot, ident);
}
void JIT_OPERATION operationThrow(ExecState* exec, EncodedJSValue encodedExceptionValue)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
JSValue exceptionValue = JSValue::decode(encodedExceptionValue);
- vm->throwException(exec, exceptionValue);
+ throwException(exec, scope, exceptionValue);
+
+ // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForCatch
+ genericUnwind(vm, exec);
+}
+
+char* JIT_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState* exec, JSObject* object)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ ASSERT(!object->structure()->outOfLineCapacity());
+ Butterfly* result = object->allocateMoreOutOfLineStorage(vm, 0, initialOutOfLineCapacity);
+ object->nukeStructureAndSetButterfly(vm, object->structureID(), result);
+ return reinterpret_cast<char*>(result);
+}
- // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForThrow
- genericUnwind(vm, exec, exceptionValue);
+char* JIT_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState* exec, JSObject* object, size_t newSize)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ Butterfly* result = object->allocateMoreOutOfLineStorage(vm, object->structure()->outOfLineCapacity(), newSize);
+ object->nukeStructureAndSetButterfly(vm, object->structureID(), result);
+ return reinterpret_cast<char*>(result);
}
-void JIT_OPERATION operationFlushWriteBarrierBuffer(ExecState* exec, JSCell* cell)
+void JIT_OPERATION operationOSRWriteBarrier(ExecState* exec, JSCell* cell)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- vm->heap.flushWriteBarrierBuffer(cell);
+ vm->heap.writeBarrier(cell);
}
-void JIT_OPERATION operationOSRWriteBarrier(ExecState* exec, JSCell* cell)
+void JIT_OPERATION operationWriteBarrierSlowPath(ExecState* exec, JSCell* cell)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- exec->heap()->writeBarrier(cell);
+ vm->heap.writeBarrierSlowPath(cell);
}
-// NB: We don't include the value as part of the barrier because the write barrier elision
-// phase in the DFG only tracks whether the object being stored to has been barriered. It
-// would be much more complicated to try to model the value being stored as well.
-void JIT_OPERATION operationUnconditionalWriteBarrier(ExecState* exec, JSCell* cell)
+void JIT_OPERATION lookupExceptionHandler(VM* vm, ExecState* exec)
+{
+ NativeCallFrameTracer tracer(vm, exec);
+ genericUnwind(vm, exec);
+ ASSERT(vm->targetMachinePCForThrow);
+}
+
+void JIT_OPERATION lookupExceptionHandlerFromCallerFrame(VM* vm, ExecState* exec)
+{
+ vm->topCallFrame = exec->callerFrame();
+ genericUnwind(vm, exec, UnwindFromCallerFrame);
+ ASSERT(vm->targetMachinePCForThrow);
+}
+
+void JIT_OPERATION operationVMHandleException(ExecState* exec)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- Heap::writeBarrier(cell);
+ genericUnwind(vm, exec);
}
-void JIT_OPERATION operationInitGlobalConst(ExecState* exec, Instruction* pc)
+// 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);
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+ UNUSED_PARAM(scope);
+#if COMPILER(GCC_OR_CLANG)
+ void* returnPC = __builtin_return_address(0);
+ doExceptionFuzzing(exec, scope, "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);
+ if (!base)
+ return JSValue::encode(JSValue());
+ return JSValue::encode(jsBoolean(base->hasPropertyGeneric(exec, asString(propertyName)->toIdentifier(exec), PropertySlot::InternalMethodType::GetOwnProperty)));
+}
+
+EncodedJSValue JIT_OPERATION operationHasIndexedProperty(ExecState* exec, JSCell* baseCell, int32_t subscript, int32_t internalMethodType)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ JSObject* object = baseCell->toObject(exec, exec->lexicalGlobalObject());
+ return JSValue::encode(jsBoolean(object->hasPropertyGeneric(exec, subscript, static_cast<PropertySlot::InternalMethodType>(internalMethodType))));
+}
+
+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());
+}
- JSValue value = exec->r(pc[2].u.operand).jsValue();
- pc[1].u.registerPointer->set(*vm, exec->codeBlock()->globalObject(), value);
+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 lookupExceptionHandler(ExecState* exec)
+ALWAYS_INLINE static EncodedJSValue unprofiledAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
{
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ return JSValue::encode(jsAdd(exec, op1, op2));
+}
- JSValue exceptionValue = exec->exception();
- ASSERT(exceptionValue);
+ALWAYS_INLINE static EncodedJSValue profiledAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
- genericUnwind(vm, exec, exceptionValue);
- ASSERT(vm->targetMachinePCForThrow);
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ arithProfile.observeLHSAndRHS(op1, op2);
+ JSValue result = jsAdd(exec, op1, op2);
+ arithProfile.observeResult(result);
+
+ return JSValue::encode(result);
}
-void JIT_OPERATION operationVMHandleException(ExecState* exec)
+EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ return unprofiledAdd(exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
+{
+ ASSERT(arithProfile);
+ return profiledAdd(exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ ArithProfile* arithProfile = addIC->arithProfile();
+ ASSERT(arithProfile);
+ arithProfile->observeLHSAndRHS(op1, op2);
+ auto nonOptimizeVariant = operationValueAddProfiledNoOptimize;
+ addIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ JSValue result = jsAdd(exec, op1, op2);
+ arithProfile->observeResult(result);
+
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ArithProfile* arithProfile = addIC->arithProfile();
+ ASSERT(arithProfile);
+ return profiledAdd(exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ auto nonOptimizeVariant = operationValueAddNoOptimize;
+ if (ArithProfile* arithProfile = addIC->arithProfile())
+ arithProfile->observeLHSAndRHS(op1, op2);
+ addIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ return JSValue::encode(jsAdd(exec, op1, op2));
+}
+
+EncodedJSValue JIT_OPERATION operationValueAddNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC*)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ JSValue result = jsAdd(exec, op1, op2);
+
+ return JSValue::encode(result);
+}
+
+ALWAYS_INLINE static EncodedJSValue unprofiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ double a = op1.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ double b = op2.toNumber(exec);
+ return JSValue::encode(jsNumber(a * b));
+}
+
+ALWAYS_INLINE static EncodedJSValue profiledMul(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ if (shouldObserveLHSAndRHSTypes)
+ arithProfile.observeLHSAndRHS(op1, op2);
+
+ double a = op1.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ double b = op2.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+ JSValue result = jsNumber(a * b);
+ arithProfile.observeResult(result);
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMul(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ auto nonOptimizeVariant = operationValueMulNoOptimize;
+ if (ArithProfile* arithProfile = mulIC->arithProfile())
+ arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+ mulIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ return unprofiledMul(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ASSERT(arithProfile);
+ return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ArithProfile* arithProfile = mulIC->arithProfile();
+ ASSERT(arithProfile);
+ arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+ auto nonOptimizeVariant = operationValueMulProfiledNoOptimize;
+ mulIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile, false);
+}
+
+EncodedJSValue JIT_OPERATION operationValueMulProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ArithProfile* arithProfile = mulIC->arithProfile();
+ ASSERT(arithProfile);
+ return profiledMul(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+ALWAYS_INLINE static EncodedJSValue unprofiledNegate(ExecState* exec, EncodedJSValue encodedOperand)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ JSValue operand = JSValue::decode(encodedOperand);
+ double number = operand.toNumber(exec);
+ if (UNLIKELY(scope.exception()))
+ return JSValue::encode(JSValue());
+ return JSValue::encode(jsNumber(-number));
+}
+
+ALWAYS_INLINE static EncodedJSValue profiledNegate(ExecState* exec, EncodedJSValue encodedOperand, ArithProfile& arithProfile)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ JSValue operand = JSValue::decode(encodedOperand);
+ arithProfile.observeLHS(operand);
+ double number = operand.toNumber(exec);
+ if (UNLIKELY(scope.exception()))
+ return JSValue::encode(JSValue());
+
+ JSValue result = jsNumber(-number);
+ arithProfile.observeResult(result);
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationArithNegate(ExecState* exec, EncodedJSValue operand)
+{
+ return unprofiledNegate(exec, operand);
+}
+
+EncodedJSValue JIT_OPERATION operationArithNegateProfiled(ExecState* exec, EncodedJSValue operand, ArithProfile* arithProfile)
{
+ ASSERT(arithProfile);
+ return profiledNegate(exec, operand, *arithProfile);
+}
+
+EncodedJSValue JIT_OPERATION operationArithNegateProfiledOptimize(ExecState* exec, EncodedJSValue encodedOperand, JITNegIC* negIC)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ JSValue operand = JSValue::decode(encodedOperand);
+
+ ArithProfile* arithProfile = negIC->arithProfile();
+ ASSERT(arithProfile);
+ arithProfile->observeLHS(operand);
+ negIC->generateOutOfLine(vm, exec->codeBlock(), operationArithNegateProfiled);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ double number = operand.toNumber(exec);
+ if (UNLIKELY(scope.exception()))
+ return JSValue::encode(JSValue());
+ JSValue result = jsNumber(-number);
+ arithProfile->observeResult(result);
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationArithNegateOptimize(ExecState* exec, EncodedJSValue encodedOperand, JITNegIC* negIC)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ NativeCallFrameTracer tracer(&vm, exec);
+
+ JSValue operand = JSValue::decode(encodedOperand);
+
+ if (ArithProfile* arithProfile = negIC->arithProfile())
+ arithProfile->observeLHS(operand);
+ negIC->generateOutOfLine(vm, exec->codeBlock(), operationArithNegate);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ double number = operand.toNumber(exec);
+ if (UNLIKELY(scope.exception()))
+ return JSValue::encode(JSValue());
+ return JSValue::encode(jsNumber(-number));
+}
+
+ALWAYS_INLINE static EncodedJSValue unprofiledSub(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ double a = op1.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ double b = op2.toNumber(exec);
+ return JSValue::encode(jsNumber(a - b));
+}
+
+ALWAYS_INLINE static EncodedJSValue profiledSub(VM& vm, ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSValue op1 = JSValue::decode(encodedOp1);
+ JSValue op2 = JSValue::decode(encodedOp2);
+
+ if (shouldObserveLHSAndRHSTypes)
+ arithProfile.observeLHSAndRHS(op1, op2);
+
+ double a = op1.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ double b = op2.toNumber(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+ JSValue result = jsNumber(a - b);
+ arithProfile.observeResult(result);
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+ return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubProfiled(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, ArithProfile* arithProfile)
+{
+ ASSERT(arithProfile);
+
VM* vm = &exec->vm();
NativeCallFrameTracer tracer(vm, exec);
- ASSERT(!exec->isVMEntrySentinel());
- genericUnwind(vm, exec, vm->exception());
+ return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ auto nonOptimizeVariant = operationValueSubNoOptimize;
+ if (ArithProfile* arithProfile = subIC->arithProfile())
+ arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+ subIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC*)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ return unprofiledSub(*vm, exec, encodedOp1, encodedOp2);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubProfiledOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ArithProfile* arithProfile = subIC->arithProfile();
+ ASSERT(arithProfile);
+ arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+ auto nonOptimizeVariant = operationValueSubProfiledNoOptimize;
+ subIC->generateOutOfLine(*vm, exec->codeBlock(), nonOptimizeVariant);
+
+#if ENABLE(MATH_IC_STATS)
+ exec->codeBlock()->dumpMathICStats();
+#endif
+
+ return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile, false);
+}
+
+EncodedJSValue JIT_OPERATION operationValueSubProfiledNoOptimize(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)
+{
+ VM* vm = &exec->vm();
+ NativeCallFrameTracer tracer(vm, exec);
+
+ ArithProfile* arithProfile = subIC->arithProfile();
+ ASSERT(arithProfile);
+ return profiledSub(*vm, exec, encodedOp1, encodedOp2, *arithProfile);
+}
+
+void JIT_OPERATION operationProcessTypeProfilerLog(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ vm.typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside baseline JIT"));
+}
+
+void JIT_OPERATION operationProcessShadowChickenLog(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ vm.shadowChicken().update(vm, exec);
+}
+
+int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ RELEASE_ASSERT(!!scope.exception());
+
+ if (isTerminatedExecutionException(vm, scope.exception())) {
+ genericUnwind(&vm, exec);
+ return 1;
+ }
+ return 0;
}
} // extern "C"
@@ -1758,28 +2726,32 @@ extern "C" EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValueWi
return JSValue::encode(exec->vm().hostCallReturnValue);
}
-#if COMPILER(GCC) && CPU(X86_64)
+#if COMPILER(GCC_OR_CLANG) && CPU(X86_64)
asm (
".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
HIDE_SYMBOL(getHostCallReturnValue) "\n"
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
- "mov 0(%rbp), %rbp\n" // CallerFrameAndPC::callerFrame
- "mov %rbp, %rdi\n"
+ "lea -8(%rsp), %rdi\n"
"jmp " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n"
);
-#elif COMPILER(GCC) && CPU(X86)
+#elif COMPILER(GCC_OR_CLANG) && CPU(X86)
asm (
".text" "\n" \
".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
HIDE_SYMBOL(getHostCallReturnValue) "\n"
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
- "mov 0(%ebp), %ebp\n" // CallerFrameAndPC::callerFrame
- "mov %ebp, 4(%esp)\n"
- "jmp " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\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"
);
-#elif COMPILER(GCC) && CPU(ARM_THUMB2)
+#elif COMPILER(GCC_OR_CLANG) && CPU(ARM_THUMB2)
asm (
".text" "\n"
".align 2" "\n"
@@ -1788,20 +2760,18 @@ HIDE_SYMBOL(getHostCallReturnValue) "\n"
".thumb" "\n"
".thumb_func " THUMB_FUNC_PARAM(getHostCallReturnValue) "\n"
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
- "ldr r7, [r7, #0]" "\n" // CallerFrameAndPC::callerFrame
- "mov r0, r7" "\n"
+ "sub r0, sp, #8" "\n"
"b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n"
);
-#elif COMPILER(GCC) && CPU(ARM_TRADITIONAL)
+#elif COMPILER(GCC_OR_CLANG) && CPU(ARM_TRADITIONAL)
asm (
".text" "\n"
".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
HIDE_SYMBOL(getHostCallReturnValue) "\n"
INLINE_ARM_FUNCTION(getHostCallReturnValue)
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
- "ldr r11, [r11, #0]" "\n" // CallerFrameAndPC::callerFrame
- "mov r0, r11" "\n"
+ "sub r0, sp, #8" "\n"
"b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n"
);
@@ -1812,44 +2782,38 @@ asm (
".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
HIDE_SYMBOL(getHostCallReturnValue) "\n"
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
- "ldur x29, [x29, #0]" "\n"
- "mov x0, x29" "\n"
+ "sub x0, sp, #16" "\n"
"b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n"
);
-#elif COMPILER(GCC) && CPU(MIPS)
+#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
+
asm (
".text" "\n"
".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
HIDE_SYMBOL(getHostCallReturnValue) "\n"
SYMBOL_STRING(getHostCallReturnValue) ":" "\n"
LOAD_FUNCTION_TO_T9(getHostCallReturnValueWithExecState)
- "lw $fp, 0($fp)" "\n" // CallerFrameAndPC::callerFrame
- "move $a0, $fp" "\n"
+ "addi $a0, $sp, -8" "\n"
"b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n"
);
-#elif COMPILER(GCC) && CPU(SH4)
-asm (
-".text" "\n"
-".globl " SYMBOL_STRING(getHostCallReturnValue) "\n"
-HIDE_SYMBOL(getHostCallReturnValue) "\n"
-SYMBOL_STRING(getHostCallReturnValue) ":" "\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"
- "1: .balign 4" "\n"
- "2: .long " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "-1b\n"
-);
-
#elif COMPILER(MSVC) && CPU(X86)
extern "C" {
__declspec(naked) EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValue()
{
- __asm mov ebp, [ebp + 0]; // CallerFrameAndPC::callerFrame
- __asm mov [esp + 4], ebp;
+ __asm lea eax, [esp - 4]
+ __asm mov [esp + 4], eax;
__asm jmp getHostCallReturnValueWithExecState
}
}