diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGOperations.cpp')
| -rw-r--r-- | Source/JavaScriptCore/dfg/DFGOperations.cpp | 595 |
1 files changed, 226 insertions, 369 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 34a097643..efe19a4f6 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,19 +26,17 @@ #include "config.h" #include "DFGOperations.h" +#include "Arguments.h" #include "ButterflyInlines.h" -#include "ClonedArguments.h" #include "CodeBlock.h" #include "CommonSlowPaths.h" #include "CopiedSpaceInlines.h" #include "DFGDriver.h" -#include "DFGJITCode.h" #include "DFGOSRExit.h" #include "DFGThunks.h" #include "DFGToFTLDeferredCompilationCallback.h" #include "DFGToFTLForOSREntryDeferredCompilationCallback.h" #include "DFGWorklist.h" -#include "DirectArguments.h" #include "FTLForOSREntryJITCode.h" #include "FTLOSREntry.h" #include "HostCallReturnValue.h" @@ -46,16 +44,16 @@ #include "Interpreter.h" #include "JIT.h" #include "JITExceptions.h" -#include "JSCInlines.h" -#include "JSLexicalEnvironment.h" +#include "JITOperationWrappers.h" +#include "JSActivation.h" +#include "VM.h" +#include "JSNameScope.h" +#include "NameInstance.h" #include "ObjectConstructor.h" +#include "Operations.h" #include "Repatch.h" -#include "ScopedArguments.h" #include "StringConstructor.h" -#include "Symbol.h" -#include "TypeProfilerLog.h" #include "TypedArrayInlines.h" -#include "VM.h" #include <wtf/InlineASM.h> #if ENABLE(JIT) @@ -68,7 +66,6 @@ static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - ASSERT(isIndex(index)); if (direct) { RELEASE_ASSERT(baseValue.isObject()); asObject(baseValue)->putDirectIndex(exec, index, value, 0, strict ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); @@ -81,7 +78,7 @@ static inline void putByVal(ExecState* exec, JSValue baseValue, uint32_t index, return; } - object->methodTable(vm)->putByIndex(object, exec, index, value, strict); + object->methodTable()->putByIndex(object, exec, index, value, strict); return; } @@ -99,8 +96,6 @@ ALWAYS_INLINE static void JIT_OPERATION operationPutByValInternal(ExecState* exe JSValue value = JSValue::decode(encodedValue); if (LIKELY(property.isUInt32())) { - // Despite its name, JSValue::isUInt32 will return true only for positive boxed int32_t; all those values are valid array indices. - ASSERT(isIndex(property.asUInt32())); putByVal<strict, direct>(exec, baseValue, property.asUInt32(), value); return; } @@ -108,26 +103,32 @@ ALWAYS_INLINE static void JIT_OPERATION operationPutByValInternal(ExecState* exe if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble); - if (propertyAsDouble == propertyAsUInt32 && isIndex(propertyAsUInt32)) { + if (propertyAsDouble == propertyAsUInt32) { putByVal<strict, direct>(exec, baseValue, propertyAsUInt32, value); return; } } - // Don't put to an object if toString throws an exception. - auto propertyName = property.toPropertyKey(exec); - if (vm->exception()) + if (isName(property)) { + PutPropertySlot slot(baseValue, strict); + if (direct) { + RELEASE_ASSERT(baseValue.isObject()); + asObject(baseValue)->putDirect(*vm, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot); + } else + baseValue.put(exec, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot); return; + } - PutPropertySlot slot(baseValue, strict); - if (direct) { - RELEASE_ASSERT(baseValue.isObject()); - if (Optional<uint32_t> index = parseIndex(propertyName)) - asObject(baseValue)->putDirectIndex(exec, index.value(), value, 0, strict ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); - else - asObject(baseValue)->putDirect(*vm, propertyName, value, slot); - } else - baseValue.put(exec, propertyName, value, slot); + // Don't put to an object if toString throws an exception. + Identifier ident(exec, property.toString(exec)->value(exec)); + if (!vm->exception()) { + PutPropertySlot slot(baseValue, strict); + if (direct) { + RELEASE_ASSERT(baseValue.isObject()); + asObject(baseValue)->putDirect(*vm, jsCast<NameInstance*>(property.asCell())->privateName(), value, slot); + } else + baseValue.put(exec, ident, value, slot); + } } template<typename ViewClass> @@ -136,7 +137,7 @@ char* newTypedArrayWithSize(ExecState* exec, Structure* structure, int32_t size) VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); if (size < 0) { - vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative"))); + vm.throwException(exec, createRangeError(exec, "Requested length is negative")); return 0; } return bitwise_cast<char*>(ViewClass::create(exec, structure, size)); @@ -155,7 +156,7 @@ char* newTypedArrayWithOneArgument( RefPtr<ArrayBuffer> buffer = jsBuffer->impl(); if (buffer->byteLength() % ViewClass::elementSize) { - vm.throwException(exec, createRangeError(exec, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size"))); + vm.throwException(exec, createRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size")); return 0; } return bitwise_cast<char*>( @@ -182,18 +183,18 @@ char* newTypedArrayWithOneArgument( if (value.isInt32()) length = value.asInt32(); else if (!value.isNumber()) { - vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument"))); + vm.throwException(exec, createTypeError(exec, "Invalid array length argument")); return 0; } else { length = static_cast<int>(value.asNumber()); if (length != value.asNumber()) { - vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument (fractional lengths not allowed)"))); + vm.throwException(exec, createTypeError(exec, "Invalid array length argument (fractional lengths not allowed)")); return 0; } } if (length < 0) { - vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative"))); + vm.throwException(exec, createRangeError(exec, "Requested length is negative")); return 0; } @@ -220,15 +221,15 @@ EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState* exec, EncodedJSVal JSCell* JIT_OPERATION operationCreateThis(ExecState* exec, JSObject* constructor, int32_t inlineCapacity) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); #if !ASSERT_DISABLED ConstructData constructData; - ASSERT(jsCast<JSFunction*>(constructor)->methodTable(vm)->getConstructData(jsCast<JSFunction*>(constructor), constructData) == ConstructTypeJS); + ASSERT(jsCast<JSFunction*>(constructor)->methodTable()->getConstructData(jsCast<JSFunction*>(constructor), constructData) == ConstructTypeJS); #endif - return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->rareData(exec, inlineCapacity)->allocationProfile()->structure()); + return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->allocationProfile(exec, inlineCapacity)->structure()); } EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) @@ -258,7 +259,7 @@ EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState* exec, Encoded return JSValue::encode(jsAddSlowCase(exec, op1, op2)); } -static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index) +static inline EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); @@ -277,8 +278,8 @@ static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint EncodedJSValue JIT_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); @@ -291,32 +292,25 @@ EncodedJSValue JIT_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue e } else if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble); - if (propertyAsUInt32 == propertyAsDouble && isIndex(propertyAsUInt32)) + if (propertyAsUInt32 == propertyAsDouble) return getByVal(exec, base, propertyAsUInt32); } else if (property.isString()) { - Structure& structure = *base->structure(vm); - if (JSCell::canUseFastGetOwnProperty(structure)) { - if (RefPtr<AtomicStringImpl> existingAtomicString = asString(property)->toExistingAtomicString(exec)) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomicString.get())) - return JSValue::encode(result); - } - } + if (JSValue result = base->fastGetOwnProperty(exec, asString(property)->value(exec))) + return JSValue::encode(result); } } - baseValue.requireObjectCoercible(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - auto propertyName = property.toPropertyKey(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - return JSValue::encode(baseValue.get(exec, propertyName)); + if (isName(property)) + return JSValue::encode(baseValue.get(exec, jsCast<NameInstance*>(property.asCell())->privateName())); + + Identifier ident(exec, property.toString(exec)->value(exec)); + return JSValue::encode(baseValue.get(exec, ident)); } EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState* exec, JSCell* base, EncodedJSValue encodedProperty) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); JSValue property = JSValue::decode(encodedProperty); @@ -328,19 +322,15 @@ EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState* exec, JSCell* base if (propertyAsUInt32 == propertyAsDouble) return getByVal(exec, base, propertyAsUInt32); } else if (property.isString()) { - Structure& structure = *base->structure(vm); - if (JSCell::canUseFastGetOwnProperty(structure)) { - if (RefPtr<AtomicStringImpl> existingAtomicString = asString(property)->toExistingAtomicString(exec)) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomicString.get())) - return JSValue::encode(result); - } - } + if (JSValue result = base->fastGetOwnProperty(exec, asString(property)->value(exec))) + return JSValue::encode(result); } - auto propertyName = property.toPropertyKey(exec); - if (exec->hadException()) - return JSValue::encode(jsUndefined()); - return JSValue::encode(JSValue(base).get(exec, propertyName)); + if (isName(property)) + return JSValue::encode(JSValue(base).get(exec, jsCast<NameInstance*>(property.asCell())->privateName())); + + Identifier ident(exec, property.toString(exec)->value(exec)); + return JSValue::encode(JSValue(base).get(exec, ident)); } ALWAYS_INLINE EncodedJSValue getByValCellInt(ExecState* exec, JSCell* base, int32_t index) @@ -401,8 +391,8 @@ void JIT_OPERATION operationPutByValCellNonStrict(ExecState* exec, JSCell* cell, void JIT_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, EncodedJSValue encodedValue) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); if (index >= 0) { array->putByIndexInline(exec, index, JSValue::decode(encodedValue), true); @@ -761,184 +751,79 @@ char* JIT_OPERATION operationNewFloat64ArrayWithOneArgument( return newTypedArrayWithOneArgument<JSFloat64Array>(exec, structure, encodedValue); } -JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table, EncodedJSValue initialValueEncoded) +JSCell* JIT_OPERATION operationCreateInlinedArguments( + ExecState* exec, InlineCallFrame* inlineCallFrame) { - JSValue initialValue = JSValue::decode(initialValueEncoded); - ASSERT(initialValue == jsUndefined() || initialValue == jsTDZValue()); VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - return JSLexicalEnvironment::create(vm, structure, scope, table, initialValue); -} - -JSCell* JIT_OPERATION operationCreateDirectArguments(ExecState* exec, Structure* structure, int32_t length, int32_t minCapacity) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer target(&vm, exec); - DirectArguments* result = DirectArguments::create( - vm, structure, length, std::max(length, minCapacity)); - // The caller will store to this object without barriers. Most likely, at this point, this is - // still a young object and so no barriers are needed. But it's good to be careful anyway, - // since the GC should be allowed to do crazy (like pretenuring, for example). - vm.heap.writeBarrier(result); + // 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, inlineCallFrame); + ASSERT(!vm.exception()); return result; } -JSCell* JIT_OPERATION operationCreateScopedArguments(ExecState* exec, Structure* structure, Register* argumentStart, int32_t length, JSFunction* callee, JSLexicalEnvironment* scope) +void JIT_OPERATION operationTearOffInlinedArguments( + ExecState* exec, JSCell* argumentsCell, JSCell* activationCell, InlineCallFrame* inlineCallFrame) { - VM& vm = exec->vm(); - NativeCallFrameTracer target(&vm, exec); - - // We could pass the ScopedArgumentsTable* as an argument. We currently don't because I - // didn't feel like changing the max number of arguments for a slow path call from 6 to 7. - ScopedArgumentsTable* table = scope->symbolTable()->arguments(); - - return ScopedArguments::createByCopyingFrom( - vm, structure, argumentStart, length, callee, table, scope); -} - -JSCell* JIT_OPERATION operationCreateClonedArguments(ExecState* exec, Structure* structure, Register* argumentStart, int32_t length, JSFunction* callee) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer target(&vm, exec); - return ClonedArguments::createByCopyingFrom( - exec, structure, argumentStart, length, callee); + ASSERT_UNUSED(activationCell, !activationCell); // Currently, we don't inline functions with activations. + jsCast<Arguments*>(argumentsCell)->tearOff(exec, inlineCallFrame); } -JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(ExecState* exec, InlineCallFrame* inlineCallFrame, JSFunction* callee, int32_t argumentCount) +EncodedJSValue JIT_OPERATION operationGetArgumentByVal(ExecState* exec, int32_t argumentsRegister, int32_t index) { VM& vm = exec->vm(); - NativeCallFrameTracer target(&vm, exec); - - DeferGCForAWhile deferGC(vm.heap); - - CodeBlock* codeBlock; - if (inlineCallFrame) - codeBlock = baselineCodeBlockForInlineCallFrame(inlineCallFrame); - else - codeBlock = exec->codeBlock(); - - unsigned length = argumentCount - 1; - unsigned capacity = std::max(length, static_cast<unsigned>(codeBlock->numParameters() - 1)); - DirectArguments* result = DirectArguments::create( - vm, codeBlock->globalObject()->directArgumentsStructure(), length, capacity); - - result->callee().set(vm, result, callee); - - Register* arguments = - exec->registers() + (inlineCallFrame ? inlineCallFrame->stackOffset : 0) + - CallFrame::argumentOffset(0); - for (unsigned i = length; i--;) - result->setIndexQuickly(vm, i, arguments[i].jsValue()); - - return result; -} + NativeCallFrameTracer tracer(&vm, exec); -JSCell* JIT_OPERATION operationCreateClonedArgumentsDuringExit(ExecState* exec, InlineCallFrame* inlineCallFrame, JSFunction* callee, int32_t argumentCount) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer target(&vm, exec); - - DeferGCForAWhile deferGC(vm.heap); + JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); - CodeBlock* codeBlock; - if (inlineCallFrame) - codeBlock = baselineCodeBlockForInlineCallFrame(inlineCallFrame); - else - codeBlock = exec->codeBlock(); - - unsigned length = argumentCount - 1; - ClonedArguments* result = ClonedArguments::createEmpty( - vm, codeBlock->globalObject()->outOfBandArgumentsStructure(), callee); - - Register* arguments = - exec->registers() + (inlineCallFrame ? inlineCallFrame->stackOffset : 0) + - CallFrame::argumentOffset(0); - for (unsigned i = length; i--;) - result->putDirectIndex(exec, i, arguments[i].jsValue()); - - result->putDirect(vm, vm.propertyNames->length, jsNumber(length)); + // If there are no arguments, and we're accessing out of bounds, then we have to create the + // arguments in case someone has installed a getter on a numeric property. + if (!argumentsValue) + exec->uncheckedR(argumentsRegister) = argumentsValue = Arguments::create(exec->vm(), exec); - return result; + return JSValue::encode(argumentsValue.get(exec, index)); } -size_t JIT_OPERATION operationObjectIsObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) +EncodedJSValue JIT_OPERATION operationGetInlinedArgumentByVal( + ExecState* exec, int32_t argumentsRegister, InlineCallFrame* inlineCallFrame, int32_t index) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - ASSERT(jsDynamicCast<JSObject*>(object)); + JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); - if (object->structure(vm)->masqueradesAsUndefined(globalObject)) - return false; - if (object->type() == JSFunctionType) - return false; - if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { - CallData callData; - if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) - return false; + // If there are no arguments, and we're accessing out of bounds, then we have to create the + // arguments in case someone has installed a getter on a numeric property. + if (!argumentsValue) { + exec->uncheckedR(argumentsRegister) = argumentsValue = + Arguments::create(exec->vm(), exec, inlineCallFrame); } - return true; + return JSValue::encode(argumentsValue.get(exec, index)); } -size_t JIT_OPERATION operationObjectIsFunction(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) +JSCell* JIT_OPERATION operationNewFunctionNoCheck(ExecState* exec, JSCell* functionExecutable) { + ASSERT(functionExecutable->inherits(FunctionExecutable::info())); VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - - ASSERT(jsDynamicCast<JSObject*>(object)); - - if (object->structure(vm)->masqueradesAsUndefined(globalObject)) - return false; - if (object->type() == JSFunctionType) - return true; - if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { - CallData callData; - if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) - return true; - } - - return false; + return JSFunction::create(vm, static_cast<FunctionExecutable*>(functionExecutable), exec->scope()); } -JSCell* JIT_OPERATION operationTypeOfObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) +size_t JIT_OPERATION operationIsObject(ExecState* exec, EncodedJSValue value) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - ASSERT(jsDynamicCast<JSObject*>(object)); - - if (object->structure(vm)->masqueradesAsUndefined(globalObject)) - return vm.smallStrings.undefinedString(); - if (object->type() == JSFunctionType) - return vm.smallStrings.functionString(); - if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { - CallData callData; - if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) - return vm.smallStrings.functionString(); - } - - return vm.smallStrings.objectString(); + return jsIsObjectType(exec, JSValue::decode(value)); } -int32_t JIT_OPERATION operationTypeOfObjectAsTypeofType(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) +size_t JIT_OPERATION operationIsFunction(EncodedJSValue value) { - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + return jsIsFunctionType(JSValue::decode(value)); +} - ASSERT(jsDynamicCast<JSObject*>(object)); - - if (object->structure(vm)->masqueradesAsUndefined(globalObject)) - return static_cast<int32_t>(TypeofType::Undefined); - if (object->type() == JSFunctionType) - return static_cast<int32_t>(TypeofType::Function); - if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { - CallData callData; - if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) - return static_cast<int32_t>(TypeofType::Function); - } - - return static_cast<int32_t>(TypeofType::Object); +JSCell* JIT_OPERATION operationTypeOf(ExecState* exec, JSCell* value) +{ + return jsTypeStringForValue(exec, JSValue(value)).asCell(); } char* JIT_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState* exec) @@ -1015,6 +900,17 @@ char* JIT_OPERATION operationEnsureContiguous(ExecState* exec, JSCell* cell) return reinterpret_cast<char*>(asObject(cell)->ensureContiguous(vm).data()); } +char* JIT_OPERATION operationRageEnsureContiguous(ExecState* exec, JSCell* cell) +{ + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + if (!cell->isObject()) + return 0; + + return reinterpret_cast<char*>(asObject(cell)->rageEnsureContiguous(vm).data()); +} + char* JIT_OPERATION operationEnsureArrayStorage(ExecState* exec, JSCell* cell) { VM& vm = exec->vm(); @@ -1066,22 +962,6 @@ JSCell* JIT_OPERATION operationToString(ExecState* exec, EncodedJSValue value) return JSValue::decode(value).toString(exec); } -JSCell* JIT_OPERATION operationCallStringConstructorOnCell(ExecState* exec, JSCell* cell) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - return stringConstructor(exec, cell); -} - -JSCell* JIT_OPERATION operationCallStringConstructor(ExecState* exec, EncodedJSValue value) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - return stringConstructor(exec, JSValue::decode(value)); -} - JSCell* JIT_OPERATION operationMakeRope2(ExecState* exec, JSString* left, JSString* right) { VM& vm = exec->vm(); @@ -1130,48 +1010,12 @@ char* JIT_OPERATION operationSwitchString(ExecState* exec, size_t tableIndex, JS return static_cast<char*>(exec->codeBlock()->stringSwitchJumpTable(tableIndex).ctiForValue(string->value(exec).impl()).executableAddress()); } -int32_t JIT_OPERATION operationSwitchStringAndGetBranchOffset(ExecState* exec, size_t tableIndex, JSString* string) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - return exec->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(string->value(exec).impl(), std::numeric_limits<int32_t>::min()); -} - -void JIT_OPERATION operationNotifyWrite(ExecState* exec, WatchpointSet* set) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - - set->touch("Executed NotifyWrite"); -} - -void JIT_OPERATION operationThrowStackOverflowForVarargs(ExecState* exec) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - throwStackOverflowError(exec); -} - -int32_t JIT_OPERATION operationSizeOfVarargs(ExecState* exec, EncodedJSValue encodedArguments, int32_t firstVarArgOffset) +void JIT_OPERATION operationInvalidate(ExecState* exec, VariableWatchpointSet* set) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - JSValue arguments = JSValue::decode(encodedArguments); - - return sizeOfVarargs(exec, arguments, firstVarArgOffset); -} -void JIT_OPERATION operationLoadVarargs(ExecState* exec, int32_t firstElementDest, EncodedJSValue encodedArguments, int32_t offset, int32_t length, int32_t mandatoryMinimum) -{ - VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); - JSValue arguments = JSValue::decode(encodedArguments); - - loadVarargs(exec, VirtualRegister(firstElementDest), arguments, offset, length); - - for (int32_t i = length; i < mandatoryMinimum; ++i) - exec->r(firstElementDest + i) = jsUndefined(); + set->invalidate(); } double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b) @@ -1186,24 +1030,6 @@ JSCell* JIT_OPERATION operationStringFromCharCode(ExecState* exec, int32_t op1) return JSC::stringFromCharCode(exec, op1); } -int64_t JIT_OPERATION operationConvertBoxedDoubleToInt52(EncodedJSValue encodedValue) -{ - JSValue value = JSValue::decode(encodedValue); - if (!value.isDouble()) - return JSValue::notInt52; - return tryConvertToInt52(value.asDouble()); -} - -int64_t JIT_OPERATION operationConvertDoubleToInt52(double value) -{ - return tryConvertToInt52(value); -} - -void JIT_OPERATION operationProcessTypeProfilerLogDFG(ExecState* exec) -{ - exec->vm().typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside DFG.")); -} - size_t JIT_OPERATION dfgConvertJSValueToInt32(ExecState* exec, EncodedJSValue value) { VM* vm = &exec->vm(); @@ -1221,8 +1047,8 @@ void JIT_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* SpeculationFailureDebugInfo* debugInfo = static_cast<SpeculationFailureDebugInfo*>(debugInfoRaw); CodeBlock* codeBlock = debugInfo->codeBlock; CodeBlock* alternative = codeBlock->alternative(); - dataLog("Speculation failure in ", *codeBlock); - dataLog(" @ exit #", vm->osrExitIndex, " (bc#", debugInfo->bytecodeOffset, ", ", exitKindToString(debugInfo->kind), ") with "); + dataLog( + "Speculation failure in ", *codeBlock, " with "); if (alternative) { dataLog( "executeCounter = ", alternative->jitExecuteCounter(), @@ -1251,7 +1077,7 @@ void JIT_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* dataLog("\n"); } -extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock, OSRExitBase* exit) +extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) { // It's sort of preferable that we don't GC while in here. Anyways, doing so wouldn't // really be profitable. @@ -1275,21 +1101,13 @@ extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock, OSR ASSERT(codeBlock->hasOptimizedReplacement()); CodeBlock* optimizedCodeBlock = codeBlock->replacement(); ASSERT(JITCode::isOptimizingJIT(optimizedCodeBlock->jitType())); - - bool didTryToEnterIntoInlinedLoops = false; - for (InlineCallFrame* inlineCallFrame = exit->m_codeOrigin.inlineCallFrame; inlineCallFrame; inlineCallFrame = inlineCallFrame->caller.inlineCallFrame) { - if (inlineCallFrame->executable->didTryToEnterInLoop()) { - didTryToEnterIntoInlinedLoops = true; - break; - } - } // In order to trigger reoptimization, one of two things must have happened: // 1) We exited more than some number of times. // 2) We exited and got stuck in a loop, and now we're exiting again. bool didExitABunch = optimizedCodeBlock->shouldReoptimizeNow(); bool didGetStuckInLoop = - (codeBlock->checkIfOptimizationThresholdReached() || didTryToEnterIntoInlinedLoops) + codeBlock->checkIfOptimizationThresholdReached() && optimizedCodeBlock->shouldReoptimizeFromLoopNow(); if (!didExitABunch && !didGetStuckInLoop) { @@ -1299,12 +1117,25 @@ extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock, OSR return; } - optimizedCodeBlock->jettison(Profiler::JettisonDueToOSRExit, CountReoptimization); + optimizedCodeBlock->jettison(CountReoptimization); } #if ENABLE(FTL_JIT) -static void triggerFTLReplacementCompile(VM* vm, CodeBlock* codeBlock, JITCode* jitCode) +void JIT_OPERATION triggerTierUpNow(ExecState* exec) { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + DeferGC deferGC(vm->heap); + CodeBlock* codeBlock = exec->codeBlock(); + + JITCode* jitCode = codeBlock->jitCode()->dfg(); + + if (Options::verboseOSR()) { + dataLog( + *codeBlock, ": Entered triggerTierUpNow with executeCounter = ", + jitCode->tierUpCounter, "\n"); + } + if (codeBlock->baselineVersion()->m_didFailFTLCompilation) { if (Options::verboseOSR()) dataLog("Deferring FTL-optimization of ", *codeBlock, " indefinitely because there was an FTL failure.\n"); @@ -1319,7 +1150,7 @@ static void triggerFTLReplacementCompile(VM* vm, CodeBlock* codeBlock, JITCode* } Worklist::State worklistState; - if (Worklist* worklist = existingGlobalFTLWorklistOrNull()) { + if (Worklist* worklist = vm->worklist.get()) { worklistState = worklist->completeAllReadyPlansForVM( *vm, CompilationKey(codeBlock->baselineVersion(), FTLMode)); } else @@ -1348,43 +1179,8 @@ static void triggerFTLReplacementCompile(VM* vm, CodeBlock* codeBlock, JITCode* // We need to compile the code. compile( - *vm, codeBlock->newReplacement().get(), codeBlock, FTLMode, UINT_MAX, - Operands<JSValue>(), ToFTLDeferredCompilationCallback::create(codeBlock)); -} - -static void triggerTierUpNowCommon(ExecState* exec, bool inLoop) -{ - VM* vm = &exec->vm(); - NativeCallFrameTracer tracer(vm, exec); - DeferGC deferGC(vm->heap); - CodeBlock* codeBlock = exec->codeBlock(); - - if (codeBlock->jitType() != JITCode::DFGJIT) { - dataLog("Unexpected code block in DFG->FTL tier-up: ", *codeBlock, "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - - JITCode* jitCode = codeBlock->jitCode()->dfg(); - - if (Options::verboseOSR()) { - dataLog( - *codeBlock, ": Entered triggerTierUpNow with executeCounter = ", - jitCode->tierUpCounter, "\n"); - } - if (inLoop) - jitCode->nestedTriggerIsSet = 1; - - triggerFTLReplacementCompile(vm, codeBlock, jitCode); -} - -void JIT_OPERATION triggerTierUpNow(ExecState* exec) -{ - triggerTierUpNowCommon(exec, false); -} - -void JIT_OPERATION triggerTierUpNowInLoop(ExecState* exec) -{ - triggerTierUpNowCommon(exec, true); + *vm, codeBlock->newReplacement().get(), FTLMode, UINT_MAX, Operands<JSValue>(), + ToFTLDeferredCompilationCallback::create(codeBlock), vm->ensureWorklist()); } char* JIT_OPERATION triggerOSREntryNow( @@ -1395,61 +1191,63 @@ char* JIT_OPERATION triggerOSREntryNow( DeferGC deferGC(vm->heap); CodeBlock* codeBlock = exec->codeBlock(); - if (codeBlock->jitType() != JITCode::DFGJIT) { - dataLog("Unexpected code block in DFG->FTL tier-up: ", *codeBlock, "\n"); - RELEASE_ASSERT_NOT_REACHED(); - } - JITCode* jitCode = codeBlock->jitCode()->dfg(); - jitCode->nestedTriggerIsSet = 0; if (Options::verboseOSR()) { dataLog( - *codeBlock, ": Entered triggerOSREntryNow with executeCounter = ", + *codeBlock, ": Entered triggerTierUpNow with executeCounter = ", jitCode->tierUpCounter, "\n"); } - // - If we don't have an FTL code block, then try to compile one. - // - If we do have an FTL code block, then try to enter for a while. - // - If we couldn't enter for a while, then trigger OSR entry. - - triggerFTLReplacementCompile(vm, codeBlock, jitCode); - - if (!codeBlock->hasOptimizedReplacement()) + if (codeBlock->baselineVersion()->m_didFailFTLCompilation) { + if (Options::verboseOSR()) + dataLog("Deferring FTL-optimization of ", *codeBlock, " indefinitely because there was an FTL failure.\n"); + jitCode->dontOptimizeAnytimeSoon(codeBlock); return 0; + } - if (jitCode->osrEntryRetry < Options::ftlOSREntryRetryThreshold()) { - jitCode->osrEntryRetry++; + if (!jitCode->checkIfOptimizationThresholdReached(codeBlock)) { + if (Options::verboseOSR()) + dataLog("Choosing not to FTL-optimize ", *codeBlock, " yet.\n"); return 0; } - // It's time to try to compile code for OSR entry. Worklist::State worklistState; - if (Worklist* worklist = existingGlobalFTLWorklistOrNull()) { + if (Worklist* worklist = vm->worklist.get()) { worklistState = worklist->completeAllReadyPlansForVM( *vm, CompilationKey(codeBlock->baselineVersion(), FTLForOSREntryMode)); } else worklistState = Worklist::NotKnown; - if (worklistState == Worklist::Compiling) + if (worklistState == Worklist::Compiling) { + ASSERT(!jitCode->osrEntryBlock); + jitCode->setOptimizationThresholdBasedOnCompilationResult( + codeBlock, CompilationDeferred); return 0; + } if (CodeBlock* entryBlock = jitCode->osrEntryBlock.get()) { void* address = FTL::prepareOSREntry( exec, codeBlock, entryBlock, bytecodeIndex, streamIndex); - if (address) + if (address) { + jitCode->optimizeSoon(codeBlock); return static_cast<char*>(address); + } FTL::ForOSREntryJITCode* entryCode = entryBlock->jitCode()->ftlForOSREntry(); entryCode->countEntryFailure(); if (entryCode->entryFailureCount() < - Options::ftlOSREntryFailureCountForReoptimization()) + Options::ftlOSREntryFailureCountForReoptimization()) { + + jitCode->optimizeSoon(codeBlock); return 0; + } // OSR entry failed. Oh no! This implies that we need to retry. We retry // without exponential backoff and we only do this for the entry code block. - jitCode->osrEntryBlock = nullptr; - jitCode->osrEntryRetry = 0; + jitCode->osrEntryBlock.clear(); + + jitCode->optimizeAfterWarmUp(codeBlock); return 0; } @@ -1460,28 +1258,87 @@ char* JIT_OPERATION triggerOSREntryNow( return 0; } - // We aren't compiling and haven't compiled anything for OSR entry. So, try to compile - // something. + // The first order of business is to trigger a for-entry compile. Operands<JSValue> mustHandleValues; jitCode->reconstruct( exec, codeBlock, CodeOrigin(bytecodeIndex), streamIndex, mustHandleValues); - RefPtr<CodeBlock> replacementCodeBlock = codeBlock->newReplacement(); - CompilationResult forEntryResult = compile( - *vm, replacementCodeBlock.get(), codeBlock, FTLForOSREntryMode, bytecodeIndex, - mustHandleValues, ToFTLForOSREntryDeferredCompilationCallback::create(codeBlock)); + CompilationResult forEntryResult = DFG::compile( + *vm, codeBlock->newReplacement().get(), FTLForOSREntryMode, bytecodeIndex, + mustHandleValues, ToFTLForOSREntryDeferredCompilationCallback::create(codeBlock), + vm->ensureWorklist()); + + // But we also want to trigger a replacement compile. Of course, we don't want to + // trigger it if we don't need to. Note that this is kind of weird because we might + // have just finished an FTL compile and that compile failed or was invalidated. + // But this seems uncommon enough that we sort of don't care. It's certainly sound + // to fire off another compile right now so long as we're not already compiling and + // we don't already have an optimized replacement. Note, we don't do this for + // obviously bad cases like global code, where we know that there is a slim chance + // of this code being invoked ever again. + CompilationKey keyForReplacement(codeBlock->baselineVersion(), FTLMode); + if (codeBlock->codeType() != GlobalCode + && !codeBlock->hasOptimizedReplacement() + && (!vm->worklist.get() + || vm->worklist->compilationState(keyForReplacement) == Worklist::NotKnown)) { + compile( + *vm, codeBlock->newReplacement().get(), FTLMode, UINT_MAX, Operands<JSValue>(), + ToFTLDeferredCompilationCallback::create(codeBlock), vm->ensureWorklist()); + } - if (forEntryResult != CompilationSuccessful) { - ASSERT(forEntryResult == CompilationDeferred || replacementCodeBlock->hasOneRef()); + if (forEntryResult != CompilationSuccessful) return 0; - } - + // It's possible that the for-entry compile already succeeded. In that case OSR // entry will succeed unless we ran out of stack. It's not clear what we should do. // We signal to try again after a while if that happens. void* address = FTL::prepareOSREntry( exec, codeBlock, jitCode->osrEntryBlock.get(), bytecodeIndex, streamIndex); + if (address) + jitCode->optimizeSoon(codeBlock); + else + jitCode->optimizeAfterWarmUp(codeBlock); return static_cast<char*>(address); } + +// FIXME: Make calls work well. Currently they're a pure regression. +// https://bugs.webkit.org/show_bug.cgi?id=113621 +EncodedJSValue JIT_OPERATION operationFTLCall(ExecState* exec) +{ + ExecState* callerExec = exec->callerFrame(); + + VM* vm = &callerExec->vm(); + NativeCallFrameTracer tracer(vm, callerExec); + + JSValue callee = exec->calleeAsValue(); + CallData callData; + CallType callType = getCallData(callee, callData); + if (callType == CallTypeNone) { + vm->throwException(callerExec, createNotAFunctionError(callerExec, callee)); + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(call(callerExec, callee, callType, callData, exec->thisValue(), exec)); +} + +// FIXME: Make calls work well. Currently they're a pure regression. +// https://bugs.webkit.org/show_bug.cgi?id=113621 +EncodedJSValue JIT_OPERATION operationFTLConstruct(ExecState* exec) +{ + ExecState* callerExec = exec->callerFrame(); + + VM* vm = &callerExec->vm(); + NativeCallFrameTracer tracer(vm, callerExec); + + JSValue callee = exec->calleeAsValue(); + ConstructData constructData; + ConstructType constructType = getConstructData(callee, constructData); + if (constructType == ConstructTypeNone) { + vm->throwException(callerExec, createNotAFunctionError(callerExec, callee)); + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(construct(callerExec, callee, constructType, constructData, exec)); +} #endif // ENABLE(FTL_JIT) } // extern "C" |
