diff options
| author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-07-11 13:45:28 +0200 |
|---|---|---|
| committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-07-11 13:45:28 +0200 |
| commit | d6a599dbc9d824a462b2b206316e102bf8136446 (patch) | |
| tree | ecb257a5e55b2239d74b90fdad62fccd661cf286 /Source/JavaScriptCore/dfg | |
| parent | 3ccc3a85f09a83557b391aae380d3bf5f81a2911 (diff) | |
| download | qtwebkit-d6a599dbc9d824a462b2b206316e102bf8136446.tar.gz | |
Imported WebKit commit 8ff1f22783a32de82fee915abd55bd1b298f2644 (http://svn.webkit.org/repository/webkit/trunk@122325)
New snapshot that should work with the latest Qt build system changes
Diffstat (limited to 'Source/JavaScriptCore/dfg')
49 files changed, 2226 insertions, 859 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index c2d49f7ee..4cd31f2a8 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -311,31 +311,35 @@ bool AbstractState::execute(unsigned indexInBlock) if (left && right && left.isInt32() && right.isInt32()) { int32_t a = left.asInt32(); int32_t b = right.asInt32(); + bool constantWasSet; switch (node.op()) { case BitAnd: - forNode(nodeIndex).set(JSValue(a & b)); + constantWasSet = trySetConstant(nodeIndex, JSValue(a & b)); break; case BitOr: - forNode(nodeIndex).set(JSValue(a | b)); + constantWasSet = trySetConstant(nodeIndex, JSValue(a | b)); break; case BitXor: - forNode(nodeIndex).set(JSValue(a ^ b)); + constantWasSet = trySetConstant(nodeIndex, JSValue(a ^ b)); break; case BitRShift: - forNode(nodeIndex).set(JSValue(a >> static_cast<uint32_t>(b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(a >> static_cast<uint32_t>(b))); break; case BitLShift: - forNode(nodeIndex).set(JSValue(a << static_cast<uint32_t>(b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(a << static_cast<uint32_t>(b))); break; case BitURShift: - forNode(nodeIndex).set(JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b))); break; default: ASSERT_NOT_REACHED(); + constantWasSet = false; + } + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); + break; } - m_foundConstants = true; - node.setCanExit(false); - break; } speculateInt32Binary(node); forNode(nodeIndex).set(SpecInt32); @@ -346,10 +350,11 @@ bool AbstractState::execute(unsigned indexInBlock) JSValue child = forNode(node.child1()).value(); if (child && child.isNumber()) { ASSERT(child.isInt32()); - forNode(nodeIndex).set(JSValue(child.asUInt32())); - m_foundConstants = true; - node.setCanExit(false); - break; + if (trySetConstant(nodeIndex, JSValue(child.asUInt32()))) { + m_foundConstants = true; + node.setCanExit(false); + break; + } } if (!node.canSpeculateInteger()) { forNode(nodeIndex).set(SpecDouble); @@ -367,8 +372,8 @@ bool AbstractState::execute(unsigned indexInBlock) if (child && child.isNumber()) { double asDouble = child.asNumber(); int32_t asInt = JSC::toInt32(asDouble); - if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble)) { - forNode(nodeIndex).set(JSValue(asInt)); + if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble) + && trySetConstant(nodeIndex, JSValue(asInt))) { m_foundConstants = true; break; } @@ -382,13 +387,16 @@ bool AbstractState::execute(unsigned indexInBlock) case ValueToInt32: { JSValue child = forNode(node.child1()).value(); if (child && child.isNumber()) { + bool constantWasSet; if (child.isInt32()) - forNode(nodeIndex).set(child); + constantWasSet = trySetConstant(nodeIndex, child); else - forNode(nodeIndex).set(JSValue(JSC::toInt32(child.asDouble()))); - m_foundConstants = true; - node.setCanExit(false); - break; + constantWasSet = trySetConstant(nodeIndex, JSValue(JSC::toInt32(child.asDouble()))); + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); + break; + } } if (m_graph[node.child1()].shouldSpeculateInteger()) speculateInt32Unary(node); @@ -405,8 +413,8 @@ bool AbstractState::execute(unsigned indexInBlock) case Int32ToDouble: { JSValue child = forNode(node.child1()).value(); - if (child && child.isNumber()) { - forNode(nodeIndex).set(JSValue(JSValue::EncodeAsDouble, child.asNumber())); + if (child && child.isNumber() + && trySetConstant(nodeIndex, JSValue(JSValue::EncodeAsDouble, child.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -424,8 +432,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithAdd: { JSValue left = forNode(node.child1()).value(); JSValue right = forNode(node.child2()).value(); - if (left && right && left.isNumber() && right.isNumber()) { - forNode(nodeIndex).set(JSValue(left.asNumber() + right.asNumber())); + if (left && right && left.isNumber() && right.isNumber() + && trySetConstant(nodeIndex, JSValue(left.asNumber() + right.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -456,8 +464,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithSub: { JSValue left = forNode(node.child1()).value(); JSValue right = forNode(node.child2()).value(); - if (left && right && left.isNumber() && right.isNumber()) { - forNode(nodeIndex).set(JSValue(left.asNumber() - right.asNumber())); + if (left && right && left.isNumber() && right.isNumber() + && trySetConstant(nodeIndex, JSValue(left.asNumber() - right.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -475,8 +483,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithNegate: { JSValue child = forNode(node.child1()).value(); - if (child && child.isNumber()) { - forNode(nodeIndex).set(JSValue(-child.asNumber())); + if (child && child.isNumber() + && trySetConstant(nodeIndex, JSValue(-child.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -495,8 +503,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithMul: { JSValue left = forNode(node.child1()).value(); JSValue right = forNode(node.child2()).value(); - if (left && right && left.isNumber() && right.isNumber()) { - forNode(nodeIndex).set(JSValue(left.asNumber() * right.asNumber())); + if (left && right && left.isNumber() && right.isNumber() + && trySetConstant(nodeIndex, JSValue(left.asNumber() * right.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -523,26 +531,30 @@ bool AbstractState::execute(unsigned indexInBlock) if (left && right && left.isNumber() && right.isNumber()) { double a = left.asNumber(); double b = right.asNumber(); + bool constantWasSet; switch (node.op()) { case ArithDiv: - forNode(nodeIndex).set(JSValue(a / b)); + constantWasSet = trySetConstant(nodeIndex, JSValue(a / b)); break; case ArithMin: - forNode(nodeIndex).set(JSValue(a < b ? a : (b <= a ? b : a + b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(a < b ? a : (b <= a ? b : a + b))); break; case ArithMax: - forNode(nodeIndex).set(JSValue(a > b ? a : (b >= a ? b : a + b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(a > b ? a : (b >= a ? b : a + b))); break; case ArithMod: - forNode(nodeIndex).set(JSValue(fmod(a, b))); + constantWasSet = trySetConstant(nodeIndex, JSValue(fmod(a, b))); break; default: ASSERT_NOT_REACHED(); + constantWasSet = false; + break; + } + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); break; } - m_foundConstants = true; - node.setCanExit(false); - break; } if (Node::shouldSpeculateInteger( m_graph[node.child1()], m_graph[node.child2()]) @@ -558,8 +570,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithAbs: { JSValue child = forNode(node.child1()).value(); - if (child && child.isNumber()) { - forNode(nodeIndex).set(JSValue(fabs(child.asNumber()))); + if (child && child.isNumber() + && trySetConstant(nodeIndex, JSValue(fabs(child.asNumber())))) { m_foundConstants = true; node.setCanExit(false); break; @@ -577,8 +589,8 @@ bool AbstractState::execute(unsigned indexInBlock) case ArithSqrt: { JSValue child = forNode(node.child1()).value(); - if (child && child.isNumber()) { - forNode(nodeIndex).set(JSValue(sqrt(child.asNumber()))); + if (child && child.isNumber() + && trySetConstant(nodeIndex, JSValue(sqrt(child.asNumber())))) { m_foundConstants = true; node.setCanExit(false); break; @@ -590,8 +602,7 @@ bool AbstractState::execute(unsigned indexInBlock) case LogicalNot: { JSValue childConst = forNode(node.child1()).value(); - if (childConst) { - forNode(nodeIndex).set(jsBoolean(!childConst.toBoolean())); + if (childConst && trySetConstant(nodeIndex, jsBoolean(!childConst.toBoolean()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -626,27 +637,28 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(false); JSValue child = forNode(node.child1()).value(); if (child) { - bool foundConstant = true; + bool constantWasSet; switch (node.op()) { case IsUndefined: - forNode(nodeIndex).set(jsBoolean( + constantWasSet = trySetConstant(nodeIndex, jsBoolean( child.isCell() ? child.asCell()->structure()->typeInfo().masqueradesAsUndefined() : child.isUndefined())); break; case IsBoolean: - forNode(nodeIndex).set(jsBoolean(child.isBoolean())); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(child.isBoolean())); break; case IsNumber: - forNode(nodeIndex).set(jsBoolean(child.isNumber())); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(child.isNumber())); break; case IsString: - forNode(nodeIndex).set(jsBoolean(isJSString(child))); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(isJSString(child))); break; default: + constantWasSet = false; break; } - if (foundConstant) { + if (constantWasSet) { m_foundConstants = true; break; } @@ -665,29 +677,33 @@ bool AbstractState::execute(unsigned indexInBlock) if (leftConst && rightConst && leftConst.isNumber() && rightConst.isNumber()) { double a = leftConst.asNumber(); double b = rightConst.asNumber(); + bool constantWasSet; switch (node.op()) { case CompareLess: - forNode(nodeIndex).set(jsBoolean(a < b)); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(a < b)); break; case CompareLessEq: - forNode(nodeIndex).set(jsBoolean(a <= b)); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(a <= b)); break; case CompareGreater: - forNode(nodeIndex).set(jsBoolean(a > b)); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(a > b)); break; case CompareGreaterEq: - forNode(nodeIndex).set(jsBoolean(a >= b)); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(a >= b)); break; case CompareEq: - forNode(nodeIndex).set(jsBoolean(a == b)); + constantWasSet = trySetConstant(nodeIndex, jsBoolean(a == b)); break; default: ASSERT_NOT_REACHED(); + constantWasSet = false; + break; + } + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); break; } - m_foundConstants = true; - node.setCanExit(false); - break; } forNode(nodeIndex).set(SpecBoolean); @@ -767,8 +783,8 @@ bool AbstractState::execute(unsigned indexInBlock) case CompareStrictEq: { JSValue left = forNode(node.child1()).value(); JSValue right = forNode(node.child2()).value(); - if (left && right && left.isNumber() && right.isNumber()) { - forNode(nodeIndex).set(jsBoolean(left.asNumber() == right.asNumber())); + if (left && right && left.isNumber() && right.isNumber() + && trySetConstant(nodeIndex, jsBoolean(left.asNumber() == right.asNumber()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -1106,8 +1122,7 @@ bool AbstractState::execute(unsigned indexInBlock) case ToPrimitive: { JSValue childConst = forNode(node.child1()).value(); - if (childConst && childConst.isNumber()) { - forNode(nodeIndex).set(childConst); + if (childConst && childConst.isNumber() && trySetConstant(nodeIndex, childConst)) { m_foundConstants = true; node.setCanExit(false); break; diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index 9bb74cd86..95cadecbb 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h @@ -267,6 +267,23 @@ private: childValue2.filter(SpecNumber); } + bool trySetConstant(NodeIndex nodeIndex, JSValue value) + { + // Make sure we don't constant fold something that will produce values that contravene + // predictions. If that happens then we know that the code will OSR exit, forcing + // recompilation. But if we tried to constant fold then we'll have a very degenerate + // IR: namely we'll have a JSConstant that contravenes its own prediction. There's a + // lot of subtle code that assumes that + // speculationFromValue(jsConstant) == jsConstant.prediction(). "Hardening" that code + // is probably less sane than just pulling back on constant folding. + SpeculatedType oldType = m_graph[nodeIndex].prediction(); + if (mergeSpeculations(speculationFromValue(value), oldType) != oldType) + return false; + + forNode(nodeIndex).set(value); + return true; + } + CodeBlock* m_codeBlock; Graph& m_graph; diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index 28e686aef..9208cde1b 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -627,8 +627,9 @@ public: continue; // If this is a CreateArguments for an InlineCallFrame* that does // not create arguments, then replace it with a PhantomArguments. - // PhantomArguments is a constant that represents JSValue() (the - // empty value) in DFG and arguments creation for OSR exit. + // PhantomArguments is a non-executing node that just indicates + // that the node should be reified as an arguments object on OSR + // exit. if (m_createsArguments.contains(node.codeOrigin.inlineCallFrame)) continue; if (node.shouldGenerate()) { @@ -641,12 +642,30 @@ public: } node.setOpAndDefaultFlags(PhantomArguments); node.children.reset(); + changed = true; } insertionSet.execute(*block); } - if (changed) + if (changed) { m_graph.collectGarbage(); + + // Verify that PhantomArguments nodes are not shouldGenerate(). +#if !ASSERT_DISABLED + for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { + BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + if (!block) + continue; + for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { + NodeIndex nodeIndex = block->at(indexInBlock); + Node& node = m_graph[nodeIndex]; + if (node.op() != PhantomArguments) + continue; + ASSERT(!node.shouldGenerate()); + } + } +#endif + } return changed; } @@ -815,6 +834,7 @@ private: bool performArgumentsSimplification(Graph& graph) { + SamplingRegion samplingRegion("DFG Arguments Simplification Phase"); return runPhase<ArgumentsSimplificationPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h b/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h index f86c15e65..4bea292f3 100644 --- a/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGAssemblyHelpers.h @@ -182,7 +182,7 @@ public: move(TrustedImmPtr(scratchBuffer->activeLengthPtr()), GPRInfo::regT0); storePtr(TrustedImmPtr(scratchSize), GPRInfo::regT0); -#if CPU(X86_64) || CPU(ARM_THUMB2) +#if CPU(X86_64) || CPU(ARM) move(TrustedImmPtr(argument), GPRInfo::argumentGPR1); move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); GPRReg scratch = selectScratchGPR(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1); diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index cdb0b639a..91b882399 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -99,7 +99,7 @@ private: bool handleConstantInternalFunction(bool usesResult, int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, CodeSpecializationKind); void handleGetByOffset( int destinationOperand, SpeculatedType, NodeIndex base, unsigned identifierNumber, - bool useInlineStorage, size_t offset); + PropertyOffset); void handleGetById( int destinationOperand, SpeculatedType, NodeIndex base, unsigned identifierNumber, const GetByIdStatus&); @@ -871,7 +871,7 @@ private: // care about when the outcome of the division is not an integer, which // is what the special fast case counter tells us. - if (!m_inlineStackTop->m_profiledBlock->likelyToTakeSpecialFastCase(m_currentIndex) + if (!m_inlineStackTop->m_profiledBlock->couldTakeSpecialFastCase(m_currentIndex) && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow) && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) return nodeIndex; @@ -1273,7 +1273,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c unsigned depth = 0; for (InlineStackEntry* entry = m_inlineStackTop; entry; entry = entry->m_caller) { ++depth; - if (depth >= Options::maximumInliningDepth) + if (depth >= Options::maximumInliningDepth()) return false; // Depth exceeded. if (entry->executable() == executable) @@ -1630,25 +1630,20 @@ bool ByteCodeParser::handleConstantInternalFunction( void ByteCodeParser::handleGetByOffset( int destinationOperand, SpeculatedType prediction, NodeIndex base, unsigned identifierNumber, - bool useInlineStorage, size_t offset) + PropertyOffset offset) { NodeIndex propertyStorage; - size_t offsetOffset; - if (useInlineStorage) { + if (isInlineOffset(offset)) propertyStorage = base; - ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue))); - offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue); - } else { + else propertyStorage = addToGraph(GetPropertyStorage, base); - offsetOffset = 0; - } set(destinationOperand, addToGraph( GetByOffset, OpInfo(m_graph.m_storageAccessData.size()), OpInfo(prediction), propertyStorage)); StorageAccessData storageAccessData; - storageAccessData.offset = offset + offsetOffset; + storageAccessData.offset = indexRelativeToBase(offset); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); } @@ -1677,7 +1672,6 @@ void ByteCodeParser::handleGetById( addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(getByIdStatus.structureSet())), base); - bool useInlineStorage; if (!getByIdStatus.chain().isEmpty()) { Structure* currentStructure = getByIdStatus.structureSet().singletonStructure(); JSObject* currentObject = 0; @@ -1686,9 +1680,7 @@ void ByteCodeParser::handleGetById( currentStructure = getByIdStatus.chain()[i]; base = addStructureTransitionCheck(currentObject, currentStructure); } - useInlineStorage = currentStructure->isUsingInlineStorage(); - } else - useInlineStorage = getByIdStatus.structureSet().allAreUsingInlinePropertyStorage(); + } // Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to // ensure that the base of the original get_by_id is kept alive until we're done with @@ -1707,8 +1699,7 @@ void ByteCodeParser::handleGetById( } handleGetByOffset( - destinationOperand, prediction, base, identifierNumber, useInlineStorage, - getByIdStatus.offset()); + destinationOperand, prediction, base, identifierNumber, getByIdStatus.offset()); } void ByteCodeParser::prepareToParseBlock() @@ -2172,7 +2163,8 @@ bool ByteCodeParser::parseBlock(unsigned limit) SpeculatedType prediction = getPrediction(); - ASSERT(interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id); + ASSERT(interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id + || interpreter->getOpcodeID(getInstruction->u.opcode) == op_get_by_id_out_of_line); NodeIndex base = get(getInstruction[2].u.operand); unsigned identifier = m_inlineStackTop->m_identifierRemap[getInstruction[3].u.operand]; @@ -2225,7 +2217,8 @@ bool ByteCodeParser::parseBlock(unsigned limit) addToGraph(PutScopedVar, OpInfo(slot), getScopeChain, get(source)); NEXT_OPCODE(op_put_scoped_var); } - case op_get_by_id: { + case op_get_by_id: + case op_get_by_id_out_of_line: { SpeculatedType prediction = getPredictionWithoutOSRExit(); NodeIndex base = get(currentInstruction[2].u.operand); @@ -2241,8 +2234,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_get_by_id); } case op_put_by_id: + case op_put_by_id_out_of_line: case op_put_by_id_transition_direct: - case op_put_by_id_transition_normal: { + case op_put_by_id_transition_normal: + case op_put_by_id_transition_direct_out_of_line: + case op_put_by_id_transition_normal_out_of_line: { NodeIndex value = get(currentInstruction[3].u.operand); NodeIndex base = get(currentInstruction[1].u.operand); unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[2].u.operand]; @@ -2259,25 +2255,20 @@ bool ByteCodeParser::parseBlock(unsigned limit) if (!hasExitSite && putByIdStatus.isSimpleReplace()) { addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(putByIdStatus.oldStructure())), base); - size_t offsetOffset; NodeIndex propertyStorage; - if (putByIdStatus.oldStructure()->isUsingInlineStorage()) { + if (isInlineOffset(putByIdStatus.offset())) propertyStorage = base; - ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue))); - offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue); - } else { + else propertyStorage = addToGraph(GetPropertyStorage, base); - offsetOffset = 0; - } addToGraph(PutByOffset, OpInfo(m_graph.m_storageAccessData.size()), propertyStorage, base, value); StorageAccessData storageAccessData; - storageAccessData.offset = putByIdStatus.offset() + offsetOffset; + storageAccessData.offset = indexRelativeToBase(putByIdStatus.offset()); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); } else if (!hasExitSite && putByIdStatus.isSimpleTransition() - && putByIdStatus.oldStructure()->propertyStorageCapacity() == putByIdStatus.newStructure()->propertyStorageCapacity() + && putByIdStatus.oldStructure()->outOfLineCapacity() == putByIdStatus.newStructure()->outOfLineCapacity() && structureChainIsStillValid( direct, putByIdStatus.oldStructure(), @@ -2308,16 +2299,11 @@ bool ByteCodeParser::parseBlock(unsigned limit) putByIdStatus.newStructure()))), base); - size_t offsetOffset; NodeIndex propertyStorage; - if (putByIdStatus.newStructure()->isUsingInlineStorage()) { + if (isInlineOffset(putByIdStatus.offset())) propertyStorage = base; - ASSERT(!(sizeof(JSObject) % sizeof(EncodedJSValue))); - offsetOffset = sizeof(JSObject) / sizeof(EncodedJSValue); - } else { + else propertyStorage = addToGraph(GetPropertyStorage, base); - offsetOffset = 0; - } addToGraph( PutByOffset, OpInfo(m_graph.m_storageAccessData.size()), @@ -2326,7 +2312,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) value); StorageAccessData storageAccessData; - storageAccessData.offset = putByIdStatus.offset() + offsetOffset; + storageAccessData.offset = indexRelativeToBase(putByIdStatus.offset()); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); } else { @@ -2738,8 +2724,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) } else { handleGetByOffset( currentInstruction[1].u.operand, prediction, globalObject, - identifierNumber, status.structure()->isUsingInlineStorage(), - status.offset()); + identifierNumber, status.offset()); } m_globalResolveNumber++; // Skip over the unused global resolve info. @@ -3341,6 +3326,7 @@ bool ByteCodeParser::parse() bool parse(ExecState* exec, Graph& graph) { + SamplingRegion samplingRegion("DFG Parsing"); #if DFG_DEBUG_LOCAL_DISBALE UNUSED_PARAM(exec); UNUSED_PARAM(graph); diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h index b60290870..9c1718bdb 100644 --- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h @@ -434,7 +434,7 @@ public: { setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2); } -#else +#elif CPU(ARM) ALWAYS_INLINE void setupArguments(FPRReg arg1) { assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); @@ -445,6 +445,8 @@ public: assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg2); } +#else +#error "DFG JIT not supported on this platform." #endif ALWAYS_INLINE void setupArguments(GPRReg arg1) diff --git a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp index c6042448a..c52349645 100644 --- a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp @@ -132,6 +132,7 @@ private: bool performCFA(Graph& graph) { + SamplingRegion samplingRegion("DFG CFA Phase"); return runPhase<CFAPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp index 161f51e30..c234e6e4e 100644 --- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp @@ -613,7 +613,7 @@ private: ASSERT(node.shouldGenerate()); Node& possibleLocalOp = m_graph[node.child1()]; - if (possibleLocalOp.hasLocal()) { + if (possibleLocalOp.hasLocal() && !possibleLocalOp.variableAccessData()->isCaptured()) { NodeIndex setLocalIndex = firstBlock->variablesAtTail.operand(possibleLocalOp.local()); Node& setLocal = m_graph[setLocalIndex]; @@ -745,6 +745,7 @@ private: bool performCFGSimplification(Graph& graph) { + SamplingRegion samplingRegion("DFG CFG Simplification Phase"); return runPhase<CFGSimplificationPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index be0012f56..108cf1965 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -1172,6 +1172,7 @@ private: bool performCSE(Graph& graph, OptimizationFixpointState fixpointState) { + SamplingRegion samplingRegion("DFG CSE Phase"); return runPhase<CSEPhase>(graph, fixpointState); } diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h index 1aec0bca1..2bc9b2965 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.h +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h @@ -41,29 +41,29 @@ namespace JSC { namespace DFG { // check opcodes. inline bool mightCompileEval(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount(); } inline bool mightCompileProgram(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount(); } inline bool mightCompileFunctionForCall(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount(); } inline bool mightCompileFunctionForConstruct(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount; + return codeBlock->instructionCount() <= Options::maximumOptimizationCandidateInstructionCount(); } inline bool mightInlineFunctionForCall(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumFunctionForCallInlineCandidateInstructionCount + return codeBlock->instructionCount() <= Options::maximumFunctionForCallInlineCandidateInstructionCount() && !codeBlock->ownerExecutable()->needsActivation(); } inline bool mightInlineFunctionForConstruct(CodeBlock* codeBlock) { - return codeBlock->instructionCount() <= Options::maximumFunctionForConstructInlineCandidateInstructionCount + return codeBlock->instructionCount() <= Options::maximumFunctionForConstructInlineCandidateInstructionCount() && !codeBlock->ownerExecutable()->needsActivation(); } @@ -119,9 +119,13 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_get_scoped_var: case op_put_scoped_var: case op_get_by_id: + case op_get_by_id_out_of_line: case op_put_by_id: + case op_put_by_id_out_of_line: case op_put_by_id_transition_direct: + case op_put_by_id_transition_direct_out_of_line: case op_put_by_id_transition_normal: + case op_put_by_id_transition_normal_out_of_line: case op_get_global_var: case op_get_global_var_watchable: case op_put_global_var: diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h index c9d3cbc32..1a64a248c 100644 --- a/Source/JavaScriptCore/dfg/DFGCommon.h +++ b/Source/JavaScriptCore/dfg/DFGCommon.h @@ -136,7 +136,7 @@ enum OptimizationFixpointState { FixpointConverged, FixpointNotConverged }; inline bool shouldShowDisassembly() { - return Options::showDisassembly || Options::showDFGDisassembly; + return Options::showDisassembly() || Options::showDFGDisassembly(); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index 9e6720c80..d3029b39a 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -159,6 +159,7 @@ public: bool performConstantFolding(Graph& graph) { + SamplingRegion samplingRegion("DFG Constant Folding Phase"); return runPhase<ConstantFoldingPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp index 1dde37cf2..cfbb936b8 100644 --- a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp +++ b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp @@ -43,7 +43,7 @@ void Disassembler::dump(LinkBuffer& linkBuffer) { m_graph.m_dominators.computeIfNecessary(m_graph); - dataLog("Generated JIT code for DFG CodeBlock %p:\n", m_graph.m_codeBlock); + dataLog("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount()); dataLog(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize()); const char* prefix = " "; @@ -59,7 +59,7 @@ void Disassembler::dump(LinkBuffer& linkBuffer) m_graph.dumpBlockHeader(prefix, blockIndex, Graph::DumpLivePhisOnly); NodeIndex lastNodeIndexForDisassembly = block->at(0); for (size_t i = 0; i < block->size(); ++i) { - if (!m_graph[block->at(i)].willHaveCodeGen()) + if (!m_graph[block->at(i)].willHaveCodeGenOrOSR()) continue; MacroAssembler::Label currentLabel; if (m_labelForNodeIndex[block->at(i)].isSet()) diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index 5033aa2c0..64fc0c7e5 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -40,6 +40,7 @@ #include "DFGRedundantPhiEliminationPhase.h" #include "DFGValidate.h" #include "DFGVirtualRegisterAllocationPhase.h" +#include "Options.h" namespace JSC { namespace DFG { @@ -60,7 +61,10 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo ASSERT(codeBlock); ASSERT(codeBlock->alternative()); ASSERT(codeBlock->alternative()->getJITType() == JITCode::BaselineJIT); - + + if (!Options::useDFGJIT()) + return false; + #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount()); #endif diff --git a/Source/JavaScriptCore/dfg/DFGFPRInfo.h b/Source/JavaScriptCore/dfg/DFGFPRInfo.h index 6af45dd81..e817ed396 100644 --- a/Source/JavaScriptCore/dfg/DFGFPRInfo.h +++ b/Source/JavaScriptCore/dfg/DFGFPRInfo.h @@ -102,7 +102,7 @@ public: #endif -#if CPU(ARM_THUMB2) +#if CPU(ARM) class FPRInfo { public: diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index f6e3c0a96..2e7389f21 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -402,6 +402,7 @@ private: bool performFixup(Graph& graph) { + SamplingRegion samplingRegion("DFG Fixup Phase"); return runPhase<FixupPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGGPRInfo.h b/Source/JavaScriptCore/dfg/DFGGPRInfo.h index bd4fa32d1..89faef94b 100644 --- a/Source/JavaScriptCore/dfg/DFGGPRInfo.h +++ b/Source/JavaScriptCore/dfg/DFGGPRInfo.h @@ -384,7 +384,7 @@ private: #endif -#if CPU(ARM_THUMB2) +#if CPU(ARM) #define NUMBER_OF_ARGUMENT_REGISTERS 4 class GPRInfo { @@ -410,7 +410,7 @@ public: static const GPRReg argumentGPR1 = ARMRegisters::r1; // regT1 static const GPRReg argumentGPR2 = ARMRegisters::r2; // regT2 // FIXME: r3 is currently used be the MacroAssembler as a temporary - it seems - // This could threoretically be a problem if theis is used in code generation + // This could threoretically be a problem if this is used in code generation // between the arguments being set up, and the call being made. That said, // any change introducing a problem here is likely to be immediately apparent! static const GPRReg argumentGPR3 = ARMRegisters::r3; // FIXME! diff --git a/Source/JavaScriptCore/dfg/DFGGenerationInfo.h b/Source/JavaScriptCore/dfg/DFGGenerationInfo.h index 125a5a4f9..905c5c5fb 100644 --- a/Source/JavaScriptCore/dfg/DFGGenerationInfo.h +++ b/Source/JavaScriptCore/dfg/DFGGenerationInfo.h @@ -29,8 +29,10 @@ #if ENABLE(DFG_JIT) +#include "DFGJITCompiler.h" +#include "DFGVariableEvent.h" +#include "DFGVariableEventStream.h" #include "DataFormat.h" -#include <dfg/DFGJITCompiler.h> namespace JSC { namespace DFG { @@ -51,6 +53,7 @@ public: , m_registerFormat(DataFormatNone) , m_spillFormat(DataFormatNone) , m_canFill(false) + , m_bornForOSR(false) { } @@ -61,6 +64,7 @@ public: m_registerFormat = DataFormatNone; m_spillFormat = DataFormatNone; m_canFill = true; + m_bornForOSR = false; ASSERT(m_useCount); } void initInteger(NodeIndex nodeIndex, uint32_t useCount, GPRReg gpr) @@ -71,6 +75,7 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; + m_bornForOSR = false; ASSERT(m_useCount); } #if USE(JSVALUE64) @@ -84,6 +89,7 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; + m_bornForOSR = false; ASSERT(m_useCount); } #elif USE(JSVALUE32_64) @@ -98,6 +104,7 @@ public: m_canFill = false; u.v.tagGPR = tagGPR; u.v.payloadGPR = payloadGPR; + m_bornForOSR = false; ASSERT(m_useCount); } #endif @@ -109,6 +116,7 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; + m_bornForOSR = false; ASSERT(m_useCount); } void initBoolean(NodeIndex nodeIndex, uint32_t useCount, GPRReg gpr) @@ -119,6 +127,7 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; + m_bornForOSR = false; ASSERT(m_useCount); } void initDouble(NodeIndex nodeIndex, uint32_t useCount, FPRReg fpr) @@ -130,6 +139,7 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.fpr = fpr; + m_bornForOSR = false; ASSERT(m_useCount); } void initStorage(NodeIndex nodeIndex, uint32_t useCount, GPRReg gpr) @@ -140,19 +150,44 @@ public: m_spillFormat = DataFormatNone; m_canFill = false; u.gpr = gpr; + m_bornForOSR = false; ASSERT(m_useCount); } // Get the index of the node that produced this value. NodeIndex nodeIndex() { return m_nodeIndex; } + + void noticeOSRBirth(VariableEventStream& stream, NodeIndex nodeIndex, VirtualRegister virtualRegister) + { + if (m_nodeIndex != nodeIndex) + return; + if (!alive()) + return; + if (m_bornForOSR) + return; + + m_bornForOSR = true; + + if (m_registerFormat != DataFormatNone) + appendFill(BirthToFill, stream); + else if (m_spillFormat != DataFormatNone) + appendSpill(BirthToSpill, stream, virtualRegister); + } // Mark the value as having been used (decrement the useCount). // Returns true if this was the last use of the value, and any // associated machine registers may be freed. - bool use() + bool use(VariableEventStream& stream) { ASSERT(m_useCount); - return !--m_useCount; + bool result = !--m_useCount; + + if (result && m_bornForOSR) { + ASSERT(m_nodeIndex != NoNode); + stream.appendAndLog(VariableEvent::death(m_nodeIndex)); + } + + return result; } // Used to check the operands of operations to see if they are on @@ -225,7 +260,7 @@ public: } // Called when a VirtualRegister is being spilled to the RegisterFile for the first time. - void spill(DataFormat spillFormat) + void spill(VariableEventStream& stream, VirtualRegister virtualRegister, DataFormat spillFormat) { // We shouldn't be spill values that don't need spilling. ASSERT(!m_canFill); @@ -236,15 +271,21 @@ public: m_registerFormat = DataFormatNone; m_spillFormat = spillFormat; m_canFill = true; + + if (m_bornForOSR) + appendSpill(Spill, stream, virtualRegister); } // Called on values that don't need spilling (constants and values that have // already been spilled), to mark them as no longer being in machine registers. - void setSpilled() + void setSpilled(VariableEventStream& stream, VirtualRegister virtualRegister) { // Should only be called on values that don't need spilling, and are currently in registers. ASSERT(m_canFill && m_registerFormat != DataFormatNone); m_registerFormat = DataFormatNone; + + if (m_bornForOSR) + appendSpill(Spill, stream, virtualRegister); } void killSpilled() @@ -256,46 +297,67 @@ public: // Record that this value is filled into machine registers, // tracking which registers, and what format the value has. #if USE(JSVALUE64) - void fillJSValue(GPRReg gpr, DataFormat format = DataFormatJS) + void fillJSValue(VariableEventStream& stream, GPRReg gpr, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); m_registerFormat = format; u.gpr = gpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } #elif USE(JSVALUE32_64) - void fillJSValue(GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) + void fillJSValue(VariableEventStream& stream, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) { ASSERT(format & DataFormatJS); m_registerFormat = format; u.v.tagGPR = tagGPR; // FIXME: for JSValues with known type (boolean, integer, cell etc.) no tagGPR is needed? u.v.payloadGPR = payloadGPR; + + if (m_bornForOSR) + appendFill(Fill, stream); } - void fillCell(GPRReg gpr) + void fillCell(VariableEventStream& stream, GPRReg gpr) { m_registerFormat = DataFormatCell; u.gpr = gpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } #endif - void fillInteger(GPRReg gpr) + void fillInteger(VariableEventStream& stream, GPRReg gpr) { m_registerFormat = DataFormatInteger; u.gpr = gpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } - void fillBoolean(GPRReg gpr) + void fillBoolean(VariableEventStream& stream, GPRReg gpr) { m_registerFormat = DataFormatBoolean; u.gpr = gpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } - void fillDouble(FPRReg fpr) + void fillDouble(VariableEventStream& stream, FPRReg fpr) { ASSERT(fpr != InvalidFPRReg); m_registerFormat = DataFormatDouble; u.fpr = fpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } - void fillStorage(GPRReg gpr) + void fillStorage(VariableEventStream& stream, GPRReg gpr) { m_registerFormat = DataFormatStorage; u.gpr = gpr; + + if (m_bornForOSR) + appendFill(Fill, stream); } bool alive() @@ -304,12 +366,33 @@ public: } private: + void appendFill(VariableEventKind kind, VariableEventStream& stream) + { + if (m_registerFormat == DataFormatDouble) { + stream.appendAndLog(VariableEvent::fillFPR(kind, m_nodeIndex, u.fpr)); + return; + } +#if USE(JSVALUE32_64) + if (m_registerFormat & DataFormatJS) { + stream.appendAndLog(VariableEvent::fillPair(kind, m_nodeIndex, u.v.tagGPR, u.v.payloadGPR)); + return; + } +#endif + stream.appendAndLog(VariableEvent::fillGPR(kind, m_nodeIndex, u.gpr, m_registerFormat)); + } + + void appendSpill(VariableEventKind kind, VariableEventStream& stream, VirtualRegister virtualRegister) + { + stream.appendAndLog(VariableEvent::spill(kind, m_nodeIndex, virtualRegister, m_spillFormat)); + } + // The index of the node whose result is stored in this virtual register. NodeIndex m_nodeIndex; uint32_t m_useCount; DataFormat m_registerFormat; DataFormat m_spillFormat; bool m_canFill; + bool m_bornForOSR; union { GPRReg gpr; FPRReg fpr; diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index 4689470c8..c7a4d94d2 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -327,14 +327,11 @@ void Graph::dumpBlockHeader(const char* prefix, BlockIndex blockIndex, PhiNodeDu dataLog("\n"); } dataLog("%s Phi Nodes:", prefix); - unsigned count = 0; for (size_t i = 0; i < block->phis.size(); ++i) { NodeIndex phiNodeIndex = block->phis[i]; Node& phiNode = at(phiNodeIndex); if (!phiNode.shouldGenerate() && phiNodeDumpMode == DumpLivePhisOnly) continue; - if (!((++count) % 4)) - dataLog("\n%s ", prefix); dataLog(" @%u->(", phiNodeIndex); if (phiNode.child1()) { dataLog("@%u", phiNode.child1().index()); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp index 3c85cc77c..497fc346f 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp @@ -88,8 +88,6 @@ void JITCompiler::compileBody(SpeculativeJIT& speculative) breakpoint(); #endif - addPtr(TrustedImm32(1), AbsoluteAddress(codeBlock()->addressOfSpeculativeSuccessCounter())); - bool compiledSpeculative = speculative.compile(); ASSERT_UNUSED(compiledSpeculative, compiledSpeculative); } @@ -174,6 +172,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer) #endif info.patch.dfg.deltaCallToSlowCase = differenceBetweenCodePtr(callReturnLocation, linkBuffer.locationOf(m_propertyAccesses[i].m_slowPathGenerator->label())); info.patch.dfg.deltaCallToDone = differenceBetweenCodePtr(callReturnLocation, linkBuffer.locationOf(m_propertyAccesses[i].m_done)); + info.patch.dfg.deltaCallToStorageLoad = differenceBetweenCodePtr(callReturnLocation, linkBuffer.locationOf(m_propertyAccesses[i].m_propertyStorageLoad)); info.patch.dfg.baseGPR = m_propertyAccesses[i].m_baseGPR; #if USE(JSVALUE64) info.patch.dfg.valueGPR = m_propertyAccesses[i].m_valueGPR; @@ -205,11 +204,14 @@ void JITCompiler::link(LinkBuffer& linkBuffer) codeBlock()->watchpoint(exit.m_watchpointIndex).correctLabels(linkBuffer); } + codeBlock()->minifiedDFG().setOriginalGraphSize(m_graph.size()); codeBlock()->shrinkToFit(CodeBlock::LateShrink); } bool JITCompiler::compile(JITCode& entry) { + SamplingRegion samplingRegion("DFG Backend"); + setStartOfCode(); compileEntry(); SpeculativeJIT speculative(*this); @@ -243,6 +245,8 @@ bool JITCompiler::compile(JITCode& entry) bool JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck) { + SamplingRegion samplingRegion("DFG Backend"); + setStartOfCode(); compileEntry(); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index ed16459cc..24dbbdcd0 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -136,6 +136,7 @@ struct PropertyAccessRecord { CodeOrigin codeOrigin, MacroAssembler::DataLabelPtr structureImm, MacroAssembler::PatchableJump structureCheck, + MacroAssembler::ConvertibleLoadLabel propertyStorageLoad, MacroAssembler::DataLabelCompact loadOrStore, SlowPathGenerator* slowPathGenerator, MacroAssembler::Label done, @@ -148,6 +149,7 @@ struct PropertyAccessRecord { CodeOrigin codeOrigin, MacroAssembler::DataLabelPtr structureImm, MacroAssembler::PatchableJump structureCheck, + MacroAssembler::ConvertibleLoadLabel propertyStorageLoad, MacroAssembler::DataLabelCompact tagLoadOrStore, MacroAssembler::DataLabelCompact payloadLoadOrStore, SlowPathGenerator* slowPathGenerator, @@ -161,6 +163,7 @@ struct PropertyAccessRecord { : m_codeOrigin(codeOrigin) , m_structureImm(structureImm) , m_structureCheck(structureCheck) + , m_propertyStorageLoad(propertyStorageLoad) #if USE(JSVALUE64) , m_loadOrStore(loadOrStore) #elif USE(JSVALUE32_64) @@ -182,6 +185,7 @@ struct PropertyAccessRecord { CodeOrigin m_codeOrigin; MacroAssembler::DataLabelPtr m_structureImm; MacroAssembler::PatchableJump m_structureCheck; + MacroAssembler::ConvertibleLoadLabel m_propertyStorageLoad; #if USE(JSVALUE64) MacroAssembler::DataLabelCompact m_loadOrStore; #elif USE(JSVALUE32_64) diff --git a/Source/JavaScriptCore/dfg/DFGMinifiedGraph.h b/Source/JavaScriptCore/dfg/DFGMinifiedGraph.h new file mode 100644 index 000000000..b38ef07ed --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGMinifiedGraph.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGMinifiedGraph_h +#define DFGMinifiedGraph_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGMinifiedNode.h" +#include <algorithm> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +namespace JSC { namespace DFG { + +class MinifiedGraph { +public: + MinifiedGraph() { } + + MinifiedNode* at(NodeIndex nodeIndex) + { + if (!m_list.size()) + return 0; + MinifiedNode* entry = + binarySearch<MinifiedNode, NodeIndex, MinifiedNode::getIndex>( + m_list.begin(), m_list.size(), nodeIndex, WTF::KeyMustNotBePresentInArray); + if (entry->index() != nodeIndex) + return 0; + return entry; + } + + void append(const MinifiedNode& node) + { + m_list.append(node); + } + + void prepareAndShrink() + { + std::sort(m_list.begin(), m_list.end(), MinifiedNode::compareByNodeIndex); + m_list.shrinkToFit(); + } + + void setOriginalGraphSize(size_t size) { m_size = size; } + + size_t originalGraphSize() const { return m_size; } + +private: + Vector<MinifiedNode> m_list; + size_t m_size; +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGMinifiedGraph_h + diff --git a/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp b/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp new file mode 100644 index 000000000..6362344fb --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DFGMinifiedNode.h" + +#if ENABLE(DFG_JIT) + +#include "DFGNode.h" + +namespace JSC { namespace DFG { + +MinifiedNode MinifiedNode::fromNode(NodeIndex nodeIndex, Node& node) +{ + ASSERT(belongsInMinifiedGraph(node.op())); + MinifiedNode result; + result.m_index = nodeIndex; + result.m_op = node.op(); + if (hasChild(node.op())) + result.m_childOrInfo = node.child1().index(); + else if (hasConstantNumber(node.op())) + result.m_childOrInfo = node.constantNumber(); + else if (hasWeakConstant(node.op())) + result.m_childOrInfo = bitwise_cast<uintptr_t>(node.weakConstant()); + else { + ASSERT(node.op() == PhantomArguments); + result.m_childOrInfo = 0; + } + return result; +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + diff --git a/Source/JavaScriptCore/dfg/DFGMinifiedNode.h b/Source/JavaScriptCore/dfg/DFGMinifiedNode.h new file mode 100644 index 000000000..b80cbd777 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGMinifiedNode.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGMinifiedNode_h +#define DFGMinifiedNode_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGCommon.h" +#include "DFGNodeType.h" + +namespace JSC { namespace DFG { + +struct Node; + +inline bool belongsInMinifiedGraph(NodeType type) +{ + switch (type) { + case JSConstant: + case WeakJSConstant: + case ValueToInt32: + case Int32ToDouble: + case UInt32ToNumber: + case DoubleAsInt32: + case PhantomArguments: + return true; + default: + return false; + } +} + +class MinifiedNode { +public: + MinifiedNode() { } + + static MinifiedNode fromNode(NodeIndex, Node&); + + NodeIndex index() const { return m_index; } + NodeType op() const { return m_op; } + + bool hasChild1() const { return hasChild(m_op); } + + NodeIndex child1() const + { + ASSERT(hasChild(m_op)); + return m_childOrInfo; + } + + bool hasConstant() const { return hasConstantNumber() || hasWeakConstant(); } + + bool hasConstantNumber() const { return hasConstantNumber(m_op); } + + unsigned constantNumber() const + { + ASSERT(hasConstantNumber(m_op)); + return m_childOrInfo; + } + + bool hasWeakConstant() const { return hasWeakConstant(m_op); } + + JSCell* weakConstant() const + { + ASSERT(hasWeakConstant(m_op)); + return bitwise_cast<JSCell*>(m_childOrInfo); + } + + static NodeIndex getIndex(MinifiedNode* node) { return node->index(); } + static bool compareByNodeIndex(const MinifiedNode& a, const MinifiedNode& b) + { + return a.m_index < b.m_index; + } + +private: + static bool hasChild(NodeType type) + { + switch (type) { + case ValueToInt32: + case Int32ToDouble: + case UInt32ToNumber: + case DoubleAsInt32: + return true; + default: + return false; + } + } + static bool hasConstantNumber(NodeType type) + { + return type == JSConstant; + } + static bool hasWeakConstant(NodeType type) + { + return type == WeakJSConstant; + } + + NodeIndex m_index; + NodeType m_op; + uintptr_t m_childOrInfo; // Nodes in the minified graph have only one child each. +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGMinifiedNode_h + diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index 40701c3bd..ae07d5512 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -40,6 +40,7 @@ #include "JSValue.h" #include "Operands.h" #include "SpeculatedType.h" +#include "StructureSet.h" #include "ValueProfile.h" namespace JSC { namespace DFG { @@ -707,7 +708,7 @@ struct Node { ASSERT(m_virtualRegister != InvalidVirtualRegister); return m_virtualRegister; } - + void setVirtualRegister(VirtualRegister virtualRegister) { ASSERT(hasResult()); @@ -731,9 +732,21 @@ struct Node { return m_refCount; } - bool willHaveCodeGen() + bool willHaveCodeGenOrOSR() { - return shouldGenerate() && op() != Phantom && op() != Nop; + switch (op()) { + case SetLocal: + case Int32ToDouble: + case ValueToInt32: + case UInt32ToNumber: + case DoubleAsInt32: + return true; + case Phantom: + case Nop: + return false; + default: + return shouldGenerate(); + } } unsigned refCount() diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp index d0e0de9da..e9b02b2e3 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp @@ -33,17 +33,7 @@ namespace JSC { namespace DFG { -static unsigned computeNumVariablesForCodeOrigin( - CodeBlock* codeBlock, const CodeOrigin& codeOrigin) -{ - if (!codeOrigin.inlineCallFrame) - return codeBlock->m_numCalleeRegisters; - return - codeOrigin.inlineCallFrame->stackOffset + - baselineCodeBlockForInlineCallFrame(codeOrigin.inlineCallFrame)->m_numCalleeRegisters; -} - -OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAValueProfile valueProfile, MacroAssembler::Jump check, SpeculativeJIT* jit, unsigned recoveryIndex) +OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAValueProfile valueProfile, MacroAssembler::Jump check, SpeculativeJIT* jit, unsigned streamIndex, unsigned recoveryIndex) : m_jsValueSource(jsValueSource) , m_valueProfile(valueProfile) , m_check(check) @@ -54,29 +44,15 @@ OSRExit::OSRExit(ExitKind kind, JSValueSource jsValueSource, MethodOfGettingAVal , m_watchpointIndex(std::numeric_limits<unsigned>::max()) , m_kind(kind) , m_count(0) - , m_arguments(jit->m_arguments.size()) - , m_variables(computeNumVariablesForCodeOrigin(jit->m_jit.graph().m_profiledBlock, jit->m_codeOriginForOSR)) + , m_streamIndex(streamIndex) , m_lastSetOperand(jit->m_lastSetOperand) { ASSERT(m_codeOrigin.isSet()); - for (unsigned argument = 0; argument < m_arguments.size(); ++argument) - m_arguments[argument] = jit->computeValueRecoveryFor(jit->m_arguments[argument]); - for (unsigned variable = 0; variable < m_variables.size(); ++variable) - m_variables[variable] = jit->computeValueRecoveryFor(jit->m_variables[variable]); -} - -void OSRExit::dump(FILE* out) const -{ - for (unsigned argument = 0; argument < m_arguments.size(); ++argument) - m_arguments[argument].dump(out); - fprintf(out, " : "); - for (unsigned variable = 0; variable < m_variables.size(); ++variable) - m_variables[variable].dump(out); } bool OSRExit::considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, CodeBlock* profiledCodeBlock) { - if (static_cast<double>(m_count) / dfgCodeBlock->speculativeFailCounter() <= Options::osrExitProminenceForFrequentExitSite) + if (static_cast<double>(m_count) / dfgCodeBlock->osrExitCounter() <= Options::osrExitProminenceForFrequentExitSite()) return false; FrequentExitSite exitSite; diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.h b/Source/JavaScriptCore/dfg/DFGOSRExit.h index 683f260f1..cd2434c11 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExit.h +++ b/Source/JavaScriptCore/dfg/DFGOSRExit.h @@ -35,6 +35,7 @@ #include "DFGCorrectableJumpPoint.h" #include "DFGExitProfile.h" #include "DFGGPRInfo.h" +#include "DFGValueRecoveryOverride.h" #include "MacroAssembler.h" #include "MethodOfGettingAValueProfile.h" #include "Operands.h" @@ -83,7 +84,7 @@ private: // This structure describes how to exit the speculative path by // going into baseline code. struct OSRExit { - OSRExit(ExitKind, JSValueSource, MethodOfGettingAValueProfile, MacroAssembler::Jump, SpeculativeJIT*, unsigned recoveryIndex = 0); + OSRExit(ExitKind, JSValueSource, MethodOfGettingAValueProfile, MacroAssembler::Jump, SpeculativeJIT*, unsigned streamIndex, unsigned recoveryIndex = 0); MacroAssemblerCodeRef m_code; @@ -101,38 +102,6 @@ struct OSRExit { ExitKind m_kind; uint32_t m_count; - // Convenient way of iterating over ValueRecoveries while being - // generic over argument versus variable. - int numberOfRecoveries() const { return m_arguments.size() + m_variables.size(); } - const ValueRecovery& valueRecovery(int index) const - { - if (index < (int)m_arguments.size()) - return m_arguments[index]; - return m_variables[index - m_arguments.size()]; - } - ValueRecovery& valueRecoveryForOperand(int operand) - { - if (operandIsArgument(operand)) - return m_arguments[operandToArgument(operand)]; - return m_variables[operand]; - } - bool isArgument(int index) const { return index < (int)m_arguments.size(); } - bool isVariable(int index) const { return !isArgument(index); } - int argumentForIndex(int index) const - { - return index; - } - int variableForIndex(int index) const - { - return index - m_arguments.size(); - } - int operandForIndex(int index) const - { - if (index < (int)m_arguments.size()) - return operandToArgument(index); - return index - m_arguments.size(); - } - bool considerAddingAsFrequentExitSite(CodeBlock* dfgCodeBlock, CodeBlock* profiledCodeBlock) { if (!m_count || !exitKindIsCountable(m_kind)) @@ -140,11 +109,10 @@ struct OSRExit { return considerAddingAsFrequentExitSiteSlow(dfgCodeBlock, profiledCodeBlock); } - void dump(FILE* out) const; - - Vector<ValueRecovery, 0> m_arguments; - Vector<ValueRecovery, 0> m_variables; + unsigned m_streamIndex; int m_lastSetOperand; + + RefPtr<ValueRecoveryOverride> m_valueRecoveryOverride; private: bool considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, CodeBlock* profiledCodeBlock); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp index e617b5479..2ce1c887b 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp @@ -29,6 +29,7 @@ #if ENABLE(DFG_JIT) #include "CallFrame.h" +#include "DFGCommon.h" #include "LinkBuffer.h" #include "RepatchBuffer.h" @@ -38,6 +39,8 @@ extern "C" { void compileOSRExit(ExecState* exec) { + SamplingRegion samplingRegion("DFG OSR Exit Compilation"); + CodeBlock* codeBlock = exec->codeBlock(); ASSERT(codeBlock); @@ -63,12 +66,22 @@ void compileOSRExit(ExecState* exec) ->jitCompile(exec); } + // Compute the value recoveries. + Operands<ValueRecovery> operands; + codeBlock->variableEventStream().reconstruct(codeBlock, exit.m_codeOrigin, codeBlock->minifiedDFG(), exit.m_streamIndex, operands); + + // There may be an override, for forward speculations. + if (!!exit.m_valueRecoveryOverride) { + operands.setOperand( + exit.m_valueRecoveryOverride->operand, exit.m_valueRecoveryOverride->recovery); + } + SpeculationRecovery* recovery = 0; if (exit.m_recoveryIndex) recovery = &codeBlock->speculationRecovery(exit.m_recoveryIndex - 1); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Generating OSR exit #%u (bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock); + dataLog("Generating OSR exit #%u (seq#%u, bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_streamIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock); #endif { @@ -76,10 +89,11 @@ void compileOSRExit(ExecState* exec) OSRExitCompiler exitCompiler(jit); jit.jitAssertHasValidCallFrame(); - exitCompiler.compileExit(exit, recovery); + exitCompiler.compileExit(exit, operands, recovery); LinkBuffer patchBuffer(*globalData, &jit, codeBlock); - exit.m_code = FINALIZE_CODE( + exit.m_code = FINALIZE_CODE_IF( + shouldShowDisassembly(), patchBuffer, ("DFG OSR exit #%u (bc#%u, @%u, %s) from CodeBlock %p", exitIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, @@ -102,42 +116,14 @@ void OSRExitCompiler::handleExitCounts(const OSRExit& exit) m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.codeBlock()), GPRInfo::regT0); - AssemblyHelpers::JumpList tooFewFails; + AssemblyHelpers::Jump tooFewFails; - if (exit.m_kind == InadequateCoverage) { - // Proceed based on the assumption that we can profitably optimize this code once - // it has executed enough times. - - m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfForcedOSRExitCounter()), GPRInfo::regT2); - m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter()), GPRInfo::regT1); - m_jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); - m_jit.add32(AssemblyHelpers::TrustedImm32(-1), GPRInfo::regT1); - m_jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfForcedOSRExitCounter())); - m_jit.store32(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter())); - - m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.baselineCodeBlock()), GPRInfo::regT0); - - tooFewFails.append(m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(Options::forcedOSRExitCountForReoptimization))); - - } else { - // Proceed based on the assumption that we can handle these exits so long as they - // don't get too frequent. - - m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter()), GPRInfo::regT2); - m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter()), GPRInfo::regT1); - m_jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); - m_jit.add32(AssemblyHelpers::TrustedImm32(-1), GPRInfo::regT1); - m_jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeFailCounter())); - m_jit.store32(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfSpeculativeSuccessCounter())); - - m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.baselineCodeBlock()), GPRInfo::regT0); + m_jit.load32(AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfOSRExitCounter()), GPRInfo::regT2); + m_jit.add32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); + m_jit.store32(GPRInfo::regT2, AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfOSRExitCounter())); + m_jit.move(AssemblyHelpers::TrustedImmPtr(m_jit.baselineCodeBlock()), GPRInfo::regT0); + tooFewFails = m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(m_jit.codeBlock()->exitCountThresholdForReoptimization())); - tooFewFails.append(m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, AssemblyHelpers::TrustedImm32(m_jit.codeBlock()->largeFailCountThreshold()))); - m_jit.mul32(AssemblyHelpers::TrustedImm32(Options::desiredSpeculativeSuccessFailRatio), GPRInfo::regT2, GPRInfo::regT2); - - tooFewFails.append(m_jit.branch32(AssemblyHelpers::BelowOrEqual, GPRInfo::regT2, GPRInfo::regT1)); - } - // Reoptimize as soon as possible. #if !NUMBER_OF_ARGUMENT_REGISTERS m_jit.poke(GPRInfo::regT0); @@ -157,6 +143,7 @@ void OSRExitCompiler::handleExitCounts(const OSRExit& exit) m_jit.baselineCodeBlock()->counterValueForOptimizeAfterLongWarmUp(), m_jit.baselineCodeBlock()); m_jit.store32(AssemblyHelpers::TrustedImm32(-targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecuteCounter())); + targetValue = ExecutionCounter::clippedThreshold(m_jit.codeBlock()->globalObject(), targetValue); m_jit.store32(AssemblyHelpers::TrustedImm32(targetValue), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionActiveThreshold())); m_jit.store32(AssemblyHelpers::TrustedImm32(ExecutionCounter::formattedTotalCount(targetValue)), AssemblyHelpers::Address(GPRInfo::regT0, CodeBlock::offsetOfJITExecutionTotalCount())); diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.h b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.h index ae29a92d5..a2be5b849 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.h @@ -48,7 +48,7 @@ public: { } - void compileExit(const OSRExit&, SpeculationRecovery*); + void compileExit(const OSRExit&, const Operands<ValueRecovery>&, SpeculationRecovery*); private: #if !ASSERT_DISABLED diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index 09912b3e5..6bc136da4 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -29,10 +29,11 @@ #if ENABLE(DFG_JIT) && USE(JSVALUE32_64) #include "DFGOperations.h" +#include <wtf/DataLog.h> namespace JSC { namespace DFG { -void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* recovery) +void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecovery>& operands, SpeculationRecovery* recovery) { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) @@ -44,7 +45,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } dataLog(") at JIT offset 0x%x ", m_jit.debugOffset()); - exit.dump(WTF::dataFile()); + dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) SpeculationFailureDebugInfo* debugInfo = new SpeculationFailureDebugInfo; @@ -113,7 +114,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // GPRInfo::numberOfRegisters of them. Also see if there are any constants, // any undefined slots, any FPR slots, and any unboxed ints. - Vector<bool> poisonedVirtualRegisters(exit.m_variables.size()); + Vector<bool> poisonedVirtualRegisters(operands.numberOfLocals()); for (unsigned i = 0; i < poisonedVirtualRegisters.size(); ++i) poisonedVirtualRegisters[i] = false; @@ -133,8 +134,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco bool haveUndefined = false; bool haveArguments = false; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: case Int32DisplacedInRegisterFile: @@ -150,8 +151,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // to ensure this happens efficiently. Note that we expect this case // to be rare, so the handling of it is optimized for the cases in // which it does not happen. - if (recovery.virtualRegister() < (int)exit.m_variables.size()) { - switch (exit.m_variables[recovery.virtualRegister()].technique()) { + if (recovery.virtualRegister() < (int)operands.numberOfLocals()) { + switch (operands.local(recovery.virtualRegister()).technique()) { case InGPR: case UnboxedInt32InGPR: case UnboxedBooleanInGPR: @@ -214,19 +215,19 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 5) Perform all reboxing of integers and cells, except for those in registers. if (haveUnboxedInt32InRegisterFile || haveUnboxedCellInRegisterFile || haveUnboxedBooleanInRegisterFile) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case AlreadyInRegisterFileAsUnboxedInt32: - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(operands.operandForIndex(index)))); break; case AlreadyInRegisterFileAsUnboxedCell: - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(operands.operandForIndex(index)))); break; case AlreadyInRegisterFileAsUnboxedBoolean: - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::BooleanTag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::BooleanTag), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(operands.operandForIndex(index)))); break; default: @@ -239,19 +240,19 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // Note that GPRs do not have a fast change (like haveFPRs) because we expect that // most OSR failure points will have at least one GPR that needs to be dumped. - initializePoisoned(exit.m_variables.size()); + initializePoisoned(operands.numberOfLocals()); unsigned currentPoisonIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); - int operand = exit.operandForIndex(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; + int operand = operands.operandForIndex(index); switch (recovery.technique()) { case InGPR: case UnboxedInt32InGPR: case UnboxedBooleanInGPR: - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.store32(recovery.gpr(), reinterpret_cast<char*>(scratchDataBuffer + currentPoisonIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } else { uint32_t tag = JSValue::EmptyValueTag; @@ -266,10 +267,10 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } break; case InPair: - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.store32(recovery.tagGPR(), reinterpret_cast<char*>(scratchDataBuffer + currentPoisonIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); m_jit.store32(recovery.payloadGPR(), reinterpret_cast<char*>(scratchDataBuffer + currentPoisonIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } else { m_jit.store32(recovery.tagGPR(), AssemblyHelpers::tagFor((VirtualRegister)operand)); @@ -291,7 +292,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco m_jit.convertInt32ToDouble(recovery.gpr(), FPRInfo::fpRegT0); m_jit.addDouble(AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), FPRInfo::fpRegT0); - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.move(AssemblyHelpers::TrustedImmPtr(scratchDataBuffer + currentPoisonIndex), addressGPR); m_jit.storeDouble(FPRInfo::fpRegT0, addressGPR); } else @@ -301,7 +302,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco positive.link(&m_jit); - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.store32(recovery.gpr(), reinterpret_cast<char*>(scratchDataBuffer + currentPoisonIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), reinterpret_cast<char*>(scratchDataBuffer + currentPoisonIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); } else { @@ -315,8 +316,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco m_jit.loadDouble(addressGPR, FPRInfo::fpRegT0); m_jit.loadPtr(myScratch, addressGPR); - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } break; @@ -329,16 +330,16 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 7) Dump all doubles into the register file, or to the scratch storage if the // destination virtual register is poisoned. if (haveFPRs) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != InFPR) continue; - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.storeDouble(recovery.fpr(), scratchDataBuffer + currentPoisonIndex); - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } else - m_jit.storeDouble(recovery.fpr(), AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storeDouble(recovery.fpr(), AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); } } @@ -356,8 +357,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // that is far from guaranteed. unsigned displacementIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: m_jit.load32(AssemblyHelpers::payloadFor(recovery.virtualRegister()), GPRInfo::toRegister(displacementIndex++)); @@ -381,15 +382,15 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } displacementIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: case Int32DisplacedInRegisterFile: case CellDisplacedInRegisterFile: case BooleanDisplacedInRegisterFile: - m_jit.store32(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); break; default: break; @@ -414,8 +415,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // to their new (old JIT) locations. unsigned scratchIndex = numberOfPoisonedVirtualRegisters; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: m_jit.load32(AssemblyHelpers::payloadFor(recovery.virtualRegister()), GPRInfo::regT0); @@ -436,30 +437,30 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } scratchIndex = numberOfPoisonedVirtualRegisters; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: m_jit.load32(reinterpret_cast<char*>(scratchDataBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), GPRInfo::regT0); m_jit.load32(reinterpret_cast<char*>(scratchDataBuffer + scratchIndex) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag), GPRInfo::regT1); - m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::regT1, AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::regT1, AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); scratchIndex++; break; case Int32DisplacedInRegisterFile: m_jit.load32(reinterpret_cast<char*>(scratchDataBuffer + scratchIndex++) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), GPRInfo::regT0); - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::Int32Tag), AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); break; case CellDisplacedInRegisterFile: m_jit.load32(reinterpret_cast<char*>(scratchDataBuffer + scratchIndex++) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), GPRInfo::regT0); - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::CellTag), AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); break; case BooleanDisplacedInRegisterFile: m_jit.load32(reinterpret_cast<char*>(scratchDataBuffer + scratchIndex++) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), GPRInfo::regT0); - m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::BooleanTag), AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(AssemblyHelpers::TrustedImm32(JSValue::BooleanTag), AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); break; default: break; @@ -473,11 +474,11 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 9) Dump all poisoned virtual registers. if (numberOfPoisonedVirtualRegisters) { - for (int virtualRegister = 0; virtualRegister < (int)exit.m_variables.size(); ++virtualRegister) { + for (int virtualRegister = 0; virtualRegister < (int)operands.numberOfLocals(); ++virtualRegister) { if (!poisonedVirtualRegisters[virtualRegister]) continue; - const ValueRecovery& recovery = exit.m_variables[virtualRegister]; + const ValueRecovery& recovery = operands.local(virtualRegister); switch (recovery.technique()) { case InGPR: case UnboxedInt32InGPR: @@ -519,16 +520,16 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco m_jit.move(AssemblyHelpers::TrustedImm32(jsUndefined().tag()), GPRInfo::regT1); } - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != Constant) continue; if (recovery.constant().isUndefined()) { - m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(GPRInfo::regT1, AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(GPRInfo::regT0, AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(GPRInfo::regT1, AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); } else { - m_jit.store32(AssemblyHelpers::TrustedImm32(recovery.constant().payload()), AssemblyHelpers::payloadFor((VirtualRegister)exit.operandForIndex(index))); - m_jit.store32(AssemblyHelpers::TrustedImm32(recovery.constant().tag()), AssemblyHelpers::tagFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.store32(AssemblyHelpers::TrustedImm32(recovery.constant().payload()), AssemblyHelpers::payloadFor((VirtualRegister)operands.operandForIndex(index))); + m_jit.store32(AssemblyHelpers::TrustedImm32(recovery.constant().tag()), AssemblyHelpers::tagFor((VirtualRegister)operands.operandForIndex(index))); } } } @@ -611,11 +612,11 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // registers. if (haveArguments) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != ArgumentsThatWereNotCreated) continue; - int operand = exit.operandForIndex(index); + int operand = operands.operandForIndex(index); // Find the right inline call frame. InlineCallFrame* inlineCallFrame = 0; for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame; diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index 33ba69a35..2f38ba79b 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -29,10 +29,11 @@ #if ENABLE(DFG_JIT) && USE(JSVALUE64) #include "DFGOperations.h" +#include <wtf/DataLog.h> namespace JSC { namespace DFG { -void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* recovery) +void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecovery>& operands, SpeculationRecovery* recovery) { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) @@ -44,7 +45,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } dataLog(") "); - exit.dump(WTF::dataFile()); + dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) SpeculationFailureDebugInfo* debugInfo = new SpeculationFailureDebugInfo; @@ -110,7 +111,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // GPRInfo::numberOfRegisters of them. Also see if there are any constants, // any undefined slots, any FPR slots, and any unboxed ints. - Vector<bool> poisonedVirtualRegisters(exit.m_variables.size()); + Vector<bool> poisonedVirtualRegisters(operands.numberOfLocals()); for (unsigned i = 0; i < poisonedVirtualRegisters.size(); ++i) poisonedVirtualRegisters[i] = false; @@ -129,8 +130,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco bool haveUInt32s = false; bool haveArguments = false; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case Int32DisplacedInRegisterFile: case DoubleDisplacedInRegisterFile: @@ -145,8 +146,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // to ensure this happens efficiently. Note that we expect this case // to be rare, so the handling of it is optimized for the cases in // which it does not happen. - if (recovery.virtualRegister() < (int)exit.m_variables.size()) { - switch (exit.m_variables[recovery.virtualRegister()].technique()) { + if (recovery.virtualRegister() < (int)operands.numberOfLocals()) { + switch (operands.local(recovery.virtualRegister()).technique()) { case InGPR: case UnboxedInt32InGPR: case UInt32InGPR: @@ -224,8 +225,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 5) Perform all reboxing of integers. if (haveUnboxedInt32s || haveUInt32s) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case UnboxedInt32InGPR: if (recovery.gpr() != alreadyBoxed) @@ -233,7 +234,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco break; case AlreadyInRegisterFileAsUnboxedInt32: - m_jit.store32(AssemblyHelpers::TrustedImm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(exit.operandForIndex(index)))); + m_jit.store32(AssemblyHelpers::TrustedImm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(operands.operandForIndex(index)))); break; case UInt32InGPR: { @@ -284,19 +285,19 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // Note that GPRs do not have a fast change (like haveFPRs) because we expect that // most OSR failure points will have at least one GPR that needs to be dumped. - initializePoisoned(exit.m_variables.size()); + initializePoisoned(operands.numberOfLocals()); unsigned currentPoisonIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); - int operand = exit.operandForIndex(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; + int operand = operands.operandForIndex(index); switch (recovery.technique()) { case InGPR: case UnboxedInt32InGPR: case UInt32InGPR: - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.storePtr(recovery.gpr(), scratchDataBuffer + currentPoisonIndex); - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } else m_jit.storePtr(recovery.gpr(), AssemblyHelpers::addressFor((VirtualRegister)operand)); @@ -311,8 +312,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco if (haveFPRs) { // 7) Box all doubles (relies on there being more GPRs than FPRs) - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != InFPR) continue; FPRReg fpr = recovery.fpr(); @@ -323,17 +324,17 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 8) Dump all doubles into the register file, or to the scratch storage if // the destination virtual register is poisoned. - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != InFPR) continue; GPRReg gpr = GPRInfo::toRegister(FPRInfo::toIndex(recovery.fpr())); - if (exit.isVariable(index) && poisonedVirtualRegisters[exit.variableForIndex(index)]) { + if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { m_jit.storePtr(gpr, scratchDataBuffer + currentPoisonIndex); - m_poisonScratchIndices[exit.variableForIndex(index)] = currentPoisonIndex; + m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; currentPoisonIndex++; } else - m_jit.storePtr(gpr, AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(gpr, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); } } @@ -341,13 +342,13 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 9) Box all unboxed doubles in the register file. if (haveUnboxedDoubles) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != AlreadyInRegisterFileAsUnboxedDouble) continue; - m_jit.loadDouble(AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index)), FPRInfo::fpRegT0); + m_jit.loadDouble(AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index)), FPRInfo::fpRegT0); m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); - m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); } } @@ -363,8 +364,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // that is far from guaranteed. unsigned displacementIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: m_jit.loadPtr(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::toRegister(displacementIndex++)); @@ -390,13 +391,13 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } displacementIndex = 0; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: case Int32DisplacedInRegisterFile: case DoubleDisplacedInRegisterFile: - m_jit.storePtr(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); break; default: @@ -422,8 +423,8 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // to their new (old JIT) locations. unsigned scratchIndex = numberOfPoisonedVirtualRegisters; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: @@ -451,14 +452,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco } scratchIndex = numberOfPoisonedVirtualRegisters; - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; switch (recovery.technique()) { case DisplacedInRegisterFile: case Int32DisplacedInRegisterFile: case DoubleDisplacedInRegisterFile: m_jit.loadPtr(scratchDataBuffer + scratchIndex++, GPRInfo::regT0); - m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); break; default: @@ -473,11 +474,11 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // 11) Dump all poisoned virtual registers. if (numberOfPoisonedVirtualRegisters) { - for (int virtualRegister = 0; virtualRegister < (int)exit.m_variables.size(); ++virtualRegister) { + for (int virtualRegister = 0; virtualRegister < (int)operands.numberOfLocals(); ++virtualRegister) { if (!poisonedVirtualRegisters[virtualRegister]) continue; - const ValueRecovery& recovery = exit.m_variables[virtualRegister]; + const ValueRecovery& recovery = operands.local(virtualRegister); switch (recovery.technique()) { case InGPR: case UnboxedInt32InGPR: @@ -500,14 +501,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco if (haveUndefined) m_jit.move(AssemblyHelpers::TrustedImmPtr(JSValue::encode(jsUndefined())), GPRInfo::regT0); - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != Constant) continue; if (recovery.constant().isUndefined()) - m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); else - m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(JSValue::encode(recovery.constant())), AssemblyHelpers::addressFor((VirtualRegister)exit.operandForIndex(index))); + m_jit.storePtr(AssemblyHelpers::TrustedImmPtr(JSValue::encode(recovery.constant())), AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); } } @@ -586,11 +587,11 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, SpeculationRecovery* reco // registers. if (haveArguments) { - for (int index = 0; index < exit.numberOfRecoveries(); ++index) { - const ValueRecovery& recovery = exit.valueRecovery(index); + for (size_t index = 0; index < operands.size(); ++index) { + const ValueRecovery& recovery = operands[index]; if (recovery.technique() != ArgumentsThatWereNotCreated) continue; - int operand = exit.operandForIndex(index); + int operand = operands.operandForIndex(index); // Find the right inline call frame. InlineCallFrame* inlineCallFrame = 0; for (InlineCallFrame* current = exit.m_codeOrigin.inlineCallFrame; diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 11362f432..5d6575a6f 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -140,6 +140,62 @@ "b " LOCAL_REFERENCE(function) "WithReturnAddress" "\n" \ ); +#elif COMPILER(GCC) && CPU(ARM_TRADITIONAL) + +#define FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_E(function) \ + asm ( \ + ".text" "\n" \ + ".globl " SYMBOL_STRING(function) "\n" \ + HIDE_SYMBOL(function) "\n" \ + INLINE_ARM_FUNCTION(function) \ + SYMBOL_STRING(function) ":" "\n" \ + "mov a2, lr" "\n" \ + "b " LOCAL_REFERENCE(function) "WithReturnAddress" "\n" \ + ); + +#define FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_ECI(function) \ + asm ( \ + ".text" "\n" \ + ".globl " SYMBOL_STRING(function) "\n" \ + HIDE_SYMBOL(function) "\n" \ + INLINE_ARM_FUNCTION(function) \ + SYMBOL_STRING(function) ":" "\n" \ + "mov a4, lr" "\n" \ + "b " LOCAL_REFERENCE(function) "WithReturnAddress" "\n" \ + ); + +// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned even-numbered register (r0, r2 or [sp]). +// As a result, return address will be at a 4-byte further location in the following cases. +#if COMPILER_SUPPORTS(EABI) && CPU(ARM) +#define INSTRUCTION_STORE_RETURN_ADDRESS_EJI "str lr, [sp, #4]" +#define INSTRUCTION_STORE_RETURN_ADDRESS_EJCI "str lr, [sp, #8]" +#else +#define INSTRUCTION_STORE_RETURN_ADDRESS_EJI "str lr, [sp, #0]" +#define INSTRUCTION_STORE_RETURN_ADDRESS_EJCI "str lr, [sp, #4]" +#endif + +#define FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_EJI(function) \ + asm ( \ + ".text" "\n" \ + ".globl " SYMBOL_STRING(function) "\n" \ + HIDE_SYMBOL(function) "\n" \ + INLINE_ARM_FUNCTION(function) \ + SYMBOL_STRING(function) ":" "\n" \ + INSTRUCTION_STORE_RETURN_ADDRESS_EJI "\n" \ + "b " LOCAL_REFERENCE(function) "WithReturnAddress" "\n" \ + ); + +#define FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_EJCI(function) \ + asm ( \ + ".text" "\n" \ + ".globl " SYMBOL_STRING(function) "\n" \ + HIDE_SYMBOL(function) "\n" \ + INLINE_ARM_FUNCTION(function) \ + SYMBOL_STRING(function) ":" "\n" \ + INSTRUCTION_STORE_RETURN_ADDRESS_EJCI "\n" \ + "b " LOCAL_REFERENCE(function) "WithReturnAddress" "\n" \ + ); + #endif #define P_FUNCTION_WRAPPER_WITH_RETURN_ADDRESS_E(function) \ @@ -1250,15 +1306,13 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* CodeBlock* alternative = codeBlock->alternative(); dataLog("Speculation failure in %p at @%u with executeCounter = %s, " "reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, " - "success/fail %u/(%u+%u)\n", + "osrExitCounter = %u\n", codeBlock, debugInfo->nodeIndex, alternative ? alternative->jitExecuteCounter().status() : 0, alternative ? alternative->reoptimizationRetryCounter() : 0, alternative ? alternative->optimizationDelayCounter() : 0, - codeBlock->speculativeSuccessCounter(), - codeBlock->speculativeFailCounter(), - codeBlock->forcedOSRExitCounter()); + codeBlock->osrExitCounter()); } #endif @@ -1324,6 +1378,17 @@ SYMBOL_STRING(getHostCallReturnValue) ":" "\n" "mov r0, r5" "\n" "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" ); +#elif CPU(ARM_TRADITIONAL) +asm ( +".text" "\n" +".globl " SYMBOL_STRING(getHostCallReturnValue) "\n" +HIDE_SYMBOL(getHostCallReturnValue) "\n" +INLINE_ARM_FUNCTION(getHostCallReturnValue) +SYMBOL_STRING(getHostCallReturnValue) ":" "\n" + "ldr r5, [r5, #-40]" "\n" + "mov r0, r5" "\n" + "b " LOCAL_REFERENCE(getHostCallReturnValueWithExecState) "\n" +); #endif extern "C" EncodedJSValue HOST_CALL_RETURN_VALUE_OPTION getHostCallReturnValueWithExecState(ExecState* exec) diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h index 53055a215..80fd6914a 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.h +++ b/Source/JavaScriptCore/dfg/DFGPhase.h @@ -49,6 +49,8 @@ public: endPhase(); } + const char* name() const { return m_name; } + // Each phase must have a run() method. protected: @@ -76,17 +78,28 @@ private: }; template<typename PhaseType> +bool runAndLog(PhaseType& phase) +{ + bool result = phase.run(); +#if DFG_ENABLE(DEBUG_VERBOSE) + if (result) + dataLog("Phase %s changed the IR.\n", phase.name()); +#endif + return result; +} + +template<typename PhaseType> bool runPhase(Graph& graph) { PhaseType phase(graph); - return phase.run(); + return runAndLog(phase); } template<typename PhaseType, typename ArgumentType1> bool runPhase(Graph& graph, ArgumentType1 arg1) { PhaseType phase(graph, arg1); - return phase.run(); + return runAndLog(phase); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 0bd81ec44..320eb6cb6 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -908,6 +908,7 @@ private: bool performPredictionPropagation(Graph& graph) { + SamplingRegion samplingRegion("DFG Prediction Propagation Phase"); return runPhase<PredictionPropagationPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp index 5453469fe..32e4ef157 100644 --- a/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGRedundantPhiEliminationPhase.cpp @@ -169,6 +169,7 @@ private: bool performRedundantPhiElimination(Graph& graph) { + SamplingRegion samplingRegion("DFG Redundant Phi Elimination Phase"); return runPhase<RedundantPhiEliminationPhase>(graph); } diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp index 9c3391be5..752316f9c 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp +++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp @@ -30,6 +30,7 @@ #include "DFGCCallHelpers.h" #include "DFGSpeculativeJIT.h" +#include "GCAwareJITStubRoutine.h" #include "LinkBuffer.h" #include "Operations.h" #include "PolymorphicPutByIdList.h" @@ -43,7 +44,7 @@ static void dfgRepatchCall(CodeBlock* codeblock, CodeLocationCall call, Function repatchBuffer.relink(call, newCalleeFunction); } -static void dfgRepatchByIdSelfAccess(CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure, size_t offset, const FunctionPtr &slowPathFunction, bool compact) +static void dfgRepatchByIdSelfAccess(CodeBlock* codeBlock, StructureStubInfo& stubInfo, Structure* structure, PropertyOffset offset, const FunctionPtr &slowPathFunction, bool compact) { RepatchBuffer repatchBuffer(codeBlock); @@ -52,18 +53,19 @@ static void dfgRepatchByIdSelfAccess(CodeBlock* codeBlock, StructureStubInfo& st // Patch the structure check & the offset of the load. repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), structure); + repatchBuffer.setLoadInstructionIsActive(stubInfo.callReturnLocation.convertibleLoadAtOffset(stubInfo.patch.dfg.deltaCallToStorageLoad), isOutOfLineOffset(offset)); #if USE(JSVALUE64) if (compact) - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), sizeof(JSValue) * offset); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset)); else - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), sizeof(JSValue) * offset); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), offsetRelativeToPatchedStorage(offset)); #elif USE(JSVALUE32_64) if (compact) { - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToTagLoadOrStore), sizeof(JSValue) * offset + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToPayloadLoadOrStore), sizeof(JSValue) * offset + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); } else { - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToTagLoadOrStore), sizeof(JSValue) * offset + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToPayloadLoadOrStore), sizeof(JSValue) * offset + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToTagLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToPayloadLoadOrStore), offsetRelativeToPatchedStorage(offset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); } #endif } @@ -105,7 +107,7 @@ static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratc linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase)); } -static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, size_t offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, MacroAssemblerCodeRef& stubRoutine) +static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, PropertyOffset offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine) { JSGlobalData* globalData = &exec->globalData(); @@ -139,13 +141,23 @@ static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stu currStructure = it->get(); } - stubJit.loadPtr(protoObject->addressOfPropertyStorage(), resultGPR); + if (isInlineOffset(offset)) { #if USE(JSVALUE64) - stubJit.loadPtr(MacroAssembler::Address(resultGPR, offset * sizeof(WriteBarrier<Unknown>)), resultGPR); + stubJit.loadPtr(protoObject->locationForOffset(offset), resultGPR); #elif USE(JSVALUE32_64) - stubJit.load32(MacroAssembler::Address(resultGPR, offset * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); - stubJit.load32(MacroAssembler::Address(resultGPR, offset * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); + stubJit.move(MacroAssembler::TrustedImmPtr(protoObject->locationForOffset(offset)), resultGPR); + stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); + stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); #endif + } else { + stubJit.loadPtr(protoObject->addressOfOutOfLineStorage(), resultGPR); +#if USE(JSVALUE64) + stubJit.loadPtr(MacroAssembler::Address(resultGPR, offsetInOutOfLineStorage(offset) * sizeof(WriteBarrier<Unknown>)), resultGPR); +#elif USE(JSVALUE32_64) + stubJit.load32(MacroAssembler::Address(resultGPR, offsetInOutOfLineStorage(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); + stubJit.load32(MacroAssembler::Address(resultGPR, offsetInOutOfLineStorage(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); +#endif + } MacroAssembler::Jump success, fail; @@ -155,7 +167,7 @@ static void generateProtoChainAccessStub(ExecState* exec, StructureStubInfo& stu linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, successLabel, slowCaseLabel); - stubRoutine = FINALIZE_CODE( + stubRoutine = FINALIZE_CODE_FOR_STUB( patchBuffer, ("DFG prototype chain access stub for CodeBlock %p, return point %p", exec->codeBlock(), successLabel.executableAddress())); @@ -209,14 +221,14 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier linkRestoreScratch(patchBuffer, needToRestoreScratch, stubInfo, success, fail, failureCases); - stubInfo.stubRoutine = FINALIZE_CODE( + stubInfo.stubRoutine = FINALIZE_CODE_FOR_STUB( patchBuffer, ("DFG GetById array length stub for CodeBlock %p, return point %p", exec->codeBlock(), stubInfo.callReturnLocation.labelAtOffset( stubInfo.patch.dfg.deltaCallToDone).executableAddress())); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code())); + repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById); return true; @@ -253,7 +265,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier if (slot.cachedPropertyType() != PropertySlot::Value) return false; - size_t offset = slot.cachedOffset(); + PropertyOffset offset = slot.cachedOffset(); size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset); if (!count) return false; @@ -265,7 +277,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase), stubInfo.stubRoutine); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code())); + repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList); stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain, count, true); @@ -312,7 +324,7 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi listIndex = 0; } else if (stubInfo.accessType == access_get_by_id_self) { ASSERT(!stubInfo.stubRoutine); - polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), MacroAssemblerCodeRef::createSelfManagedCodeRef(stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase)), stubInfo.u.getByIdSelf.baseObjectStructure.get(), true); + polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), JITStubRoutine::createSelfManagedRoutine(stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase)), stubInfo.u.getByIdSelf.baseObjectStructure.get(), true); stubInfo.initGetByIdSelfList(polymorphicStructureList, 1); listIndex = 1; } else { @@ -349,12 +361,20 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi || slot.cachedPropertyType() == PropertySlot::Custom) { if (slot.cachedPropertyType() == PropertySlot::Getter) { ASSERT(baseGPR != scratchGPR); - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); + if (isInlineOffset(slot.cachedOffset())) { #if USE(JSVALUE64) - stubJit.loadPtr(MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue)), scratchGPR); -#elif USE(JSVALUE32_64) - stubJit.load32(MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), scratchGPR); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR); +#else + stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR); #endif + } else { + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); +#if USE(JSVALUE64) + stubJit.loadPtr(MacroAssembler::Address(scratchGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR); +#else + stubJit.load32(MacroAssembler::Address(scratchGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR); +#endif + } stubJit.setupArgumentsWithExecState(baseGPR, scratchGPR); operationFunction = operationCallGetter; } else { @@ -385,13 +405,27 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi handlerCall = stubJit.call(); stubJit.jump(GPRInfo::returnValueGPR2); } else { - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR); + if (isInlineOffset(slot.cachedOffset())) { #if USE(JSVALUE64) - stubJit.loadPtr(MacroAssembler::Address(resultGPR, slot.cachedOffset() * sizeof(JSValue)), resultGPR); -#elif USE(JSVALUE32_64) - stubJit.load32(MacroAssembler::Address(resultGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); - stubJit.load32(MacroAssembler::Address(resultGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), resultGPR); +#else + if (baseGPR == resultTagGPR) { + stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); + stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); + } else { + stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); + stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); + } +#endif + } else { + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR); +#if USE(JSVALUE64) + stubJit.loadPtr(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset())), resultGPR); +#else + stubJit.load32(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); + stubJit.load32(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR); #endif + } success = stubJit.jump(); isDirect = true; } @@ -400,7 +434,7 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi CodeLocationLabel lastProtoBegin; if (listIndex) - lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine.code()); + lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine->code().code()); else lastProtoBegin = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase); ASSERT(!!lastProtoBegin); @@ -412,17 +446,23 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi patchBuffer.link(handlerCall, lookupExceptionHandlerInStub); } - MacroAssemblerCodeRef stubRoutine = FINALIZE_CODE( - patchBuffer, - ("DFG GetById polymorphic list access for CodeBlock %p, return point %p", - exec->codeBlock(), stubInfo.callReturnLocation.labelAtOffset( - stubInfo.patch.dfg.deltaCallToDone).executableAddress())); + RefPtr<JITStubRoutine> stubRoutine = + createJITStubRoutine( + FINALIZE_CODE( + patchBuffer, + ("DFG GetById polymorphic list access for CodeBlock %p, return point %p", + exec->codeBlock(), stubInfo.callReturnLocation.labelAtOffset( + stubInfo.patch.dfg.deltaCallToDone).executableAddress())), + *globalData, + codeBlock->ownerExecutable(), + slot.cachedPropertyType() == PropertySlot::Getter + || slot.cachedPropertyType() == PropertySlot::Custom); polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, isDirect); CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.code())); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -450,7 +490,7 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I ASSERT(slot.slotBase().isObject()); - size_t offset = slot.cachedOffset(); + PropertyOffset offset = slot.cachedOffset(); size_t count = normalizePrototypeChain(exec, baseValue, slot.slotBase(), propertyName, offset); if (!count) return false; @@ -466,7 +506,7 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I if (stubInfo.accessType == access_get_by_id_chain) { ASSERT(!!stubInfo.stubRoutine); polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get(), true); - stubInfo.stubRoutine = MacroAssemblerCodeRef(); + stubInfo.stubRoutine.clear(); stubInfo.initGetByIdProtoList(polymorphicStructureList, 1); } else { ASSERT(stubInfo.accessType == access_get_by_id_proto_list); @@ -477,10 +517,10 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I if (listIndex < POLYMORPHIC_LIST_CACHE_SIZE) { stubInfo.u.getByIdProtoList.listSize++; - CodeLocationLabel lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine.code()); + CodeLocationLabel lastProtoBegin = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine->code().code()); ASSERT(!!lastProtoBegin); - MacroAssemblerCodeRef stubRoutine; + RefPtr<JITStubRoutine> stubRoutine; generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone), lastProtoBegin, stubRoutine); @@ -488,7 +528,7 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine.code())); + repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -548,7 +588,7 @@ static void emitPutReplaceStub( PutKind, Structure* structure, CodeLocationLabel failureLabel, - MacroAssemblerCodeRef& stubRoutine) + RefPtr<JITStubRoutine>& stubRoutine) { JSGlobalData* globalData = &exec->globalData(); GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.dfg.baseGPR); @@ -567,7 +607,7 @@ static void emitPutReplaceStub( MacroAssembler stubJit; - if (scratchGPR == InvalidGPRReg && (writeBarrierNeeded || !structure->isUsingInlineStorage())) { + if (scratchGPR == InvalidGPRReg && (writeBarrierNeeded || isOutOfLineOffset(slot.cachedOffset()))) { scratchGPR = SpeculativeJIT::selectScratchGPR(baseGPR, valueGPR); needToRestoreScratch = true; stubJit.push(scratchGPR); @@ -586,20 +626,20 @@ static void emitPutReplaceStub( #endif #if USE(JSVALUE64) - if (structure->isUsingInlineStorage()) - stubJit.storePtr(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue))); + if (isInlineOffset(slot.cachedOffset())) + stubJit.storePtr(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue))); else { - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); - stubJit.storePtr(valueGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue))); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); + stubJit.storePtr(valueGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue))); } #elif USE(JSVALUE32_64) - if (structure->isUsingInlineStorage()) { - stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); - stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); + if (isInlineOffset(slot.cachedOffset())) { + stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); } else { - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); - stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); - stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); + stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); } #endif @@ -622,7 +662,7 @@ static void emitPutReplaceStub( patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone)); patchBuffer.link(failure, failureLabel); - stubRoutine = FINALIZE_CODE( + stubRoutine = FINALIZE_CODE_FOR_STUB( patchBuffer, ("DFG PutById replace stub for CodeBlock %p, return point %p", exec->codeBlock(), stubInfo.callReturnLocation.labelAtOffset( @@ -640,7 +680,7 @@ static void emitPutTransitionStub( Structure* oldStructure, StructureChain* prototypeChain, CodeLocationLabel failureLabel, - MacroAssemblerCodeRef& stubRoutine) + RefPtr<JITStubRoutine>& stubRoutine) { JSGlobalData* globalData = &exec->globalData(); @@ -685,20 +725,20 @@ static void emitPutTransitionStub( stubJit.storePtr(MacroAssembler::TrustedImmPtr(structure), MacroAssembler::Address(baseGPR, JSCell::structureOffset())); #if USE(JSVALUE64) - if (structure->isUsingInlineStorage()) - stubJit.storePtr(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue))); + if (isInlineOffset(slot.cachedOffset())) + stubJit.storePtr(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue))); else { - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); - stubJit.storePtr(valueGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue))); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); + stubJit.storePtr(valueGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue))); } #elif USE(JSVALUE32_64) - if (structure->isUsingInlineStorage()) { - stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); - stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); + if (isInlineOffset(slot.cachedOffset())) { + stubJit.store32(valueGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32(valueTagGPR, MacroAssembler::Address(baseGPR, JSObject::offsetOfInlineStorage() + offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); } else { - stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); - stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); - stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR, slot.cachedOffset() * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); + stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); + stubJit.store32(valueGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32(valueTagGPR, MacroAssembler::Address(scratchGPR, offsetInOutOfLineStorage(slot.cachedOffset()) * sizeof(JSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); } #endif @@ -721,8 +761,8 @@ static void emitPutTransitionStub( patchBuffer.link(failure, failureLabel); else patchBuffer.link(failureCases, failureLabel); - - stubRoutine = FINALIZE_CODE( + + stubRoutine = FINALIZE_CODE_FOR_STUB( patchBuffer, ("DFG PutById transition stub for CodeBlock %p, return point %p", exec->codeBlock(), stubInfo.callReturnLocation.labelAtOffset( @@ -752,7 +792,7 @@ static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier return false; // skip optimizing the case where we need a realloc - if (oldStructure->propertyStorageCapacity() != structure->propertyStorageCapacity()) + if (oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()) return false; normalizePrototypeChain(exec, baseCell); @@ -766,7 +806,7 @@ static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier stubInfo.stubRoutine); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine.code())); + repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); repatchBuffer.relink(stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind)); stubInfo.initPutByIdTransition(*globalData, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain, putKind == Direct); @@ -808,14 +848,14 @@ static bool tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identi // Optimize self access. if (slot.base() == baseValue) { PolymorphicPutByIdList* list; - MacroAssemblerCodeRef stubRoutine; + RefPtr<JITStubRoutine> stubRoutine; if (slot.type() == PutPropertySlot::NewProperty) { if (structure->isDictionary()) return false; // skip optimizing the case where we need a realloc - if (oldStructure->propertyStorageCapacity() != structure->propertyStorageCapacity()) + if (oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()) return false; normalizePrototypeChain(exec, baseCell); @@ -855,7 +895,7 @@ static bool tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identi } RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubRoutine.code())); + repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubRoutine->code().code())); if (list->isFull()) repatchBuffer.relink(stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind)); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 0c0f3260f..c6ec62129 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -45,6 +45,8 @@ SpeculativeJIT::SpeculativeJIT(JITCompiler& jit) , m_variables(jit.graph().m_localVars) , m_lastSetOperand(std::numeric_limits<int>::max()) , m_state(m_jit.graph()) + , m_stream(&jit.codeBlock()->variableEventStream()) + , m_minifiedGraph(&jit.codeBlock()->minifiedDFG()) , m_isCheckingArgumentTypes(false) { } @@ -99,7 +101,7 @@ GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillStorage(gpr); + info.fillStorage(*m_stream, gpr); return gpr; } @@ -780,39 +782,6 @@ FPRTemporary::FPRTemporary(SpeculativeJIT* jit, JSValueOperand& op1) } #endif -void ValueSource::dump(FILE* out) const -{ - switch (kind()) { - case SourceNotSet: - fprintf(out, "NotSet"); - break; - case SourceIsDead: - fprintf(out, "IsDead"); - break; - case ValueInRegisterFile: - fprintf(out, "InRegFile"); - break; - case Int32InRegisterFile: - fprintf(out, "Int32"); - break; - case CellInRegisterFile: - fprintf(out, "Cell"); - break; - case BooleanInRegisterFile: - fprintf(out, "Bool"); - break; - case DoubleInRegisterFile: - fprintf(out, "Double"); - break; - case ArgumentsSource: - fprintf(out, "Arguments"); - break; - case HaveNode: - fprintf(out, "Node(%d)", m_nodeIndex); - break; - } -} - void SpeculativeJIT::compilePeepHoleDoubleBranch(Node& node, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition condition) { Node& branchNode = at(branchNodeIndex); @@ -953,12 +922,30 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa return false; } +void SpeculativeJIT::noticeOSRBirth(NodeIndex nodeIndex, Node& node) +{ + if (!node.hasVirtualRegister()) + return; + + VirtualRegister virtualRegister = node.virtualRegister(); + GenerationInfo& info = m_generationInfo[virtualRegister]; + + info.noticeOSRBirth(*m_stream, nodeIndex, virtualRegister); +} + void SpeculativeJIT::compileMovHint(Node& node) { ASSERT(node.op() == SetLocal); - setNodeIndexForOperand(node.child1().index(), node.local()); m_lastSetOperand = node.local(); + + Node& child = at(node.child1()); + noticeOSRBirth(node.child1().index(), child); + + if (child.op() == UInt32ToNumber) + noticeOSRBirth(child.child1().index(), at(child.child1())); + + m_stream->appendAndLog(VariableEvent::movHint(node.child1().index(), node.local())); } void SpeculativeJIT::compile(BasicBlock& block) @@ -983,11 +970,20 @@ void SpeculativeJIT::compile(BasicBlock& block) m_jit.breakpoint(); #endif +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("Setting up state for block #%u: ", m_block); +#endif + + m_stream->appendAndLog(VariableEvent::reset()); + m_jit.jitAssertHasValidCallFrame(); ASSERT(m_arguments.size() == block.variablesAtHead.numberOfArguments()); - for (size_t i = 0; i < m_arguments.size(); ++i) - m_arguments[i] = ValueSource(ValueInRegisterFile); + for (size_t i = 0; i < m_arguments.size(); ++i) { + ValueSource valueSource = ValueSource(ValueInRegisterFile); + m_arguments[i] = valueSource; + m_stream->appendAndLog(VariableEvent::setLocal(argumentToOperand(i), valueSource.dataFormat())); + } m_state.reset(); m_state.beginBasicBlock(&block); @@ -995,18 +991,21 @@ void SpeculativeJIT::compile(BasicBlock& block) ASSERT(m_variables.size() == block.variablesAtHead.numberOfLocals()); for (size_t i = 0; i < m_variables.size(); ++i) { NodeIndex nodeIndex = block.variablesAtHead.local(i); + ValueSource valueSource; if (nodeIndex == NoNode) - m_variables[i] = ValueSource(SourceIsDead); + valueSource = ValueSource(SourceIsDead); else if (at(nodeIndex).variableAccessData()->isArgumentsAlias()) - m_variables[i] = ValueSource(ArgumentsSource); + valueSource = ValueSource(ArgumentsSource); else if (at(nodeIndex).variableAccessData()->isCaptured()) - m_variables[i] = ValueSource(ValueInRegisterFile); + valueSource = ValueSource(ValueInRegisterFile); else if (!at(nodeIndex).refCount()) - m_variables[i] = ValueSource(SourceIsDead); + valueSource = ValueSource(SourceIsDead); else if (at(nodeIndex).variableAccessData()->shouldUseDoubleFormat()) - m_variables[i] = ValueSource(DoubleInRegisterFile); + valueSource = ValueSource(DoubleInRegisterFile); else - m_variables[i] = ValueSource::forSpeculation(at(nodeIndex).variableAccessData()->argumentAwarePrediction()); + valueSource = ValueSource::forSpeculation(at(nodeIndex).variableAccessData()->argumentAwarePrediction()); + m_variables[i] = valueSource; + m_stream->appendAndLog(VariableEvent::setLocal(i, valueSource.dataFormat())); } m_lastSetOperand = std::numeric_limits<int>::max(); @@ -1019,6 +1018,10 @@ void SpeculativeJIT::compile(BasicBlock& block) verificationSucceeded.link(&m_jit); } +#if DFG_ENABLE(DEBUG_VERBOSE) + dataLog("\n"); +#endif + for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { m_compileIndex = block[m_indexInBlock]; m_jit.setForNode(m_compileIndex); @@ -1029,6 +1032,15 @@ void SpeculativeJIT::compile(BasicBlock& block) dataLog("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); #endif switch (node.op()) { + case JSConstant: + m_minifiedGraph->append(MinifiedNode::fromNode(m_compileIndex, node)); + break; + + case WeakJSConstant: + m_jit.addWeakReference(node.weakConstant()); + m_minifiedGraph->append(MinifiedNode::fromNode(m_compileIndex, node)); + break; + case SetLocal: compileMovHint(node); break; @@ -1073,11 +1085,9 @@ void SpeculativeJIT::compile(BasicBlock& block) break; } - case WeakJSConstant: - m_jit.addWeakReference(node.weakConstant()); - break; - default: + if (belongsInMinifiedGraph(node.op())) + m_minifiedGraph->append(MinifiedNode::fromNode(m_compileIndex, node)); break; } } else { @@ -1100,6 +1110,11 @@ void SpeculativeJIT::compile(BasicBlock& block) return; } + if (belongsInMinifiedGraph(node.op())) { + m_minifiedGraph->append(MinifiedNode::fromNode(m_compileIndex, node)); + noticeOSRBirth(m_compileIndex, node); + } + #if DFG_ENABLE(DEBUG_VERBOSE) if (node.hasResult()) { GenerationInfo& info = m_generationInfo[node.virtualRegister()]; @@ -1120,16 +1135,6 @@ void SpeculativeJIT::compile(BasicBlock& block) #endif } -#if DFG_ENABLE(VERBOSE_VALUE_RECOVERIES) - for (size_t i = 0; i < m_arguments.size(); ++i) - computeValueRecoveryFor(argumentToOperand(i)).dump(stderr); - - dataLog(" : "); - - for (int operand = 0; operand < (int)m_variables.size(); ++operand) - computeValueRecoveryFor(operand).dump(stderr); -#endif - #if DFG_ENABLE(DEBUG_VERBOSE) dataLog("\n"); #endif @@ -1366,154 +1371,14 @@ void SpeculativeJIT::linkOSREntries(LinkBuffer& linkBuffer) ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSource) { - switch (valueSource.kind()) { - case SourceIsDead: - return ValueRecovery::constant(jsUndefined()); - - case ValueInRegisterFile: - return ValueRecovery::alreadyInRegisterFile(); + if (valueSource.isInRegisterFile()) + return valueSource.valueRecovery(); - case Int32InRegisterFile: - return ValueRecovery::alreadyInRegisterFileAsUnboxedInt32(); - - case CellInRegisterFile: - return ValueRecovery::alreadyInRegisterFileAsUnboxedCell(); - - case BooleanInRegisterFile: - return ValueRecovery::alreadyInRegisterFileAsUnboxedBoolean(); - - case DoubleInRegisterFile: - return ValueRecovery::alreadyInRegisterFileAsUnboxedDouble(); - - case ArgumentsSource: - return ValueRecovery::argumentsThatWereNotCreated(); - - case HaveNode: { - Node* nodePtr = &at(valueSource.nodeIndex()); - - if (nodePtr->isPhantomArguments()) - return ValueRecovery::argumentsThatWereNotCreated(); - - if (nodePtr->hasConstant()) - return ValueRecovery::constant(valueOfJSConstant(valueSource.nodeIndex())); - - if (!nodePtr->shouldGenerate()) { - // It's legitimately dead. As in, nobody will ever use this node, or operand, - // ever. Set it to Undefined to make the GC happy after the OSR. - return ValueRecovery::constant(jsUndefined()); - } + ASSERT(valueSource.kind() == HaveNode); + if (isConstant(valueSource.nodeIndex())) + return ValueRecovery::constant(valueOfJSConstant(valueSource.nodeIndex())); - GenerationInfo* infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; - if (!infoPtr->alive() || infoPtr->nodeIndex() != valueSource.nodeIndex()) { - // Try to see if there is an alternate node that would contain the value we want. - // There are four possibilities: - // - // Int32ToDouble: We can use this in place of the original node, but - // we'd rather not; so we use it only if it is the only remaining - // live version. - // - // ValueToInt32: If the only remaining live version of the value is - // ValueToInt32, then we can use it. - // - // UInt32ToNumber: If the only live version of the value is a UInt32ToNumber - // then the only remaining uses are ones that want a properly formed number - // rather than a UInt32 intermediate. - // - // The reverse of the above: This node could be a UInt32ToNumber, but its - // alternative is still alive. This means that the only remaining uses of - // the number would be fine with a UInt32 intermediate. - // - // DoubleAsInt32: Same as UInt32ToNumber. - // - - bool found = false; - - if (nodePtr->op() == UInt32ToNumber || nodePtr->op() == DoubleAsInt32) { - NodeIndex nodeIndex = nodePtr->child1().index(); - nodePtr = &at(nodeIndex); - infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; - if (infoPtr->alive() && infoPtr->nodeIndex() == nodeIndex) - found = true; - } - - if (!found) { - NodeIndex int32ToDoubleIndex = NoNode; - NodeIndex valueToInt32Index = NoNode; - NodeIndex uint32ToNumberIndex = NoNode; - NodeIndex doubleAsInt32Index = NoNode; - - for (unsigned virtualRegister = 0; virtualRegister < m_generationInfo.size(); ++virtualRegister) { - GenerationInfo& info = m_generationInfo[virtualRegister]; - if (!info.alive()) - continue; - if (info.nodeIndex() == NoNode) - continue; - Node& node = at(info.nodeIndex()); - if (node.child1Unchecked() != valueSource.nodeIndex()) - continue; - switch (node.op()) { - case Int32ToDouble: - int32ToDoubleIndex = info.nodeIndex(); - break; - case ValueToInt32: - valueToInt32Index = info.nodeIndex(); - break; - case UInt32ToNumber: - uint32ToNumberIndex = info.nodeIndex(); - break; - case DoubleAsInt32: - doubleAsInt32Index = info.nodeIndex(); - default: - break; - } - } - - NodeIndex nodeIndexToUse; - if (doubleAsInt32Index != NoNode) - nodeIndexToUse = doubleAsInt32Index; - else if (int32ToDoubleIndex != NoNode) - nodeIndexToUse = int32ToDoubleIndex; - else if (valueToInt32Index != NoNode) - nodeIndexToUse = valueToInt32Index; - else if (uint32ToNumberIndex != NoNode) - nodeIndexToUse = uint32ToNumberIndex; - else - nodeIndexToUse = NoNode; - - if (nodeIndexToUse != NoNode) { - nodePtr = &at(nodeIndexToUse); - infoPtr = &m_generationInfo[nodePtr->virtualRegister()]; - ASSERT(infoPtr->alive() && infoPtr->nodeIndex() == nodeIndexToUse); - found = true; - } - } - - if (!found) - return ValueRecovery::constant(jsUndefined()); - } - - ASSERT(infoPtr->alive()); - - if (infoPtr->registerFormat() != DataFormatNone) { - if (infoPtr->registerFormat() == DataFormatDouble) - return ValueRecovery::inFPR(infoPtr->fpr()); -#if USE(JSVALUE32_64) - if (infoPtr->registerFormat() & DataFormatJS) - return ValueRecovery::inPair(infoPtr->tagGPR(), infoPtr->payloadGPR()); -#endif - return ValueRecovery::inGPR(infoPtr->gpr(), infoPtr->registerFormat()); - } - if (infoPtr->spillFormat() != DataFormatNone) - return ValueRecovery::displacedInRegisterFile(static_cast<VirtualRegister>(nodePtr->virtualRegister()), infoPtr->spillFormat()); - - ASSERT_NOT_REACHED(); - return ValueRecovery(); - } - - default: - ASSERT_NOT_REACHED(); - return ValueRecovery(); - } + return ValueRecovery(); } void SpeculativeJIT::compileGetCharCodeAt(Node& node) @@ -1652,10 +1517,11 @@ GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(NodeIndex node case DataFormatJSDouble: case DataFormatDouble: return GeneratedOperandDouble; + + default: + ASSERT_NOT_REACHED(); + return GeneratedOperandTypeUnknown; } - - ASSERT_NOT_REACHED(); - return GeneratedOperandTypeUnknown; } void SpeculativeJIT::compileValueToInt32(Node& node) diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 67a22b767..57bc84a12 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2012 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,6 +26,8 @@ #ifndef DFGSpeculativeJIT_h #define DFGSpeculativeJIT_h +#include <wtf/Platform.h> + #if ENABLE(DFG_JIT) #include "DFGAbstractState.h" @@ -34,6 +36,7 @@ #include "DFGOSRExit.h" #include "DFGOperations.h" #include "DFGSilentRegisterSavePlan.h" +#include "DFGValueSource.h" #include "MarkedAllocator.h" #include "ValueRecovery.h" @@ -48,87 +51,6 @@ class SpeculateDoubleOperand; class SpeculateCellOperand; class SpeculateBooleanOperand; - -enum ValueSourceKind { - SourceNotSet, - ValueInRegisterFile, - Int32InRegisterFile, - CellInRegisterFile, - BooleanInRegisterFile, - DoubleInRegisterFile, - ArgumentsSource, - SourceIsDead, - HaveNode -}; - -class ValueSource { -public: - ValueSource() - : m_nodeIndex(nodeIndexFromKind(SourceNotSet)) - { - } - - explicit ValueSource(ValueSourceKind valueSourceKind) - : m_nodeIndex(nodeIndexFromKind(valueSourceKind)) - { - ASSERT(kind() != SourceNotSet); - ASSERT(kind() != HaveNode); - } - - explicit ValueSource(NodeIndex nodeIndex) - : m_nodeIndex(nodeIndex) - { - ASSERT(kind() == HaveNode); - } - - static ValueSource forSpeculation(SpeculatedType prediction) - { - if (isInt32Speculation(prediction)) - return ValueSource(Int32InRegisterFile); - if (isArraySpeculation(prediction)) - return ValueSource(CellInRegisterFile); - if (isBooleanSpeculation(prediction)) - return ValueSource(BooleanInRegisterFile); - return ValueSource(ValueInRegisterFile); - } - - bool isSet() const - { - return kindFromNodeIndex(m_nodeIndex) != SourceNotSet; - } - - ValueSourceKind kind() const - { - return kindFromNodeIndex(m_nodeIndex); - } - - NodeIndex nodeIndex() const - { - ASSERT(kind() == HaveNode); - return m_nodeIndex; - } - - void dump(FILE* out) const; - -private: - static NodeIndex nodeIndexFromKind(ValueSourceKind kind) - { - ASSERT(kind >= SourceNotSet && kind < HaveNode); - return NoNode - kind; - } - - static ValueSourceKind kindFromNodeIndex(NodeIndex nodeIndex) - { - unsigned kind = static_cast<unsigned>(NoNode - nodeIndex); - if (kind >= static_cast<unsigned>(HaveNode)) - return HaveNode; - return static_cast<ValueSourceKind>(kind); - } - - NodeIndex m_nodeIndex; -}; - - enum GeneratedOperandType { GeneratedOperandTypeUnknown, GeneratedOperandInteger, GeneratedOperandDouble, GeneratedOperandJSValue}; // === SpeculativeJIT === @@ -326,7 +248,7 @@ public: // use() returns true when the value becomes dead, and any // associated resources may be freed. - if (!info.use()) + if (!info.use(*m_stream)) return; // Release the associated machine registers. @@ -376,6 +298,7 @@ public: void runSlowPathGenerators(); void compile(Node&); + void noticeOSRBirth(NodeIndex, Node&); void compileMovHint(Node&); void compile(BasicBlock&); @@ -777,7 +700,7 @@ public: // Check the GenerationInfo to see if this value need writing // to the RegisterFile - if not, mark it as spilled & return. if (!info.needsSpill()) { - info.setSpilled(); + info.setSpilled(*m_stream, spillMe); return; } @@ -787,20 +710,20 @@ public: // This is special, since it's not a JS value - as in it's not visible to JS // code. m_jit.storePtr(info.gpr(), JITCompiler::addressFor(spillMe)); - info.spill(DataFormatStorage); + info.spill(*m_stream, spillMe, DataFormatStorage); return; } case DataFormatInteger: { m_jit.store32(info.gpr(), JITCompiler::payloadFor(spillMe)); - info.spill(DataFormatInteger); + info.spill(*m_stream, spillMe, DataFormatInteger); return; } #if USE(JSVALUE64) case DataFormatDouble: { m_jit.storeDouble(info.fpr(), JITCompiler::addressFor(spillMe)); - info.spill(DataFormatDouble); + info.spill(*m_stream, spillMe, DataFormatDouble); return; } @@ -816,13 +739,13 @@ public: // Spill the value, and record it as spilled in its boxed form. m_jit.storePtr(reg, JITCompiler::addressFor(spillMe)); - info.spill((DataFormat)(spillFormat | DataFormatJS)); + info.spill(*m_stream, spillMe, (DataFormat)(spillFormat | DataFormatJS)); return; #elif USE(JSVALUE32_64) case DataFormatCell: case DataFormatBoolean: { m_jit.store32(info.gpr(), JITCompiler::payloadFor(spillMe)); - info.spill(spillFormat); + info.spill(*m_stream, spillMe, spillFormat); return; } @@ -830,7 +753,7 @@ public: case DataFormatJSDouble: { // On JSVALUE32_64 boxing a double is a no-op. m_jit.storeDouble(info.fpr(), JITCompiler::addressFor(spillMe)); - info.spill(DataFormatJSDouble); + info.spill(*m_stream, spillMe, DataFormatJSDouble); return; } @@ -839,7 +762,7 @@ public: ASSERT(spillFormat & DataFormatJS); m_jit.store32(info.tagGPR(), JITCompiler::tagFor(spillMe)); m_jit.store32(info.payloadGPR(), JITCompiler::payloadFor(spillMe)); - info.spill(spillFormat); + info.spill(*m_stream, spillMe, spillFormat); return; #endif } @@ -1800,7 +1723,7 @@ public: } #endif -#if !defined(NDEBUG) && !CPU(ARM_THUMB2) +#if !defined(NDEBUG) && !CPU(ARM) void prepareForExternalCall() { for (unsigned i = 0; i < sizeof(void*) / 4; i++) @@ -2180,8 +2103,7 @@ public: m_jit.storePtr(MacroAssembler::TrustedImmPtr(0), MacroAssembler::Address(resultGPR, JSObject::offsetOfInheritorID())); // Initialize the object's property storage pointer. - m_jit.addPtr(MacroAssembler::TrustedImm32(sizeof(JSObject)), resultGPR, scratchGPR); - m_jit.storePtr(scratchGPR, MacroAssembler::Address(resultGPR, ClassType::offsetOfPropertyStorage())); + m_jit.storePtr(MacroAssembler::TrustedImmPtr(0), MacroAssembler::Address(resultGPR, ClassType::offsetOfOutOfLineStorage())); } // It is acceptable to have structure be equal to scratch, so long as you're fine @@ -2204,7 +2126,7 @@ public: if (!m_compileOkay) return; ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this)); + m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size())); } void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail) { @@ -2231,7 +2153,7 @@ public: return; ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); m_jit.codeBlock()->appendSpeculationRecovery(recovery); - m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_jit.codeBlock()->numberOfSpeculationRecoveries())); + m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries())); } void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) { @@ -2252,7 +2174,7 @@ public: m_jit.codeBlock()->appendOSRExit( OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), - JITCompiler::Jump(), this))); + JITCompiler::Jump(), this, m_stream->size()))); exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint( Watchpoint(m_jit.watchpointLabel())); return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex); @@ -2295,7 +2217,8 @@ public: exit.m_codeOrigin = nextNode->codeOrigin; exit.m_lastSetOperand = setLocal->local(); - exit.valueRecoveryForOperand(setLocal->local()) = valueRecovery; + exit.m_valueRecoveryOverride = adoptRef( + new ValueRecoveryOverride(setLocal->local(), valueRecovery)); } void forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery) { @@ -2362,6 +2285,13 @@ public: return m_variables[operand]; } + void recordSetLocal(int operand, ValueSource valueSource) + { + valueSourceReferenceForOperand(operand) = valueSource; + m_stream->appendAndLog(VariableEvent::setLocal(operand, valueSource.dataFormat())); + } + + // The JIT, while also provides MacroAssembler functionality. JITCompiler& m_jit; // The current node being generated. @@ -2395,6 +2325,9 @@ public: AbstractState m_state; + VariableEventStream* m_stream; + MinifiedGraph* m_minifiedGraph; + bool m_isCheckingArgumentTypes; Vector<SlowPathGenerator*, 8> m_slowPathGenerators; // doesn't use OwnPtr<> because I don't want to include DFGSlowPathGenerator.h diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 05609baa8..bbbf3c40c 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -62,7 +62,7 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr); } - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -91,7 +91,7 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat m_gprs.release(tagGPR); m_gprs.release(payloadGPR); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderInteger); - info.fillInteger(payloadGPR); + info.fillInteger(*m_stream, payloadGPR); returnFormat = DataFormatInteger; return payloadGPR; } @@ -103,10 +103,11 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat returnFormat = DataFormatInteger; return gpr; } - } - ASSERT_NOT_REACHED(); - return InvalidGPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; + } } FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) @@ -123,13 +124,13 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); unlock(gpr); } else if (isNumberConstant(nodeIndex)) { FPRReg fpr = fprAllocate(); m_jit.loadDouble(addressOfDoubleConstant(nodeIndex), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } else { // FIXME: should not be reachable? @@ -142,7 +143,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) FPRReg fpr = fprAllocate(); m_jit.loadDouble(JITCompiler::addressFor(virtualRegister), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderSpilled); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -162,7 +163,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) hasUnboxedDouble.link(&m_jit); m_fprs.retain(fpr, virtualRegister, SpillOrderSpilled); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } } @@ -207,7 +208,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_gprs.unlock(tagGPR); m_gprs.unlock(payloadGPR); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); info.killSpilled(); return fpr; } @@ -227,10 +228,11 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_fprs.lock(fpr); return fpr; } - } - ASSERT_NOT_REACHED(); - return InvalidFPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidFPRReg; + } } bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& payloadGPR, FPRReg& fpr) @@ -252,7 +254,7 @@ bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& pa m_jit.move(Imm32(valueOfJSConstant(nodeIndex).payload()), payloadGPR); m_gprs.retain(tagGPR, virtualRegister, SpillOrderConstant); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderConstant); - info.fillJSValue(tagGPR, payloadGPR, isInt32Constant(nodeIndex) ? DataFormatJSInteger : DataFormatJS); + info.fillJSValue(*m_stream, tagGPR, payloadGPR, isInt32Constant(nodeIndex) ? DataFormatJSInteger : DataFormatJS); } else { DataFormat spillFormat = info.spillFormat(); ASSERT(spillFormat != DataFormatNone && spillFormat != DataFormatStorage); @@ -278,7 +280,7 @@ bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& pa m_jit.load32(JITCompiler::payloadFor(virtualRegister), payloadGPR); m_gprs.retain(tagGPR, virtualRegister, SpillOrderSpilled); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderSpilled); - info.fillJSValue(tagGPR, payloadGPR, spillFormat == DataFormatJSDouble ? DataFormatJS : spillFormat); + info.fillJSValue(*m_stream, tagGPR, payloadGPR, spillFormat == DataFormatJSDouble ? DataFormatJS : spillFormat); } return true; @@ -320,7 +322,7 @@ bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& pa m_gprs.release(gpr); m_gprs.retain(tagGPR, virtualRegister, SpillOrderJS); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderJS); - info.fillJSValue(tagGPR, payloadGPR, fillFormat); + info.fillJSValue(*m_stream, tagGPR, payloadGPR, fillFormat); return true; } @@ -335,7 +337,7 @@ bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& pa m_fprs.release(oldFPR); m_gprs.retain(tagGPR, virtualRegister, SpillOrderJS); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderJS); - info.fillJSValue(tagGPR, payloadGPR, DataFormatJS); + info.fillJSValue(*m_stream, tagGPR, payloadGPR, DataFormatJS); return true; } @@ -353,10 +355,11 @@ bool SpeculativeJIT::fillJSValue(NodeIndex nodeIndex, GPRReg& tagGPR, GPRReg& pa case DataFormatStorage: // this type currently never occurs ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return true; + default: + ASSERT_NOT_REACHED(); + return true; + } } class ValueToNumberSlowPathGenerator @@ -505,7 +508,7 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseTagGPROrNon JITCompiler::DataLabelPtr structureToCompare; JITCompiler::PatchableJump structureCheck = m_jit.patchableBranchPtrWithPatch(JITCompiler::NotEqual, JITCompiler::Address(basePayloadGPR, JSCell::structureOffset()), structureToCompare, JITCompiler::TrustedImmPtr(reinterpret_cast<void*>(-1))); - m_jit.loadPtr(JITCompiler::Address(basePayloadGPR, JSObject::offsetOfPropertyStorage()), resultPayloadGPR); + JITCompiler::ConvertibleLoadLabel propertyStorageLoad = m_jit.convertibleLoadPtr(JITCompiler::Address(basePayloadGPR, JSObject::offsetOfOutOfLineStorage()), resultPayloadGPR); JITCompiler::DataLabelCompact tagLoadWithPatch = m_jit.load32WithCompactAddressOffsetPatch(JITCompiler::Address(resultPayloadGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); JITCompiler::DataLabelCompact payloadLoadWithPatch = m_jit.load32WithCompactAddressOffsetPatch(JITCompiler::Address(resultPayloadGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultPayloadGPR); @@ -547,7 +550,7 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseTagGPROrNon } m_jit.addPropertyAccess( PropertyAccessRecord( - codeOrigin, structureToCompare, structureCheck, + codeOrigin, structureToCompare, structureCheck, propertyStorageLoad, tagLoadWithPatch, payloadLoadWithPatch, slowPath.get(), doneLabel, safeCast<int8_t>(basePayloadGPR), safeCast<int8_t>(resultTagGPR), safeCast<int8_t>(resultPayloadGPR), safeCast<int8_t>(scratchGPR), @@ -562,7 +565,7 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR, writeBarrier(basePayloadGPR, valueTagGPR, valueUse, WriteBarrierForPropertyAccess, scratchGPR); - m_jit.loadPtr(JITCompiler::Address(basePayloadGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); + JITCompiler::ConvertibleLoadLabel propertyStorageLoad = m_jit.convertibleLoadPtr(JITCompiler::Address(basePayloadGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); JITCompiler::DataLabel32 tagStoreWithPatch = m_jit.store32WithAddressOffsetPatch(valueTagGPR, JITCompiler::Address(scratchGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); JITCompiler::DataLabel32 payloadStoreWithPatch = m_jit.store32WithAddressOffsetPatch(valuePayloadGPR, JITCompiler::Address(scratchGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); @@ -594,7 +597,7 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR, } m_jit.addPropertyAccess( PropertyAccessRecord( - codeOrigin, structureToCompare, structureCheck, + codeOrigin, structureToCompare, structureCheck, propertyStorageLoad, JITCompiler::DataLabelCompact(tagStoreWithPatch.label()), JITCompiler::DataLabelCompact(payloadStoreWithPatch.label()), slowPath.get(), doneLabel, safeCast<int8_t>(basePayloadGPR), @@ -1065,7 +1068,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& GPRReg gpr = allocate(); m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -1080,7 +1083,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& GPRReg gpr = allocate(); m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -1098,7 +1101,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& m_gprs.release(tagGPR); m_gprs.release(payloadGPR); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderInteger); - info.fillInteger(payloadGPR); + info.fillInteger(*m_stream, payloadGPR); // If !strict we're done, return. returnFormat = DataFormatInteger; return payloadGPR; @@ -1119,10 +1122,11 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& case DataFormatJSBoolean: case DataFormatStorage: ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return InvalidGPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; + } } GPRReg SpeculativeJIT::fillSpeculateInt(NodeIndex nodeIndex, DataFormat& returnFormat) @@ -1160,24 +1164,24 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); unlock(gpr); } else if (isNumberConstant(nodeIndex)) { FPRReg fpr = fprAllocate(); m_jit.loadDouble(addressOfDoubleConstant(nodeIndex), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderConstant); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } else ASSERT_NOT_REACHED(); } else { DataFormat spillFormat = info.spillFormat(); ASSERT((spillFormat & DataFormatJS) || spillFormat == DataFormatInteger); - if (spillFormat == DataFormatJSDouble) { + if (spillFormat == DataFormatJSDouble || spillFormat == DataFormatDouble) { FPRReg fpr = fprAllocate(); m_jit.loadDouble(JITCompiler::addressFor(virtualRegister), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderSpilled); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -1200,7 +1204,8 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) hasUnboxedDouble.link(&m_jit); m_fprs.retain(fpr, virtualRegister, SpillOrderSpilled); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); + info.killSpilled(); return fpr; } } @@ -1237,7 +1242,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) m_gprs.unlock(tagGPR); m_gprs.unlock(payloadGPR); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); info.killSpilled(); return fpr; } @@ -1265,10 +1270,11 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) case DataFormatBoolean: case DataFormatJSBoolean: ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return InvalidFPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidFPRReg; + } } GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) @@ -1295,7 +1301,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); m_jit.move(MacroAssembler::TrustedImmPtr(jsValue.asCell()), gpr); - info.fillCell(gpr); + info.fillCell(*m_stream, gpr); return gpr; } @@ -1305,7 +1311,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); - info.fillCell(gpr); + info.fillCell(*m_stream, gpr); return gpr; } @@ -1327,7 +1333,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) m_gprs.release(tagGPR); m_gprs.release(payloadGPR); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderCell); - info.fillCell(payloadGPR); + info.fillCell(*m_stream, payloadGPR); return payloadGPR; } @@ -1339,10 +1345,11 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) case DataFormatBoolean: case DataFormatStorage: ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return InvalidGPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; + } } GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) @@ -1369,7 +1376,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); m_jit.move(MacroAssembler::TrustedImm32(jsValue.asBoolean()), gpr); - info.fillBoolean(gpr); + info.fillBoolean(*m_stream, gpr); return gpr; } @@ -1381,7 +1388,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) GPRReg gpr = allocate(); m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); - info.fillBoolean(gpr); + info.fillBoolean(*m_stream, gpr); return gpr; } @@ -1404,7 +1411,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) m_gprs.release(tagGPR); m_gprs.release(payloadGPR); m_gprs.retain(payloadGPR, virtualRegister, SpillOrderBoolean); - info.fillBoolean(payloadGPR); + info.fillBoolean(*m_stream, payloadGPR); return payloadGPR; } @@ -1416,10 +1423,11 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) case DataFormatCell: case DataFormatStorage: ASSERT_NOT_REACHED(); - } - ASSERT_NOT_REACHED(); - return InvalidGPRReg; + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; + } } JITCompiler::Jump SpeculativeJIT::convertToDouble(JSValueOperand& op, FPRReg result) @@ -2003,7 +2011,7 @@ void SpeculativeJIT::compile(Node& node) // Indicate that it's no longer necessary to retrieve the value of // this bytecode variable from registers or other locations in the register file, // but that it is stored as a double. - valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + recordSetLocal(node.local(), ValueSource(DoubleInRegisterFile)); break; } SpeculatedType predictedType = node.variableAccessData()->argumentAwarePrediction(); @@ -2011,14 +2019,14 @@ void SpeculativeJIT::compile(Node& node) DoubleOperand value(this, node.child1()); m_jit.storeDouble(value.fpr(), JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + recordSetLocal(node.local(), ValueSource(DoubleInRegisterFile)); break; } if (isInt32Speculation(predictedType)) { SpeculateIntegerOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(Int32InRegisterFile); + recordSetLocal(node.local(), ValueSource(Int32InRegisterFile)); break; } if (isArraySpeculation(predictedType)) { @@ -2028,14 +2036,14 @@ void SpeculativeJIT::compile(Node& node) speculationCheck(BadType, JSValueSource::unboxedCell(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + recordSetLocal(node.local(), ValueSource(CellInRegisterFile)); break; } if (isBooleanSpeculation(predictedType)) { SpeculateBooleanOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(BooleanInRegisterFile); + recordSetLocal(node.local(), ValueSource(BooleanInRegisterFile)); break; } } @@ -2043,7 +2051,7 @@ void SpeculativeJIT::compile(Node& node) m_jit.store32(value.payloadGPR(), JITCompiler::payloadFor(node.local())); m_jit.store32(value.tagGPR(), JITCompiler::tagFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(ValueInRegisterFile); + recordSetLocal(node.local(), ValueSource(ValueInRegisterFile)); break; } @@ -3542,7 +3550,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseGPR = base.gpr(); GPRReg resultGPR = result.gpr(); - m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR); + m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR); storageResult(resultGPR, m_compileIndex); break; @@ -3883,10 +3891,15 @@ void SpeculativeJIT::compile(Node& node) JITCompiler::Jump structuresNotMatch = m_jit.branchPtr(JITCompiler::NotEqual, resultPayloadGPR, JITCompiler::Address(globalObjectGPR, JSCell::structureOffset())); // Fast case - m_jit.loadPtr(JITCompiler::Address(globalObjectGPR, JSObject::offsetOfPropertyStorage()), resultPayloadGPR); + m_jit.loadPtr(JITCompiler::Address(globalObjectGPR, JSObject::offsetOfOutOfLineStorage()), resultPayloadGPR); m_jit.load32(JITCompiler::Address(resolveInfoGPR, OBJECT_OFFSETOF(GlobalResolveInfo, offset)), resolveInfoGPR); - m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR); - m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultPayloadGPR); +#if DFG_ENABLE(JIT_ASSERT) + JITCompiler::Jump isOutOfLine = m_jit.branch32(JITCompiler::GreaterThanOrEqual, resolveInfoGPR, TrustedImm32(inlineStorageCapacity)); + m_jit.breakpoint(); + isOutOfLine.link(&m_jit); +#endif + m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag) - inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultTagGPR); + m_jit.load32(JITCompiler::BaseIndex(resultPayloadGPR, resolveInfoGPR, JITCompiler::TimesEight, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload) - inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultPayloadGPR); addSlowPathGenerator( slowPathCall( diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 215f8013d..27eb28fa7 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -48,7 +48,7 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); if (isInt32Constant(nodeIndex)) { m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -74,7 +74,7 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat // Since we statically know that we're filling an integer, and values // in the RegisterFile are boxed, this must be DataFormatJSInteger. // We will check this with a jitAssert below. - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); unlock(gpr); } @@ -107,10 +107,11 @@ GPRReg SpeculativeJIT::fillInteger(NodeIndex nodeIndex, DataFormat& returnFormat returnFormat = DataFormatInteger; return gpr; } + + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidGPRReg; } FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) @@ -127,7 +128,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) // FIXME: should not be reachable? m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); unlock(gpr); } else if (isNumberConstant(nodeIndex)) { FPRReg fpr = fprAllocate(); @@ -136,7 +137,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) unlock(gpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } else { // FIXME: should not be reachable? @@ -144,7 +145,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) JSValue jsValue = valueOfJSConstant(nodeIndex); m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); - info.fillJSValue(gpr, DataFormatJS); + info.fillJSValue(*m_stream, gpr, DataFormatJS); unlock(gpr); } } else { @@ -154,7 +155,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) FPRReg fpr = fprAllocate(); m_jit.loadDouble(JITCompiler::addressFor(virtualRegister), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -163,7 +164,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); unlock(gpr); break; } @@ -174,7 +175,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) ASSERT(spillFormat & DataFormatJS); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillJSValue(gpr, spillFormat); + info.fillJSValue(*m_stream, gpr, spillFormat); unlock(gpr); break; } @@ -216,7 +217,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_gprs.unlock(jsValueGpr); m_gprs.unlock(tempGpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); info.killSpilled(); return fpr; } @@ -247,7 +248,7 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_gprs.release(gpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -256,10 +257,11 @@ FPRReg SpeculativeJIT::fillDouble(NodeIndex nodeIndex) m_fprs.lock(fpr); return fpr; } + + default: + ASSERT_NOT_REACHED(); + return InvalidFPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidFPRReg; } GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) @@ -274,18 +276,18 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) if (node.hasConstant()) { if (isInt32Constant(nodeIndex)) { - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); JSValue jsValue = jsNumber(valueOfInt32Constant(nodeIndex)); m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr); } else if (isNumberConstant(nodeIndex)) { - info.fillJSValue(gpr, DataFormatJSDouble); + info.fillJSValue(*m_stream, gpr, DataFormatJSDouble); JSValue jsValue(JSValue::EncodeAsDouble, valueOfNumberConstant(nodeIndex)); m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), gpr); } else { ASSERT(isJSConstant(nodeIndex)); JSValue jsValue = valueOfJSConstant(nodeIndex); m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); - info.fillJSValue(gpr, DataFormatJS); + info.fillJSValue(*m_stream, gpr, DataFormatJS); } m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); @@ -305,7 +307,7 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) } else ASSERT(spillFormat & DataFormatJS); } - info.fillJSValue(gpr, spillFormat); + info.fillJSValue(*m_stream, gpr, spillFormat); } return gpr; } @@ -321,7 +323,7 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) } m_gprs.lock(gpr); m_jit.orPtr(GPRInfo::tagTypeNumberRegister, gpr); - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); return gpr; } @@ -330,7 +332,7 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) GPRReg gpr = boxDouble(fpr); // Update all info - info.fillJSValue(gpr, DataFormatJSDouble); + info.fillJSValue(*m_stream, gpr, DataFormatJSDouble); m_fprs.release(fpr); m_gprs.retain(gpr, virtualRegister, SpillOrderJS); @@ -353,10 +355,11 @@ GPRReg SpeculativeJIT::fillJSValue(NodeIndex nodeIndex) case DataFormatStorage: // this type currently never occurs ASSERT_NOT_REACHED(); + + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidGPRReg; } class ValueToNumberSlowPathGenerator @@ -494,7 +497,8 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg JITCompiler::DataLabelPtr structureToCompare; JITCompiler::PatchableJump structureCheck = m_jit.patchableBranchPtrWithPatch(JITCompiler::NotEqual, JITCompiler::Address(baseGPR, JSCell::structureOffset()), structureToCompare, JITCompiler::TrustedImmPtr(reinterpret_cast<void*>(-1))); - m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR); + JITCompiler::ConvertibleLoadLabel propertyStorageLoad = + m_jit.convertibleLoadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR); JITCompiler::DataLabelCompact loadWithPatch = m_jit.loadPtrWithCompactAddressOffsetPatch(JITCompiler::Address(resultGPR, 0), resultGPR); JITCompiler::Label doneLabel = m_jit.label(); @@ -514,8 +518,8 @@ void SpeculativeJIT::cachedGetById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg } m_jit.addPropertyAccess( PropertyAccessRecord( - codeOrigin, structureToCompare, structureCheck, loadWithPatch, slowPath.get(), - doneLabel, safeCast<int8_t>(baseGPR), safeCast<int8_t>(resultGPR), + codeOrigin, structureToCompare, structureCheck, propertyStorageLoad, loadWithPatch, + slowPath.get(), doneLabel, safeCast<int8_t>(baseGPR), safeCast<int8_t>(resultGPR), safeCast<int8_t>(scratchGPR), spillMode == NeedToSpill ? PropertyAccessRecord::RegistersInUse : PropertyAccessRecord::RegistersFlushed)); addSlowPathGenerator(slowPath.release()); @@ -533,7 +537,8 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg writeBarrier(baseGPR, valueGPR, valueUse, WriteBarrierForPropertyAccess, scratchGPR); - m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), scratchGPR); + JITCompiler::ConvertibleLoadLabel propertyStorageLoad = + m_jit.convertibleLoadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), scratchGPR); JITCompiler::DataLabel32 storeWithPatch = m_jit.storePtrWithAddressOffsetPatch(valueGPR, JITCompiler::Address(scratchGPR, 0)); JITCompiler::Label doneLabel = m_jit.label(); @@ -563,7 +568,11 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg slowCases, this, optimizedCall, NoResult, valueGPR, baseGPR, identifier(identifierNumber)); } - m_jit.addPropertyAccess(PropertyAccessRecord(codeOrigin, structureToCompare, structureCheck, JITCompiler::DataLabelCompact(storeWithPatch.label()), slowPath.get(), doneLabel, safeCast<int8_t>(baseGPR), safeCast<int8_t>(valueGPR), safeCast<int8_t>(scratchGPR))); + m_jit.addPropertyAccess( + PropertyAccessRecord( + codeOrigin, structureToCompare, structureCheck, propertyStorageLoad, + JITCompiler::DataLabelCompact(storeWithPatch.label()), slowPath.get(), doneLabel, + safeCast<int8_t>(baseGPR), safeCast<int8_t>(valueGPR), safeCast<int8_t>(scratchGPR))); addSlowPathGenerator(slowPath.release()); } @@ -1047,7 +1056,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); ASSERT(isInt32Constant(nodeIndex)); m_jit.move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -1062,7 +1071,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& // If we know this was spilled as an integer we can fill without checking. if (strict) { m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); returnFormat = DataFormatInteger; return gpr; } @@ -1071,14 +1080,14 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& m_jit.orPtr(GPRInfo::tagTypeNumberRegister, gpr); } else m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); returnFormat = DataFormatJSInteger; return gpr; } m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); // Fill as JSValue, and fall through. - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); m_gprs.unlock(gpr); } @@ -1088,7 +1097,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& m_gprs.lock(gpr); if (!isInt32Speculation(type)) speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchPtr(MacroAssembler::Below, gpr, GPRInfo::tagTypeNumberRegister)); - info.fillJSValue(gpr, DataFormatJSInteger); + info.fillJSValue(*m_stream, gpr, DataFormatJSInteger); // If !strict we're done, return. if (!strict) { returnFormat = DataFormatJSInteger; @@ -1109,7 +1118,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& result = allocate(); else { m_gprs.lock(gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); result = gpr; } m_jit.zeroExtend32ToPtr(gpr, result); @@ -1151,10 +1160,11 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& case DataFormatStorage: ASSERT_NOT_REACHED(); + + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidGPRReg; } GPRReg SpeculativeJIT::fillSpeculateInt(NodeIndex nodeIndex, DataFormat& returnFormat) @@ -1191,7 +1201,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) unlock(gpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } if (isNumberConstant(nodeIndex)) { @@ -1201,7 +1211,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) unlock(gpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1214,7 +1224,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) FPRReg fpr = fprAllocate(); m_jit.loadDouble(JITCompiler::addressFor(virtualRegister), fpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -1223,7 +1233,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.load32(JITCompiler::addressFor(virtualRegister), gpr); - info.fillInteger(gpr); + info.fillInteger(*m_stream, gpr); unlock(gpr); break; } @@ -1234,7 +1244,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) ASSERT(spillFormat & DataFormatJS); m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillJSValue(gpr, spillFormat); + info.fillJSValue(*m_stream, gpr, spillFormat); unlock(gpr); break; } @@ -1277,7 +1287,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) m_gprs.unlock(jsValueGpr); m_gprs.unlock(tempGpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); info.killSpilled(); return fpr; } @@ -1308,7 +1318,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) m_gprs.release(gpr); m_fprs.retain(fpr, virtualRegister, SpillOrderDouble); - info.fillDouble(fpr); + info.fillDouble(*m_stream, fpr); return fpr; } @@ -1317,10 +1327,11 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) m_fprs.lock(fpr); return fpr; } + + default: + ASSERT_NOT_REACHED(); + return InvalidFPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidFPRReg; } GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) @@ -1347,7 +1358,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) if (jsValue.isCell()) { m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); m_jit.move(MacroAssembler::TrustedImmPtr(jsValue.asCell()), gpr); - info.fillJSValue(gpr, DataFormatJSCell); + info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1357,10 +1368,10 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillJSValue(gpr, DataFormatJS); + info.fillJSValue(*m_stream, gpr, DataFormatJS); if (!isCellSpeculation(type)) speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister)); - info.fillJSValue(gpr, DataFormatJSCell); + info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } @@ -1376,7 +1387,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) m_gprs.lock(gpr); if (!isCellSpeculation(type)) speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister)); - info.fillJSValue(gpr, DataFormatJSCell); + info.fillJSValue(*m_stream, gpr, DataFormatJSCell); return gpr; } @@ -1392,10 +1403,11 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex) case DataFormatStorage: ASSERT_NOT_REACHED(); + + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidGPRReg; } GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) @@ -1422,7 +1434,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) if (jsValue.isBoolean()) { m_gprs.retain(gpr, virtualRegister, SpillOrderConstant); m_jit.move(MacroAssembler::TrustedImmPtr(JSValue::encode(jsValue)), gpr); - info.fillJSValue(gpr, DataFormatJSBoolean); + info.fillJSValue(*m_stream, gpr, DataFormatJSBoolean); return gpr; } terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1432,13 +1444,13 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled); m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), gpr); - info.fillJSValue(gpr, DataFormatJS); + info.fillJSValue(*m_stream, gpr, DataFormatJS); if (!isBooleanSpeculation(type)) { m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), gpr); speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, TrustedImm32(static_cast<int32_t>(~1))), SpeculationRecovery(BooleanSpeculationCheck, gpr, InvalidGPRReg)); m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), gpr); } - info.fillJSValue(gpr, DataFormatJSBoolean); + info.fillJSValue(*m_stream, gpr, DataFormatJSBoolean); return gpr; } @@ -1457,7 +1469,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, TrustedImm32(static_cast<int32_t>(~1))), SpeculationRecovery(BooleanSpeculationCheck, gpr, InvalidGPRReg)); m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), gpr); } - info.fillJSValue(gpr, DataFormatJSBoolean); + info.fillJSValue(*m_stream, gpr, DataFormatJSBoolean); return gpr; } @@ -1473,10 +1485,11 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) case DataFormatStorage: ASSERT_NOT_REACHED(); + + default: + ASSERT_NOT_REACHED(); + return InvalidGPRReg; } - - ASSERT_NOT_REACHED(); - return InvalidGPRReg; } JITCompiler::Jump SpeculativeJIT::convertToDouble(GPRReg value, FPRReg result, GPRReg tmp) @@ -2055,7 +2068,7 @@ void SpeculativeJIT::compile(Node& node) // Indicate that it's no longer necessary to retrieve the value of // this bytecode variable from registers or other locations in the register file, // but that it is stored as a double. - valueSourceReferenceForOperand(node.local()) = ValueSource(DoubleInRegisterFile); + recordSetLocal(node.local(), ValueSource(DoubleInRegisterFile)); break; } @@ -2064,7 +2077,7 @@ void SpeculativeJIT::compile(Node& node) SpeculateIntegerOperand value(this, node.child1()); m_jit.store32(value.gpr(), JITCompiler::payloadFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(Int32InRegisterFile); + recordSetLocal(node.local(), ValueSource(Int32InRegisterFile)); break; } if (isArraySpeculation(predictedType)) { @@ -2074,14 +2087,14 @@ void SpeculativeJIT::compile(Node& node) speculationCheck(BadType, JSValueRegs(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info))); m_jit.storePtr(cellGPR, JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(CellInRegisterFile); + recordSetLocal(node.local(), ValueSource(CellInRegisterFile)); break; } if (isBooleanSpeculation(predictedType)) { SpeculateBooleanOperand boolean(this, node.child1()); m_jit.storePtr(boolean.gpr(), JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(BooleanInRegisterFile); + recordSetLocal(node.local(), ValueSource(BooleanInRegisterFile)); break; } } @@ -2090,7 +2103,7 @@ void SpeculativeJIT::compile(Node& node) m_jit.storePtr(value.gpr(), JITCompiler::addressFor(node.local())); noResult(m_compileIndex); - valueSourceReferenceForOperand(node.local()) = ValueSource(ValueInRegisterFile); + recordSetLocal(node.local(), ValueSource(ValueInRegisterFile)); break; } @@ -3567,7 +3580,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg baseGPR = base.gpr(); GPRReg resultGPR = result.gpr(); - m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfPropertyStorage()), resultGPR); + m_jit.loadPtr(JITCompiler::Address(baseGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR); storageResult(resultGPR, m_compileIndex); break; @@ -3883,9 +3896,14 @@ void SpeculativeJIT::compile(Node& node) JITCompiler::Jump structuresDontMatch = m_jit.branchPtr(JITCompiler::NotEqual, resultGPR, JITCompiler::Address(globalObjectGPR, JSCell::structureOffset())); // Fast case - m_jit.loadPtr(JITCompiler::Address(globalObjectGPR, JSObject::offsetOfPropertyStorage()), resultGPR); m_jit.load32(JITCompiler::Address(resolveInfoGPR, OBJECT_OFFSETOF(GlobalResolveInfo, offset)), resolveInfoGPR); - m_jit.loadPtr(JITCompiler::BaseIndex(resultGPR, resolveInfoGPR, JITCompiler::ScalePtr), resultGPR); +#if DFG_ENABLE(JIT_ASSERT) + JITCompiler::Jump isOutOfLine = m_jit.branch32(JITCompiler::GreaterThanOrEqual, resolveInfoGPR, TrustedImm32(inlineStorageCapacity)); + m_jit.breakpoint(); + isOutOfLine.link(&m_jit); +#endif + m_jit.loadPtr(JITCompiler::Address(globalObjectGPR, JSObject::offsetOfOutOfLineStorage()), resultGPR); + m_jit.loadPtr(JITCompiler::BaseIndex(resultGPR, resolveInfoGPR, JITCompiler::ScalePtr, -inlineStorageCapacity * static_cast<ptrdiff_t>(sizeof(JSValue))), resultGPR); addSlowPathGenerator( slowPathCall( diff --git a/Source/JavaScriptCore/dfg/DFGValueRecoveryOverride.h b/Source/JavaScriptCore/dfg/DFGValueRecoveryOverride.h new file mode 100644 index 000000000..317111aec --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGValueRecoveryOverride.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGValueRecoveryOverride_h +#define DFGValueRecoveryOverride_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "ValueRecovery.h" +#include <wtf/RefCounted.h> + +namespace JSC { namespace DFG { + +class ValueRecoveryOverride : public RefCounted<ValueRecoveryOverride> { +public: + ValueRecoveryOverride() { } + + ValueRecoveryOverride(int operand, const ValueRecovery& recovery) + : operand(operand) + , recovery(recovery) + { + } + + int operand; + ValueRecovery recovery; +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGValueRecoveryOverride_h + diff --git a/Source/JavaScriptCore/dfg/DFGValueSource.cpp b/Source/JavaScriptCore/dfg/DFGValueSource.cpp new file mode 100644 index 000000000..25d43ee6b --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGValueSource.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DFGValueSource.h" + +#if ENABLE(DFG_JIT) + +namespace JSC { namespace DFG { + +void ValueSource::dump(FILE* out) const +{ + switch (kind()) { + case SourceNotSet: + fprintf(out, "NotSet"); + break; + case SourceIsDead: + fprintf(out, "IsDead"); + break; + case ValueInRegisterFile: + fprintf(out, "InRegFile"); + break; + case Int32InRegisterFile: + fprintf(out, "Int32"); + break; + case CellInRegisterFile: + fprintf(out, "Cell"); + break; + case BooleanInRegisterFile: + fprintf(out, "Bool"); + break; + case DoubleInRegisterFile: + fprintf(out, "Double"); + break; + case ArgumentsSource: + fprintf(out, "Arguments"); + break; + case HaveNode: + fprintf(out, "Node(%d)", m_nodeIndex); + break; + } +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + diff --git a/Source/JavaScriptCore/dfg/DFGValueSource.h b/Source/JavaScriptCore/dfg/DFGValueSource.h new file mode 100644 index 000000000..be4a6e081 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGValueSource.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGValueSource_h +#define DFGValueSource_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGCommon.h" +#include "DataFormat.h" +#include "SpeculatedType.h" +#include "ValueRecovery.h" + +namespace JSC { namespace DFG { + +enum ValueSourceKind { + SourceNotSet, + ValueInRegisterFile, + Int32InRegisterFile, + CellInRegisterFile, + BooleanInRegisterFile, + DoubleInRegisterFile, + ArgumentsSource, + SourceIsDead, + HaveNode +}; + +static inline ValueSourceKind dataFormatToValueSourceKind(DataFormat dataFormat) +{ + switch (dataFormat) { + case DataFormatInteger: + return Int32InRegisterFile; + case DataFormatDouble: + return DoubleInRegisterFile; + case DataFormatBoolean: + return BooleanInRegisterFile; + case DataFormatCell: + return CellInRegisterFile; + case DataFormatDead: + return SourceIsDead; + case DataFormatArguments: + return ArgumentsSource; + default: + ASSERT(dataFormat & DataFormatJS); + return ValueInRegisterFile; + } +} + +static inline DataFormat valueSourceKindToDataFormat(ValueSourceKind kind) +{ + switch (kind) { + case ValueInRegisterFile: + return DataFormatJS; + case Int32InRegisterFile: + return DataFormatInteger; + case CellInRegisterFile: + return DataFormatCell; + case BooleanInRegisterFile: + return DataFormatBoolean; + case DoubleInRegisterFile: + return DataFormatDouble; + case ArgumentsSource: + return DataFormatArguments; + case SourceIsDead: + return DataFormatDead; + default: + return DataFormatNone; + } +} + +static inline bool isInRegisterFile(ValueSourceKind kind) +{ + DataFormat format = valueSourceKindToDataFormat(kind); + return format != DataFormatNone && format < DataFormatOSRMarker; +} + +// Can this value be recovered without having to look at register allocation state or +// DFG node liveness? +static inline bool isTriviallyRecoverable(ValueSourceKind kind) +{ + return valueSourceKindToDataFormat(kind) != DataFormatNone; +} + +class ValueSource { +public: + ValueSource() + : m_nodeIndex(nodeIndexFromKind(SourceNotSet)) + { + } + + explicit ValueSource(ValueSourceKind valueSourceKind) + : m_nodeIndex(nodeIndexFromKind(valueSourceKind)) + { + ASSERT(kind() != SourceNotSet); + ASSERT(kind() != HaveNode); + } + + explicit ValueSource(NodeIndex nodeIndex) + : m_nodeIndex(nodeIndex) + { + ASSERT(nodeIndex != NoNode); + ASSERT(kind() == HaveNode); + } + + static ValueSource forSpeculation(SpeculatedType prediction) + { + if (isInt32Speculation(prediction)) + return ValueSource(Int32InRegisterFile); + if (isArraySpeculation(prediction)) + return ValueSource(CellInRegisterFile); + if (isBooleanSpeculation(prediction)) + return ValueSource(BooleanInRegisterFile); + return ValueSource(ValueInRegisterFile); + } + + static ValueSource forDataFormat(DataFormat dataFormat) + { + return ValueSource(dataFormatToValueSourceKind(dataFormat)); + } + + bool isSet() const + { + return kindFromNodeIndex(m_nodeIndex) != SourceNotSet; + } + + ValueSourceKind kind() const + { + return kindFromNodeIndex(m_nodeIndex); + } + + bool isInRegisterFile() const { return JSC::DFG::isInRegisterFile(kind()); } + bool isTriviallyRecoverable() const { return JSC::DFG::isTriviallyRecoverable(kind()); } + + DataFormat dataFormat() const + { + return valueSourceKindToDataFormat(kind()); + } + + ValueRecovery valueRecovery() const + { + ASSERT(isTriviallyRecoverable()); + switch (kind()) { + case ValueInRegisterFile: + return ValueRecovery::alreadyInRegisterFile(); + + case Int32InRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedInt32(); + + case CellInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedCell(); + + case BooleanInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedBoolean(); + + case DoubleInRegisterFile: + return ValueRecovery::alreadyInRegisterFileAsUnboxedDouble(); + + case SourceIsDead: + return ValueRecovery::constant(jsUndefined()); + + case ArgumentsSource: + return ValueRecovery::argumentsThatWereNotCreated(); + + default: + ASSERT_NOT_REACHED(); + return ValueRecovery(); + } + } + + NodeIndex nodeIndex() const + { + ASSERT(kind() == HaveNode); + return m_nodeIndex; + } + + void dump(FILE* out) const; + +private: + static NodeIndex nodeIndexFromKind(ValueSourceKind kind) + { + ASSERT(kind >= SourceNotSet && kind < HaveNode); + return NoNode - kind; + } + + static ValueSourceKind kindFromNodeIndex(NodeIndex nodeIndex) + { + unsigned kind = static_cast<unsigned>(NoNode - nodeIndex); + if (kind >= static_cast<unsigned>(HaveNode)) + return HaveNode; + return static_cast<ValueSourceKind>(kind); + } + + NodeIndex m_nodeIndex; +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGValueSource_h + diff --git a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h index 382907d27..e734e6387 100644 --- a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h +++ b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h @@ -176,7 +176,7 @@ public: // If the variable has been voted to become a double, then make it a // double. - if (doubleVoteRatio() >= Options::doubleVoteRatioForDoubleFormat) + if (doubleVoteRatio() >= Options::doubleVoteRatioForDoubleFormat()) return true; return false; diff --git a/Source/JavaScriptCore/dfg/DFGVariableEvent.cpp b/Source/JavaScriptCore/dfg/DFGVariableEvent.cpp new file mode 100644 index 000000000..3e84a6ba1 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGVariableEvent.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DFGVariableEvent.h" + +#if ENABLE(DFG_JIT) + +#include "DFGFPRInfo.h" +#include "DFGGPRInfo.h" + +namespace JSC { namespace DFG { + +void VariableEvent::dump(FILE* out) const +{ + switch (kind()) { + case Reset: + fprintf(out, "Reset"); + break; + case BirthToFill: + dumpFillInfo("BirthToFill", out); + break; + case BirthToSpill: + dumpSpillInfo("BirthToSpill", out); + break; + case Fill: + dumpFillInfo("Fill", out); + break; + case Spill: + dumpSpillInfo("Spill", out); + break; + case Death: + fprintf(out, "Death(@%u)", nodeIndex()); + break; + case MovHint: + fprintf(out, "MovHint(@%u, r%d)", nodeIndex(), operand()); + break; + case SetLocalEvent: + fprintf(out, "SetLocal(r%d, %s)", operand(), dataFormatToString(dataFormat())); + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void VariableEvent::dumpFillInfo(const char* name, FILE* out) const +{ + fprintf(out, "%s(@%u, ", name, nodeIndex()); + if (dataFormat() == DataFormatDouble) + fprintf(out, "%s", FPRInfo::debugName(fpr())); +#if USE(JSVALUE32_64) + else if (dataFormat() & DataFormatJS) + fprintf(out, "%s:%s", GPRInfo::debugName(tagGPR()), GPRInfo::debugName(payloadGPR())); +#endif + else + fprintf(out, "%s", GPRInfo::debugName(gpr())); + fprintf(out, ", %s)", dataFormatToString(dataFormat())); +} + +void VariableEvent::dumpSpillInfo(const char* name, FILE* out) const +{ + fprintf(out, "%s(@%u, r%d, %s)", name, nodeIndex(), virtualRegister(), dataFormatToString(dataFormat())); +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + diff --git a/Source/JavaScriptCore/dfg/DFGVariableEvent.h b/Source/JavaScriptCore/dfg/DFGVariableEvent.h new file mode 100644 index 000000000..a491a3ebf --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGVariableEvent.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGVariableEvent_h +#define DFGVariableEvent_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGCommon.h" +#include "DataFormat.h" +#include "MacroAssembler.h" +#include <stdio.h> + +namespace JSC { namespace DFG { + +enum VariableEventKind { + // Marks the beginning of a checkpoint. If you interpret the variable + // events starting at a Reset point then you'll get everything you need. + Reset, + + // Node births. Points in the code where a node becomes relevant for OSR. + // It may be the point where it is actually born (i.e. assigned) or it may + // be a later point, if it's only later in the sequence of instructions + // that we start to care about this node. + BirthToFill, + BirthToSpill, + + // Events related to how a node is represented. + Fill, + Spill, + + // Death of a node - after this we no longer care about this node. + Death, + + // A MovHint means that a node is being associated with a bytecode operand, + // but that it has not been stored into that operand. + MovHint, + + // A SetLocalEvent means that a node's value has actually been stored into the + // bytecode operand that it's associated with. + SetLocalEvent, + + // Used to indicate an uninitialized VariableEvent. Don't use for other + // purposes. + InvalidEventKind +}; + +union VariableRepresentation { + MacroAssembler::RegisterID gpr; + MacroAssembler::FPRegisterID fpr; +#if USE(JSVALUE32_64) + struct { + MacroAssembler::RegisterID tagGPR; + MacroAssembler::RegisterID payloadGPR; + } pair; +#endif + int32_t virtualReg; +}; + +class VariableEvent { +public: + VariableEvent() + : m_kind(InvalidEventKind) + { + } + + static VariableEvent reset() + { + VariableEvent event; + event.m_kind = Reset; + return event; + } + + static VariableEvent fillGPR(VariableEventKind kind, NodeIndex nodeIndex, MacroAssembler::RegisterID gpr, DataFormat dataFormat) + { + ASSERT(kind == BirthToFill || kind == Fill); + ASSERT(dataFormat != DataFormatDouble); +#if USE(JSVALUE32_64) + ASSERT(!(dataFormat & DataFormatJS)); +#endif + VariableEvent event; + event.m_index = nodeIndex; + event.u.gpr = gpr; + event.m_kind = kind; + event.m_dataFormat = dataFormat; + return event; + } + +#if USE(JSVALUE32_64) + static VariableEvent fillPair(VariableEventKind kind, NodeIndex nodeIndex, MacroAssembler::RegisterID tagGPR, MacroAssembler::RegisterID payloadGPR) + { + ASSERT(kind == BirthToFill || kind == Fill); + VariableEvent event; + event.m_index = nodeIndex; + event.u.pair.tagGPR = tagGPR; + event.u.pair.payloadGPR = payloadGPR; + event.m_kind = kind; + event.m_dataFormat = DataFormatJS; + return event; + } +#endif // USE(JSVALUE32_64) + + static VariableEvent fillFPR(VariableEventKind kind, NodeIndex nodeIndex, MacroAssembler::FPRegisterID fpr) + { + ASSERT(kind == BirthToFill || kind == Fill); + VariableEvent event; + event.m_index = nodeIndex; + event.u.fpr = fpr; + event.m_kind = kind; + event.m_dataFormat = DataFormatDouble; + return event; + } + + static VariableEvent spill(VariableEventKind kind, NodeIndex nodeIndex, VirtualRegister virtualRegister, DataFormat format) + { + ASSERT(kind == BirthToSpill || kind == Spill); + VariableEvent event; + event.m_index = nodeIndex; + event.u.virtualReg = virtualRegister; + event.m_kind = kind; + event.m_dataFormat = format; + return event; + } + + static VariableEvent death(NodeIndex nodeIndex) + { + VariableEvent event; + event.m_index = nodeIndex; + event.m_kind = Death; + return event; + } + + static VariableEvent setLocal(int operand, DataFormat format) + { + VariableEvent event; + event.u.virtualReg = operand; + event.m_kind = SetLocalEvent; + event.m_dataFormat = format; + return event; + } + + static VariableEvent movHint(NodeIndex nodeIndex, int operand) + { + VariableEvent event; + event.m_index = nodeIndex; + event.u.virtualReg = operand; + event.m_kind = MovHint; + return event; + } + + VariableEventKind kind() const + { + return static_cast<VariableEventKind>(m_kind); + } + + NodeIndex nodeIndex() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill + || m_kind == BirthToSpill || m_kind == Spill + || m_kind == Death || m_kind == MovHint); + return m_index; + } + + DataFormat dataFormat() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill + || m_kind == BirthToSpill || m_kind == Spill + || m_kind == SetLocalEvent); + return static_cast<DataFormat>(m_dataFormat); + } + + MacroAssembler::RegisterID gpr() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill); + ASSERT(m_dataFormat); + ASSERT(m_dataFormat != DataFormatDouble); +#if USE(JSVALUE32_64) + ASSERT(!(m_dataFormat & DataFormatJS)); +#endif + return u.gpr; + } + +#if USE(JSVALUE32_64) + MacroAssembler::RegisterID tagGPR() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill); + ASSERT(m_dataFormat & DataFormatJS); + return u.pair.tagGPR; + } + MacroAssembler::RegisterID payloadGPR() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill); + ASSERT(m_dataFormat & DataFormatJS); + return u.pair.payloadGPR; + } +#endif // USE(JSVALUE32_64) + + MacroAssembler::FPRegisterID fpr() const + { + ASSERT(m_kind == BirthToFill || m_kind == Fill); + ASSERT(m_dataFormat == DataFormatDouble); + return u.fpr; + } + + VirtualRegister virtualRegister() const + { + ASSERT(m_kind == BirthToSpill || m_kind == Spill); + return static_cast<VirtualRegister>(u.virtualReg); + } + + int operand() const + { + ASSERT(m_kind == SetLocalEvent || m_kind == MovHint); + return u.virtualReg; + } + + const VariableRepresentation& variableRepresentation() const { return u; } + + void dump(FILE*) const; + +private: + void dumpFillInfo(const char* name, FILE*) const; + void dumpSpillInfo(const char* name, FILE*) const; + + NodeIndex m_index; + + // For BirthToFill, Fill: + // - The GPR or FPR, or a GPR pair. + // For BirthToSpill, Spill: + // - The virtual register. + // For MovHint, SetLocalEvent: + // - The bytecode operand. + // For Death: + // - Unused. + VariableRepresentation u; + + int8_t m_kind; + int8_t m_dataFormat; +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGVariableEvent_h + diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp new file mode 100644 index 000000000..5d548a755 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DFGVariableEventStream.h" + +#if ENABLE(DFG_JIT) + +#include "CodeBlock.h" +#include "DFGValueSource.h" +#include <wtf/DataLog.h> + +namespace JSC { namespace DFG { + +void VariableEventStream::logEvent(const VariableEvent& event) +{ + dataLog("seq#%u:", static_cast<unsigned>(size())); + event.dump(WTF::dataFile()); + dataLog(" "); +} + +struct MinifiedGenerationInfo { + bool filled; // true -> in gpr/fpr/pair, false -> spilled + VariableRepresentation u; + DataFormat format; + + MinifiedGenerationInfo() + : format(DataFormatNone) + { + } + + void update(const VariableEvent& event) + { + switch (event.kind()) { + case BirthToFill: + case Fill: + filled = true; + break; + case BirthToSpill: + case Spill: + filled = false; + break; + case Death: + format = DataFormatNone; + return; + default: + return; + } + + u = event.variableRepresentation(); + format = event.dataFormat(); + } +}; + +void VariableEventStream::reconstruct( + CodeBlock* codeBlock, CodeOrigin codeOrigin, MinifiedGraph& graph, + unsigned index, Operands<ValueRecovery>& valueRecoveries) const +{ + ASSERT(codeBlock->getJITType() == JITCode::DFGJIT); + CodeBlock* baselineCodeBlock = codeBlock->baselineVersion(); + + unsigned numVariables; + if (codeOrigin.inlineCallFrame) + numVariables = baselineCodeBlockForInlineCallFrame(codeOrigin.inlineCallFrame)->m_numCalleeRegisters + codeOrigin.inlineCallFrame->stackOffset; + else + numVariables = baselineCodeBlock->m_numCalleeRegisters; + + // Crazy special case: if we're at index == 0 then this must be an argument check + // failure, in which case all variables are already set up. The recoveries should + // reflect this. + if (!index) { + valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables); + for (size_t i = 0; i < valueRecoveries.size(); ++i) + valueRecoveries[i] = ValueRecovery::alreadyInRegisterFile(); + return; + } + + // Step 1: Find the last checkpoint, and figure out the number of virtual registers as we go. + unsigned startIndex = index - 1; + while (at(startIndex).kind() != Reset) + startIndex--; + + // Step 2: Create a mock-up of the DFG's state and execute the events. + Operands<ValueSource> operandSources(codeBlock->numParameters(), numVariables); + Vector<MinifiedGenerationInfo, 32> generationInfos(graph.originalGraphSize()); + for (unsigned i = startIndex; i < index; ++i) { + const VariableEvent& event = at(i); + switch (event.kind()) { + case Reset: + // nothing to do. + break; + case BirthToFill: + case BirthToSpill: + case Fill: + case Spill: + case Death: + generationInfos[event.nodeIndex()].update(event); + break; + case MovHint: + if (operandSources.hasOperand(event.operand())) + operandSources.setOperand(event.operand(), ValueSource(event.nodeIndex())); + break; + case SetLocalEvent: + if (operandSources.hasOperand(event.operand())) + operandSources.setOperand(event.operand(), ValueSource::forDataFormat(event.dataFormat())); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + } + + // Step 3: Record the things that are live, so we can get to them more quickly. + Vector<unsigned, 16> indicesOfLiveThings; + for (unsigned i = 0; i < generationInfos.size(); ++i) { + if (generationInfos[i].format != DataFormatNone) + indicesOfLiveThings.append(i); + } + + // Step 4: Compute value recoveries! + valueRecoveries = Operands<ValueRecovery>(codeBlock->numParameters(), numVariables); + for (unsigned i = 0; i < operandSources.size(); ++i) { + ValueSource& source = operandSources[i]; + if (source.isTriviallyRecoverable()) { + valueRecoveries[i] = source.valueRecovery(); + continue; + } + + ASSERT(source.kind() == HaveNode); + MinifiedNode* node = graph.at(source.nodeIndex()); + if (node) { + if (node->hasConstantNumber()) { + valueRecoveries[i] = ValueRecovery::constant( + codeBlock->constantRegister( + FirstConstantRegisterIndex + node->constantNumber()).get()); + continue; + } + if (node->hasWeakConstant()) { + valueRecoveries[i] = ValueRecovery::constant(node->weakConstant()); + continue; + } + if (node->op() == PhantomArguments) { + valueRecoveries[i] = ValueRecovery::argumentsThatWereNotCreated(); + continue; + } + } + + MinifiedGenerationInfo* info = &generationInfos[source.nodeIndex()]; + if (info->format == DataFormatNone) { + // Try to see if there is an alternate node that would contain the value we want. + // There are four possibilities: + // + // Int32ToDouble: We can use this in place of the original node, but + // we'd rather not; so we use it only if it is the only remaining + // live version. + // + // ValueToInt32: If the only remaining live version of the value is + // ValueToInt32, then we can use it. + // + // UInt32ToNumber: If the only live version of the value is a UInt32ToNumber + // then the only remaining uses are ones that want a properly formed number + // rather than a UInt32 intermediate. + // + // DoubleAsInt32: Same as UInt32ToNumber. + // + // The reverse of the above: This node could be a UInt32ToNumber, but its + // alternative is still alive. This means that the only remaining uses of + // the number would be fine with a UInt32 intermediate. + + bool found = false; + + if (node && node->op() == UInt32ToNumber) { + NodeIndex nodeIndex = node->child1(); + node = graph.at(nodeIndex); + info = &generationInfos[nodeIndex]; + if (info->format != DataFormatNone) + found = true; + } + + if (!found) { + NodeIndex int32ToDoubleIndex = NoNode; + NodeIndex valueToInt32Index = NoNode; + NodeIndex uint32ToNumberIndex = NoNode; + NodeIndex doubleAsInt32Index = NoNode; + + for (unsigned i = 0; i < indicesOfLiveThings.size(); ++i) { + NodeIndex nodeIndex = indicesOfLiveThings[i]; + node = graph.at(nodeIndex); + if (!node) + continue; + if (!node->hasChild1()) + continue; + if (node->child1() != source.nodeIndex()) + continue; + ASSERT(generationInfos[nodeIndex].format != DataFormatNone); + switch (node->op()) { + case Int32ToDouble: + int32ToDoubleIndex = nodeIndex; + break; + case ValueToInt32: + valueToInt32Index = nodeIndex; + break; + case UInt32ToNumber: + uint32ToNumberIndex = nodeIndex; + break; + case DoubleAsInt32: + doubleAsInt32Index = nodeIndex; + break; + default: + break; + } + } + + NodeIndex nodeIndexToUse; + if (doubleAsInt32Index != NoNode) + nodeIndexToUse = doubleAsInt32Index; + else if (int32ToDoubleIndex != NoNode) + nodeIndexToUse = int32ToDoubleIndex; + else if (valueToInt32Index != NoNode) + nodeIndexToUse = valueToInt32Index; + else if (uint32ToNumberIndex != NoNode) + nodeIndexToUse = uint32ToNumberIndex; + else + nodeIndexToUse = NoNode; + + if (nodeIndexToUse != NoNode) { + info = &generationInfos[nodeIndexToUse]; + ASSERT(info->format != DataFormatNone); + found = true; + } + } + + if (!found) { + valueRecoveries[i] = ValueRecovery::constant(jsUndefined()); + continue; + } + } + + ASSERT(info->format != DataFormatNone); + + if (info->filled) { + if (info->format == DataFormatDouble) { + valueRecoveries[i] = ValueRecovery::inFPR(info->u.fpr); + continue; + } +#if USE(JSVALUE32_64) + if (info->format & DataFormatJS) { + valueRecoveries[i] = ValueRecovery::inPair(info->u.pair.tagGPR, info->u.pair.payloadGPR); + continue; + } +#endif + valueRecoveries[i] = ValueRecovery::inGPR(info->u.gpr, info->format); + continue; + } + + valueRecoveries[i] = + ValueRecovery::displacedInRegisterFile(static_cast<VirtualRegister>(info->u.virtualReg), info->format); + } +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.h b/Source/JavaScriptCore/dfg/DFGVariableEventStream.h new file mode 100644 index 000000000..0d10eb048 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DFGVariableEventStream_h +#define DFGVariableEventStream_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +#include "DFGCommon.h" +#include "DFGMinifiedGraph.h" +#include "DFGVariableEvent.h" +#include "Operands.h" +#include <wtf/Vector.h> + +namespace JSC { namespace DFG { + +class VariableEventStream : public Vector<VariableEvent> { +public: + void appendAndLog(const VariableEvent& event) + { +#if DFG_ENABLE(DEBUG_VERBOSE) + logEvent(event); +#endif + append(event); + } + + void reconstruct( + CodeBlock*, CodeOrigin, MinifiedGraph&, + unsigned index, Operands<ValueRecovery>&) const; + +private: + void logEvent(const VariableEvent&); +}; + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGVariableEventStream_h + diff --git a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp index 2d7ce33c9..86b33835d 100644 --- a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp @@ -132,6 +132,7 @@ public: bool performVirtualRegisterAllocation(Graph& graph) { + SamplingRegion samplingRegion("DFG Virtual Register Allocation Phase"); return runPhase<VirtualRegisterAllocationPhase>(graph); } |
