X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/2d39b0e377c0896910ee49ae70082ba665faf986..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/dfg/DFGOperations.cpp?ds=sidebyside diff --git a/dfg/DFGOperations.cpp b/dfg/DFGOperations.cpp index 15cb4f5..6f9512b 100644 --- a/dfg/DFGOperations.cpp +++ b/dfg/DFGOperations.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2013-2015 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,8 +26,8 @@ #include "config.h" #include "DFGOperations.h" -#include "Arguments.h" #include "ButterflyInlines.h" +#include "ClonedArguments.h" #include "CodeBlock.h" #include "CommonSlowPaths.h" #include "CopiedSpaceInlines.h" @@ -38,6 +38,7 @@ #include "DFGToFTLDeferredCompilationCallback.h" #include "DFGToFTLForOSREntryDeferredCompilationCallback.h" #include "DFGWorklist.h" +#include "DirectArguments.h" #include "FTLForOSREntryJITCode.h" #include "FTLOSREntry.h" #include "HostCallReturnValue.h" @@ -45,15 +46,17 @@ #include "Interpreter.h" #include "JIT.h" #include "JITExceptions.h" -#include "JSActivation.h" -#include "VM.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" #include "JSNameScope.h" -#include "NameInstance.h" #include "ObjectConstructor.h" -#include "JSCInlines.h" #include "Repatch.h" +#include "ScopedArguments.h" #include "StringConstructor.h" +#include "Symbol.h" +#include "TypeProfilerLog.h" #include "TypedArrayInlines.h" +#include "VM.h" #include #if ENABLE(JIT) @@ -66,6 +69,7 @@ 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); @@ -96,6 +100,8 @@ 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(exec, baseValue, property.asUInt32(), value); return; } @@ -103,32 +109,26 @@ ALWAYS_INLINE static void JIT_OPERATION operationPutByValInternal(ExecState* exe if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast(propertyAsDouble); - if (propertyAsDouble == propertyAsUInt32) { + if (propertyAsDouble == propertyAsUInt32 && isIndex(propertyAsUInt32)) { putByVal(exec, baseValue, propertyAsUInt32, value); return; } } - if (isName(property)) { - PutPropertySlot slot(baseValue, strict); - if (direct) { - RELEASE_ASSERT(baseValue.isObject()); - asObject(baseValue)->putDirect(*vm, jsCast(property.asCell())->privateName(), value, slot); - } else - baseValue.put(exec, jsCast(property.asCell())->privateName(), value, slot); + // Don't put to an object if toString throws an exception. + auto propertyName = property.toPropertyKey(exec); + if (vm->exception()) return; - } - // Don't put to an object if toString throws an exception. - Identifier ident = property.toString(exec)->toIdentifier(exec); - if (!vm->exception()) { - PutPropertySlot slot(baseValue, strict); - if (direct) { - RELEASE_ASSERT(baseValue.isObject()); - asObject(baseValue)->putDirect(*vm, jsCast(property.asCell())->privateName(), value, slot); - } else - baseValue.put(exec, ident, value, slot); - } + PutPropertySlot slot(baseValue, strict); + if (direct) { + RELEASE_ASSERT(baseValue.isObject()); + if (Optional 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); } template @@ -137,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, "Requested length is negative")); + vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative"))); return 0; } return bitwise_cast(ViewClass::create(exec, structure, size)); @@ -156,7 +156,7 @@ char* newTypedArrayWithOneArgument( RefPtr buffer = jsBuffer->impl(); if (buffer->byteLength() % ViewClass::elementSize) { - vm.throwException(exec, createRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size")); + vm.throwException(exec, createRangeError(exec, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size"))); return 0; } return bitwise_cast( @@ -183,18 +183,18 @@ char* newTypedArrayWithOneArgument( if (value.isInt32()) length = value.asInt32(); else if (!value.isNumber()) { - vm.throwException(exec, createTypeError(exec, "Invalid array length argument")); + vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument"))); return 0; } else { length = static_cast(value.asNumber()); if (length != value.asNumber()) { - vm.throwException(exec, createTypeError(exec, "Invalid array length argument (fractional lengths not allowed)")); + vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument (fractional lengths not allowed)"))); return 0; } } if (length < 0) { - vm.throwException(exec, createRangeError(exec, "Requested length is negative")); + vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative"))); return 0; } @@ -229,7 +229,7 @@ JSCell* JIT_OPERATION operationCreateThis(ExecState* exec, JSObject* constructor ASSERT(jsCast(constructor)->methodTable(vm)->getConstructData(jsCast(constructor), constructData) == ConstructTypeJS); #endif - return constructEmptyObject(exec, jsCast(constructor)->allocationProfile(exec, inlineCapacity)->structure()); + return constructEmptyObject(exec, jsCast(constructor)->rareData(exec, inlineCapacity)->allocationProfile()->structure()); } EncodedJSValue JIT_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) @@ -292,22 +292,26 @@ EncodedJSValue JIT_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue e } else if (property.isDouble()) { double propertyAsDouble = property.asDouble(); uint32_t propertyAsUInt32 = static_cast(propertyAsDouble); - if (propertyAsUInt32 == propertyAsDouble) + if (propertyAsUInt32 == propertyAsDouble && isIndex(propertyAsUInt32)) return getByVal(exec, base, propertyAsUInt32); } else if (property.isString()) { Structure& structure = *base->structure(vm); if (JSCell::canUseFastGetOwnProperty(structure)) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, asString(property)->value(exec))) - return JSValue::encode(result); + if (RefPtr existingAtomicString = asString(property)->toExistingAtomicString(exec)) { + if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomicString.get())) + return JSValue::encode(result); + } } } } - if (isName(property)) - return JSValue::encode(baseValue.get(exec, jsCast(property.asCell())->privateName())); - - Identifier ident = property.toString(exec)->toIdentifier(exec); - return JSValue::encode(baseValue.get(exec, ident)); + 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)); } EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState* exec, JSCell* base, EncodedJSValue encodedProperty) @@ -327,16 +331,17 @@ EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState* exec, JSCell* base } else if (property.isString()) { Structure& structure = *base->structure(vm); if (JSCell::canUseFastGetOwnProperty(structure)) { - if (JSValue result = base->fastGetOwnProperty(vm, structure, asString(property)->value(exec))) - return JSValue::encode(result); + if (RefPtr existingAtomicString = asString(property)->toExistingAtomicString(exec)) { + if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomicString.get())) + return JSValue::encode(result); + } } } - if (isName(property)) - return JSValue::encode(JSValue(base).get(exec, jsCast(property.asCell())->privateName())); - - Identifier ident = property.toString(exec)->toIdentifier(exec); - return JSValue::encode(JSValue(base).get(exec, ident)); + auto propertyName = property.toPropertyKey(exec); + if (exec->hadException()) + return JSValue::encode(jsUndefined()); + return JSValue::encode(JSValue(base).get(exec, propertyName)); } ALWAYS_INLINE EncodedJSValue getByValCellInt(ExecState* exec, JSCell* base, int32_t index) @@ -757,87 +762,182 @@ char* JIT_OPERATION operationNewFloat64ArrayWithOneArgument( return newTypedArrayWithOneArgument(exec, structure, encodedValue); } -JSCell* JIT_OPERATION operationCreateInlinedArguments( - ExecState* exec, InlineCallFrame* inlineCallFrame) +JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table) { 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, inlineCallFrame); - ASSERT(!vm.exception()); + return JSLexicalEnvironment::create(vm, structure, scope, table); +} + +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); return result; } -JSCell* JIT_OPERATION operationCreateInlinedArgumentsDuringOSRExit(ExecState* exec, InlineCallFrame* inlineCallFrame) +JSCell* JIT_OPERATION operationCreateScopedArguments(ExecState* exec, Structure* structure, Register* argumentStart, int32_t length, JSFunction* callee, JSLexicalEnvironment* scope) { - DeferGCForAWhile(exec->vm().heap); - return operationCreateInlinedArguments(exec, 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); } -void JIT_OPERATION operationTearOffInlinedArguments( - ExecState* exec, JSCell* argumentsCell, JSCell* activationCell, InlineCallFrame* inlineCallFrame) +JSCell* JIT_OPERATION operationCreateClonedArguments(ExecState* exec, Structure* structure, Register* argumentStart, int32_t length, JSFunction* callee) { - ASSERT_UNUSED(activationCell, !activationCell); // Currently, we don't inline functions with activations. - jsCast(argumentsCell)->tearOff(exec, inlineCallFrame); + VM& vm = exec->vm(); + NativeCallFrameTracer target(&vm, exec); + return ClonedArguments::createByCopyingFrom( + exec, structure, argumentStart, length, callee); } -EncodedJSValue JIT_OPERATION operationGetArgumentByVal(ExecState* exec, int32_t argumentsRegister, int32_t index) +JSCell* JIT_OPERATION operationCreateDirectArgumentsDuringExit(ExecState* exec, InlineCallFrame* inlineCallFrame, JSFunction* callee, int32_t argumentCount) { VM& vm = exec->vm(); - NativeCallFrameTracer tracer(&vm, exec); + 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(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; +} - JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); +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); - // 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); + CodeBlock* codeBlock; + if (inlineCallFrame) + codeBlock = baselineCodeBlockForInlineCallFrame(inlineCallFrame); + else + codeBlock = exec->codeBlock(); - return JSValue::encode(argumentsValue.get(exec, index)); + 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)); + + return result; } -EncodedJSValue JIT_OPERATION operationGetInlinedArgumentByVal( - ExecState* exec, int32_t argumentsRegister, InlineCallFrame* inlineCallFrame, int32_t index) +size_t JIT_OPERATION operationObjectIsObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - JSValue argumentsValue = exec->uncheckedR(argumentsRegister).jsValue(); + ASSERT(jsDynamicCast(object)); - // 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); + 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; } - return JSValue::encode(argumentsValue.get(exec, index)); + return true; } -JSCell* JIT_OPERATION operationNewFunctionNoCheck(ExecState* exec, JSCell* functionExecutable) +size_t JIT_OPERATION operationObjectIsFunction(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) { - ASSERT(functionExecutable->inherits(FunctionExecutable::info())); VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - return JSFunction::create(vm, static_cast(functionExecutable), exec->scope()); -} -size_t JIT_OPERATION operationIsObject(ExecState* exec, EncodedJSValue value) -{ - return jsIsObjectType(exec, JSValue::decode(value)); + ASSERT(jsDynamicCast(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; } -size_t JIT_OPERATION operationIsFunction(EncodedJSValue value) +JSCell* JIT_OPERATION operationTypeOfObject(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) { - return jsIsFunctionType(JSValue::decode(value)); + VM& vm = exec->vm(); + NativeCallFrameTracer tracer(&vm, exec); + + ASSERT(jsDynamicCast(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(); } -JSCell* JIT_OPERATION operationTypeOf(ExecState* exec, JSCell* value) +int32_t JIT_OPERATION operationTypeOfObjectAsTypeofType(ExecState* exec, JSGlobalObject* globalObject, JSCell* object) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - return jsTypeStringForValue(exec, JSValue(value)).asCell(); + + ASSERT(jsDynamicCast(object)); + + if (object->structure(vm)->masqueradesAsUndefined(globalObject)) + return static_cast(TypeofType::Undefined); + if (object->type() == JSFunctionType) + return static_cast(TypeofType::Function); + if (object->inlineTypeFlags() & TypeOfShouldCallGetCallData) { + CallData callData; + if (object->methodTable(vm)->getCallData(object, callData) != CallTypeNone) + return static_cast(TypeofType::Function); + } + + return static_cast(TypeofType::Object); } char* JIT_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecState* exec) @@ -914,17 +1014,6 @@ char* JIT_OPERATION operationEnsureContiguous(ExecState* exec, JSCell* cell) return reinterpret_cast(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(asObject(cell)->rageEnsureContiguous(vm).data()); -} - char* JIT_OPERATION operationEnsureArrayStorage(ExecState* exec, JSCell* cell) { VM& vm = exec->vm(); @@ -976,6 +1065,22 @@ 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(); @@ -1024,13 +1129,48 @@ char* JIT_OPERATION operationSwitchString(ExecState* exec, size_t tableIndex, JS return static_cast(exec->codeBlock()->stringSwitchJumpTable(tableIndex).ctiForValue(string->value(exec).impl()).executableAddress()); } -void JIT_OPERATION operationNotifyWrite(ExecState* exec, VariableWatchpointSet* set, EncodedJSValue encodedValue) +int32_t JIT_OPERATION operationSwitchStringAndGetBranchOffset(ExecState* exec, size_t tableIndex, JSString* string) { VM& vm = exec->vm(); NativeCallFrameTracer tracer(&vm, exec); - JSValue value = JSValue::decode(encodedValue); - set->notifyWrite(vm, value); + return exec->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(string->value(exec).impl(), std::numeric_limits::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) +{ + 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(); } double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b) @@ -1058,6 +1198,11 @@ 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(); @@ -1105,7 +1250,7 @@ void JIT_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* dataLog("\n"); } -extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) +extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock, OSRExitBase* exit) { // It's sort of preferable that we don't GC while in here. Anyways, doing so wouldn't // really be profitable. @@ -1129,13 +1274,21 @@ extern "C" void JIT_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) 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() + (codeBlock->checkIfOptimizationThresholdReached() || didTryToEnterIntoInlinedLoops) && optimizedCodeBlock->shouldReoptimizeFromLoopNow(); if (!didExitABunch && !didGetStuckInLoop) { @@ -1198,13 +1351,18 @@ static void triggerFTLReplacementCompile(VM* vm, CodeBlock* codeBlock, JITCode* Operands(), ToFTLDeferredCompilationCallback::create(codeBlock)); } -void JIT_OPERATION triggerTierUpNow(ExecState* exec) +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()) { @@ -1212,10 +1370,22 @@ void JIT_OPERATION triggerTierUpNow(ExecState* exec) *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); +} + char* JIT_OPERATION triggerOSREntryNow( ExecState* exec, int32_t bytecodeIndex, int32_t streamIndex) { @@ -1224,11 +1394,17 @@ 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 triggerTierUpNow with executeCounter = ", + *codeBlock, ": Entered triggerOSREntryNow with executeCounter = ", jitCode->tierUpCounter, "\n"); } @@ -1271,7 +1447,7 @@ char* JIT_OPERATION triggerOSREntryNow( // 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.clear(); + jitCode->osrEntryBlock = nullptr; jitCode->osrEntryRetry = 0; return 0; }