diff options
| author | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-22 09:09:45 +0100 |
|---|---|---|
| committer | Simon Hausmann <simon.hausmann@digia.com> | 2012-11-22 09:10:13 +0100 |
| commit | 470286ecfe79d59df14944e5b5d34630fc739391 (patch) | |
| tree | 43983212872e06cebefd2ae474418fa2908ca54c /Source/JavaScriptCore/dfg | |
| parent | 23037105e948c2065da5a937d3a2396b0ff45c1e (diff) | |
| download | qtwebkit-470286ecfe79d59df14944e5b5d34630fc739391.tar.gz | |
Imported WebKit commit e89504fa9195b2063b2530961d4b73dd08de3242 (http://svn.webkit.org/repository/webkit/trunk@135485)
Change-Id: I03774e5ac79721c13ffa30d152537a74d0b12e66
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'Source/JavaScriptCore/dfg')
53 files changed, 3040 insertions, 901 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp index e518c24a8..23b84cedf 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp @@ -30,6 +30,8 @@ #include "CodeBlock.h" #include "DFGBasicBlock.h" +#include "GetByIdStatus.h" +#include "PutByIdStatus.h" namespace JSC { namespace DFG { @@ -150,16 +152,16 @@ void AbstractState::initialize(Graph& graph) int operand = graph.m_mustHandleValues.operandForIndex(i); block->valuesAtHead.operand(operand).merge(value); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); + dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); block->valuesAtHead.operand(operand).dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif } block->cfaShouldRevisit = true; } } -bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDirectionPtr) +bool AbstractState::endBasicBlock(MergeMode mergeMode) { ASSERT(m_block); @@ -167,6 +169,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi block->cfaFoundConstants = m_foundConstants; block->cfaDidFinish = m_isValid; + block->cfaBranchDirection = m_branchDirection; if (!m_isValid) { reset(); @@ -178,7 +181,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi if (mergeMode != DontMerge || !ASSERT_DISABLED) { for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging state for argument %zu.\n", argument); + dataLogF(" Merging state for argument %zu.\n", argument); #endif AbstractValue& destination = block->valuesAtTail.argument(argument); changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); @@ -186,7 +189,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging state for local %zu.\n", local); + dataLogF(" Merging state for local %zu.\n", local); #endif AbstractValue& destination = block->valuesAtTail.local(local); changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); @@ -195,12 +198,8 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi ASSERT(mergeMode != DontMerge || !changed); - BranchDirection branchDirection = m_branchDirection; - if (branchDirectionPtr) - *branchDirectionPtr = branchDirection; - #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Branch direction = %s\n", branchDirectionToString(branchDirection)); + dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection)); #endif reset(); @@ -208,7 +207,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi if (mergeMode != MergeToSuccessors) return changed; - return mergeToSuccessors(m_graph, block, branchDirection); + return mergeToSuccessors(m_graph, block); } void AbstractState::reset() @@ -218,6 +217,27 @@ void AbstractState::reset() m_branchDirection = InvalidBranchDirection; } +AbstractState::BooleanResult AbstractState::booleanResult(Node& node, AbstractValue& value) +{ + JSValue childConst = value.value(); + if (childConst) { + if (childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec())) + return DefinitelyTrue; + return DefinitelyFalse; + } + + // Next check if we can fold because we know that the source is an object or string and does not equal undefined. + if (isCellSpeculation(value.m_type) + && value.m_currentKnownStructure.hasSingleton()) { + Structure* structure = value.m_currentKnownStructure.singleton(); + if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node.codeOrigin)) + && structure->typeInfo().type() != StringType) + return DefinitelyTrue; + } + + return UnknownBooleanResult; +} + bool AbstractState::execute(unsigned indexInBlock) { ASSERT(m_block); @@ -239,6 +259,12 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(false); break; } + + case Identity: { + forNode(nodeIndex) = forNode(node.child1()); + node.setCanExit(false); + break; + } case GetLocal: { VariableAccessData* variableAccessData = node.variableAccessData(); @@ -424,7 +450,10 @@ bool AbstractState::execute(unsigned indexInBlock) break; } speculateNumberUnary(node); - forNode(nodeIndex).set(SpecDouble); + if (isInt32Speculation(forNode(node.child1()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } @@ -448,9 +477,13 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).set(SpecInt32); break; } - if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) { + if (Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()])) { speculateNumberBinary(node); - forNode(nodeIndex).set(SpecDouble); + if (isRealNumberSpeculation(forNode(node.child1()).m_type) + && isRealNumberSpeculation(forNode(node.child2()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } if (node.op() == ValueAdd) { @@ -522,7 +555,11 @@ bool AbstractState::execute(unsigned indexInBlock) break; } speculateNumberBinary(node); - forNode(nodeIndex).set(SpecDouble); + if (isRealNumberSpeculation(forNode(node.child1()).m_type) + || isRealNumberSpeculation(forNode(node.child2()).m_type)) + forNode(nodeIndex).set(SpecDoubleReal); + else + forNode(nodeIndex).set(SpecDouble); break; } @@ -560,7 +597,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } } - if (Node::shouldSpeculateInteger( + if (Node::shouldSpeculateIntegerForArithmetic( m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) { speculateInt32Binary(node, true); // forcing can-exit, which is a bit on the conservative side. @@ -580,7 +617,7 @@ bool AbstractState::execute(unsigned indexInBlock) node.setCanExit(false); break; } - if (m_graph[node.child1()].shouldSpeculateInteger() + if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger()) { speculateInt32Unary(node, true); forNode(nodeIndex).set(SpecInt32); @@ -605,8 +642,18 @@ bool AbstractState::execute(unsigned indexInBlock) } case LogicalNot: { - JSValue childConst = forNode(node.child1()).value(); - if (childConst && trySetConstant(nodeIndex, jsBoolean(!childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec())))) { + bool didSetConstant = false; + switch (booleanResult(node, forNode(node.child1()))) { + case DefinitelyTrue: + didSetConstant = trySetConstant(nodeIndex, jsBoolean(false)); + break; + case DefinitelyFalse: + didSetConstant = trySetConstant(nodeIndex, jsBoolean(true)); + break; + default: + break; + } + if (didSetConstant) { m_foundConstants = true; node.setCanExit(false); break; @@ -678,12 +725,13 @@ bool AbstractState::execute(unsigned indexInBlock) case CompareGreater: case CompareGreaterEq: case CompareEq: { + bool constantWasSet = false; + JSValue leftConst = forNode(node.child1()).value(); JSValue rightConst = forNode(node.child2()).value(); if (leftConst && rightConst && leftConst.isNumber() && rightConst.isNumber()) { double a = leftConst.asNumber(); double b = rightConst.asNumber(); - bool constantWasSet; switch (node.op()) { case CompareLess: constantWasSet = trySetConstant(nodeIndex, jsBoolean(a < b)); @@ -705,11 +753,20 @@ bool AbstractState::execute(unsigned indexInBlock) constantWasSet = false; break; } - if (constantWasSet) { - m_foundConstants = true; - node.setCanExit(false); - break; - } + } + + if (!constantWasSet && node.op() == CompareEq) { + SpeculatedType leftType = forNode(node.child1()).m_type; + SpeculatedType rightType = forNode(node.child2()).m_type; + if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType)) + || (isOtherSpeculation(leftType) && isInt32Speculation(rightType))) + constantWasSet = trySetConstant(nodeIndex, jsBoolean(false)); + } + + if (constantWasSet) { + m_foundConstants = true; + node.setCanExit(false); + break; } forNode(nodeIndex).set(SpecBoolean); @@ -842,6 +899,7 @@ bool AbstractState::execute(unsigned indexInBlock) switch (node.arrayMode().type()) { case Array::SelectUsingPredictions: case Array::Unprofiled: + case Array::Undecided: ASSERT_NOT_REACHED(); break; case Array::ForceExit: @@ -859,6 +917,24 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(node.child2()).filter(SpecInt32); forNode(nodeIndex).makeTop(); break; + case Array::Int32: + forNode(node.child2()).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) { + clobberWorld(node.codeOrigin, indexInBlock); + forNode(nodeIndex).makeTop(); + } else + forNode(nodeIndex).set(SpecInt32); + break; + case Array::Double: + forNode(node.child2()).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) { + clobberWorld(node.codeOrigin, indexInBlock); + forNode(nodeIndex).makeTop(); + } else if (node.arrayMode().isSaneChain()) + forNode(nodeIndex).set(SpecDouble); + else + forNode(nodeIndex).set(SpecDoubleReal); + break; case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -926,6 +1002,20 @@ bool AbstractState::execute(unsigned indexInBlock) case Array::Generic: clobberWorld(node.codeOrigin, indexInBlock); break; + case Array::Int32: + forNode(child1).filter(SpecCell); + forNode(child2).filter(SpecInt32); + forNode(child3).filter(SpecInt32); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); + break; + case Array::Double: + forNode(child1).filter(SpecCell); + forNode(child2).filter(SpecInt32); + forNode(child3).filter(SpecRealNumber); + if (node.arrayMode().isOutOfBounds()) + clobberWorld(node.codeOrigin, indexInBlock); + break; case Array::Contiguous: case Array::ArrayStorage: forNode(child1).filter(SpecCell); @@ -1018,6 +1108,16 @@ bool AbstractState::execute(unsigned indexInBlock) case ArrayPush: node.setCanExit(true); + switch (node.arrayMode().type()) { + case Array::Int32: + forNode(node.child2()).filter(SpecInt32); + break; + case Array::Double: + forNode(node.child2()).filter(SpecRealNumber); + break; + default: + break; + } clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).set(SpecNumber); break; @@ -1043,23 +1143,21 @@ bool AbstractState::execute(unsigned indexInBlock) break; case Branch: { - JSValue value = forNode(node.child1()).value(); - if (value) { - bool booleanValue = value.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec()); - if (booleanValue) - m_branchDirection = TakeTrue; - else - m_branchDirection = TakeFalse; + BooleanResult result = booleanResult(node, forNode(node.child1())); + if (result == DefinitelyTrue) { + m_branchDirection = TakeTrue; + node.setCanExit(false); + break; + } + if (result == DefinitelyFalse) { + m_branchDirection = TakeFalse; node.setCanExit(false); break; } // FIXME: The above handles the trivial cases of sparse conditional // constant propagation, but we can do better: - // 1) If the abstract value does not have a concrete value but describes - // something that is known to evaluate true (or false) then we ought - // to sparse conditional that. - // 2) We can specialize the source variable's value on each direction of - // the branch. + // We can specialize the source variable's value on each direction of + // the branch. Node& child = m_graph[node.child1()]; if (child.shouldSpeculateBoolean()) speculateBooleanUnary(node); @@ -1122,13 +1220,13 @@ bool AbstractState::execute(unsigned indexInBlock) case NewArray: node.setCanExit(true); - forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure()); + forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); m_haveStructures = true; break; case NewArrayBuffer: node.setCanExit(true); - forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure()); + forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); m_haveStructures = true; break; @@ -1156,6 +1254,7 @@ bool AbstractState::execute(unsigned indexInBlock) // be hit, but then again, you never know. destination = source; node.setCanExit(false); + m_foundConstants = true; // Tell the constant folder to turn this into Identity. break; } @@ -1188,10 +1287,14 @@ bool AbstractState::execute(unsigned indexInBlock) destination.set(SpecFinalObject); break; } + + case InheritorIDWatchpoint: + node.setCanExit(true); + break; case NewObject: node.setCanExit(false); - forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure()); + forNode(nodeIndex).set(node.structure()); m_haveStructures = true; break; @@ -1308,8 +1411,30 @@ bool AbstractState::execute(unsigned indexInBlock) m_isValid = false; break; } - if (isCellSpeculation(m_graph[node.child1()].prediction())) + if (isCellSpeculation(m_graph[node.child1()].prediction())) { forNode(node.child1()).filter(SpecCell); + + if (Structure* structure = forNode(node.child1()).bestProvenStructure()) { + GetByIdStatus status = GetByIdStatus::computeFor( + m_graph.m_globalData, structure, + m_graph.m_codeBlock->identifier(node.identifierNumber())); + if (status.isSimple()) { + // Assert things that we can't handle and that the computeFor() method + // above won't be able to return. + ASSERT(status.structureSet().size() == 1); + ASSERT(status.chain().isEmpty()); + + if (status.specificValue()) + forNode(nodeIndex).set(status.specificValue()); + else + forNode(nodeIndex).makeTop(); + forNode(node.child1()).filter(status.structureSet()); + + m_foundConstants = true; + break; + } + } + } clobberWorld(node.codeOrigin, indexInBlock); forNode(nodeIndex).makeTop(); break; @@ -1374,7 +1499,7 @@ bool AbstractState::execute(unsigned indexInBlock) forNode(nodeIndex).clear(); // The result is not a JS value. break; case CheckArray: { - if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -1384,11 +1509,11 @@ bool AbstractState::execute(unsigned indexInBlock) case Array::String: forNode(node.child1()).filter(SpecString); break; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: - // This doesn't filter anything meaningful right now. We may want to add - // CFA tracking of array mode speculations, but we don't have that, yet. forNode(node.child1()).filter(SpecCell); break; case Array::Arguments: @@ -1430,7 +1555,7 @@ bool AbstractState::execute(unsigned indexInBlock) break; } case Arrayify: { - if (node.arrayMode().alreadyChecked(forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) { m_foundConstants = true; node.setCanExit(false); break; @@ -1472,25 +1597,65 @@ bool AbstractState::execute(unsigned indexInBlock) break; } case GetByOffset: - node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecCell); + if (!m_graph[node.child1()].hasStorageResult()) { + node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); + forNode(node.child1()).filter(SpecCell); + } forNode(nodeIndex).makeTop(); break; - case PutByOffset: - node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type)); - forNode(node.child1()).filter(SpecCell); + case PutByOffset: { + bool canExit = false; + if (!m_graph[node.child1()].hasStorageResult()) { + canExit |= !isCellSpeculation(forNode(node.child1()).m_type); + forNode(node.child1()).filter(SpecCell); + } + canExit |= !isCellSpeculation(forNode(node.child2()).m_type); + forNode(node.child2()).filter(SpecCell); + node.setCanExit(canExit); break; + } - case CheckFunction: + case CheckFunction: { + JSValue value = forNode(node.child1()).value(); + if (value == node.function()) { + m_foundConstants = true; + ASSERT(value); + node.setCanExit(false); + break; + } + node.setCanExit(true); // Lies! We can do better. - forNode(node.child1()).filter(SpecFunction); - // FIXME: Should be able to propagate the fact that we know what the function is. + if (!forNode(node.child1()).filterByValue(node.function())) { + m_isValid = false; + break; + } break; + } case PutById: case PutByIdDirect: node.setCanExit(true); + if (Structure* structure = forNode(node.child1()).bestProvenStructure()) { + PutByIdStatus status = PutByIdStatus::computeFor( + m_graph.m_globalData, + m_graph.globalObjectFor(node.codeOrigin), + structure, + m_graph.m_codeBlock->identifier(node.identifierNumber()), + node.op() == PutByIdDirect); + if (status.isSimpleReplace()) { + forNode(node.child1()).filter(structure); + m_foundConstants = true; + break; + } + if (status.isSimpleTransition()) { + clobberStructures(indexInBlock); + forNode(node.child1()).set(status.newStructure()); + m_haveStructures = true; + m_foundConstants = true; + break; + } + } forNode(node.child1()).filter(SpecCell); clobberWorld(node.codeOrigin, indexInBlock); break; @@ -1622,15 +1787,15 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract return false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's live, node @%u.\n", nodeIndex); + dataLogF(" It's live, node @%u.\n", nodeIndex); #endif if (node.variableAccessData()->isCaptured()) { source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Transfering "); + dataLogF(" Transfering "); source.dump(WTF::dataFile()); - dataLog(" from last access due to captured variable.\n"); + dataLogF(" from last access due to captured variable.\n"); #endif } else { switch (node.op()) { @@ -1640,9 +1805,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // The block transfers the value from head to tail. source = inVariable; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Transfering "); + dataLogF(" Transfering "); source.dump(WTF::dataFile()); - dataLog(" from head to tail.\n"); + dataLogF(" from head to tail.\n"); #endif break; @@ -1650,9 +1815,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // The block refines the value with additional speculations. source = forNode(nodeIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Refining to "); + dataLogF(" Refining to "); source.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif break; @@ -1665,9 +1830,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract } else source = forNode(node.child1()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting to "); + dataLogF(" Setting to "); source.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif break; @@ -1681,7 +1846,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // Abstract execution did not change the output value of the variable, for this // basic block, on this iteration. #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Not changed!\n"); + dataLogF(" Not changed!\n"); #endif return false; } @@ -1691,7 +1856,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract // true to indicate that the fixpoint must go on! destination = source; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Changed!\n"); + dataLogF(" Changed!\n"); #endif return true; } @@ -1722,7 +1887,7 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to) } inline bool AbstractState::mergeToSuccessors( - Graph& graph, BasicBlock* basicBlock, BranchDirection branchDirection) + Graph& graph, BasicBlock* basicBlock) { Node& terminal = graph[basicBlock->last()]; @@ -1730,25 +1895,25 @@ inline bool AbstractState::mergeToSuccessors( switch (terminal.op()) { case Jump: { - ASSERT(branchDirection == InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex()); #endif return merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get()); } case Branch: { - ASSERT(branchDirection != InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection); bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex()); #endif - if (branchDirection != TakeFalse) + if (basicBlock->cfaBranchDirection != TakeFalse) changed |= merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Merging to block #%u.\n", terminal.notTakenBlockIndex()); + dataLogF(" Merging to block #%u.\n", terminal.notTakenBlockIndex()); #endif - if (branchDirection != TakeTrue) + if (basicBlock->cfaBranchDirection != TakeTrue) changed |= merge(basicBlock, graph.m_blocks[terminal.notTakenBlockIndex()].get()); return changed; } @@ -1756,7 +1921,7 @@ inline bool AbstractState::mergeToSuccessors( case Return: case Throw: case ThrowReferenceError: - ASSERT(branchDirection == InvalidBranchDirection); + ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); return false; default: diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h index ec1a06231..230cd836c 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractState.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractState.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 @@ -31,6 +31,7 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGBranchDirection.h" #include "DFGGraph.h" #include "DFGNode.h" #include <wtf/Vector.h> @@ -92,36 +93,6 @@ public: MergeToSuccessors }; - enum BranchDirection { - // This is not a branch and so there is no branch direction, or - // the branch direction has yet to be set. - InvalidBranchDirection, - - // The branch takes the true case. - TakeTrue, - - // The branch takes the false case. - TakeFalse, - - // For all we know, the branch could go either direction, so we - // have to assume the worst. - TakeBoth - }; - - static const char* branchDirectionToString(BranchDirection branchDirection) - { - switch (branchDirection) { - case InvalidBranchDirection: - return "Invalid"; - case TakeTrue: - return "TakeTrue"; - case TakeFalse: - return "TakeFalse"; - case TakeBoth: - return "TakeBoth"; - } - } - AbstractState(Graph&); ~AbstractState(); @@ -174,11 +145,7 @@ public: // A true return means that you must revisit (at least) the successor // blocks. This also sets cfaShouldRevisit to true for basic blocks // that must be visited next. - // - // If you'd like to know what direction the branch at the end of the - // basic block is thought to have taken, you can pass a non-0 pointer - // for BranchDirection. - bool endBasicBlock(MergeMode, BranchDirection* = 0); + bool endBasicBlock(MergeMode); // Reset the AbstractState. This throws away any results, and at this point // you can safely call beginBasicBlock() on any basic block. @@ -211,8 +178,8 @@ public: // successors. Returns true if any of the successors' states changed. Note // that this is automatically called in endBasicBlock() if MergeMode is // MergeToSuccessors. - bool mergeToSuccessors(Graph&, BasicBlock*, BranchDirection); - + bool mergeToSuccessors(Graph&, BasicBlock*); + void dump(FILE* out); private: @@ -268,6 +235,13 @@ private: childValue2.filter(SpecNumber); } + enum BooleanResult { + UnknownBooleanResult, + DefinitelyFalse, + DefinitelyTrue + }; + BooleanResult booleanResult(Node&, AbstractValue&); + bool trySetConstant(NodeIndex nodeIndex, JSValue value) { // Make sure we don't constant fold something that will produce values that contravene diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h index c198b5e52..c60b792f6 100644 --- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h +++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h @@ -284,6 +284,21 @@ struct AbstractValue { checkConsistency(); } + bool filterByValue(JSValue value) + { + if (!validate(value)) + return false; + + if (!!value && value.isCell()) + filter(StructureSet(value.asCell()->structure())); + else + filter(speculationFromValue(value)); + + m_value = value; + + return true; + } + bool validateType(JSValue value) const { if (isTop()) @@ -327,6 +342,15 @@ struct AbstractValue { return true; } + Structure* bestProvenStructure() const + { + if (m_currentKnownStructure.hasSingleton()) + return m_currentKnownStructure.singleton(); + if (m_futurePossibleStructure.hasSingleton()) + return m_futurePossibleStructure.singleton(); + return 0; + } + void checkConsistency() const { if (!(m_type & SpecCell)) { @@ -351,7 +375,7 @@ struct AbstractValue { { fprintf(out, "(%s, %s, ", speculationToString(m_type), arrayModesToString(m_arrayModes)); m_currentKnownStructure.dump(out); - dataLog(", "); + dataLogF(", "); m_futurePossibleStructure.dump(out); if (!!m_value) fprintf(out, ", %s", m_value.description()); diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp index 00b1109f6..b02e0112c 100644 --- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp @@ -359,49 +359,49 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Arguments aliasing states:\n"); + dataLogF("Arguments aliasing states:\n"); for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) { VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i]; if (!variableAccessData->isRoot()) continue; - dataLog(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData)); + dataLogF(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData)); if (variableAccessData->isCaptured()) - dataLog("Captured"); + dataLogF("Captured"); else { ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value; bool first = true; if (data.callContextIsValid()) { if (!first) - dataLog(", "); - dataLog("Have Call Context: %p", data.callContext); + dataLogF(", "); + dataLogF("Have Call Context: %p", data.callContext); first = false; if (!m_createsArguments.contains(data.callContext)) - dataLog(" (Does Not Create Arguments)"); + dataLogF(" (Does Not Create Arguments)"); } if (data.argumentsAssignmentIsValid()) { if (!first) - dataLog(", "); - dataLog("Arguments Assignment Is Valid"); + dataLogF(", "); + dataLogF("Arguments Assignment Is Valid"); first = false; } if (!data.escapes) { if (!first) - dataLog(", "); - dataLog("Does Not Escape"); + dataLogF(", "); + dataLogF("Does Not Escape"); first = false; } if (!first) - dataLog(", "); + dataLogF(", "); if (data.isValid()) { if (m_createsArguments.contains(data.callContext)) - dataLog("VALID"); + dataLogF("VALID"); else - dataLog("INVALID (due to argument creation)"); + dataLogF("INVALID (due to argument creation)"); } else - dataLog("INVALID (due to bad variable use)"); + dataLogF("INVALID (due to bad variable use)"); } - dataLog("\n"); + dataLogF("\n"); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp index 699902a16..3bfb6a43e 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp @@ -29,24 +29,52 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGGraph.h" namespace JSC { namespace DFG { ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe) { - switch (profile->observedArrayModes()) { + ArrayModes observed = profile->observedArrayModes(); + switch (observed) { case 0: return ArrayMode(Array::Unprofiled); case asArrayModes(NonArray): if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this. + return ArrayMode(Array::Undecided, Array::NonArray, Array::OutOfBounds, Array::Convert); return ArrayMode(Array::SelectUsingPredictions); + + case asArrayModes(ArrayWithUndecided): + if (action == Array::Write) + return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::Generic); + + case asArrayModes(NonArray) | asArrayModes(ArrayWithUndecided): + if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) + return ArrayMode(Array::Undecided, Array::PossiblyArray, Array::OutOfBounds, Array::Convert); + return ArrayMode(Array::SelectUsingPredictions); + + case asArrayModes(NonArrayWithInt32): + return ArrayMode(Array::Int32, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(ArrayWithInt32): + return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32): + return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + + case asArrayModes(NonArrayWithDouble): + return ArrayMode(Array::Double, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(ArrayWithDouble): + return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble): + return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous): return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); + case asArrayModes(NonArrayWithArrayStorage): return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe); case asArrayModes(NonArrayWithSlowPutArrayStorage): @@ -62,36 +90,39 @@ ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, b case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage): return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe); - case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage): - return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withProfile(profile, makeSafe); - case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous): - if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); - return ArrayMode(Array::SelectUsingPredictions); - case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage): - case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage): - if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert); - return ArrayMode(Array::SelectUsingPredictions); - case asArrayModes(NonArray) | asArrayModes(NonArrayWithSlowPutArrayStorage): - case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage): - if (action == Array::Write && !profile->mayInterceptIndexedAccesses()) - return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert); - return ArrayMode(Array::SelectUsingPredictions); + default: - // We know that this is possibly a kind of array for which, though there is no - // useful data in the array profile, we may be able to extract useful data from - // the value profiles of the inputs. Hence, we leave it as undecided, and let - // the predictions propagator decide later. - return ArrayMode(Array::SelectUsingPredictions); + if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses()) + return ArrayMode(Array::SelectUsingPredictions); + + Array::Type type; + Array::Class arrayClass; + + if (shouldUseSlowPutArrayStorage(observed)) + type = Array::SlowPutArrayStorage; + else if (shouldUseFastArrayStorage(observed)) + type = Array::ArrayStorage; + else if (shouldUseContiguous(observed)) + type = Array::Contiguous; + else if (shouldUseDouble(observed)) + type = Array::Double; + else if (shouldUseInt32(observed)) + type = Array::Int32; + else + type = Array::Undecided; + + if (observed & (asArrayModes(ArrayWithUndecided) | asArrayModes(ArrayWithInt32) | asArrayModes(ArrayWithDouble) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) + arrayClass = Array::Array; + else if (observed & (asArrayModes(NonArray) | asArrayModes(NonArrayWithInt32) | asArrayModes(NonArrayWithDouble) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage))) + arrayClass = Array::NonArray; + else + arrayClass = Array::PossiblyArray; + + return ArrayMode(type, arrayClass, Array::Convert).withProfile(profile, makeSafe); } } -ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const +ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value) const { if (!base || !index) { // It can be that we had a legitimate arrayMode but no incoming predictions. That'll @@ -104,52 +135,124 @@ ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const if (!isInt32Speculation(index) || !isCellSpeculation(base)) return ArrayMode(Array::Generic); - if (type() == Array::Unprofiled) { - // If the indexing type wasn't recorded in the array profile but the values are - // base=cell property=int, then we know that this access didn't execute. + switch (type()) { + case Array::Unprofiled: return ArrayMode(Array::ForceExit); - } - - if (type() != Array::SelectUsingPredictions) + + case Array::Undecided: + if (!value) + return withType(Array::ForceExit); + if (isInt32Speculation(value)) + return withTypeAndConversion(Array::Int32, Array::Convert); + if (isNumberSpeculation(value)) + return withTypeAndConversion(Array::Double, Array::Convert); + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::Int32: + if (!value || isInt32Speculation(value)) + return *this; + if (isNumberSpeculation(value)) + return withTypeAndConversion(Array::Double, Array::Convert); + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::Double: + if (!value || isNumberSpeculation(value)) + return *this; + return withTypeAndConversion(Array::Contiguous, Array::Convert); + + case Array::SelectUsingPredictions: + if (isStringSpeculation(base)) + return ArrayMode(Array::String); + + if (isArgumentsSpeculation(base)) + return ArrayMode(Array::Arguments); + + if (isInt8ArraySpeculation(base)) + return ArrayMode(Array::Int8Array); + + if (isInt16ArraySpeculation(base)) + return ArrayMode(Array::Int16Array); + + if (isInt32ArraySpeculation(base)) + return ArrayMode(Array::Int32Array); + + if (isUint8ArraySpeculation(base)) + return ArrayMode(Array::Uint8Array); + + if (isUint8ClampedArraySpeculation(base)) + return ArrayMode(Array::Uint8ClampedArray); + + if (isUint16ArraySpeculation(base)) + return ArrayMode(Array::Uint16Array); + + if (isUint32ArraySpeculation(base)) + return ArrayMode(Array::Uint32Array); + + if (isFloat32ArraySpeculation(base)) + return ArrayMode(Array::Float32Array); + + if (isFloat64ArraySpeculation(base)) + return ArrayMode(Array::Float64Array); + + return ArrayMode(Array::Generic); + + default: return *this; + } +} + +Structure* ArrayMode::originalArrayStructure(Graph& graph, const CodeOrigin& codeOrigin) const +{ + if (!isJSArrayWithOriginalStructure()) + return 0; - if (isStringSpeculation(base)) - return ArrayMode(Array::String); - - if (isArgumentsSpeculation(base)) - return ArrayMode(Array::Arguments); - - if (isInt8ArraySpeculation(base)) - return ArrayMode(Array::Int8Array); - - if (isInt16ArraySpeculation(base)) - return ArrayMode(Array::Int16Array); - - if (isInt32ArraySpeculation(base)) - return ArrayMode(Array::Int32Array); - - if (isUint8ArraySpeculation(base)) - return ArrayMode(Array::Uint8Array); - - if (isUint8ClampedArraySpeculation(base)) - return ArrayMode(Array::Uint8ClampedArray); - - if (isUint16ArraySpeculation(base)) - return ArrayMode(Array::Uint16Array); - - if (isUint32ArraySpeculation(base)) - return ArrayMode(Array::Uint32Array); - - if (isFloat32ArraySpeculation(base)) - return ArrayMode(Array::Float32Array); - - if (isFloat64ArraySpeculation(base)) - return ArrayMode(Array::Float64Array); + JSGlobalObject* globalObject = graph.globalObjectFor(codeOrigin); - return ArrayMode(Array::Generic); + switch (type()) { + case Array::Int32: + return globalObject->originalArrayStructureForIndexingType(ArrayWithInt32); + case Array::Double: + return globalObject->originalArrayStructureForIndexingType(ArrayWithDouble); + case Array::Contiguous: + return globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous); + case Array::ArrayStorage: + return globalObject->originalArrayStructureForIndexingType(ArrayWithArrayStorage); + default: + CRASH(); + return 0; + } +} + +Structure* ArrayMode::originalArrayStructure(Graph& graph, Node& node) const +{ + return originalArrayStructure(graph, node.codeOrigin); +} + +bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value, IndexingType shape) const +{ + switch (arrayClass()) { + case Array::OriginalArray: + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray) + && graph.globalObjectFor(node.codeOrigin)->isOriginalArrayStructure(value.m_currentKnownStructure.singleton()); + + case Array::Array: + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape | IsArray))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape + && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + + default: + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape) | asArrayModes(shape | IsArray))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape; + } } -bool ArrayMode::alreadyChecked(AbstractValue& value) const +bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value) const { switch (type()) { case Array::Generic: @@ -161,44 +264,37 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const case Array::String: return speculationChecked(value.m_type, SpecString); + case Array::Int32: + return alreadyChecked(graph, node, value, Int32Shape); + + case Array::Double: + return alreadyChecked(graph, node, value, DoubleShape); + case Array::Contiguous: - if (isJSArray()) { - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType()) - && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); - } - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType()); + return alreadyChecked(graph, node, value, ContiguousShape); case Array::ArrayStorage: - if (isJSArray()) { - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) - && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); - } - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); + return alreadyChecked(graph, node, value, ArrayStorageShape); case Array::SlowPutArrayStorage: - if (isJSArray()) { + switch (arrayClass()) { + case Array::OriginalArray: + CRASH(); + return false; + + case Array::Array: if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) return true; return value.m_currentKnownStructure.hasSingleton() && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); + + default: + if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) + return true; + return value.m_currentKnownStructure.hasSingleton() + && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); } - if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) - return true; - return value.m_currentKnownStructure.hasSingleton() - && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); case Array::Arguments: return speculationChecked(value.m_type, SpecArguments); @@ -232,6 +328,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const case Array::SelectUsingPredictions: case Array::Unprofiled: + case Array::Undecided: break; } @@ -252,6 +349,12 @@ const char* arrayTypeToString(Array::Type type) return "ForceExit"; case Array::String: return "String"; + case Array::Undecided: + return "Undecided"; + case Array::Int32: + return "Int32"; + case Array::Double: + return "Double"; case Array::Contiguous: return "Contiguous"; case Array::ArrayStorage: @@ -306,6 +409,8 @@ const char* arrayClassToString(Array::Class arrayClass) const char* arraySpeculationToString(Array::Speculation speculation) { switch (speculation) { + case Array::SaneChain: + return "SaneChain"; case Array::InBounds: return "InBounds"; case Array::ToHole: diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h index 615965c92..0799868d6 100644 --- a/Source/JavaScriptCore/dfg/DFGArrayMode.h +++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h @@ -33,9 +33,15 @@ #include "ArrayProfile.h" #include "SpeculatedType.h" -namespace JSC { namespace DFG { +namespace JSC { +struct CodeOrigin; + +namespace DFG { + +class Graph; struct AbstractValue; +struct Node; // Use a namespace + enum instead of enum alone to avoid the namespace collision // that would otherwise occur, since we say things like "Int8Array" and "JSArray" @@ -52,7 +58,10 @@ enum Type { ForceExit, // Implies that we have no idea how to execute this operation, so we should just give up. Generic, String, - + + Undecided, + Int32, + Double, Contiguous, ArrayStorage, SlowPutArrayStorage, @@ -77,11 +86,11 @@ enum Class { }; enum Speculation { - InBounds, - ToHole, - OutOfBounds + SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment. + InBounds, // In bounds and not loading a hole. + ToHole, // Potentially storing to a hole. + OutOfBounds // Out-of-bounds access and anything can happen. }; - enum Conversion { AsIs, Convert @@ -159,7 +168,7 @@ public: mySpeculation = Array::InBounds; if (isJSArray()) { - if (profile->usesOriginalArrayStructures()) + if (profile->usesOriginalArrayStructures() && benefitsFromOriginalArray()) myArrayClass = Array::OriginalArray; else myArrayClass = Array::Array; @@ -169,15 +178,27 @@ public: return ArrayMode(type(), myArrayClass, mySpeculation, conversion()); } - ArrayMode refine(SpeculatedType base, SpeculatedType index) const; + ArrayMode withType(Array::Type type) const + { + return ArrayMode(type, arrayClass(), speculation(), conversion()); + } - bool alreadyChecked(AbstractValue&) const; + ArrayMode withTypeAndConversion(Array::Type type, Array::Conversion conversion) const + { + return ArrayMode(type, arrayClass(), speculation(), conversion); + } + + ArrayMode refine(SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const; + + bool alreadyChecked(Graph&, Node&, AbstractValue&) const; const char* toString() const; bool usesButterfly() const { switch (type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -203,9 +224,20 @@ public: return arrayClass() == Array::OriginalArray; } + bool isSaneChain() const + { + return speculation() == Array::SaneChain; + } + bool isInBounds() const { - return speculation() == Array::InBounds; + switch (speculation()) { + case Array::SaneChain: + case Array::InBounds: + return true; + default: + return false; + } } bool mayStoreToHole() const @@ -263,6 +295,7 @@ public: case Array::Unprofiled: case Array::ForceExit: case Array::Generic: + case Array::Undecided: return false; default: return true; @@ -277,6 +310,8 @@ public: case Array::ForceExit: case Array::Generic: return false; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: @@ -286,6 +321,23 @@ public: } } + bool benefitsFromOriginalArray() const + { + switch (type()) { + case Array::Int32: + case Array::Double: + case Array::Contiguous: + case Array::ArrayStorage: + return true; + default: + return false; + } + } + + // Returns 0 if this is not OriginalArray. + Structure* originalArrayStructure(Graph&, const CodeOrigin&) const; + Structure* originalArrayStructure(Graph&, Node&) const; + bool benefitsFromStructureCheck() const { switch (type()) { @@ -309,6 +361,10 @@ public: switch (type()) { case Array::Generic: return ALL_ARRAY_MODES; + case Array::Int32: + return arrayModesWithIndexingShape(Int32Shape); + case Array::Double: + return arrayModesWithIndexingShape(DoubleShape); case Array::Contiguous: return arrayModesWithIndexingShape(ContiguousShape); case Array::ArrayStorage: @@ -354,6 +410,8 @@ private: } } + bool alreadyChecked(Graph&, Node&, AbstractValue&, IndexingType shape) const; + union { struct { uint8_t type; diff --git a/Source/JavaScriptCore/dfg/DFGBasicBlock.h b/Source/JavaScriptCore/dfg/DFGBasicBlock.h index 441e2e75e..6f348f2e1 100644 --- a/Source/JavaScriptCore/dfg/DFGBasicBlock.h +++ b/Source/JavaScriptCore/dfg/DFGBasicBlock.h @@ -29,6 +29,7 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractValue.h" +#include "DFGBranchDirection.h" #include "DFGNode.h" #include "Operands.h" #include <wtf/OwnPtr.h> @@ -46,6 +47,7 @@ struct BasicBlock : Vector<NodeIndex, 8> { , cfaShouldRevisit(false) , cfaFoundConstants(false) , cfaDidFinish(true) + , cfaBranchDirection(InvalidBranchDirection) #if !ASSERT_DISABLED , isLinked(false) #endif @@ -105,6 +107,7 @@ struct BasicBlock : Vector<NodeIndex, 8> { bool cfaShouldRevisit; bool cfaFoundConstants; bool cfaDidFinish; + BranchDirection cfaBranchDirection; #if !ASSERT_DISABLED bool isLinked; #endif diff --git a/Source/JavaScriptCore/dfg/DFGBranchDirection.h b/Source/JavaScriptCore/dfg/DFGBranchDirection.h new file mode 100644 index 000000000..8bbe3c635 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGBranchDirection.h @@ -0,0 +1,88 @@ +/* + * 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 DFGBranchDirection_h +#define DFGBranchDirection_h + +#include <wtf/Platform.h> + +#if ENABLE(DFG_JIT) + +namespace JSC { namespace DFG { + +enum BranchDirection { + // This is not a branch and so there is no branch direction, or + // the branch direction has yet to be set. + InvalidBranchDirection, + + // The branch takes the true case. + TakeTrue, + + // The branch takes the false case. + TakeFalse, + + // For all we know, the branch could go either direction, so we + // have to assume the worst. + TakeBoth +}; + +static inline const char* branchDirectionToString(BranchDirection branchDirection) +{ + switch (branchDirection) { + case InvalidBranchDirection: + return "Invalid"; + case TakeTrue: + return "TakeTrue"; + case TakeFalse: + return "TakeFalse"; + case TakeBoth: + return "TakeBoth"; + } +} + +static inline bool isKnownDirection(BranchDirection branchDirection) +{ + switch (branchDirection) { + case TakeTrue: + case TakeFalse: + return true; + default: + return false; + } +} + +static inline bool branchCondition(BranchDirection branchDirection) +{ + if (branchDirection == TakeTrue) + return true; + ASSERT(branchDirection == TakeFalse); + return false; +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGBranchDirection_h diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp index 70aa2b637..9b879b9e3 100644 --- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp +++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp @@ -218,7 +218,7 @@ private: if (operand == JSStack::Callee) return getCallee(); - + // Is this an argument? if (operandIsArgument(operand)) return getArgument(operand); @@ -256,7 +256,7 @@ private: m_inlineStackTop->m_lazyOperands.prediction( LazyOperandValueProfileKey(m_currentIndex, node.local())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n", + dataLogF("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n", nodeIndex, m_currentIndex, node.local(), speculationToString(prediction)); #endif node.variableAccessData()->predict(prediction); @@ -876,7 +876,7 @@ private: SpeculatedType prediction = m_inlineStackTop->m_profiledBlock->valueProfilePredictionForBytecodeOffset(bytecodeIndex); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction)); + dataLogF("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction)); #endif return prediction; @@ -905,10 +905,15 @@ private: return getPrediction(m_graph.size(), m_currentProfilingIndex); } - ArrayMode getArrayMode(ArrayProfile* profile) + ArrayMode getArrayMode(ArrayProfile* profile, Array::Action action) { profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock); - return ArrayMode::fromObserved(profile, Array::Read, false); + return ArrayMode::fromObserved(profile, action, false); + } + + ArrayMode getArrayMode(ArrayProfile* profile) + { + return getArrayMode(profile, Array::Read); } ArrayMode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base) @@ -917,8 +922,8 @@ private: #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (m_inlineStackTop->m_profiledBlock->numberOfRareCaseProfiles()) - dataLog("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter); - dataLog("Array profile for bc#%u: %p%s%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes()); + dataLogF("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter); + dataLogF("Array profile for bc#%u: %p%s%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes()); #endif bool makeSafe = @@ -962,13 +967,13 @@ private: if (m_inlineStackTop->m_profiledBlock->likelyToTakeDeepestSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making ArithMul @%u take deepest slow case.\n", nodeIndex); + dataLogF("Making ArithMul @%u take deepest slow case.\n", nodeIndex); #endif m_graph[nodeIndex].mergeFlags(NodeMayOverflow | NodeMayNegZero); } else if (m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex) || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making ArithMul @%u take faster slow case.\n", nodeIndex); + dataLogF("Making ArithMul @%u take faster slow case.\n", nodeIndex); #endif m_graph[nodeIndex].mergeFlags(NodeMayNegZero); } @@ -998,7 +1003,7 @@ private: return nodeIndex; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); + dataLogF("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)); #endif // FIXME: It might be possible to make this more granular. The DFG certainly can @@ -1272,19 +1277,19 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn m_inlineStackTop->m_profiledBlock, m_currentIndex); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex); + dataLogF("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex); if (callLinkStatus.isSet()) { if (callLinkStatus.couldTakeSlowPath()) - dataLog("could take slow path, "); - dataLog("target = %p\n", callLinkStatus.callTarget()); + dataLogF("could take slow path, "); + dataLogF("target = %p\n", callLinkStatus.callTarget()); } else - dataLog("not set.\n"); + dataLogF("not set.\n"); #endif if (m_graph.isFunctionConstant(callTarget)) { callType = ConstantFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n", + dataLogF("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n", m_graph.size(), m_currentIndex, m_graph.valueOfFunctionConstant(callTarget), m_graph.valueOfFunctionConstant(callTarget)->executable()); @@ -1292,7 +1297,7 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn } else if (m_graph.isInternalFunctionConstant(callTarget)) { callType = ConstantInternalFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] has an internal function constant: %p.\n", + dataLogF("Call at [@%lu, bc#%u] has an internal function constant: %p.\n", m_graph.size(), m_currentIndex, m_graph.valueOfInternalFunctionConstant(callTarget)); #endif @@ -1300,14 +1305,14 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)) { callType = LinkedFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n", + dataLogF("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n", m_graph.size(), m_currentIndex, callLinkStatus.callTarget(), callLinkStatus.callTarget()->executable()); #endif } else { callType = UnknownFunction; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n", + dataLogF("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n", m_graph.size(), m_currentIndex); #endif } @@ -1432,7 +1437,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c ASSERT(canInlineFunctionFor(codeBlock, kind)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Inlining executable %p.\n", executable); + dataLogF("Inlining executable %p.\n", executable); #endif // Now we know without a doubt that we are committed to inlining. So begin the process @@ -1517,7 +1522,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // caller. It doesn't need to be linked to, but it needs outgoing links. if (!inlineStackEntry.m_unlinkedBlocks.isEmpty()) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex); + dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex); #endif // For debugging purposes, set the bytecodeBegin. Note that this doesn't matter // for release builds because this block will never serve as a potential target @@ -1529,7 +1534,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c m_currentBlock = m_graph.m_blocks.last().get(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Done inlining executable %p, continuing code generation at epilogue.\n", executable); + dataLogF("Done inlining executable %p, continuing code generation at epilogue.\n", executable); #endif return true; } @@ -1556,7 +1561,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // Need to create a new basic block for the continuation at the caller. OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(nextOffset, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif m_currentBlock = block.get(); ASSERT(m_inlineStackTop->m_caller->m_blockLinkingTargets.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_caller->m_blockLinkingTargets.last()]->bytecodeBegin < nextOffset); @@ -1568,7 +1573,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c // At this point we return and continue to generate code for the caller, but // in the new basic block. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Done inlining executable %p, continuing code generation in new block.\n", executable); + dataLogF("Done inlining executable %p, continuing code generation in new block.\n", executable); #endif return true; } @@ -1649,7 +1654,12 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!arrayMode.isJSArray()) + return false; switch (arrayMode.type()) { + case Array::Undecided: + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: { NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1))); @@ -1669,7 +1679,11 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins return false; ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile); + if (!arrayMode.isJSArray()) + return false; switch (arrayMode.type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: { NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0))); @@ -1689,7 +1703,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins int thisOperand = registerOffset + argumentToOperand(0); int indexOperand = registerOffset + argumentToOperand(1); - NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); + NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand)); if (usesResult) set(resultOperand, charCode); @@ -1702,7 +1716,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins int thisOperand = registerOffset + argumentToOperand(0); int indexOperand = registerOffset + argumentToOperand(1); - NodeIndex charCode = addToGraph(StringCharAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand)); + NodeIndex charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand)); if (usesResult) set(resultOperand, charCode); @@ -1754,7 +1768,7 @@ bool ByteCodeParser::handleConstantInternalFunction( if (argumentCountIncludingThis == 2) { setIntrinsicResult( usesResult, resultOperand, - addToGraph(NewArrayWithSize, get(registerOffset + argumentToOperand(1)))); + addToGraph(NewArrayWithSize, OpInfo(ArrayWithUndecided), get(registerOffset + argumentToOperand(1)))); return true; } @@ -1762,7 +1776,7 @@ bool ByteCodeParser::handleConstantInternalFunction( addVarArgChild(get(registerOffset + argumentToOperand(i))); setIntrinsicResult( usesResult, resultOperand, - addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0))); + addToGraph(Node::VarArg, NewArray, OpInfo(ArrayWithUndecided), OpInfo(0))); return true; } @@ -2063,7 +2077,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) addToGraph(Jump, OpInfo(m_currentIndex)); else { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock); + dataLogF("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock); #endif } return shouldContinueParsing; @@ -2090,9 +2104,9 @@ bool ByteCodeParser::parseBlock(unsigned limit) m_inlineStackTop->m_profiledBlock->valueProfileForBytecodeOffset(m_currentProfilingIndex); profile->computeUpdatedPrediction(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile); + dataLogF("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile); profile->dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif if (profile->m_singletonValueIsTop || !profile->m_singletonValue @@ -2110,36 +2124,65 @@ bool ByteCodeParser::parseBlock(unsigned limit) } case op_create_this: { - set(currentInstruction[1].u.operand, addToGraph(CreateThis, get(JSStack::Callee))); + int calleeOperand = currentInstruction[2].u.operand; + NodeIndex callee = get(calleeOperand); + bool alreadyEmitted = false; + if (m_graph[callee].op() == WeakJSConstant) { + JSCell* cell = m_graph[callee].weakConstant(); + ASSERT(cell->inherits(&JSFunction::s_info)); + + JSFunction* function = jsCast<JSFunction*>(cell); + Structure* inheritorID = function->tryGetKnownInheritorID(); + if (inheritorID) { + addToGraph(InheritorIDWatchpoint, OpInfo(function)); + set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(inheritorID))); + alreadyEmitted = true; + } + } + if (!alreadyEmitted) + set(currentInstruction[1].u.operand, addToGraph(CreateThis, callee)); NEXT_OPCODE(op_create_this); } case op_new_object: { - set(currentInstruction[1].u.operand, addToGraph(NewObject)); + set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(m_inlineStackTop->m_codeBlock->globalObject()->emptyObjectStructure()))); NEXT_OPCODE(op_new_object); } case op_new_array: { int startOperand = currentInstruction[2].u.operand; int numOperands = currentInstruction[3].u.operand; + ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile; for (int operandIdx = startOperand; operandIdx < startOperand + numOperands; ++operandIdx) addVarArgChild(get(operandIdx)); - set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0))); + set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(profile->selectIndexingType()), OpInfo(0))); NEXT_OPCODE(op_new_array); } case op_new_array_with_size: { int lengthOperand = currentInstruction[2].u.operand; - set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, get(lengthOperand))); + ArrayAllocationProfile* profile = currentInstruction[3].u.arrayAllocationProfile; + set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, OpInfo(profile->selectIndexingType()), get(lengthOperand))); NEXT_OPCODE(op_new_array_with_size); } case op_new_array_buffer: { int startConstant = currentInstruction[2].u.operand; int numConstants = currentInstruction[3].u.operand; + ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile; NewArrayBufferData data; data.startConstant = m_inlineStackTop->m_constantBufferRemap[startConstant]; data.numConstants = numConstants; + data.indexingType = profile->selectIndexingType(); + + // If this statement has never executed, we'll have the wrong indexing type in the profile. + for (int i = 0; i < numConstants; ++i) { + data.indexingType = + leastUpperBoundOfIndexingTypeAndValue( + data.indexingType, + m_codeBlock->constantBuffer(data.startConstant)[i]); + } + m_graph.m_newArrayBufferData.append(data); set(currentInstruction[1].u.operand, addToGraph(NewArrayBuffer, OpInfo(&m_graph.m_newArrayBufferData.last()))); NEXT_OPCODE(op_new_array_buffer); @@ -2150,6 +2193,22 @@ bool ByteCodeParser::parseBlock(unsigned limit) NEXT_OPCODE(op_new_regexp); } + case op_get_callee: { + ValueProfile* profile = currentInstruction[2].u.profile; + profile->computeUpdatedPrediction(); + if (profile->m_singletonValueIsTop + || !profile->m_singletonValue + || !profile->m_singletonValue.isCell()) + set(currentInstruction[1].u.operand, get(JSStack::Callee)); + else { + ASSERT(profile->m_singletonValue.asCell()->inherits(&JSFunction::s_info)); + NodeIndex actualCallee = get(JSStack::Callee); + addToGraph(CheckFunction, OpInfo(profile->m_singletonValue.asCell()), actualCallee); + set(currentInstruction[1].u.operand, addToGraph(WeakJSConstant, OpInfo(profile->m_singletonValue.asCell()))); + } + NEXT_OPCODE(op_get_callee); + } + // === Bitwise operations === case op_bitand: { @@ -3215,12 +3274,12 @@ void ByteCodeParser::processPhiStack() VariableAccessData* dataForPhi = m_graph[entry.m_phi].variableAccessData(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi); + dataLogF(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi); #endif for (size_t i = 0; i < predecessors.size(); ++i) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Dealing with predecessor block %u.\n", predecessors[i]); + dataLogF(" Dealing with predecessor block %u.\n", predecessors[i]); #endif BasicBlock* predecessorBlock = m_graph.m_blocks[predecessors[i]].get(); @@ -3230,7 +3289,7 @@ void ByteCodeParser::processPhiStack() NodeIndex valueInPredecessor = var; if (valueInPredecessor == NoNode) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Did not find node, adding phi.\n"); + dataLogF(" Did not find node, adding phi.\n"); #endif valueInPredecessor = insertPhiNode(OpInfo(newVariableAccessData(stackType == ArgumentPhiStack ? argumentToOperand(varNo) : static_cast<int>(varNo), false)), predecessorBlock); @@ -3242,7 +3301,7 @@ void ByteCodeParser::processPhiStack() phiStack.append(PhiStackEntry(predecessorBlock, valueInPredecessor, varNo)); } else if (m_graph[valueInPredecessor].op() == GetLocal) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Found GetLocal @%u.\n", valueInPredecessor); + dataLogF(" Found GetLocal @%u.\n", valueInPredecessor); #endif // We want to ensure that the VariableAccessDatas are identical between the @@ -3254,7 +3313,7 @@ void ByteCodeParser::processPhiStack() valueInPredecessor = m_graph[valueInPredecessor].child1().index(); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Found @%u.\n", valueInPredecessor); + dataLogF(" Found @%u.\n", valueInPredecessor); #endif } ASSERT(m_graph[valueInPredecessor].op() == SetLocal @@ -3269,48 +3328,48 @@ void ByteCodeParser::processPhiStack() Node* phiNode = &m_graph[entry.m_phi]; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount()); + dataLogF(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount()); #endif if (phiNode->refCount()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Reffing @%u.\n", valueInPredecessor); + dataLogF(" Reffing @%u.\n", valueInPredecessor); #endif m_graph.ref(valueInPredecessor); } if (!phiNode->child1()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild1(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } if (!phiNode->child2()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild2(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } if (!phiNode->child3()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor); + dataLogF(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor); #endif phiNode->children.setChild3(Edge(valueInPredecessor)); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif continue; } @@ -3318,7 +3377,7 @@ void ByteCodeParser::processPhiStack() NodeIndex newPhi = insertPhiNode(OpInfo(dataForPhi), entry.m_block); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi); + dataLogF(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi); #endif phiNode = &m_graph[entry.m_phi]; // reload after vector resize @@ -3329,17 +3388,17 @@ void ByteCodeParser::processPhiStack() newPhiNode.children = phiNode->children; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", newPhi); + dataLogF(" Children of @%u: ", newPhi); newPhiNode.dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif phiNode->children.initialize(newPhi, valueInPredecessor, NoNode); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Children of @%u: ", entry.m_phi); + dataLogF(" Children of @%u: ", entry.m_phi); phiNode->dumpChildren(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif } } @@ -3366,7 +3425,7 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa case Jump: node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex()); + dataLogF("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex()); #endif break; @@ -3374,13 +3433,13 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing())); node.setNotTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.notTakenBytecodeOffsetDuringParsing())); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex()); + dataLogF("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex()); #endif break; default: #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Marking basic block %p as linked.\n", block); + dataLogF("Marking basic block %p as linked.\n", block); #endif break; } @@ -3489,9 +3548,9 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry( } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Current captured variables: "); + dataLogF("Current captured variables: "); inlineCallFrame.capturedVars.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif byteCodeParser->m_codeBlock->inlineCallFrames().append(inlineCallFrame); @@ -3598,7 +3657,7 @@ void ByteCodeParser::parseCodeBlock() CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", + dataLogF("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n", codeBlock, codeTypeToString(codeBlock->codeType()), codeBlock->symbolTable() ? codeBlock->symbolTable()->captureCount() : 0, @@ -3612,7 +3671,7 @@ void ByteCodeParser::parseCodeBlock() // The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions. unsigned limit = jumpTargetIndex < codeBlock->numberOfJumpTargets() ? codeBlock->jumpTarget(jumpTargetIndex) : codeBlock->instructions().size(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif ASSERT(m_currentIndex < limit); @@ -3634,13 +3693,13 @@ void ByteCodeParser::parseCodeBlock() // Change its bytecode begin and continue. m_currentBlock = m_graph.m_blocks.last().get(); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex); + dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex); #endif m_currentBlock->bytecodeBegin = m_currentIndex; } else { OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals)); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); + dataLogF("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame)); #endif m_currentBlock = block.get(); ASSERT(m_inlineStackTop->m_unlinkedBlocks.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_unlinkedBlocks.last().m_blockIndex]->bytecodeBegin < m_currentIndex); @@ -3697,14 +3756,14 @@ bool ByteCodeParser::parse() linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets); m_graph.determineReachability(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Processing local variable phis.\n"); + dataLogF("Processing local variable phis.\n"); #endif m_currentProfilingIndex = m_currentIndex; processPhiStack<LocalPhiStack>(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Processing argument phis.\n"); + dataLogF("Processing argument phis.\n"); #endif processPhiStack<ArgumentPhiStack>(); diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h index a2570b7ea..8adde0598 100644 --- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h +++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h @@ -210,6 +210,15 @@ public: addCallArgument(arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3) { resetCallArguments(); @@ -268,6 +277,16 @@ public: addCallArgument(arg4); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + addCallArgument(arg4); + } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImmPtr arg2, GPRReg arg3) { resetCallArguments(); @@ -317,6 +336,23 @@ public: addCallArgument(arg4); addCallArgument(arg5); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + resetCallArguments(); + addCallArgument(GPRInfo::callFrameRegister); + addCallArgument(arg1); + addCallArgument(arg2); + addCallArgument(arg3); + } #endif // !NUMBER_OF_ARGUMENT_REGISTERS // These methods are suitable for any calling convention that provides for // at least 4 argument registers, e.g. X86_64, ARMv7. @@ -463,6 +499,20 @@ public: { setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + moveDouble(arg1, FPRInfo::argumentFPR0); + move(arg2, GPRInfo::argumentGPR1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + moveDouble(arg3, FPRInfo::argumentFPR0); + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } #elif CPU(ARM) #if CPU(ARM_HARDFP) ALWAYS_INLINE void setupArguments(FPRReg arg1) @@ -485,6 +535,20 @@ public: moveDouble(ARMRegisters::d2, FPRInfo::argumentFPR1); } } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + moveDouble(arg1, FPRInfo::argumentFPR0); + move(arg2, GPRInfo::argumentGPR1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + moveDouble(arg3, FPRInfo::argumentFPR0); + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } #else ALWAYS_INLINE void setupArguments(FPRReg arg1) { @@ -496,6 +560,21 @@ public: assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1); assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg2); } + + ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2) + { + move(arg2, GPRInfo::argumentGPR3); + assembler().vmov(GPRInfo::argumentGPR1, GPRInfo::argumentGPR2, arg1); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + setupStubArguments(arg1, arg2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + assembler().vmov(GPRInfo::argumentGPR3, GPRInfo::nonArgGPR0, arg3); + poke(GPRInfo::nonArgGPR0); + } #endif // CPU(ARM_HARDFP) #else #error "DFG JIT not supported on this platform." @@ -635,6 +714,13 @@ public: move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3) + { + setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR3>(arg1, arg3); + move(arg2, GPRInfo::argumentGPR2); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3) { move(arg1, GPRInfo::argumentGPR1); @@ -723,6 +809,12 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4) + { + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImm32 arg3, GPRReg arg4) { poke(arg4); @@ -779,6 +871,12 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4) + { + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, GPRReg arg5) { poke(arg5, 1); @@ -786,6 +884,13 @@ public: setupArgumentsWithExecState(arg1, arg2, arg3); } + ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, TrustedImm32 arg5) + { + poke(arg5, 1); + poke(arg4); + setupArgumentsWithExecState(arg1, arg2, arg3); + } + ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, TrustedImmPtr arg5) { poke(arg5, 1); diff --git a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp index 24ea0b36f..1a88066d1 100644 --- a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp @@ -78,47 +78,47 @@ private: if (!block->cfaShouldRevisit) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin); + dataLogF(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin); #endif m_state.beginBasicBlock(block); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" head vars: "); + dataLogF(" head vars: "); dumpOperands(block->valuesAtHead, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif for (unsigned i = 0; i < block->size(); ++i) { NodeIndex nodeIndex = block->at(i); if (!m_graph[nodeIndex].shouldGenerate()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex); + dataLogF(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex); m_state.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif if (!m_state.execute(i)) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Expect OSR exit.\n"); + dataLogF(" Expect OSR exit.\n"); #endif break; } } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" tail regs: "); + dataLogF(" tail regs: "); m_state.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif m_changed |= m_state.endBasicBlock(AbstractState::MergeToSuccessors); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" tail vars: "); + dataLogF(" tail vars: "); dumpOperands(block->valuesAtTail, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif } void performForwardCFA() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFA [%u]\n", ++m_count); + dataLogF("CFA [%u]\n", ++m_count); #endif for (BlockIndex block = 0; block < m_graph.m_blocks.size(); ++block) diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp index e0d973992..d9ae4a274 100644 --- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp @@ -66,7 +66,7 @@ public: ASSERT(m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[0] == blockIndex); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Jump merge on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Jump merge on Block #%u to Block #%u.\n", blockIndex, m_graph.successor(block, 0)); #endif if (extremeLogging) @@ -76,14 +76,14 @@ public: break; } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Not jump merging on Block #%u to Block #%u because predecessors = ", + dataLogF("Not jump merging on Block #%u to Block #%u because predecessors = ", blockIndex, m_graph.successor(block, 0)); for (unsigned i = 0; i < m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors.size(); ++i) { if (i) - dataLog(", "); - dataLog("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]); + dataLogF(", "); + dataLogF("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]); } - dataLog(".\n"); + dataLogF(".\n"); #endif } @@ -99,14 +99,13 @@ public: case Branch: { // Branch on constant -> jettison the not-taken block and merge. - if (m_graph[m_graph[block->last()].child1()].hasConstant()) { - bool condition = - m_graph.valueOfJSConstant(m_graph[block->last()].child1().index()).toBoolean(m_graph.globalObjectFor(m_graph[block->last()].codeOrigin)->globalExec()); + if (isKnownDirection(block->cfaBranchDirection)) { + bool condition = branchCondition(block->cfaBranchDirection); BasicBlock* targetBlock = m_graph.m_blocks[ m_graph.successorForCondition(block, condition)].get(); if (targetBlock->m_predecessors.size() == 1) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n", + dataLogF("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n", condition ? "true" : "false", blockIndex, m_graph.successorForCondition(block, condition), m_graph.successorForCondition(block, !condition)); @@ -119,7 +118,7 @@ public: m_graph.successorForCondition(block, !condition)); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n", + dataLogF("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n", condition ? "true" : "false", blockIndex, m_graph.successorForCondition(block, condition), m_graph.successorForCondition(block, !condition)); @@ -153,13 +152,13 @@ public: ASSERT(targetBlock->isReachable); if (targetBlock->m_predecessors.size() == 1) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n", blockIndex, targetBlockIndex); #endif mergeBlocks(blockIndex, targetBlockIndex, NoBlock); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n", + dataLogF("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n", blockIndex, targetBlockIndex); #endif ASSERT(m_graph[block->last()].isTerminal()); @@ -180,7 +179,7 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n", + dataLogF("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n", blockIndex); #endif @@ -288,22 +287,22 @@ private: if (child.op() != GetLocal) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local()); + dataLogF(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local()); #endif if (child.variableAccessData()->isCaptured()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's captured.\n"); + dataLogF(" It's captured.\n"); #endif return; } NodeIndex originalNodeIndex = block->variablesAtTail.operand(child.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Dealing with original @%u.\n", originalNodeIndex); + dataLogF(" Dealing with original @%u.\n", originalNodeIndex); #endif ASSERT(originalNodeIndex != NoNode); Node* originalNode = &m_graph[originalNodeIndex]; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Original has local r%d.\n", originalNode->local()); + dataLogF(" Original has local r%d.\n", originalNode->local()); #endif ASSERT(child.local() == originalNode->local()); if (changeRef) @@ -328,14 +327,14 @@ private: switch (originalNode->op()) { case SetLocal: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's a SetLocal.\n"); + dataLogF(" It's a SetLocal.\n"); #endif m_graph.changeIndex(edge, originalNode->child1().index(), changeRef); break; } case GetLocal: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's a GetLocal.\n"); + dataLogF(" It's a GetLocal.\n"); #endif m_graph.changeIndex(edge, originalNodeIndex, changeRef); break; @@ -343,7 +342,7 @@ private: case Phi: case SetArgument: { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" It's Phi/SetArgument.\n"); + dataLogF(" It's Phi/SetArgument.\n"); #endif // Keep the GetLocal! break; @@ -381,7 +380,7 @@ private: Node& phiNode = m_graph[phiNodeIndex]; NodeIndex myNodeIndex = sourceBlock->variablesAtTail.operand(phiNode.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Considering removing reference from phi @%u to @%u on local r%d:", + dataLogF("Considering removing reference from phi @%u to @%u on local r%d:", phiNodeIndex, myNodeIndex, phiNode.local()); #endif if (myNodeIndex == NoNode) { @@ -395,7 +394,7 @@ private: for (unsigned j = 0; j < AdjacencyList::Size; ++j) removePotentiallyDeadPhiReference(myNodeIndex, phiNode, j, sourceBlock->isReachable); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif } } @@ -403,7 +402,7 @@ private: void fixJettisonedPredecessors(BlockIndex blockIndex, BlockIndex jettisonedBlockIndex) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n", + dataLogF("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n", jettisonedBlockIndex, blockIndex); #endif BasicBlock* jettisonedBlock = m_graph.m_blocks[jettisonedBlockIndex].get(); @@ -423,7 +422,7 @@ private: if (phiNode.children.child(edgeIndex).indexUnchecked() != myNodeIndex) return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Removing reference at child %u.", edgeIndex); + dataLogF(" Removing reference at child %u.", edgeIndex); #endif if (changeRef && phiNode.shouldGenerate()) m_graph.deref(myNodeIndex); @@ -711,7 +710,7 @@ private: bool changeRef = phiNode.shouldGenerate(); OperandSubstitution substitution = substitutions.operand(phiNode.local()); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Performing operand substitution @%u -> @%u.\n", + dataLogF(" Performing operand substitution @%u -> @%u.\n", substitution.oldChild, substitution.newChild); #endif if (!phiNode.child1()) @@ -730,6 +729,7 @@ private: } firstBlock->valuesAtTail = secondBlock->valuesAtTail; + firstBlock->cfaBranchDirection = secondBlock->cfaBranchDirection; m_graph.m_blocks[secondBlockIndex].clear(); } diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp index 19051c174..36acb2c21 100644 --- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp @@ -79,7 +79,7 @@ private: result++; ASSERT(result <= m_indexInBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" limit %u: ", result); + dataLogF(" limit %u: ", result); #endif return result; } @@ -372,7 +372,7 @@ private: return NoNode; } - bool checkFunctionElimination(JSFunction* function, NodeIndex child1) + bool checkFunctionElimination(JSCell* function, NodeIndex child1) { for (unsigned i = endIndexForPureCSE(); i--;) { NodeIndex index = m_currentBlock->at(i); @@ -970,7 +970,7 @@ private: return false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Replacing @%u -> @%u", m_compileIndex, replacement); + dataLogF(" Replacing @%u -> @%u", m_compileIndex, replacement); #endif Node& node = m_graph[m_compileIndex]; @@ -988,7 +988,7 @@ private: void eliminate() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Eliminating @%u", m_compileIndex); + dataLogF(" Eliminating @%u", m_compileIndex); #endif Node& node = m_graph[m_compileIndex]; @@ -1029,7 +1029,7 @@ private: return; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex); + dataLogF(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex); #endif // NOTE: there are some nodes that we deliberately don't CSE even though we @@ -1043,6 +1043,10 @@ private: switch (node.op()) { + case Identity: + setReplacement(node.child1().index()); + break; + // Handle the pure nodes. These nodes never have any side-effects. case BitAnd: case BitOr: @@ -1313,7 +1317,7 @@ private: m_lastSeen[node.op()] = m_indexInBlock; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h index 46d5f44cb..03713b6c5 100644 --- a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h +++ b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h @@ -61,7 +61,7 @@ protected: jit->silentSpill(m_plans[i]); jit->callOperation(m_function, m_resultGPR, m_structure, m_size); GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR); - for (unsigned i = 0; i < m_plans.size(); ++i) + for (unsigned i = m_plans.size(); i--;) jit->silentFill(m_plans[i], canTrample); jit->m_jit.loadPtr(MacroAssembler::Address(m_resultGPR, JSObject::butterflyOffset()), m_storageGPR); jumpTo(jit); @@ -106,7 +106,7 @@ protected: done.link(&jit->m_jit); jit->callOperation(m_function, m_resultGPR, scratchGPR, m_sizeGPR); GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR); - for (unsigned i = 0; i < m_plans.size(); ++i) + for (unsigned i = m_plans.size(); i--;) jit->silentFill(m_plans[i], canTrample); jumpTo(jit); } diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp index 910c3d986..869751372 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp @@ -38,7 +38,7 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, bool resul { ASSERT_UNUSED(result, !result); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); #else UNUSED_PARAM(codeBlock); UNUSED_PARAM(opcodeID); @@ -51,10 +51,10 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, Capability ASSERT(result != CanCompile); #if DFG_ENABLE(DEBUG_VERBOSE) if (result == CannotCompile) - dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]); else { ASSERT(result == ShouldProfile); - dataLog("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]); + dataLogF("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]); } #else UNUSED_PARAM(codeBlock); diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h index 1f9778efe..a89c697f6 100644 --- a/Source/JavaScriptCore/dfg/DFGCapabilities.h +++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h @@ -116,6 +116,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi case op_enter: case op_convert_this: case op_create_this: + case op_get_callee: case op_bitand: case op_bitor: case op_bitxor: diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h index c3726ed85..2c0556d60 100644 --- a/Source/JavaScriptCore/dfg/DFGCommon.h +++ b/Source/JavaScriptCore/dfg/DFGCommon.h @@ -135,11 +135,6 @@ enum NoResultTag { NoResult }; enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged }; -inline bool shouldShowDisassembly() -{ - return Options::showDisassembly() || Options::showDFGDisassembly(); -} - } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT) @@ -150,6 +145,16 @@ namespace JSC { namespace DFG { enum CapabilityLevel { CannotCompile, ShouldProfile, CanCompile, CapabilityLevelNotSet }; +// Unconditionally disable DFG disassembly support if the DFG is not compiled in. +inline bool shouldShowDisassembly() +{ +#if ENABLE(DFG_JIT) + return Options::showDisassembly() || Options::showDFGDisassembly(); +#else + return false; +#endif +} + } } // namespace JSC::DFG #endif // DFGCommon_h diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index 43aa2c007..2221954b5 100644 --- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp @@ -33,6 +33,8 @@ #include "DFGGraph.h" #include "DFGInsertionSet.h" #include "DFGPhase.h" +#include "GetByIdStatus.h" +#include "PutByIdStatus.h" namespace JSC { namespace DFG { @@ -65,7 +67,7 @@ private: bool foldConstants(BlockIndex blockIndex) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Constant folding considering Block #%u.\n", blockIndex); + dataLogF("Constant folding considering Block #%u.\n", blockIndex); #endif BasicBlock* block = m_graph.m_blocks[blockIndex].get(); bool changed = false; @@ -109,14 +111,16 @@ private: StructureAbstractValue& structureValue = value.m_futurePossibleStructure; if (structureValue.isSubsetOf(set) && structureValue.hasSingleton() - && isCellSpeculation(value.m_type)) + && isCellSpeculation(value.m_type)) { node.convertToStructureTransitionWatchpoint(structureValue.singleton()); + changed = true; + } break; } case CheckArray: case Arrayify: { - if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) + if (!node.arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node.child1()))) break; ASSERT(node.refCount() == 1); node.setOpAndDefaultFlags(Phantom); @@ -124,6 +128,206 @@ private: break; } + case CheckFunction: { + if (m_state.forNode(node.child1()).value() != node.function()) + break; + node.setOpAndDefaultFlags(Phantom); + eliminated = true; + break; + } + + case ConvertThis: { + if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type)) + break; + node.setOpAndDefaultFlags(Identity); + changed = true; + break; + } + + case GetById: + case GetByIdFlush: { + CodeOrigin codeOrigin = node.codeOrigin; + NodeIndex child = node.child1().index(); + unsigned identifierNumber = node.identifierNumber(); + + if (!isCellSpeculation(m_graph[child].prediction())) + break; + + Structure* structure = m_state.forNode(child).bestProvenStructure(); + if (!structure) + break; + + bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); + + GetByIdStatus status = GetByIdStatus::computeFor( + globalData(), structure, codeBlock()->identifier(identifierNumber)); + + if (!status.isSimple()) + break; + + ASSERT(status.structureSet().size() == 1); + ASSERT(status.chain().isEmpty()); + ASSERT(status.structureSet().singletonStructure() == structure); + + // Now before we do anything else, push the CFA forward over the GetById + // and make sure we signal to the loop that it should continue and not + // do any eliminations. + m_state.execute(indexInBlock); + eliminated = true; + + if (needsWatchpoint) { + ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure))); + m_graph[child].ref(); + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + } + + NodeIndex propertyStorageIndex; + + m_graph[child].ref(); + if (isInlineOffset(status.offset())) + propertyStorageIndex = child; + else { + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + propertyStorageIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } + + m_graph[nodeIndex].convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex); + + StorageAccessData storageAccessData; + storageAccessData.offset = indexRelativeToBase(status.offset()); + storageAccessData.identifierNumber = identifierNumber; + m_graph.m_storageAccessData.append(storageAccessData); + break; + } + + case PutById: + case PutByIdDirect: { + CodeOrigin codeOrigin = node.codeOrigin; + NodeIndex child = node.child1().index(); + unsigned identifierNumber = node.identifierNumber(); + + Structure* structure = m_state.forNode(child).bestProvenStructure(); + if (!structure) + break; + + bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); + + PutByIdStatus status = PutByIdStatus::computeFor( + globalData(), + m_graph.globalObjectFor(codeOrigin), + structure, + codeBlock()->identifier(identifierNumber), + node.op() == PutByIdDirect); + + if (!status.isSimpleReplace() && !status.isSimpleTransition()) + break; + + ASSERT(status.oldStructure() == structure); + + // Now before we do anything else, push the CFA forward over the PutById + // and make sure we signal to the loop that it should continue and not + // do any eliminations. + m_state.execute(indexInBlock); + eliminated = true; + + if (needsWatchpoint) { + ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure))); + m_graph[child].ref(); + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + } + + StructureTransitionData* transitionData = 0; + if (status.isSimpleTransition()) { + transitionData = m_graph.addStructureTransitionData( + StructureTransitionData(structure, status.newStructure())); + + if (node.op() == PutById) { + if (!structure->storedPrototype().isNull()) { + addStructureTransitionCheck( + codeOrigin, indexInBlock, + structure->storedPrototype().asCell()); + } + + for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) { + JSValue prototype = (*it)->storedPrototype(); + if (prototype.isNull()) + continue; + ASSERT(prototype.isCell()); + addStructureTransitionCheck( + codeOrigin, indexInBlock, prototype.asCell()); + } + } + } + + NodeIndex propertyStorageIndex; + + m_graph[child].ref(); + if (isInlineOffset(status.offset())) + propertyStorageIndex = child; + else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) { + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + propertyStorageIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } else if (!structure->outOfLineCapacity()) { + ASSERT(status.newStructure()->outOfLineCapacity()); + ASSERT(!isInlineOffset(status.offset())); + Node allocateStorage(AllocatePropertyStorage, codeOrigin, OpInfo(transitionData), child); + allocateStorage.ref(); // Once for the use. + allocateStorage.ref(); // Twice because it's must-generate. + propertyStorageIndex = m_graph.size(); + m_graph.append(allocateStorage); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } else { + ASSERT(structure->outOfLineCapacity()); + ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity()); + ASSERT(!isInlineOffset(status.offset())); + + Node getButterfly(GetButterfly, codeOrigin, child); + getButterfly.ref(); + NodeIndex getButterflyIndex = m_graph.size(); + m_graph.append(getButterfly); + m_insertionSet.append(indexInBlock, getButterflyIndex); + + m_graph[child].ref(); + Node reallocateStorage(ReallocatePropertyStorage, codeOrigin, OpInfo(transitionData), child, getButterflyIndex); + reallocateStorage.ref(); // Once for the use. + reallocateStorage.ref(); // Twice because it's must-generate. + propertyStorageIndex = m_graph.size(); + m_graph.append(reallocateStorage); + m_insertionSet.append(indexInBlock, propertyStorageIndex); + } + + if (status.isSimpleTransition()) { + m_graph[child].ref(); + Node putStructure(PutStructure, codeOrigin, OpInfo(transitionData), child); + putStructure.ref(); + NodeIndex putStructureIndex = m_graph.size(); + m_graph.append(putStructure); + m_insertionSet.append(indexInBlock, putStructureIndex); + } + + m_graph[nodeIndex].convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex); + + StorageAccessData storageAccessData; + storageAccessData.offset = indexRelativeToBase(status.offset()); + storageAccessData.identifierNumber = identifierNumber; + m_graph.m_storageAccessData.append(storageAccessData); + break; + } + default: break; } @@ -213,6 +417,31 @@ private: return changed; } + void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell) + { + Node weakConstant(WeakJSConstant, codeOrigin, OpInfo(cell)); + weakConstant.ref(); + weakConstant.predict(speculationFromValue(cell)); + NodeIndex weakConstantIndex = m_graph.size(); + m_graph.append(weakConstant); + m_insertionSet.append(indexInBlock, weakConstantIndex); + + if (cell->structure()->transitionWatchpointSetIsStillValid()) { + Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(cell->structure()), weakConstantIndex); + watchpoint.ref(); + NodeIndex watchpointIndex = m_graph.size(); + m_graph.append(watchpoint); + m_insertionSet.append(indexInBlock, watchpointIndex); + return; + } + + Node check(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(cell->structure())), weakConstantIndex); + check.ref(); + NodeIndex checkIndex = m_graph.size(); + m_graph.append(check); + m_insertionSet.append(indexInBlock, checkIndex); + } + // This is necessary because the CFA may reach conclusions about constants based on its // assumption that certain code must exit, but then those constants may lead future // reexecutions of the CFA to believe that the same code will now no longer exit. Thus @@ -225,7 +454,7 @@ private: bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Painting unreachable code in Block #%u.\n", blockIndex); + dataLogF("Painting unreachable code in Block #%u.\n", blockIndex); #endif BasicBlock* block = m_graph.m_blocks[blockIndex].get(); m_state.beginBasicBlock(block); diff --git a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp index cfbb936b8..654824196 100644 --- a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp +++ b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp @@ -43,8 +43,8 @@ void Disassembler::dump(LinkBuffer& linkBuffer) { m_graph.m_dominators.computeIfNecessary(m_graph); - 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()); + dataLogF("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount()); + dataLogF(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize()); const char* prefix = " "; const char* disassemblyPrefix = " "; @@ -82,7 +82,7 @@ void Disassembler::dump(LinkBuffer& linkBuffer) } } dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfMainPath, lastNodeIndex); - dataLog("%s(End Of Main Path)\n", prefix); + dataLogF("%s(End Of Main Path)\n", prefix); dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfCode, NoNode); } @@ -104,10 +104,7 @@ void Disassembler::dumpDisassembly(const char* prefix, LinkBuffer& linkBuffer, M CodeLocationLabel end = linkBuffer.locationOf(currentLabel); previousLabel = currentLabel; ASSERT(bitwise_cast<uintptr_t>(end.executableAddress()) >= bitwise_cast<uintptr_t>(start.executableAddress())); - if (tryToDisassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile())) - return; - - dataLog("%s disassembly not available for range %p...%p\n", prefixBuffer.get(), start.executableAddress(), end.executableAddress()); + disassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile()); } } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp index eb68fa344..8645c6dce 100644 --- a/Source/JavaScriptCore/dfg/DFGDriver.cpp +++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp @@ -72,7 +72,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo 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()); + dataLogF("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount()); #endif // Derive our set of must-handle values. The compilation must be at least conservative @@ -119,7 +119,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.m_fixpointState = FixpointNotConverged; for (;; ++cnt) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("DFG beginning optimization fixpoint iteration #%u.\n", cnt); + dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", cnt); #endif bool changed = false; performCFA(dfg); @@ -135,13 +135,13 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo dfg.m_fixpointState = FixpointConverged; performCSE(dfg); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt); + dataLogF("DFG optimization fixpoint converged in %u iterations.\n", cnt); #endif performVirtualRegisterAllocation(dfg); GraphDumpMode modeForFinalValidate = DumpGraph; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Graph after optimization:\n"); + dataLogF("Graph after optimization:\n"); dfg.dump(); modeForFinalValidate = DontDumpGraph; #endif diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 5a76aa8df..1ba40def3 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -69,7 +69,7 @@ private: NodeType op = node.op(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex); + dataLogF(" %s @%u: ", Graph::opName(op), m_compileIndex); #endif switch (op) { @@ -134,6 +134,17 @@ private: m_graph[node.child2()].prediction())); blessArrayOperation(node.child1(), node.child2(), 2); + + Node* nodePtr = &m_graph[m_compileIndex]; + ArrayMode arrayMode = nodePtr->arrayMode(); + if (arrayMode.type() == Array::Double + && arrayMode.arrayClass() == Array::OriginalArray + && arrayMode.speculation() == Array::InBounds + && arrayMode.conversion() == Array::AsIs + && m_graph.globalObjectFor(nodePtr->codeOrigin)->arrayPrototypeChainIsSane() + && !(nodePtr->flags() & NodeUsedAsOther)) + nodePtr->setArrayMode(arrayMode.withSpeculation(Array::SaneChain)); + break; } case StringCharAt: @@ -145,7 +156,30 @@ private: } case ArrayPush: { + // May need to refine the array mode in case the value prediction contravenes + // the array prediction. For example, we may have evidence showing that the + // array is in Int32 mode, but the value we're storing is likely to be a double. + // Then we should turn this into a conversion to Double array followed by the + // push. On the other hand, we absolutely don't want to refine based on the + // base prediction. If it has non-cell garbage in it, then we want that to be + // ignored. That's because ArrayPush can't handle any array modes that aren't + // array-related - so if refine() turned this into a "Generic" ArrayPush then + // that would break things. + node.setArrayMode( + node.arrayMode().refine( + m_graph[node.child1()].prediction() & SpecCell, + SpecInt32, + m_graph[node.child2()].prediction())); blessArrayOperation(node.child1(), node.child2(), 2); + + Node* nodePtr = &m_graph[m_compileIndex]; + switch (nodePtr->arrayMode().type()) { + case Array::Double: + fixDoubleEdge(1); + break; + default: + break; + } break; } @@ -236,7 +270,7 @@ private: case ValueAdd: { if (m_graph.addShouldSpeculateInteger(node)) break; - if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) + if (!Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()])) break; fixDoubleEdge(0); fixDoubleEdge(1); @@ -262,7 +296,7 @@ private: case ArithMin: case ArithMax: case ArithMod: { - if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) break; fixDoubleEdge(0); @@ -279,7 +313,7 @@ private: } case ArithDiv: { - if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) { if (isX86()) break; @@ -307,7 +341,7 @@ private: } case ArithAbs: { - if (m_graph[node.child1()].shouldSpeculateInteger() + if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger()) break; fixDoubleEdge(0); @@ -328,13 +362,17 @@ private: node.setArrayMode( node.arrayMode().refine( m_graph[child1].prediction(), - m_graph[child2].prediction())); + m_graph[child2].prediction(), + m_graph[child3].prediction())); blessArrayOperation(child1, child2, 3); Node* nodePtr = &m_graph[m_compileIndex]; switch (nodePtr->arrayMode().modeForPut().type()) { + case Array::Double: + fixDoubleEdge(2); + break; case Array::Int8Array: case Array::Int16Array: case Array::Int32Array: @@ -355,16 +393,29 @@ private: break; } + case NewArray: { + for (unsigned i = m_graph.varArgNumChildren(node); i--;) { + node.setIndexingType( + leastUpperBoundOfIndexingTypeAndType( + node.indexingType(), m_graph[m_graph.varArgChild(node, i)].prediction())); + } + if (node.indexingType() == ArrayWithDouble) { + for (unsigned i = m_graph.varArgNumChildren(node); i--;) + fixDoubleEdge(i); + } + break; + } + default: break; } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (!(node.flags() & NodeHasVarArgs)) { - dataLog("new children: "); + dataLogF("new children: "); node.dumpChildren(WTF::dataFile()); } - dataLog("\n"); + dataLogF("\n"); #endif } @@ -384,29 +435,12 @@ private: m_graph.ref(array); + Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin); + if (arrayMode.doesConversion()) { if (index != NoNode) m_graph.ref(index); - Structure* structure = 0; - if (arrayMode.isJSArrayWithOriginalStructure()) { - JSGlobalObject* globalObject = m_graph.baselineCodeBlockFor(codeOrigin)->globalObject(); - switch (arrayMode.type()) { - case Array::Contiguous: - structure = globalObject->arrayStructure(); - if (structure->indexingType() != ArrayWithContiguous) - structure = 0; - break; - case Array::ArrayStorage: - structure = globalObject->arrayStructureWithArrayStorage(); - if (structure->indexingType() != ArrayWithArrayStorage) - structure = 0; - break; - default: - break; - } - } - if (structure) { Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index); arrayify.ref(); @@ -421,11 +455,19 @@ private: m_insertionSet.append(m_indexInBlock, arrayifyIndex); } } else { - Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array); - checkArray.ref(); - NodeIndex checkArrayIndex = m_graph.size(); - m_graph.append(checkArray); - m_insertionSet.append(m_indexInBlock, checkArrayIndex); + if (structure) { + Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(structure)), array); + checkStructure.ref(); + NodeIndex checkStructureIndex = m_graph.size(); + m_graph.append(checkStructure); + m_insertionSet.append(m_indexInBlock, checkStructureIndex); + } else { + Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array); + checkArray.ref(); + NodeIndex checkArrayIndex = m_graph.size(); + m_graph.append(checkArray); + m_insertionSet.append(m_indexInBlock, checkArrayIndex); + } } if (!storageCheck(arrayMode)) @@ -506,7 +548,7 @@ private: NodeIndex resultIndex = (NodeIndex)m_graph.size(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("(replacing @%u->@%u with @%u->@%u) ", + dataLogF("(replacing @%u->@%u with @%u->@%u) ", m_compileIndex, edge.index(), m_compileIndex, resultIndex); #endif diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp index 8e8817f81..19587ba64 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.cpp +++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp @@ -98,7 +98,7 @@ const char* Graph::nameOfVariableAccessData(VariableAccessData* variableAccessDa static void printWhiteSpace(unsigned amount) { while (amount-- > 0) - dataLog(" "); + dataLogF(" "); } void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeIndex nodeIndex) @@ -124,16 +124,16 @@ void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeInde // Print the pops. for (unsigned i = previousInlineStack.size(); i-- > indexOfDivergence;) { - dataLog("%s", prefix); + dataLogF("%s", prefix); printWhiteSpace(i * 2); - dataLog("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get()); + dataLogF("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get()); } // Print the pushes. for (unsigned i = indexOfDivergence; i < currentInlineStack.size(); ++i) { - dataLog("%s", prefix); + dataLogF("%s", prefix); printWhiteSpace(i * 2); - dataLog("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get()); + dataLogF("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get()); } } @@ -158,7 +158,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) if (mustGenerate) --refCount; - dataLog("%s", prefix); + dataLogF("%s", prefix); printNodeWhiteSpace(node); // Example/explanation of dataflow dump output @@ -178,22 +178,22 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) // $# - the index in the CodeBlock of a constant { for numeric constants the value is displayed | for integers, in both decimal and hex }. // id# - the index in the CodeBlock of an identifier { if codeBlock is passed to dump(), the string representation is displayed }. // var# - the index of a var on the global object, used by GetGlobalVar/PutGlobalVar operations. - dataLog("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount); + dataLogF("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount); if (node.hasResult() && !skipped && node.hasVirtualRegister()) - dataLog("%u", node.virtualRegister()); + dataLogF("%u", node.virtualRegister()); else - dataLog("-"); - dataLog(">\t%s(", opName(op)); + dataLogF("-"); + dataLogF(">\t%s(", opName(op)); bool hasPrinted = false; if (node.flags() & NodeHasVarArgs) { for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) { if (hasPrinted) - dataLog(", "); + dataLogF(", "); else hasPrinted = true; if (!m_varArgChildren[childIdx]) continue; - dataLog("%s@%u%s", + dataLogF("%s@%u%s", useKindToString(m_varArgChildren[childIdx].useKind()), m_varArgChildren[childIdx].index(), speculationToAbbreviatedString( @@ -201,19 +201,19 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) } } else { if (!!node.child1()) { - dataLog("%s@%u%s", + dataLogF("%s@%u%s", useKindToString(node.child1().useKind()), node.child1().index(), speculationToAbbreviatedString(at(node.child1()).prediction())); } if (!!node.child2()) { - dataLog(", %s@%u%s", + dataLogF(", %s@%u%s", useKindToString(node.child2().useKind()), node.child2().index(), speculationToAbbreviatedString(at(node.child2()).prediction())); } if (!!node.child3()) { - dataLog(", %s@%u%s", + dataLogF(", %s@%u%s", useKindToString(node.child3().useKind()), node.child3().index(), speculationToAbbreviatedString(at(node.child3()).prediction())); @@ -222,47 +222,51 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) } if (strlen(nodeFlagsAsString(node.flags()))) { - dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags())); + dataLogF("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags())); hasPrinted = true; } if (node.hasArrayMode()) { - dataLog("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString()); + dataLogF("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString()); hasPrinted = true; } if (node.hasVarNumber()) { - dataLog("%svar%u", hasPrinted ? ", " : "", node.varNumber()); + dataLogF("%svar%u", hasPrinted ? ", " : "", node.varNumber()); hasPrinted = true; } if (node.hasRegisterPointer()) { - dataLog( + dataLogF( "%sglobal%u(%p)", hasPrinted ? ", " : "", globalObjectFor(node.codeOrigin)->findRegisterIndex(node.registerPointer()), node.registerPointer()); hasPrinted = true; } if (node.hasIdentifier()) { - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data()); + dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data()); hasPrinted = true; } if (node.hasStructureSet()) { for (size_t i = 0; i < node.structureSet().size(); ++i) { - dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType())); + dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType())); hasPrinted = true; } } if (node.hasStructure()) { - dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType())); + dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType())); hasPrinted = true; } if (node.hasStructureTransitionData()) { - dataLog("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure); + dataLogF("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure); + hasPrinted = true; + } + if (node.hasFunction()) { + dataLogF("%s%p", hasPrinted ? ", " : "", node.function()); hasPrinted = true; } if (node.hasStorageAccessData()) { StorageAccessData& storageAccessData = m_storageAccessData[node.storageAccessDataIndex()]; - dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data()); + dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data()); - dataLog(", %lu", static_cast<unsigned long>(storageAccessData.offset)); + dataLogF(", %lu", static_cast<unsigned long>(storageAccessData.offset)); hasPrinted = true; } ASSERT(node.hasVariableAccessData() == node.hasLocal()); @@ -270,100 +274,105 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex) VariableAccessData* variableAccessData = node.variableAccessData(); int operand = variableAccessData->operand(); if (operandIsArgument(operand)) - dataLog("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData)); + dataLogF("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData)); else - dataLog("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData)); + dataLogF("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData)); hasPrinted = true; } if (node.hasConstantBuffer()) { if (hasPrinted) - dataLog(", "); - dataLog("%u:[", node.startConstant()); + dataLogF(", "); + dataLogF("%u:[", node.startConstant()); for (unsigned i = 0; i < node.numConstants(); ++i) { if (i) - dataLog(", "); - dataLog("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description()); + dataLogF(", "); + dataLogF("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description()); } - dataLog("]"); + dataLogF("]"); hasPrinted = true; } + if (node.hasIndexingType()) { + if (hasPrinted) + dataLogF(", "); + dataLogF("%s", indexingTypeToString(node.indexingType())); + } if (op == JSConstant) { - dataLog("%s$%u", hasPrinted ? ", " : "", node.constantNumber()); + dataLogF("%s$%u", hasPrinted ? ", " : "", node.constantNumber()); JSValue value = valueOfJSConstant(nodeIndex); - dataLog(" = %s", value.description()); + dataLogF(" = %s", value.description()); hasPrinted = true; } if (op == WeakJSConstant) { - dataLog("%s%p", hasPrinted ? ", " : "", node.weakConstant()); + dataLogF("%s%p", hasPrinted ? ", " : "", node.weakConstant()); hasPrinted = true; } if (node.isBranch() || node.isJump()) { - dataLog("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex()); + dataLogF("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex()); hasPrinted = true; } if (node.isBranch()) { - dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex()); + dataLogF("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex()); hasPrinted = true; } - dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex); + dataLogF("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex); hasPrinted = true; (void)hasPrinted; - dataLog(")"); + dataLogF(")"); if (!skipped) { if (node.hasVariableAccessData()) - dataLog(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); + dataLogF(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : ""); else if (node.hasHeapPrediction()) - dataLog(" predicting %s", speculationToString(node.getHeapPrediction())); + dataLogF(" predicting %s", speculationToString(node.getHeapPrediction())); } - dataLog("\n"); + dataLogF("\n"); } void Graph::dumpBlockHeader(const char* prefix, BlockIndex blockIndex, PhiNodeDumpMode phiNodeDumpMode) { BasicBlock* block = m_blocks[blockIndex].get(); - dataLog("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : ""); - dataLog("%s Predecessors:", prefix); + dataLogF("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : ""); + dataLogF("%s Predecessors:", prefix); for (size_t i = 0; i < block->m_predecessors.size(); ++i) - dataLog(" #%u", block->m_predecessors[i]); - dataLog("\n"); + dataLogF(" #%u", block->m_predecessors[i]); + dataLogF("\n"); if (m_dominators.isValid()) { - dataLog("%s Dominated by:", prefix); + dataLogF("%s Dominated by:", prefix); for (size_t i = 0; i < m_blocks.size(); ++i) { if (!m_dominators.dominates(i, blockIndex)) continue; - dataLog(" #%lu", static_cast<unsigned long>(i)); + dataLogF(" #%lu", static_cast<unsigned long>(i)); } - dataLog("\n"); - dataLog("%s Dominates:", prefix); + dataLogF("\n"); + dataLogF("%s Dominates:", prefix); for (size_t i = 0; i < m_blocks.size(); ++i) { if (!m_dominators.dominates(blockIndex, i)) continue; - dataLog(" #%lu", static_cast<unsigned long>(i)); + dataLogF(" #%lu", static_cast<unsigned long>(i)); } - dataLog("\n"); + dataLogF("\n"); } - dataLog("%s Phi Nodes:", prefix); + dataLogF("%s Phi Nodes:", prefix); 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; - dataLog(" @%u->(", phiNodeIndex); + dataLogF(" @%u->(", phiNodeIndex); if (phiNode.child1()) { - dataLog("@%u", phiNode.child1().index()); + dataLogF("@%u", phiNode.child1().index()); if (phiNode.child2()) { - dataLog(", @%u", phiNode.child2().index()); + dataLogF(", @%u", phiNode.child2().index()); if (phiNode.child3()) - dataLog(", @%u", phiNode.child3().index()); + dataLogF(", @%u", phiNode.child3().index()); } } - dataLog(")%s", i + 1 < block->phis.size() ? "," : ""); + dataLogF(")%s", i + 1 < block->phis.size() ? "," : ""); } - dataLog("\n"); + dataLogF("\n"); } void Graph::dump() @@ -374,29 +383,29 @@ void Graph::dump() if (!block) continue; dumpBlockHeader("", b, DumpAllPhis); - dataLog(" vars before: "); + dataLogF(" vars before: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtHead, WTF::dataFile()); else - dataLog("<empty>"); - dataLog("\n"); - dataLog(" var links: "); + dataLogF("<empty>"); + dataLogF("\n"); + dataLogF(" var links: "); dumpOperands(block->variablesAtHead, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); for (size_t i = 0; i < block->size(); ++i) { dumpCodeOrigin("", lastNodeIndex, block->at(i)); dump("", block->at(i)); lastNodeIndex = block->at(i); } - dataLog(" vars after: "); + dataLogF(" vars after: "); if (block->cfaHasVisited) dumpOperands(block->valuesAtTail, WTF::dataFile()); else - dataLog("<empty>"); - dataLog("\n"); - dataLog(" var links: "); + dataLogF("<empty>"); + dataLogF("\n"); + dataLogF(" var links: "); dumpOperands(block->variablesAtTail, WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); } } @@ -451,7 +460,7 @@ void Graph::predictArgumentTypes() at(m_arguments[arg]).variableAccessData()->predict(profile->computeUpdatedPrediction()); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction())); + dataLogF("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction())); #endif } } diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h index 9fbb2df07..0c77b2959 100644 --- a/Source/JavaScriptCore/dfg/DFGGraph.h +++ b/Source/JavaScriptCore/dfg/DFGGraph.h @@ -220,7 +220,7 @@ public: if (right.hasConstant()) return addImmediateShouldSpeculateInteger(add, left, right); - return Node::shouldSpeculateInteger(left, right) && add.canSpeculateInteger(); + return Node::shouldSpeculateIntegerExpectingDefined(left, right) && add.canSpeculateInteger(); } bool mulShouldSpeculateInteger(Node& mul) @@ -230,18 +230,13 @@ public: Node& left = at(mul.child1()); Node& right = at(mul.child2()); - if (left.hasConstant()) - return mulImmediateShouldSpeculateInteger(mul, right, left); - if (right.hasConstant()) - return mulImmediateShouldSpeculateInteger(mul, left, right); - - return Node::shouldSpeculateInteger(left, right) && mul.canSpeculateInteger() && !nodeMayOverflow(mul.arithNodeFlags()); + return Node::shouldSpeculateIntegerForArithmetic(left, right) && mul.canSpeculateInteger(); } bool negateShouldSpeculateInteger(Node& negate) { ASSERT(negate.op() == ArithNegate); - return at(negate.child1()).shouldSpeculateInteger() && negate.canSpeculateInteger(); + return at(negate.child1()).shouldSpeculateIntegerForArithmetic() && negate.canSpeculateInteger(); } bool addShouldSpeculateInteger(NodeIndex nodeIndex) @@ -493,6 +488,8 @@ public: switch (node.arrayMode().type()) { case Array::Generic: return false; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: return !node.arrayMode().isOutOfBounds(); @@ -712,7 +709,7 @@ private: if (!immediateValue.isNumber()) return false; - if (!variable.shouldSpeculateInteger()) + if (!variable.shouldSpeculateIntegerExpectingDefined()) return false; if (immediateValue.isInt32()) @@ -734,7 +731,7 @@ private: if (!immediateValue.isInt32()) return false; - if (!variable.shouldSpeculateInteger()) + if (!variable.shouldSpeculateIntegerForArithmetic()) return false; int32_t intImmediate = immediateValue.asInt32(); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp index c7f941a7a..191aa7fe5 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp @@ -128,7 +128,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer) { // Link the code, populate data in CodeBlock data structures. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize()); + dataLogF("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize()); #endif // Link all calls out from the JIT code to their respective functions. @@ -188,11 +188,12 @@ void JITCompiler::link(LinkBuffer& linkBuffer) CallLinkInfo& info = m_codeBlock->callLinkInfo(i); info.callType = m_jsCalls[i].m_callType; info.isDFG = true; - info.bytecodeIndex = m_jsCalls[i].m_codeOrigin.bytecodeIndex; + info.codeOrigin = m_jsCalls[i].m_codeOrigin; linkBuffer.link(m_jsCalls[i].m_slowCall, FunctionPtr((m_globalData->getCTIStub(info.callType == CallLinkInfo::Construct ? linkConstructThunkGenerator : linkCallThunkGenerator)).code().executableAddress())); info.callReturnLocation = linkBuffer.locationOfNearCall(m_jsCalls[i].m_slowCall); info.hotPathBegin = linkBuffer.locationOf(m_jsCalls[i].m_targetToCheck); info.hotPathOther = linkBuffer.locationOfNearCall(m_jsCalls[i].m_fastCall); + info.calleeGPR = static_cast<unsigned>(m_jsCalls[i].m_callee); } MacroAssemblerCodeRef osrExitThunk = globalData()->getCTIStub(osrExitGenerationThunkGenerator); diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h index c73934832..0bd88b788 100644 --- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h +++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h @@ -357,9 +357,9 @@ public: m_propertyAccesses.append(record); } - void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) + void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) { - m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, codeOrigin)); + m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, callee, codeOrigin)); } void addWeakReference(JSCell* target) @@ -440,11 +440,12 @@ private: Vector<CallExceptionRecord> m_exceptionChecks; struct JSCallRecord { - JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) + JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) : m_fastCall(fastCall) , m_slowCall(slowCall) , m_targetToCheck(targetToCheck) , m_callType(callType) + , m_callee(callee) , m_codeOrigin(codeOrigin) { } @@ -453,6 +454,7 @@ private: Call m_slowCall; DataLabelPtr m_targetToCheck; CallLinkInfo::CallType m_callType; + GPRReg m_callee; CodeOrigin m_codeOrigin; }; diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h index e66629ec4..18c8ce16f 100644 --- a/Source/JavaScriptCore/dfg/DFGNode.h +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -62,6 +62,7 @@ struct StructureTransitionData { struct NewArrayBufferData { unsigned startConstant; unsigned numConstants; + IndexingType indexingType; }; // This type used in passing an immediate argument to Node constructor; @@ -268,6 +269,26 @@ struct Node { convertToStructureTransitionWatchpoint(structureSet().singletonStructure()); } + void convertToGetByOffset(unsigned storageAccessDataIndex, NodeIndex storage) + { + ASSERT(m_op == GetById || m_op == GetByIdFlush); + m_opInfo = storageAccessDataIndex; + children.setChild1(Edge(storage)); + m_op = GetByOffset; + m_flags &= ~NodeClobbersWorld; + } + + void convertToPutByOffset(unsigned storageAccessDataIndex, NodeIndex storage) + { + ASSERT(m_op == PutById || m_op == PutByIdDirect); + m_opInfo = storageAccessDataIndex; + children.setChild3(children.child2()); + children.setChild2(children.child1()); + children.setChild1(Edge(storage)); + m_op = PutByOffset; + m_flags &= ~NodeClobbersWorld; + } + JSCell* weakConstant() { ASSERT(op() == WeakJSConstant); @@ -433,6 +454,32 @@ struct Node { return newArrayBufferData()->numConstants; } + bool hasIndexingType() + { + switch (op()) { + case NewArray: + case NewArrayWithSize: + case NewArrayBuffer: + return true; + default: + return false; + } + } + + IndexingType indexingType() + { + ASSERT(hasIndexingType()); + if (op() == NewArrayBuffer) + return newArrayBufferData()->indexingType; + return m_opInfo; + } + + void setIndexingType(IndexingType indexingType) + { + ASSERT(hasIndexingType()); + m_opInfo = indexingType; + } + bool hasRegexpIndex() { return op() == NewRegexp; @@ -518,6 +565,11 @@ struct Node { return (m_flags & NodeResultMask) == NodeResultBoolean; } + bool hasStorageResult() + { + return (m_flags & NodeResultMask) == NodeResultStorage; + } + bool isJump() { return op() == Jump; @@ -649,15 +701,23 @@ struct Node { return mergeSpeculation(m_opInfo2, prediction); } - bool hasFunctionCheckData() + bool hasFunction() { - return op() == CheckFunction; + switch (op()) { + case CheckFunction: + case InheritorIDWatchpoint: + return true; + default: + return false; + } } - JSFunction* function() + JSCell* function() { - ASSERT(hasFunctionCheckData()); - return reinterpret_cast<JSFunction*>(m_opInfo); + ASSERT(hasFunction()); + JSCell* result = reinterpret_cast<JSFunction*>(m_opInfo); + ASSERT(JSValue(result).isFunction()); + return result; } bool hasStructureTransitionData() @@ -702,6 +762,7 @@ struct Node { case StructureTransitionWatchpoint: case ForwardStructureTransitionWatchpoint: case ArrayifyToStructure: + case NewObject: return true; default: return false; @@ -922,16 +983,36 @@ struct Node { return isInt32Speculation(prediction()); } + bool shouldSpeculateIntegerForArithmetic() + { + return isInt32SpeculationForArithmetic(prediction()); + } + + bool shouldSpeculateIntegerExpectingDefined() + { + return isInt32SpeculationExpectingDefined(prediction()); + } + bool shouldSpeculateDouble() { return isDoubleSpeculation(prediction()); } + bool shouldSpeculateDoubleForArithmetic() + { + return isDoubleSpeculationForArithmetic(prediction()); + } + bool shouldSpeculateNumber() { return isNumberSpeculation(prediction()); } + bool shouldSpeculateNumberExpectingDefined() + { + return isNumberSpeculationExpectingDefined(prediction()); + } + bool shouldSpeculateBoolean() { return isBooleanSpeculation(prediction()); @@ -1037,11 +1118,31 @@ struct Node { return op1.shouldSpeculateInteger() && op2.shouldSpeculateInteger(); } + static bool shouldSpeculateIntegerForArithmetic(Node& op1, Node& op2) + { + return op1.shouldSpeculateIntegerForArithmetic() && op2.shouldSpeculateIntegerForArithmetic(); + } + + static bool shouldSpeculateIntegerExpectingDefined(Node& op1, Node& op2) + { + return op1.shouldSpeculateIntegerExpectingDefined() && op2.shouldSpeculateIntegerExpectingDefined(); + } + + static bool shouldSpeculateDoubleForArithmetic(Node& op1, Node& op2) + { + return op1.shouldSpeculateDoubleForArithmetic() && op2.shouldSpeculateDoubleForArithmetic(); + } + static bool shouldSpeculateNumber(Node& op1, Node& op2) { return op1.shouldSpeculateNumber() && op2.shouldSpeculateNumber(); } + static bool shouldSpeculateNumberExpectingDefined(Node& op1, Node& op2) + { + return op1.shouldSpeculateNumberExpectingDefined() && op2.shouldSpeculateNumberExpectingDefined(); + } + static bool shouldSpeculateFinalObject(Node& op1, Node& op2) { return op1.shouldSpeculateFinalObject() && op2.shouldSpeculateFinalObject(); diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp index 480a7dab9..fb83c5a71 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp +++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp @@ -112,6 +112,12 @@ const char* nodeFlagsAsString(NodeFlags flags) ptr.strcat("PureNum"); hasPrinted = true; } + if (flags & NodeUsedAsOther) { + if (hasPrinted) + ptr.strcat("|"); + ptr.strcat("UseAsOther"); + hasPrinted = true; + } } if (flags & NodeMayOverflow) { diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h index a897d0c4f..5e41bfc6b 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h +++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h @@ -52,14 +52,15 @@ namespace JSC { namespace DFG { #define NodeMayOverflow 0x100 #define NodeMayNegZero 0x200 -#define NodeBackPropMask 0x1C00 +#define NodeBackPropMask 0x3C00 #define NodeUseBottom 0x000 #define NodeUsedAsNumber 0x400 // The result of this computation may be used in a context that observes fractional results. #define NodeNeedsNegZero 0x800 // The result of this computation may be used in a context that observes -0. -#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero) -#define NodeUsedAsInt 0x1000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. +#define NodeUsedAsOther 0x1000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined). +#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther) +#define NodeUsedAsInt 0x2000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values. -#define NodeDoesNotExit 0x2000 // This flag is negated to make it natural for the default to be that a node does exit. +#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit. typedef uint16_t NodeFlags; diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h index 624b1ae75..cbcdde660 100644 --- a/Source/JavaScriptCore/dfg/DFGNodeType.h +++ b/Source/JavaScriptCore/dfg/DFGNodeType.h @@ -43,6 +43,11 @@ namespace JSC { namespace DFG { /* code block. */\ macro(WeakJSConstant, NodeResultJS | NodeDoesNotExit) \ \ + /* Marker to indicate that an operation was optimized entirely and all that is left */\ + /* is to make one node alias another. CSE will later usually eliminate this node, */\ + /* though it may choose not to if it would corrupt predictions (very rare). */\ + macro(Identity, NodeResultJS | NodeDoesNotExit) \ + \ /* Nodes for handling functions (both as call and as construct). */\ macro(ConvertThis, NodeResultJS) \ macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \ @@ -154,6 +159,7 @@ namespace JSC { namespace DFG { macro(GlobalVarWatchpoint, NodeMustGenerate) \ macro(PutGlobalVarCheck, NodeMustGenerate) \ macro(CheckFunction, NodeMustGenerate) \ + macro(InheritorIDWatchpoint, NodeMustGenerate) \ \ /* Optimizations for array mutation. */\ macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \ diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp index b838c4fb4..ed13ed5b5 100644 --- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp @@ -44,7 +44,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn ASSERT(!codeBlock->jitCodeMap()); #if ENABLE(JIT_VERBOSE_OSR) - dataLog("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex); + dataLogF("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex); #endif JSGlobalData* globalData = &exec->globalData(); @@ -52,7 +52,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!entry) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because the entrypoint was optimized out.\n"); + dataLogF(" OSR failed because the entrypoint was optimized out.\n"); #endif return 0; } @@ -86,9 +86,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn for (size_t argument = 0; argument < entry->m_expectedValues.numberOfArguments(); ++argument) { if (argument >= exec->argumentCountIncludingThis()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because argument %zu was not passed, expected ", argument); + dataLogF(" OSR failed because argument %zu was not passed, expected ", argument); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -101,9 +101,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!entry->m_expectedValues.argument(argument).validate(value)) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description()); + dataLogF(" OSR failed because argument %zu is %s, expected ", argument, value.description()); entry->m_expectedValues.argument(argument).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -113,7 +113,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (entry->m_localsForcedDouble.get(local)) { if (!exec->registers()[local].jsValue().isNumber()) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description()); + dataLogF(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description()); #endif return 0; } @@ -121,9 +121,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn } if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); + dataLogF(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description()); entry->m_expectedValues.local(local).dump(WTF::dataFile()); - dataLog(".\n"); + dataLogF(".\n"); #endif return 0; } @@ -138,13 +138,13 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn if (!globalData->interpreter->stack().grow(&exec->registers()[codeBlock->m_numCalleeRegisters])) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR failed because stack growth failed.\n"); + dataLogF(" OSR failed because stack growth failed.\n"); #endif return 0; } #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR should succeed.\n"); + dataLogF(" OSR should succeed.\n"); #endif // 3) Perform data format conversions. @@ -162,7 +162,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn void* result = codeBlock->getJITCode().executableAddressAtOffset(entry->m_machineCodeOffset); #if ENABLE(JIT_VERBOSE_OSR) - dataLog(" OSR returning machine code address %p.\n", result); + dataLogF(" OSR returning machine code address %p.\n", result); #endif return result; diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp index 2ce1c887b..c65443e29 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp @@ -81,7 +81,7 @@ void compileOSRExit(ExecState* exec) recovery = &codeBlock->speculationRecovery(exit.m_recoveryIndex - 1); #if DFG_ENABLE(DEBUG_VERBOSE) - 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); + dataLogF("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 { diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp index df4f3c905..732e67c30 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp @@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex); + dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex); for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) { - dataLog("bc#%u", codeOrigin.bytecodeIndex); + dataLogF("bc#%u", codeOrigin.bytecodeIndex); if (!codeOrigin.inlineCallFrame) break; - dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); + dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } - dataLog(") at JIT offset 0x%x ", m_jit.debugOffset()); + dataLogF(") at JIT offset 0x%x ", m_jit.debugOffset()); dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) @@ -762,7 +762,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov m_jit.jump(GPRInfo::regT2); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog(" -> %p\n", jumpTarget); + dataLogF(" -> %p\n", jumpTarget); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp index b278997ab..b83c0b3f5 100644 --- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp +++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp @@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov { // 1) Pro-forma stuff. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex); + dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex); for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) { - dataLog("bc#%u", codeOrigin.bytecodeIndex); + dataLogF("bc#%u", codeOrigin.bytecodeIndex); if (!codeOrigin.inlineCallFrame) break; - dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); + dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get()); } - dataLog(") "); + dataLogF(") "); dumpOperands(operands, WTF::dataFile()); #endif #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) @@ -138,7 +138,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0); #if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE) - dataLog(" (have exit profile, bucket %p) ", bucket); + dataLogF(" (have exit profile, bucket %p) ", bucket); #endif if (exit.m_jsValueSource.isAddress()) { @@ -243,24 +243,24 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog(" "); + dataLogF(" "); if (numberOfPoisonedVirtualRegisters) - dataLog("Poisoned=%u ", numberOfPoisonedVirtualRegisters); + dataLogF("Poisoned=%u ", numberOfPoisonedVirtualRegisters); if (numberOfDisplacedVirtualRegisters) - dataLog("Displaced=%u ", numberOfDisplacedVirtualRegisters); + dataLogF("Displaced=%u ", numberOfDisplacedVirtualRegisters); if (haveUnboxedInt32s) - dataLog("UnboxedInt32 "); + dataLogF("UnboxedInt32 "); if (haveUnboxedDoubles) - dataLog("UnboxedDoubles "); + dataLogF("UnboxedDoubles "); if (haveUInt32s) - dataLog("UInt32 "); + dataLogF("UInt32 "); if (haveFPRs) - dataLog("FPR "); + dataLogF("FPR "); if (haveConstants) - dataLog("Constants "); + dataLogF("Constants "); if (haveUndefined) - dataLog("Undefined "); - dataLog(" "); + dataLogF("Undefined "); + dataLogF(" "); #endif ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * std::max(haveUInt32s ? 2u : 0u, numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))); @@ -710,7 +710,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov m_jit.jump(GPRInfo::regT1); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("-> %p\n", jumpTarget); + dataLogF("-> %p\n", jumpTarget); #endif } diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp index 0e45e230c..909c657a1 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.cpp +++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp @@ -27,9 +27,9 @@ #include "DFGOperations.h" #include "Arguments.h" -#include "ButterflyInlineMethods.h" +#include "ButterflyInlines.h" #include "CodeBlock.h" -#include "CopiedSpaceInlineMethods.h" +#include "CopiedSpaceInlines.h" #include "DFGOSRExit.h" #include "DFGRepatch.h" #include "DFGThunks.h" @@ -302,12 +302,12 @@ JSCell* DFG_OPERATION operationCreateThis(ExecState* exec, JSCell* constructor) return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->cachedInheritorID(exec)); } -JSCell* DFG_OPERATION operationNewObject(ExecState* exec) +JSCell* DFG_OPERATION operationNewObject(ExecState* exec, Structure* structure) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); - return constructEmptyObject(exec); + return constructEmptyObject(exec, structure); } EncodedJSValue DFG_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) @@ -580,6 +580,40 @@ void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState* exec, array, exec, Identifier::from(exec, index), JSValue::decode(encodedValue), slot); } +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, double value) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value); + + if (index >= 0) { + array->putByIndexInline(exec, index, jsValue, true); + return; + } + + PutPropertySlot slot(true); + array->methodTable()->put( + array, exec, Identifier::from(exec, index), jsValue, slot); +} + +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState* exec, JSObject* array, int32_t index, double value) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value); + + if (index >= 0) { + array->putByIndexInline(exec, index, jsValue, false); + return; + } + + PutPropertySlot slot(false); + array->methodTable()->put( + array, exec, Identifier::from(exec, index), jsValue, slot); +} + EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue encodedValue, JSArray* array) { JSGlobalData* globalData = &exec->globalData(); @@ -589,6 +623,15 @@ EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue return JSValue::encode(jsNumber(array->length())); } +EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState* exec, double value, JSArray* array) +{ + JSGlobalData* globalData = &exec->globalData(); + NativeCallFrameTracer tracer(globalData, exec); + + array->push(exec, JSValue(JSValue::EncodeAsDouble, value)); + return JSValue::encode(jsNumber(array->length())); +} + EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array) { JSGlobalData* globalData = &exec->globalData(); @@ -1019,14 +1062,14 @@ char* DFG_OPERATION operationLinkConstruct(ExecState* execCallee) return linkFor(execCallee, CodeForConstruct); } -inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) +inline char* virtualForWithFunction(ExecState* execCallee, CodeSpecializationKind kind, JSCell*& calleeAsFunctionCell) { ExecState* exec = execCallee->callerFrame(); JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); JSValue calleeAsValue = execCallee->calleeAsValue(); - JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue); + calleeAsFunctionCell = getJSFunction(calleeAsValue); if (UNLIKELY(!calleeAsFunctionCell)) return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind)); @@ -1044,6 +1087,56 @@ inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) return reinterpret_cast<char*>(executable->generatedJITCodeWithArityCheckFor(kind).executableAddress()); } +inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind) +{ + JSCell* calleeAsFunctionCellIgnored; + return virtualForWithFunction(execCallee, kind, calleeAsFunctionCellIgnored); +} + +static bool attemptToOptimizeClosureCall(ExecState* execCallee, JSCell* calleeAsFunctionCell, CallLinkInfo& callLinkInfo) +{ + if (!calleeAsFunctionCell) + return false; + + JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell); + JSFunction* oldCallee = callLinkInfo.callee.get(); + + if (!oldCallee + || oldCallee->structure() != callee->structure() + || oldCallee->executable() != callee->executable()) + return false; + + ASSERT(callee->executable()->hasJITCodeForCall()); + MacroAssemblerCodePtr codePtr = callee->executable()->generatedJITCodeForCall().addressForCall(); + + CodeBlock* codeBlock; + if (callee->executable()->isHostFunction()) + codeBlock = 0; + else { + codeBlock = &jsCast<FunctionExecutable*>(callee->executable())->generatedBytecodeForCall(); + if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters())) + return false; + } + + dfgLinkClosureCall( + execCallee, callLinkInfo, codeBlock, + callee->structure(), callee->executable(), codePtr); + + return true; +} + +char* DFG_OPERATION operationLinkClosureCall(ExecState* execCallee) +{ + JSCell* calleeAsFunctionCell; + char* result = virtualForWithFunction(execCallee, CodeForCall, calleeAsFunctionCell); + CallLinkInfo& callLinkInfo = execCallee->callerFrame()->codeBlock()->getCallLinkInfo(execCallee->returnPC()); + + if (!attemptToOptimizeClosureCall(execCallee, calleeAsFunctionCell, callLinkInfo)) + dfgLinkSlowFor(execCallee, callLinkInfo, CodeForCall); + + return result; +} + char* DFG_OPERATION operationVirtualCall(ExecState* execCallee) { return virtualFor(execCallee, CodeForCall); @@ -1327,30 +1420,36 @@ char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState* return reinterpret_cast<char*>(result); } -char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object) +char* DFG_OPERATION operationEnsureInt32(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - return reinterpret_cast<char*>(object->ensureContiguous(globalData)); + return reinterpret_cast<char*>(object->ensureInt32(globalData)); } -char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object) +char* DFG_OPERATION operationEnsureDouble(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); + + return reinterpret_cast<char*>(object->ensureDouble(globalData)); +} - return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); +char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object) +{ + JSGlobalData& globalData = exec->globalData(); + NativeCallFrameTracer tracer(&globalData, exec); + + return reinterpret_cast<char*>(object->ensureContiguous(globalData)); } -char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState* exec, JSObject* object, int32_t index) +char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object) { JSGlobalData& globalData = exec->globalData(); NativeCallFrameTracer tracer(&globalData, exec); - if (static_cast<unsigned>(index) >= MIN_SPARSE_ARRAY_INDEX) - return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); - return reinterpret_cast<char*>(object->ensureIndexedStorage(globalData)); + return reinterpret_cast<char*>(object->ensureArrayStorage(globalData)); } double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b) @@ -1423,7 +1522,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* SpeculationFailureDebugInfo* debugInfo = static_cast<SpeculationFailureDebugInfo*>(debugInfoRaw); CodeBlock* codeBlock = debugInfo->codeBlock; CodeBlock* alternative = codeBlock->alternative(); - dataLog("Speculation failure in %p at @%u with executeCounter = %s, " + dataLogF("Speculation failure in %p at @%u with executeCounter = %s, " "reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, " "osrExitCounter = %u\n", codeBlock, @@ -1438,7 +1537,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void* extern "C" void DFG_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock) { #if ENABLE(JIT_VERBOSE_OSR) - dataLog("%p: Entered reoptimize\n", codeBlock); + dataLogF("%p: Entered reoptimize\n", codeBlock); #endif // We must be called with the baseline code block. ASSERT(JITCode::isBaselineCode(codeBlock->getJITType())); diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h index 8d2beacec..00e6b07b7 100644 --- a/Source/JavaScriptCore/dfg/DFGOperations.h +++ b/Source/JavaScriptCore/dfg/DFGOperations.h @@ -64,6 +64,7 @@ typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EAZ)(ExecState*, JSArray*, typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue); +typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EDA)(ExecState*, double, JSArray*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EGriJsgI)(ExecState*, ResolveOperation*, JSGlobalObject*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EI)(ExecState*, Identifier*); typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EIRo)(ExecState*, Identifier*, ResolveOperations*); @@ -84,6 +85,7 @@ typedef JSCell* DFG_OPERATION (*C_DFGOperation_E)(ExecState*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_EC)(ExecState*, JSCell*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*); typedef JSCell* DFG_OPERATION (*C_DFGOperation_EIcf)(ExecState*, InlineCallFrame*); +typedef JSCell* DFG_OPERATION (*C_DFGOperation_ESt)(ExecState*, Structure*); typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double); typedef double DFG_OPERATION (*D_DFGOperation_ZZ)(int32_t, int32_t); typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue); @@ -92,6 +94,7 @@ typedef size_t DFG_OPERATION (*S_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*) typedef size_t DFG_OPERATION (*S_DFGOperation_EJ)(ExecState*, EncodedJSValue); typedef size_t DFG_OPERATION (*S_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue); typedef size_t DFG_OPERATION (*S_DFGOperation_J)(EncodedJSValue); +typedef void DFG_OPERATION (*V_DFGOperation_EOZD)(ExecState*, JSObject*, int32_t, double); typedef void DFG_OPERATION (*V_DFGOperation_EOZJ)(ExecState*, JSObject*, int32_t, EncodedJSValue); typedef void DFG_OPERATION (*V_DFGOperation_EC)(ExecState*, JSCell*); typedef void DFG_OPERATION (*V_DFGOperation_ECIcf)(ExecState*, JSCell*, InlineCallFrame*); @@ -116,7 +119,7 @@ typedef char* DFG_OPERATION (*P_DFGOperation_EStSS)(ExecState*, Structure*, size typedef char* DFG_OPERATION (*P_DFGOperation_EStZ)(ExecState*, Structure*, int32_t); // These routines are provide callbacks out to C++ implementations of operations too complex to JIT. -JSCell* DFG_OPERATION operationNewObject(ExecState*) WTF_INTERNAL; +JSCell* DFG_OPERATION operationNewObject(ExecState*, Structure*) WTF_INTERNAL; JSCell* DFG_OPERATION operationCreateThis(ExecState*, JSCell* constructor) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationConvertThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; @@ -148,7 +151,10 @@ void DFG_OPERATION operationPutByValCellStrict(ExecState*, JSCell*, EncodedJSVal void DFG_OPERATION operationPutByValCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL; void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL; void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL; +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL; +void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*) WTF_INTERNAL; +EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState*, double value, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPop(ExecState*, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationArrayPopAndRecoverLength(ExecState*, JSArray*) WTF_INTERNAL; EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState*, JSCell*, JSCell*) WTF_INTERNAL; @@ -175,6 +181,7 @@ size_t DFG_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue enc size_t DFG_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL; char* DFG_OPERATION operationVirtualCall(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationLinkCall(ExecState*) WTF_INTERNAL; +char* DFG_OPERATION operationLinkClosureCall(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationVirtualConstruct(ExecState*) WTF_INTERNAL; char* DFG_OPERATION operationLinkConstruct(ExecState*) WTF_INTERNAL; JSCell* DFG_OPERATION operationCreateActivation(ExecState*) WTF_INTERNAL; @@ -195,9 +202,10 @@ char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecStat char* DFG_OPERATION operationAllocatePropertyStorage(ExecState*, size_t newSize) WTF_INTERNAL; char* DFG_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState*, JSObject*) WTF_INTERNAL; char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState*, JSObject*, size_t newSize) WTF_INTERNAL; +char* DFG_OPERATION operationEnsureInt32(ExecState*, JSObject*); +char* DFG_OPERATION operationEnsureDouble(ExecState*, JSObject*); char* DFG_OPERATION operationEnsureContiguous(ExecState*, JSObject*); char* DFG_OPERATION operationEnsureArrayStorage(ExecState*, JSObject*); -char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState*, JSObject*, int32_t); // This method is used to lookup an exception hander, keyed by faultLocation, which is // the return location from one of the calls out to one of the helper operations above. diff --git a/Source/JavaScriptCore/dfg/DFGPhase.cpp b/Source/JavaScriptCore/dfg/DFGPhase.cpp index f97c49e31..20301e814 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPhase.cpp @@ -35,8 +35,8 @@ namespace JSC { namespace DFG { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) void Phase::beginPhase() { - dataLog("Beginning DFG phase %s.\n", m_name); - dataLog("Graph before %s:\n", m_name); + dataLogF("Beginning DFG phase %s.\n", m_name); + dataLogF("Graph before %s:\n", m_name); m_graph.dump(); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h index a73d26baf..939e199e0 100644 --- a/Source/JavaScriptCore/dfg/DFGPhase.h +++ b/Source/JavaScriptCore/dfg/DFGPhase.h @@ -83,7 +83,7 @@ bool runAndLog(PhaseType& phase) bool result = phase.run(); #if DFG_ENABLE(DEBUG_VERBOSE) if (result) - dataLog("Phase %s changed the IR.\n", phase.name()); + dataLogF("Phase %s changed the IR.\n", phase.name()); #endif return result; } diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp index 3e8ead5c6..4b8a17285 100644 --- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp @@ -116,6 +116,52 @@ private: return false; return !!m_graph.valueOfNumberConstant(nodeIndex); } + + bool isWithinPowerOfTwoForConstant(Node& node, int power) + { + JSValue immediateValue = node.valueOfJSConstant(codeBlock()); + if (!immediateValue.isInt32()) + return false; + int32_t intImmediate = immediateValue.asInt32(); + return intImmediate > -(1 << power) && intImmediate < (1 << power); + } + + bool isWithinPowerOfTwoNonRecursive(NodeIndex nodeIndex, int power) + { + Node& node = m_graph[nodeIndex]; + if (node.op() != JSConstant) + return false; + return isWithinPowerOfTwoForConstant(node, power); + } + + bool isWithinPowerOfTwo(NodeIndex nodeIndex, int power) + { + Node& node = m_graph[nodeIndex]; + switch (node.op()) { + case JSConstant: { + return isWithinPowerOfTwoForConstant(node, power); + } + + case BitAnd: { + return isWithinPowerOfTwoNonRecursive(node.child1().index(), power) + || isWithinPowerOfTwoNonRecursive(node.child2().index(), power); + } + + case BitRShift: + case BitURShift: { + Node& shiftAmount = m_graph[node.child2()]; + if (shiftAmount.op() != JSConstant) + return false; + JSValue immediateValue = shiftAmount.valueOfJSConstant(codeBlock()); + if (!immediateValue.isInt32()) + return false; + return immediateValue > 32 - power; + } + + default: + return false; + } + } SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value) { @@ -140,7 +186,7 @@ private: NodeFlags flags = node.flags() & NodeBackPropMask; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags)); + dataLogF(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags)); #endif bool changed = false; @@ -184,7 +230,7 @@ private: case BitURShift: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; - flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); + flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther); changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -193,7 +239,7 @@ private: case ValueToInt32: { changed |= setPrediction(SpecInt32); flags |= NodeUsedAsInt; - flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero); + flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther); changed |= m_graph[node.child1()].mergeFlags(flags); break; } @@ -221,28 +267,10 @@ private: case StringCharCodeAt: { changed |= mergePrediction(SpecInt32); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } - case ArithMod: { - SpeculatedType left = m_graph[node.child1()].prediction(); - SpeculatedType right = m_graph[node.child2()].prediction(); - - if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) - && nodeCanSpeculateInteger(node.arithNodeFlags())) - changed |= mergePrediction(SpecInt32); - else - changed |= mergePrediction(SpecDouble); - } - - flags |= NodeUsedAsValue; - changed |= m_graph[node.child1()].mergeFlags(flags); - changed |= m_graph[node.child2()].mergeFlags(flags); - break; - } - case UInt32ToNumber: { if (nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); @@ -258,7 +286,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isNumberSpeculation(left) && isNumberSpeculation(right)) { + if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)) { if (m_graph.addShouldSpeculateInteger(node)) changed |= mergePrediction(SpecInt32); else @@ -272,6 +300,8 @@ private: if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + if (m_graph[node.child1()].hasNumberResult() || m_graph[node.child2()].hasNumberResult()) + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -291,6 +321,7 @@ private: if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -310,6 +341,7 @@ private: if (isNotZero(node.child1().index()) || isNotZero(node.child2().index())) flags &= ~NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); @@ -324,6 +356,8 @@ private: changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction())); } + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); break; @@ -333,7 +367,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else @@ -341,6 +375,8 @@ private: } flags |= NodeUsedAsNumber; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -359,10 +395,20 @@ private: // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation - // can change the outcome. So, ArithMul always checks for overflow - // no matter what, and always forces its inputs to check as well. + // can change the outcome. So, ArithMul always forces its inputs to + // check for overflow. Additionally, it will have to check for overflow + // itself unless we can prove that there is no way for the values + // produced to cause double rounding. + + if (!isWithinPowerOfTwo(node.child1().index(), 22) + && !isWithinPowerOfTwo(node.child2().index(), 22)) + flags |= NodeUsedAsNumber; + + changed |= node.mergeFlags(flags); flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -373,7 +419,7 @@ private: SpeculatedType right = m_graph[node.child2()].prediction(); if (left && right) { - if (isInt32Speculation(mergeSpeculations(left, right)) + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) && nodeCanSpeculateInteger(node.arithNodeFlags())) changed |= mergePrediction(SpecInt32); else @@ -382,10 +428,32 @@ private: // As soon as a multiply happens, we can easily end up in the part // of the double domain where the point at which you do truncation - // can change the outcome. So, ArithMul always checks for overflow + // can change the outcome. So, ArithDiv always checks for overflow // no matter what, and always forces its inputs to check as well. flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + + changed |= m_graph[node.child1()].mergeFlags(flags); + changed |= m_graph[node.child2()].mergeFlags(flags); + break; + } + + case ArithMod: { + SpeculatedType left = m_graph[node.child1()].prediction(); + SpeculatedType right = m_graph[node.child2()].prediction(); + + if (left && right) { + if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()]) + && nodeCanSpeculateInteger(node.arithNodeFlags())) + changed |= mergePrediction(SpecInt32); + else + changed |= mergePrediction(SpecDouble); + } + + flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); changed |= m_graph[node.child2()].mergeFlags(flags); break; @@ -393,18 +461,20 @@ private: case ArithSqrt: { changed |= setPrediction(SpecDouble); - changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue); + flags |= NodeUsedAsNumber | NodeNeedsNegZero; + flags &= ~NodeUsedAsOther; + changed |= m_graph[node.child1()].mergeFlags(flags); break; } case ArithAbs: { SpeculatedType child = m_graph[node.child1()].prediction(); - if (nodeCanSpeculateInteger(node.arithNodeFlags())) - changed |= mergePrediction(child); + if (isInt32SpeculationForArithmetic(child) + && nodeCanSpeculateInteger(node.arithNodeFlags())) + changed |= mergePrediction(SpecInt32); else - changed |= setPrediction(speculatedDoubleTypeForPrediction(child)); + changed |= mergePrediction(speculatedDoubleTypeForPrediction(child)); - flags &= ~NodeNeedsNegZero; changed |= m_graph[node.child1()].mergeFlags(flags); break; } @@ -447,13 +517,13 @@ private: changed |= mergePrediction(node.getHeapPrediction()); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } case GetMyArgumentByValSafe: { changed |= mergePrediction(node.getHeapPrediction()); - changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } @@ -554,7 +624,7 @@ private: case NewArrayWithSize: { changed |= setPrediction(SpecArray); - changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt); break; } @@ -571,7 +641,7 @@ private: case StringCharAt: { changed |= setPrediction(SpecString); changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue); - changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); break; } @@ -580,7 +650,7 @@ private: for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx) - changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber); + changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther); break; } @@ -636,16 +706,17 @@ private: case PhantomArguments: case CheckArray: case Arrayify: - case ArrayifyToStructure: { + case ArrayifyToStructure: + case Identity: { // This node should never be visible at this stage of compilation. It is // inserted by fixup(), which follows this phase. - ASSERT_NOT_REACHED(); + CRASH(); break; } case PutByVal: changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue); - changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt); + changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt); changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue); break; @@ -690,6 +761,7 @@ private: case CheckArgumentsNotCreated: case GlobalVarWatchpoint: case GarbageValue: + case InheritorIDWatchpoint: changed |= mergeDefaultFlags(node); break; @@ -700,7 +772,7 @@ private: break; case LastNodeType: - ASSERT_NOT_REACHED(); + CRASH(); break; #else default: @@ -710,7 +782,7 @@ private: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("%s\n", speculationToString(m_graph[m_compileIndex].prediction())); + dataLogF("%s\n", speculationToString(m_graph[m_compileIndex].prediction())); #endif m_changed |= changed; @@ -743,7 +815,7 @@ private: void propagateForward() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Propagating predictions forward [%u]\n", ++m_count); + dataLogF("Propagating predictions forward [%u]\n", ++m_count); #endif for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex) propagate(m_graph[m_compileIndex]); @@ -752,7 +824,7 @@ private: void propagateBackward() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Propagating predictions backward [%u]\n", ++m_count); + dataLogF("Propagating predictions backward [%u]\n", ++m_count); #endif for (m_compileIndex = m_graph.size(); m_compileIndex-- > 0;) propagate(m_graph[m_compileIndex]); @@ -761,7 +833,7 @@ private: void doRoundOfDoubleVoting() { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Voting on double uses of locals [%u]\n", m_count); + dataLogF("Voting on double uses of locals [%u]\n", m_count); #endif for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) m_graph.m_variableAccessData[i].find()->clearVotes(); @@ -776,7 +848,7 @@ private: DoubleBallot ballot; - if (isNumberSpeculation(left) && isNumberSpeculation(right) + if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right) && !m_graph.addShouldSpeculateInteger(node)) ballot = VoteDouble; else @@ -814,7 +886,7 @@ private: DoubleBallot ballot; if (isNumberSpeculation(left) && isNumberSpeculation(right) - && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()]) + && !(Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child1()]) && node.canSpeculateInteger())) ballot = VoteDouble; else @@ -827,7 +899,7 @@ private: case ArithAbs: DoubleBallot ballot; - if (!(m_graph[node.child1()].shouldSpeculateInteger() + if (!(m_graph[node.child1()].shouldSpeculateIntegerForArithmetic() && node.canSpeculateInteger())) ballot = VoteDouble; else @@ -849,6 +921,24 @@ private: break; } + case PutByVal: + case PutByValAlias: { + Edge child1 = m_graph.varArgChild(node, 0); + Edge child2 = m_graph.varArgChild(node, 1); + Edge child3 = m_graph.varArgChild(node, 2); + m_graph.vote(child1, VoteValue); + m_graph.vote(child2, VoteValue); + switch (node.arrayMode().type()) { + case Array::Double: + m_graph.vote(child3, VoteDouble); + break; + default: + m_graph.vote(child3, VoteValue); + break; + } + break; + } + default: m_graph.vote(node, VoteValue); break; diff --git a/Source/JavaScriptCore/dfg/DFGRegisterBank.h b/Source/JavaScriptCore/dfg/DFGRegisterBank.h index 1d1d6fa52..3dbd1fe91 100644 --- a/Source/JavaScriptCore/dfg/DFGRegisterBank.h +++ b/Source/JavaScriptCore/dfg/DFGRegisterBank.h @@ -75,7 +75,6 @@ class RegisterBank { public: RegisterBank() - : m_lastAllocated(NUM_REGS - 1) { } @@ -86,12 +85,7 @@ public: { VirtualRegister ignored; - for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) { - if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister) - return allocateInternal(i, ignored); - } - // Loop over the remaining entries. - for (uint32_t i = 0; i <= m_lastAllocated; ++i) { + for (uint32_t i = 0; i < NUM_REGS; ++i) { if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister) return allocateInternal(i, ignored); } @@ -115,9 +109,6 @@ public: uint32_t currentLowest = NUM_REGS; SpillHint currentSpillOrder = SpillHintInvalid; - // Scan through all register, starting at the last allocated & looping around. - ASSERT(m_lastAllocated < NUM_REGS); - // This loop is broken into two halves, looping from the last allocated // register (the register returned last time this method was called) to // the maximum register value, then from 0 to the last allocated. @@ -125,7 +116,7 @@ public: // thrash, and minimize time spent scanning locked registers in allocation. // If a unlocked and unnamed register is found return it immediately. // Otherwise, find the first unlocked register with the lowest spillOrder. - for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) { + for (uint32_t i = 0 ; i < NUM_REGS; ++i) { // (1) If the current register is locked, it is not a candidate. if (m_data[i].lockCount) continue; @@ -140,18 +131,6 @@ public: currentLowest = i; } } - // Loop over the remaining entries. - for (uint32_t i = 0; i <= m_lastAllocated; ++i) { - if (m_data[i].lockCount) - continue; - SpillHint spillOrder = m_data[i].spillOrder; - if (spillOrder == SpillHintInvalid) - return allocateInternal(i, spillMe); - if (spillOrder < currentSpillOrder) { - currentSpillOrder = spillOrder; - currentLowest = i; - } - } // Deadlock check - this could only occur is all registers are locked! ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid); @@ -237,11 +216,11 @@ public: // For each register, print the VirtualRegister 'name'. for (uint32_t i =0; i < NUM_REGS; ++i) { if (m_data[i].name != InvalidVirtualRegister) - dataLog("[%02d]", m_data[i].name); + dataLogF("[%02d]", m_data[i].name); else - dataLog("[--]"); + dataLogF("[--]"); } - dataLog("\n"); + dataLogF("\n"); } #endif @@ -354,7 +333,6 @@ private: // Mark the register as locked (with a lock count of 1). m_data[i].lockCount = 1; - m_lastAllocated = i; return BankInfo::toRegister(i); } @@ -378,8 +356,6 @@ private: // Holds the current status of all registers. MapEntry m_data[NUM_REGS]; - // Used to to implement a simple round-robin like allocation scheme. - uint32_t m_lastAllocated; }; } } // namespace JSC::DFG diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp index 7c15ef33e..a20eb544a 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp +++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp @@ -114,6 +114,23 @@ static void addStructureTransitionCheck( failureCases, scratchGPR); } +static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target) +{ + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.replaceWithJump( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress( + stubInfo.callReturnLocation.dataLabelPtrAtOffset( + -(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall)), + CodeLocationLabel(target)); + return; + } + + repatchBuffer.relink( + stubInfo.callReturnLocation.jumpAtOffset( + stubInfo.patch.dfg.deltaCallToStructCheck), + CodeLocationLabel(target)); +} + static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases) { if (needToRestoreScratch) { @@ -284,7 +301,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier stubInfo.patch.dfg.deltaCallToDone).executableAddress())); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code())); + replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code()); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById); return true; @@ -334,7 +351,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().code())); + replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code()); repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList); stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain, count, true); @@ -518,9 +535,11 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi 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().code())); + repatchBuffer.relink( + stubInfo.callReturnLocation.jumpAtOffset( + stubInfo.patch.dfg.deltaCallToStructCheck), + CodeLocationLabel(stubRoutine->code().code())); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -584,9 +603,8 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, true); - CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck); RepatchBuffer repatchBuffer(codeBlock); - repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); + replaceWithJump(repatchBuffer, stubInfo, stubRoutine->code().code()); if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1)) return true; @@ -976,7 +994,10 @@ 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().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); @@ -1092,8 +1113,20 @@ void dfgBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identifier& p dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind)); } +static void linkSlowFor(RepatchBuffer& repatchBuffer, JSGlobalData* globalData, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind) +{ + if (kind == CodeForCall) { + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code()); + return; + } + ASSERT(kind == CodeForConstruct); + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code()); +} + void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, JSFunction* callee, MacroAssemblerCodePtr codePtr, CodeSpecializationKind kind) { + ASSERT(!callLinkInfo.stub); + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); JSGlobalData* globalData = callerCodeBlock->globalData(); @@ -1108,17 +1141,125 @@ void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCo calleeCodeBlock->linkIncomingCall(&callLinkInfo); if (kind == CodeForCall) { - repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code()); + repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(linkClosureCallThunkGenerator).code()); return; } + ASSERT(kind == CodeForConstruct); - repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code()); + linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForConstruct); +} + +void dfgLinkSlowFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind) +{ + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); + JSGlobalData* globalData = callerCodeBlock->globalData(); + + RepatchBuffer repatchBuffer(callerCodeBlock); + + linkSlowFor(repatchBuffer, globalData, callLinkInfo, kind); +} + +void dfgLinkClosureCall(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, Structure* structure, ExecutableBase* executable, MacroAssemblerCodePtr codePtr) +{ + ASSERT(!callLinkInfo.stub); + + CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock(); + JSGlobalData* globalData = callerCodeBlock->globalData(); + + GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR); + + CCallHelpers stubJit(globalData, callerCodeBlock); + + CCallHelpers::JumpList slowPath; + +#if USE(JSVALUE64) + slowPath.append( + stubJit.branchTest64( + CCallHelpers::NonZero, calleeGPR, GPRInfo::tagMaskRegister)); +#else + // We would have already checked that the callee is a cell. +#endif + + slowPath.append( + stubJit.branchPtr( + CCallHelpers::NotEqual, + CCallHelpers::Address(calleeGPR, JSCell::structureOffset()), + CCallHelpers::TrustedImmPtr(structure))); + + slowPath.append( + stubJit.branchPtr( + CCallHelpers::NotEqual, + CCallHelpers::Address(calleeGPR, JSFunction::offsetOfExecutable()), + CCallHelpers::TrustedImmPtr(executable))); + + stubJit.loadPtr( + CCallHelpers::Address(calleeGPR, JSFunction::offsetOfScopeChain()), + GPRInfo::returnValueGPR); + +#if USE(JSVALUE64) + stubJit.store64( + GPRInfo::returnValueGPR, + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain))); +#else + stubJit.storePtr( + GPRInfo::returnValueGPR, + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); + stubJit.store32( + CCallHelpers::TrustedImm32(JSValue::CellTag), + CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); +#endif + + JITCompiler::Call call = stubJit.nearCall(); + JITCompiler::Jump done = stubJit.jump(); + + slowPath.link(&stubJit); + stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation.executableAddress()), GPRInfo::nonArgGPR2); + stubJit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR2); + stubJit.move(calleeGPR, GPRInfo::nonArgGPR0); +#if USE(JSVALUE32_64) + stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::nonArgGPR1); +#endif + JITCompiler::Jump slow = stubJit.jump(); + + LinkBuffer patchBuffer(*globalData, &stubJit, callerCodeBlock); + + patchBuffer.link(call, FunctionPtr(codePtr.executableAddress())); + patchBuffer.link(done, callLinkInfo.callReturnLocation.labelAtOffset(0)); + patchBuffer.link(slow, CodeLocationLabel(globalData->getCTIStub(virtualCallThunkGenerator).code())); + + RefPtr<ClosureCallStubRoutine> stubRoutine = adoptRef(new ClosureCallStubRoutine( + FINALIZE_DFG_CODE( + patchBuffer, + ("DFG closure call stub for CodeBlock %p, return point %p, target %p (CodeBlock %p)", + callerCodeBlock, callLinkInfo.callReturnLocation.labelAtOffset(0).executableAddress(), + codePtr.executableAddress(), calleeCodeBlock)), + *globalData, callerCodeBlock->ownerExecutable(), structure, executable, callLinkInfo.codeOrigin)); + + RepatchBuffer repatchBuffer(callerCodeBlock); + + repatchBuffer.replaceWithJump( + RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin), + CodeLocationLabel(stubRoutine->code().code())); + linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForCall); + + callLinkInfo.stub = stubRoutine.release(); + + ASSERT(!calleeCodeBlock || calleeCodeBlock->isIncomingCallAlreadyLinked(&callLinkInfo)); } void dfgResetGetByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) { repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdOptimize); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1)); + CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall); + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel), + MacroAssembler::Address( + static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR), + JSCell::structureOffset()), + reinterpret_cast<void*>(-1)); + } + repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1)); #if USE(JSVALUE64) repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0); #else @@ -1143,7 +1284,16 @@ void dfgResetPutByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo) optimizedFunction = operationPutByIdDirectNonStrictOptimize; } repatchBuffer.relink(stubInfo.callReturnLocation, optimizedFunction); - repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1)); + CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall); + if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) { + repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch( + RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel), + MacroAssembler::Address( + static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR), + JSCell::structureOffset()), + reinterpret_cast<void*>(-1)); + } + repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1)); #if USE(JSVALUE64) repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0); #else diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.h b/Source/JavaScriptCore/dfg/DFGRepatch.h index 83d4e976d..97d26aab2 100644 --- a/Source/JavaScriptCore/dfg/DFGRepatch.h +++ b/Source/JavaScriptCore/dfg/DFGRepatch.h @@ -41,6 +41,8 @@ void dfgBuildGetByIDProtoList(ExecState*, JSValue, const Identifier&, const Prop void dfgRepatchPutByID(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); void dfgBuildPutByIdList(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind); void dfgLinkFor(ExecState*, CallLinkInfo&, CodeBlock*, JSFunction* callee, MacroAssemblerCodePtr, CodeSpecializationKind); +void dfgLinkSlowFor(ExecState*, CallLinkInfo&, CodeSpecializationKind); +void dfgLinkClosureCall(ExecState*, CallLinkInfo&, CodeBlock*, Structure*, ExecutableBase*, MacroAssemblerCodePtr); void dfgResetGetByID(RepatchBuffer&, StructureStubInfo&); void dfgResetPutByID(RepatchBuffer&, StructureStubInfo&); diff --git a/Source/JavaScriptCore/dfg/DFGScoreBoard.h b/Source/JavaScriptCore/dfg/DFGScoreBoard.h index 430bdf552..9b509fe2a 100644 --- a/Source/JavaScriptCore/dfg/DFGScoreBoard.h +++ b/Source/JavaScriptCore/dfg/DFGScoreBoard.h @@ -113,7 +113,7 @@ public: ASSERT(m_used[index] != max()); if (node.refCount() == ++m_used[index]) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Freeing virtual register %u.", index); + dataLogF(" Freeing virtual register %u.", index); #endif // If the use count in the scoreboard reaches the use count for the node, // then this was its last use; the virtual register is now free. @@ -122,7 +122,7 @@ public: m_free.append(index); } else { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount()); + dataLogF(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount()); #endif } } @@ -148,26 +148,26 @@ public: #ifndef NDEBUG void dump() { - dataLog(" USED: [ "); + dataLogF(" USED: [ "); for (unsigned i = 0; i < m_used.size(); ++i) { if (!m_free.contains(i)) { - dataLog("%d:", i); + dataLogF("%d:", i); if (m_used[i] == max()) - dataLog("local "); + dataLogF("local "); else - dataLog("%d ", m_used[i]); + dataLogF("%d ", m_used[i]); } } - dataLog("]\n"); + dataLogF("]\n"); - dataLog(" FREE: [ "); + dataLogF(" FREE: [ "); for (unsigned i = 0; i < m_used.size(); ++i) { if (m_free.contains(i) && m_used[i] != max()) { ASSERT(!m_used[i]); - dataLog("%d ", i); + dataLogF("%d ", i); } } - dataLog("]\n"); + dataLogF("]\n"); } #endif diff --git a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h index fa1f888e0..4acd8690a 100644 --- a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h +++ b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h @@ -49,7 +49,7 @@ public: void generate(SpeculativeJIT* jit) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset()); + dataLogF("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset()); #endif m_label = jit->m_jit.label(); jit->m_compileIndex = m_compileIndex; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index 6bedd6d68..41276d233 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp @@ -59,7 +59,7 @@ SpeculativeJIT::~SpeculativeJIT() void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, GPRReg storageGPR, unsigned numElements) { - ASSERT(hasContiguous(structure->indexingType())); + ASSERT(hasUndecided(structure->indexingType()) || hasInt32(structure->indexingType()) || hasDouble(structure->indexingType()) || hasContiguous(structure->indexingType())); GPRTemporary scratch(this); GPRReg scratchGPR = scratch.gpr(); @@ -67,6 +67,7 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, unsigned vectorLength = std::max(BASE_VECTOR_LEN, numElements); JITCompiler::JumpList slowCases; + slowCases.append( emitAllocateBasicStorage(TrustedImm32(vectorLength * sizeof(JSValue) + sizeof(IndexingHeader)), storageGPR)); m_jit.subPtr(TrustedImm32(vectorLength * sizeof(JSValue)), storageGPR); @@ -79,6 +80,21 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(structure->indexingType()) && numElements < vectorLength) { +#if USE(JSVALUE64) + m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR); + for (unsigned i = numElements; i < vectorLength; ++i) + m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i)); +#else + EncodedValueDescriptor value; + value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, QNaN)); + for (unsigned i = numElements; i < vectorLength; ++i) { + m_jit.store32(TrustedImm32(value.asBits.tag), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(TrustedImm32(value.asBits.payload), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + } +#endif + } + // I want a slow path that also loads out the storage pointer, and that's // what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot // of work for a very small piece of functionality. :-/ @@ -258,7 +274,7 @@ void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs js { ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT was terminated.\n"); + dataLogF("SpeculativeJIT was terminated.\n"); #endif if (!m_compileOkay) return; @@ -276,7 +292,7 @@ void SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection(ExitK { ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes); #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT was terminated.\n"); + dataLogF("SpeculativeJIT was terminated.\n"); #endif if (!m_compileOkay) return; @@ -292,7 +308,7 @@ void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPath void SpeculativeJIT::runSlowPathGenerators() { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Running %lu slow path generators.\n", m_slowPathGenerators.size()); + dataLogF("Running %lu slow path generators.\n", m_slowPathGenerators.size()); #endif for (unsigned i = 0; i < m_slowPathGenerators.size(); ++i) m_slowPathGenerators[i]->generate(this); @@ -343,32 +359,49 @@ const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(ArrayMode array } } +JITCompiler::Jump SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, IndexingType shape, bool invert) +{ + switch (arrayMode.arrayClass()) { + case Array::OriginalArray: { + CRASH(); + JITCompiler::Jump result; // I already know that VC++ takes unkindly to the expression "return Jump()", so I'm doing it this way in anticipation of someone eventually using VC++ to compile the DFG. + return result; + } + + case Array::Array: + m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); + return m_jit.branch32( + invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape)); + + default: + m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); + return m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)); + } +} + JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, bool invert) { JITCompiler::JumpList result; switch (arrayMode.type()) { - case Array::Contiguous: { - if (arrayMode.isJSArray()) { - m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR); - result.append( - m_jit.branch32( - invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | ContiguousShape))); - break; - } - m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); - result.append( - m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ContiguousShape))); - break; - } + case Array::Int32: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, Int32Shape, invert); + + case Array::Double: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, DoubleShape, invert); + + case Array::Contiguous: + return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, ContiguousShape, invert); + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { + ASSERT(!arrayMode.isJSArrayWithOriginalStructure()); + if (arrayMode.isJSArray()) { if (arrayMode.isSlowPut()) { if (invert) { - JITCompiler::Jump slow = - m_jit.branchTest32( - MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray)); + JITCompiler::Jump slow = m_jit.branchTest32( + MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray)); m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR); m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR); result.append( @@ -426,7 +459,7 @@ void SpeculativeJIT::checkArray(Node& node) const TypedArrayDescriptor* result = typedArrayDescriptor(node.arrayMode()); - if (node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) { + if (node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))) { noResult(m_compileIndex); return; } @@ -437,6 +470,8 @@ void SpeculativeJIT::checkArray(Node& node) case Array::String: expectedClassInfo = &JSString::s_info; break; + case Array::Int32: + case Array::Double: case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: { @@ -528,16 +563,30 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg) // If we're allegedly creating contiguous storage and the index is bogus, then // just don't. - if (node.arrayMode().type() == Array::Contiguous && propertyReg != InvalidGPRReg) { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32( - MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); + if (propertyReg != InvalidGPRReg) { + switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: + case Array::Contiguous: + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32( + MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); + break; + default: + break; + } } // Now call out to create the array storage. silentSpillAllRegisters(tempGPR); switch (node.arrayMode().type()) { + case Array::Int32: + callOperation(operationEnsureInt32, tempGPR, baseReg); + break; + case Array::Double: + callOperation(operationEnsureDouble, tempGPR, baseReg); + break; case Array::Contiguous: callOperation(operationEnsureContiguous, tempGPR, baseReg); break; @@ -956,33 +1005,33 @@ static const char* dataFormatString(DataFormat format) void SpeculativeJIT::dump(const char* label) { if (label) - dataLog("<%s>\n", label); + dataLogF("<%s>\n", label); - dataLog(" gprs:\n"); + dataLogF(" gprs:\n"); m_gprs.dump(); - dataLog(" fprs:\n"); + dataLogF(" fprs:\n"); m_fprs.dump(); - dataLog(" VirtualRegisters:\n"); + dataLogF(" VirtualRegisters:\n"); for (unsigned i = 0; i < m_generationInfo.size(); ++i) { GenerationInfo& info = m_generationInfo[i]; if (info.alive()) - dataLog(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); + dataLogF(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat())); else - dataLog(" % 3d:[__][__]", i); + dataLogF(" % 3d:[__][__]", i); if (info.registerFormat() == DataFormatDouble) - dataLog(":fpr%d\n", info.fpr()); + dataLogF(":fpr%d\n", info.fpr()); else if (info.registerFormat() != DataFormatNone #if USE(JSVALUE32_64) && !(info.registerFormat() & DataFormatJS) #endif ) { ASSERT(info.gpr() != InvalidGPRReg); - dataLog(":%s\n", GPRInfo::debugName(info.gpr())); + dataLogF(":%s\n", GPRInfo::debugName(info.gpr())); } else - dataLog("\n"); + dataLogF("\n"); } if (label) - dataLog("</%s>\n", label); + dataLogF("</%s>\n", label); } #endif @@ -994,13 +1043,13 @@ void SpeculativeJIT::checkConsistency() for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) { if (iter.isLocked()) { - dataLog("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName()); + dataLogF("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName()); failed = true; } } for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) { if (iter.isLocked()) { - dataLog("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName()); + dataLogF("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName()); failed = true; } } @@ -1028,7 +1077,7 @@ void SpeculativeJIT::checkConsistency() GPRReg gpr = info.gpr(); ASSERT(gpr != InvalidGPRReg); if (m_gprs.name(gpr) != virtualRegister) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr)); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr)); failed = true; } break; @@ -1037,7 +1086,7 @@ void SpeculativeJIT::checkConsistency() FPRReg fpr = info.fpr(); ASSERT(fpr != InvalidFPRReg); if (m_fprs.name(fpr) != virtualRegister) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr)); failed = true; } break; @@ -1053,18 +1102,18 @@ void SpeculativeJIT::checkConsistency() GenerationInfo& info = m_generationInfo[virtualRegister]; #if USE(JSVALUE64) if (iter.regID() != info.gpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } #else if (!(info.registerFormat() & DataFormatJS)) { if (iter.regID() != info.gpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } else { if (iter.regID() != info.tagGPR() && iter.regID() != info.payloadGPR()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } @@ -1078,7 +1127,7 @@ void SpeculativeJIT::checkConsistency() GenerationInfo& info = m_generationInfo[virtualRegister]; if (iter.regID() != info.fpr()) { - dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); + dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister); failed = true; } } @@ -1496,7 +1545,7 @@ void SpeculativeJIT::compile(BasicBlock& block) #endif #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Setting up state for block #%u: ", m_block); + dataLogF("Setting up state for block #%u: ", m_block); #endif m_stream->appendAndLog(VariableEvent::reset()); @@ -1544,7 +1593,7 @@ void SpeculativeJIT::compile(BasicBlock& block) } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) { @@ -1554,7 +1603,7 @@ void SpeculativeJIT::compile(BasicBlock& block) m_codeOriginForOSR = node.codeOrigin; if (!node.shouldGenerate()) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); + dataLogF("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: @@ -1601,7 +1650,7 @@ void SpeculativeJIT::compile(BasicBlock& block) // The exception is the this argument, which we don't really need to be // able to recover. #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\nRecovery for argument %d: ", i); + dataLogF("\nRecovery for argument %d: ", i); recovery.dump(WTF::dataFile()); #endif inlineCallFrame->arguments[i] = recovery; @@ -1617,7 +1666,7 @@ void SpeculativeJIT::compile(BasicBlock& block) } else { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); + dataLogF("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset()); #endif #if DFG_ENABLE(JIT_BREAK_ON_EVERY_NODE) m_jit.breakpoint(); @@ -1642,25 +1691,25 @@ void SpeculativeJIT::compile(BasicBlock& block) #if DFG_ENABLE(DEBUG_VERBOSE) if (node.hasResult()) { GenerationInfo& info = m_generationInfo[node.virtualRegister()]; - dataLog("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister()); + dataLogF("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister()); if (info.registerFormat() != DataFormatNone) { if (info.registerFormat() == DataFormatDouble) - dataLog(", %s", FPRInfo::debugName(info.fpr())); + dataLogF(", %s", FPRInfo::debugName(info.fpr())); #if USE(JSVALUE32_64) else if (info.registerFormat() & DataFormatJS) - dataLog(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); + dataLogF(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR())); #endif else - dataLog(", %s", GPRInfo::debugName(info.gpr())); + dataLogF(", %s", GPRInfo::debugName(info.gpr())); } - dataLog(" "); + dataLogF(" "); } else - dataLog(" "); + dataLogF(" "); #endif } #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("\n"); + dataLogF("\n"); #endif // Make sure that the abstract state is rematerialized for the next node. @@ -1797,6 +1846,85 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo return ValueRecovery(); } +void SpeculativeJIT::compileDoublePutByVal(Node& node, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property) +{ + Edge child3 = m_jit.graph().varArgChild(node, 2); + Edge child4 = m_jit.graph().varArgChild(node, 3); + + ArrayMode arrayMode = node.arrayMode(); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + + SpeculateDoubleOperand value(this, child3); + + FPRReg valueReg = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(child3).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueReg, valueReg)); + } + + if (!m_compileOkay) + return; + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); + + if (node.op() == PutByValAlias) { + // Store the value to the array. + GPRReg propertyReg = property.gpr(); + FPRReg valueReg = value.fpr(); + m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); + + noResult(m_compileIndex); + return; + } + + GPRTemporary temporary; + GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node); + + MacroAssembler::JumpList slowCases; + + if (arrayMode.isInBounds()) { + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + } else { + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); + + if (!arrayMode.isOutOfBounds()) + speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); + + m_jit.add32(TrustedImm32(1), propertyReg, temporaryReg); + m_jit.store32(temporaryReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + inBounds.link(&m_jit); + } + + m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight)); + + base.use(); + property.use(); + value.use(); + storage.use(); + + if (arrayMode.isOutOfBounds()) { + addSlowPathGenerator( + slowPathCall( + slowCases, this, + m_jit.codeBlock()->isStrictMode() ? operationPutDoubleByValBeyondArrayBoundsStrict : operationPutDoubleByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueReg)); + } + + noResult(m_compileIndex, UseChildrenCalledExplicitly); +} + void SpeculativeJIT::compileGetCharCodeAt(Node& node) { SpeculateCellOperand string(this, node.child1()); @@ -1841,7 +1969,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(ArrayMode(Array::String).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::String).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); // unsigned comparison so we can filter out negative indices and indices that are too large speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength()))); @@ -1878,7 +2006,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node) GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("checkGeneratedTypeForToInt32@%d ", nodeIndex); + dataLogF("checkGeneratedTypeForToInt32@%d ", nodeIndex); #endif Node& node = at(nodeIndex); VirtualRegister virtualRegister = node.virtualRegister(); @@ -2250,7 +2378,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& GPRTemporary result(this); GPRReg resultReg = result.gpr(); - ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueRegs(), NoNode, @@ -2400,7 +2528,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor GPRReg propertyReg = property.gpr(); GPRReg storageReg = storage.gpr(); - ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); FPRTemporary result(this); FPRReg resultReg = result.fpr(); @@ -2437,7 +2565,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto SpeculateDoubleOperand valueOp(this, valueUse); - ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_state.forNode(baseUse))); + ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_jit.graph(), m_jit.graph()[m_compileIndex], m_state.forNode(baseUse))); GPRTemporary result(this); @@ -2763,7 +2891,7 @@ void SpeculativeJIT::compileAdd(Node& node) return; } - if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) { + if (Node::shouldSpeculateNumberExpectingDefined(at(node.child1()), at(node.child2()))) { SpeculateDoubleOperand op1(this, node.child1()); SpeculateDoubleOperand op2(this, node.child2()); FPRTemporary result(this, op1, op2); @@ -3001,7 +3129,7 @@ void SpeculativeJIT::compileIntegerArithDivForX86(Node& node) void SpeculativeJIT::compileArithMod(Node& node) { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { compileSoftModulo(node); return; @@ -3260,7 +3388,7 @@ void SpeculativeJIT::compileGetByValOnArguments(Node& node) if (!m_compileOkay) return; - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); // Two really lame checks. speculationCheck( @@ -3317,7 +3445,7 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node) if (!m_compileOkay) return; - ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1()))); + ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))); speculationCheck( Uncountable, JSValueSource(), NoNode, @@ -3336,6 +3464,8 @@ void SpeculativeJIT::compileGetArrayLength(Node& node) const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode()); switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: { StorageOperand storage(this, node.child2()); GPRTemporary result(this, storage); diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h index 446ea7dbe..f1384e269 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h @@ -1308,6 +1308,11 @@ public: m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(identifier)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg result, FPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg result, GPRReg arg1, GPRReg arg2) { m_jit.setupArgumentsWithExecState(arg1, arg2); @@ -1363,6 +1368,11 @@ public: m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1) { m_jit.setupArguments(arg1); @@ -1453,6 +1463,11 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3) { m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); @@ -1661,11 +1676,21 @@ public: m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, TrustedImm32(arg1Tag), TrustedImmPtr(identifier)); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg resultTag, GPRReg resultPayload, FPRReg arg1, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(arg1, arg2); + return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); + } JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2) { m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2); return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); } + JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, TrustedImm32 arg1Tag, GPRReg arg1Payload, GPRReg arg2) + { + m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2); + return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag); + } JITCompiler::Call callOperation(J_DFGOperation_EJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload) { m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag); @@ -1716,6 +1741,11 @@ public: m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame)); return appendCallWithExceptionCheckSetResult(operation, result); } + JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure) + { + m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure)); + return appendCallWithExceptionCheckSetResult(operation, result); + } JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload) { m_jit.setupArguments(arg1Payload, arg1Tag); @@ -1819,11 +1849,21 @@ public: m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, arg3); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload) { m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); return appendCallWithExceptionCheck(operation); } + JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, TrustedImm32 arg3Tag, GPRReg arg3Payload) + { + m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag); + return appendCallWithExceptionCheck(operation); + } JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet) { m_jit.setupArguments(TrustedImmPtr(watchpointSet)); @@ -2270,6 +2310,11 @@ public: void compileAllocatePropertyStorage(Node&); void compileReallocatePropertyStorage(Node&); +#if USE(JSVALUE32_64) + template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType> + void compileContiguousPutByVal(Node&, BaseOperandType&, PropertyOperandType&, ValueOperandType&, GPRReg valuePayloadReg, TagType valueTag); +#endif + void compileDoublePutByVal(Node&, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property); bool putByValWillNeedExtraRegister(ArrayMode arrayMode) { return arrayMode.mayStoreToHole(); @@ -2415,6 +2460,7 @@ public: const TypedArrayDescriptor* typedArrayDescriptor(ArrayMode); + JITCompiler::Jump jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, IndexingType, bool invert); JITCompiler::JumpList jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, bool invert = false); void checkArray(Node&); void arrayify(Node&, GPRReg baseReg, GPRReg propertyReg); @@ -2955,6 +3001,11 @@ public: m_gprOrInvalid = m_jit->fillSpeculateInt(index(), m_format); return m_gprOrInvalid; } + + void use() + { + m_jit->use(m_index); + } private: SpeculativeJIT* m_jit; @@ -3035,6 +3086,11 @@ public: m_fprOrInvalid = m_jit->fillSpeculateDouble(index()); return m_fprOrInvalid; } + + void use() + { + m_jit->use(m_index); + } private: SpeculativeJIT* m_jit; diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp index 65fdf5593..05af6962e 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp @@ -29,9 +29,11 @@ #if ENABLE(DFG_JIT) +#include "ArrayPrototype.h" #include "DFGCallArrayAllocatorSlowPathGenerator.h" #include "DFGSlowPathGenerator.h" #include "JSActivation.h" +#include "ObjectPrototype.h" namespace JSC { namespace DFG { @@ -996,7 +998,6 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node& node, bool invert) void SpeculativeJIT::emitCall(Node& node) { - if (node.op() != Call) ASSERT(node.op() == Construct); @@ -1047,8 +1048,8 @@ void SpeculativeJIT::emitCall(Node& node) m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister); - slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck)); slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, calleeTagGPR, TrustedImm32(JSValue::CellTag))); + slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck)); m_jit.loadPtr(MacroAssembler::Address(calleePayloadGPR, OBJECT_OFFSETOF(JSFunction, m_scope)), resultPayloadGPR); m_jit.storePtr(resultPayloadGPR, MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload))); m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag))); @@ -1082,14 +1083,14 @@ void SpeculativeJIT::emitCall(Node& node) jsValueResult(resultTagGPR, resultPayloadGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly); - m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin); + m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleePayloadGPR, at(m_compileIndex).codeOrigin); } template<bool strict> GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecInt@%d ", nodeIndex); + dataLogF("SpecInt@%d ", nodeIndex); #endif if (isKnownNotInteger(nodeIndex)) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1187,7 +1188,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex) FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecDouble@%d ", nodeIndex); + dataLogF("SpecDouble@%d ", nodeIndex); #endif if (isKnownNotNumber(nodeIndex)) { terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode); @@ -1322,7 +1323,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecCell@%d ", nodeIndex); + dataLogF("SpecCell@%d ", nodeIndex); #endif if (isKnownNotCell(nodeIndex)) { terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation); @@ -1397,7 +1398,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecBool@%d ", nodeIndex); + dataLogF("SpecBool@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = m_jit.graph()[nodeIndex]; @@ -2046,6 +2047,69 @@ void SpeculativeJIT::emitBranch(Node& node) } } +template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType> +void SpeculativeJIT::compileContiguousPutByVal(Node& node, BaseOperandType& base, PropertyOperandType& property, ValueOperandType& value, GPRReg valuePayloadReg, TagType valueTag) +{ + Edge child4 = m_jit.graph().varArgChild(node, 3); + + ArrayMode arrayMode = node.arrayMode(); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + + StorageOperand storage(this, child4); + GPRReg storageReg = storage.gpr(); + + if (node.op() == PutByValAlias) { + // Store the value to the array. + GPRReg propertyReg = property.gpr(); + m_jit.store32(valueTag, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + + noResult(m_compileIndex); + return; + } + + MacroAssembler::JumpList slowCases; + + if (arrayMode.isInBounds()) { + speculationCheck( + Uncountable, JSValueRegs(), NoNode, + m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + } else { + MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); + + if (!arrayMode.isOutOfBounds()) + speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); + + m_jit.add32(TrustedImm32(1), propertyReg); + m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); + m_jit.sub32(TrustedImm32(1), propertyReg); + + inBounds.link(&m_jit); + } + + m_jit.store32(valueTag, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + + base.use(); + property.use(); + value.use(); + storage.use(); + + if (arrayMode.isOutOfBounds()) { + addSlowPathGenerator( + slowPathCall( + slowCases, this, + m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, + NoResult, baseReg, propertyReg, valueTag, valuePayloadReg)); + } + + noResult(m_compileIndex, UseChildrenCalledExplicitly); +} + void SpeculativeJIT::compile(Node& node) { NodeType op = node.op(); @@ -2064,6 +2128,18 @@ void SpeculativeJIT::compile(Node& node) initConstantInfo(m_compileIndex); break; + case Identity: { + // This could be done a lot better. We take the cheap way out because Identity + // is only going to stick around after CSE if we had prediction weirdness. + JSValueOperand operand(this, node.child1()); + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + m_jit.move(operand.tagGPR(), resultTag.gpr()); + m_jit.move(operand.payloadGPR(), resultPayload.gpr()); + jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); + break; + } + case GetLocal: { SpeculatedType prediction = node.variableAccessData()->prediction(); AbstractValue& value = block()->valuesAtHead.operand(node.local()); @@ -2366,7 +2442,8 @@ void SpeculativeJIT::compile(Node& node) break; case ArithDiv: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { #if CPU(X86) compileIntegerArithDivForX86(node); #else // CPU(X86) -> so non-X86 code follows @@ -2393,7 +2470,8 @@ void SpeculativeJIT::compile(Node& node) } case ArithAbs: { - if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { + if (at(node.child1()).shouldSpeculateIntegerForArithmetic() + && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); GPRTemporary result(this, op1); GPRTemporary scratch(this); @@ -2417,7 +2495,8 @@ void SpeculativeJIT::compile(Node& node) case ArithMin: case ArithMax: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { SpeculateStrictInt32Operand op1(this, node.child1()); SpeculateStrictInt32Operand op2(this, node.child2()); GPRTemporary result(this, op1); @@ -2567,6 +2646,7 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex); break; } + case Array::Int32: case Array::Contiguous: { if (node.arrayMode().isInBounds()) { SpeculateStrictInt32Operand property(this, node.child2()); @@ -2580,8 +2660,20 @@ void SpeculativeJIT::compile(Node& node) speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - GPRTemporary resultTag(this); GPRTemporary resultPayload(this); + if (node.arrayMode().type() == Array::Int32) { + speculationCheck( + OutOfBounds, JSValueRegs(), NoNode, + m_jit.branch32( + MacroAssembler::Equal, + MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), + TrustedImm32(JSValue::EmptyValueTag))); + m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); + integerResult(resultPayload.gpr(), m_compileIndex); + break; + } + + GPRTemporary resultTag(this); m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr()); speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag))); m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr()); @@ -2621,6 +2713,68 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); break; } + case Array::Double: { + if (node.arrayMode().isInBounds()) { + if (node.arrayMode().isSaneChain()) { + JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin); + ASSERT(globalObject->arrayPrototypeChainIsSane()); + globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + } + + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + FPRTemporary result(this); + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr()); + if (!node.arrayMode().isSaneChain()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr())); + doubleResult(result.fpr(), m_compileIndex); + break; + } + + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + GPRTemporary resultTag(this); + GPRTemporary resultPayload(this); + FPRTemporary temp(this); + GPRReg resultTagReg = resultTag.gpr(); + GPRReg resultPayloadReg = resultPayload.gpr(); + FPRReg tempReg = temp.fpr(); + + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg); + slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg)); + boxDouble(tempReg, resultTagReg, resultPayloadReg); + + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + JSValueRegs(resultTagReg, resultPayloadReg), baseReg, propertyReg)); + + jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex); + break; + } case Array::ArrayStorage: case Array::SlowPutArrayStorage: { if (node.arrayMode().isInBounds()) { @@ -2771,6 +2925,17 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode.type()) { + case Array::Int32: { + SpeculateIntegerOperand value(this, child3); + + GPRReg valuePayloadReg = value.gpr(); + + if (!m_compileOkay) + return; + + compileContiguousPutByVal(node, base, property, value, valuePayloadReg, TrustedImm32(JSValue::Int32Tag)); + break; + } case Array::Contiguous: { JSValueOperand value(this, child3); @@ -2784,61 +2949,14 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary scratch(this); writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratch.gpr()); } - - StorageOperand storage(this, child4); - GPRReg storageReg = storage.gpr(); - - if (node.op() == PutByValAlias) { - // Store the value to the array. - GPRReg propertyReg = property.gpr(); - m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - - noResult(m_compileIndex); - break; - } - - MacroAssembler::JumpList slowCases; - - if (arrayMode.isInBounds()) { - speculationCheck( - Uncountable, JSValueRegs(), NoNode, - m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); - } else { - MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - - slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength()))); - - if (!arrayMode.isOutOfBounds()) - speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases); - - m_jit.add32(TrustedImm32(1), propertyReg); - m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())); - m_jit.sub32(TrustedImm32(1), propertyReg); - - inBounds.link(&m_jit); - } - - m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); - base.use(); - property.use(); - value.use(); - storage.use(); - - if (arrayMode.isOutOfBounds()) { - addSlowPathGenerator( - slowPathCall( - slowCases, this, - m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict, - NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg)); - } - - noResult(m_compileIndex, UseChildrenCalledExplicitly); + compileContiguousPutByVal(node, base, property, value, valuePayloadReg, valueTagReg); + break; + } + case Array::Double: { + compileDoublePutByVal(node, base, property); break; } - case Array::ArrayStorage: case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); @@ -3028,24 +3146,47 @@ void SpeculativeJIT::compile(Node& node) ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); - JSValueOperand value(this, node.child2()); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); - GPRReg valueTagGPR = value.tagGPR(); - GPRReg valuePayloadGPR = value.payloadGPR(); GPRReg storageLengthGPR = storageLength.gpr(); - if (Heap::isWriteBarrierEnabled()) { - GPRTemporary scratch(this); - writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); - } - StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: { + SpeculateIntegerOperand value(this, node.child2()); + GPRReg valuePayloadGPR = value.gpr(); + + m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); + MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + m_jit.store32(TrustedImm32(JSValue::Int32Tag), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(valuePayloadGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPush, + JSValueRegs(storageGPR, storageLengthGPR), + TrustedImm32(JSValue::Int32Tag), valuePayloadGPR, baseGPR)); + + jsValueResult(storageGPR, storageLengthGPR, m_compileIndex); + break; + } + case Array::Contiguous: { + JSValueOperand value(this, node.child2()); + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); m_jit.store32(valueTagGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); @@ -3064,7 +3205,45 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + SpeculateDoubleOperand value(this, node.child2()); + FPRReg valueFPR = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR)); + } + + m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); + MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPushDouble, + JSValueRegs(storageGPR, storageLengthGPR), + valueFPR, baseGPR)); + + jsValueResult(storageGPR, storageLengthGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { + JSValueOperand value(this, node.child2()); + GPRReg valueTagGPR = value.tagGPR(); + GPRReg valuePayloadGPR = value.payloadGPR(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3107,6 +3286,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR); @@ -3140,6 +3320,44 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + FPRTemporary temp(this); + FPRReg tempFPR = temp.fpr(); + + m_jit.load32( + MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR); + MacroAssembler::Jump undefinedCase = + m_jit.branchTest32(MacroAssembler::Zero, valuePayloadGPR); + m_jit.sub32(TrustedImm32(1), valuePayloadGPR); + m_jit.store32( + valuePayloadGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.loadDouble( + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight), + tempFPR); + MacroAssembler::Jump slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR); + JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN); + m_jit.store32( + MacroAssembler::TrustedImm32(nan.u.asBits.tag), + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32( + MacroAssembler::TrustedImm32(nan.u.asBits.payload), + MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + boxDouble(tempFPR, valueTagGPR, valuePayloadGPR); + + addSlowPathGenerator( + slowPathMove( + undefinedCase, this, + MacroAssembler::TrustedImm32(jsUndefined().tag()), valueTagGPR, + MacroAssembler::TrustedImm32(jsUndefined().payload()), valuePayloadGPR)); + addSlowPathGenerator( + slowPathCall( + slowCase, this, operationArrayPopAndRecoverLength, + JSValueRegs(valueTagGPR, valuePayloadGPR), baseGPR)); + + jsValueResult(valueTagGPR, valuePayloadGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { GPRTemporary storageLength(this); GPRReg storageLengthGPR = storageLength.gpr(); @@ -3358,11 +3576,17 @@ void SpeculativeJIT::compile(Node& node) case NewArray: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); - ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType())); - + Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()); + ASSERT(structure->indexingType() == node.indexingType()); + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); + unsigned numElements = node.numChildren(); GPRTemporary result(this); @@ -3371,17 +3595,52 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(structure, resultGPR, storageGPR, numElements); // At this point, one way or another, resultGPR and storageGPR have pointers to // the JSArray and the Butterfly, respectively. + ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren()); + for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opTagGPR = operand.tagGPR(); - GPRReg opPayloadGPR = operand.payloadGPR(); - m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); - m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: { + SpeculateIntegerOperand operand(this, use); + m_jit.store32(TrustedImm32(JSValue::Int32Tag), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(operand.gpr(), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); + GPRReg opTagGPR = operand.tagGPR(); + GPRReg opPayloadGPR = operand.payloadGPR(); + m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + break; + } + default: + CRASH(); + break; + } } // Yuck, we should *really* have a way of also returning the storageGPR. But @@ -3399,7 +3658,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); callOperation( - operationNewEmptyArray, result.gpr(), globalObject->arrayStructure()); + operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); cellResult(result.gpr(), m_compileIndex); break; } @@ -3409,13 +3668,61 @@ void SpeculativeJIT::compile(Node& node) EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opTagGPR = operand.tagGPR(); - GPRReg opPayloadGPR = operand.payloadGPR(); - operand.use(); - - m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); - m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + // Need to perform the speculations that this node promises to perform. If we're + // emitting code here and the indexing type is not array storage then there is + // probably something hilarious going on and we're already failing at all the + // things, but at least we're going to be sound. + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, reinterpret_cast<char*>(buffer + operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: { + SpeculateIntegerOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + m_jit.store32(TrustedImm32(JSValue::Int32Tag), reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + m_jit.store32(opGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); + GPRReg opTagGPR = operand.tagGPR(); + GPRReg opPayloadGPR = operand.payloadGPR(); + + m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)); + m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)); + operand.use(); + break; + } + default: + CRASH(); + break; + } + } + + switch (node.indexingType()) { + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + useChildren(node); + break; + default: + break; } flushRegisters(); @@ -3431,8 +3738,8 @@ void SpeculativeJIT::compile(Node& node) GPRResult result(this); callOperation( - operationNewArray, result.gpr(), globalObject->arrayStructure(), - static_cast<void *>(buffer), node.numChildren()); + operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + static_cast<void*>(buffer), node.numChildren()); if (scratchSize) { GPRTemporary scratch(this); @@ -3471,17 +3778,30 @@ void SpeculativeJIT::compile(Node& node) emitAllocateBasicStorage(resultGPR, storageGPR)); m_jit.subPtr(scratchGPR, storageGPR); emitAllocateBasicJSObject<JSArray, MarkedBlock::None>( - TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR, + TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR, storageGPR, sizeof(JSArray), slowCases); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(node.indexingType())) { + JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN); + + m_jit.move(sizeGPR, scratchGPR); + MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR); + MacroAssembler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), scratchGPR); + m_jit.store32(TrustedImm32(nan.u.asBits.tag), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag))); + m_jit.store32(TrustedImm32(nan.u.asBits.payload), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload))); + m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit); + done.link(&m_jit); + } + addSlowPathGenerator(adoptPtr( new CallArrayAllocatorWithVariableSizeSlowPathGenerator( slowCases, this, operationNewArrayWithSize, resultGPR, - globalObject->arrayStructure(), - globalObject->arrayStructureWithArrayStorage(), + globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage), sizeGPR))); cellResult(resultGPR, m_compileIndex); @@ -3492,15 +3812,24 @@ void SpeculativeJIT::compile(Node& node) GPRReg sizeGPR = size.gpr(); flushRegisters(); GPRResult result(this); + GPRReg resultGPR = result.gpr(); + GPRReg structureGPR = selectScratchGPR(sizeGPR); + MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR); + MacroAssembler::Jump done = m_jit.jump(); + bigLength.link(&m_jit); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR); + done.link(&m_jit); callOperation( - operationNewArrayWithSize, result.gpr(), globalObject->arrayStructure(), sizeGPR); - cellResult(result.gpr(), m_compileIndex); + operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR); + cellResult(resultGPR, m_compileIndex); break; } case NewArrayBuffer: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + IndexingType indexingType = node.indexingType(); + if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); unsigned numElements = node.numConstants(); @@ -3511,12 +3840,25 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements); - int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant())); - for (unsigned index = 0; index < node.numConstants() * 2; ++index) { - m_jit.store32( - Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index)); + if (node.indexingType() == ArrayWithDouble) { + JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant()); + for (unsigned index = 0; index < node.numConstants(); ++index) { + union { + int32_t halves[2]; + double value; + } u; + u.value = data[index].asNumber(); + m_jit.store32(Imm32(u.halves[0]), MacroAssembler::Address(storageGPR, sizeof(double) * index)); + m_jit.store32(Imm32(u.halves[1]), MacroAssembler::Address(storageGPR, sizeof(double) * index + sizeof(int32_t))); + } + } else { + int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant())); + for (unsigned index = 0; index < node.numConstants() * 2; ++index) { + m_jit.store32( + Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index)); + } } cellResult(resultGPR, m_compileIndex); @@ -3526,7 +3868,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); - callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants()); + callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants()); cellResult(result.gpr(), m_compileIndex); break; @@ -3630,6 +3972,12 @@ void SpeculativeJIT::compile(Node& node) break; } + case InheritorIDWatchpoint: { + jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint()); + noResult(m_compileIndex); + break; + } + case NewObject: { GPRTemporary result(this); GPRTemporary scratch(this); @@ -3639,9 +3987,9 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::JumpList slowPath; - emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath); + emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath); - addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR)); + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure())); cellResult(resultGPR, m_compileIndex); break; @@ -3810,7 +4158,7 @@ void SpeculativeJIT::compile(Node& node) case CheckFunction: { SpeculateCellOperand function(this, node.child1()); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); + speculationCheck(BadCache, JSValueSource::unboxedCell(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); noResult(m_compileIndex); break; } diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp index 6c066c388..da6583c70 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp +++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp @@ -29,8 +29,10 @@ #if ENABLE(DFG_JIT) #include "Arguments.h" +#include "ArrayPrototype.h" #include "DFGCallArrayAllocatorSlowPathGenerator.h" #include "DFGSlowPathGenerator.h" +#include "ObjectPrototype.h" namespace JSC { namespace DFG { @@ -1056,14 +1058,14 @@ void SpeculativeJIT::emitCall(Node& node) jsValueResult(resultGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly); - m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin); + m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleeGPR, at(m_compileIndex).codeOrigin); } template<bool strict> GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecInt@%d ", nodeIndex); + dataLogF("SpecInt@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1211,7 +1213,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex) FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecDouble@%d ", nodeIndex); + dataLogF("SpecDouble@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1365,7 +1367,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex) GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecCell@%d ", nodeIndex); + dataLogF("SpecCell@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -1441,7 +1443,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex) { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("SpecBool@%d ", nodeIndex); + dataLogF("SpecBool@%d ", nodeIndex); #endif SpeculatedType type = m_state.forNode(nodeIndex).m_type; Node& node = at(nodeIndex); @@ -2128,6 +2130,16 @@ void SpeculativeJIT::compile(Node& node) m_jit.addWeakReference(node.weakConstant()); initConstantInfo(m_compileIndex); break; + + case Identity: { + // This could be done a lot better. We take the cheap way out because Identity + // is only going to stick around after CSE if we had prediction weirdness. + JSValueOperand operand(this, node.child1()); + GPRTemporary result(this, operand); + m_jit.move(operand.gpr(), result.gpr()); + jsValueResult(result.gpr(), m_compileIndex); + break; + } case GetLocal: { SpeculatedType prediction = node.variableAccessData()->prediction(); @@ -2403,7 +2415,8 @@ void SpeculativeJIT::compile(Node& node) break; case ArithDiv: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { compileIntegerArithDivForX86(node); break; } @@ -2426,7 +2439,8 @@ void SpeculativeJIT::compile(Node& node) } case ArithAbs: { - if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) { + if (at(node.child1()).shouldSpeculateIntegerForArithmetic() + && node.canSpeculateInteger()) { SpeculateIntegerOperand op1(this, node.child1()); GPRTemporary result(this); GPRTemporary scratch(this); @@ -2450,7 +2464,8 @@ void SpeculativeJIT::compile(Node& node) case ArithMin: case ArithMax: { - if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) { + if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2())) + && node.canSpeculateInteger()) { SpeculateStrictInt32Operand op1(this, node.child1()); SpeculateStrictInt32Operand op2(this, node.child2()); GPRTemporary result(this, op1); @@ -2598,6 +2613,7 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(result.gpr(), m_compileIndex); break; } + case Array::Int32: case Array::Contiguous: { if (node.arrayMode().isInBounds()) { SpeculateStrictInt32Operand property(this, node.child2()); @@ -2614,7 +2630,7 @@ void SpeculativeJIT::compile(Node& node) GPRTemporary result(this); m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.gpr()); speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr())); - jsValueResult(result.gpr(), m_compileIndex); + jsValueResult(result.gpr(), m_compileIndex, node.arrayMode().type() == Array::Int32 ? DataFormatJSInteger : DataFormatJS); break; } @@ -2647,6 +2663,68 @@ void SpeculativeJIT::compile(Node& node) jsValueResult(resultReg, m_compileIndex); break; } + + case Array::Double: { + if (node.arrayMode().isInBounds()) { + if (node.arrayMode().isSaneChain()) { + JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin); + ASSERT(globalObject->arrayPrototypeChainIsSane()); + globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint()); + } + + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + FPRTemporary result(this); + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr()); + if (!node.arrayMode().isSaneChain()) + speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr())); + doubleResult(result.fpr(), m_compileIndex); + break; + } + + SpeculateCellOperand base(this, node.child1()); + SpeculateStrictInt32Operand property(this, node.child2()); + StorageOperand storage(this, node.child3()); + + GPRReg baseReg = base.gpr(); + GPRReg propertyReg = property.gpr(); + GPRReg storageReg = storage.gpr(); + + if (!m_compileOkay) + return; + + GPRTemporary result(this); + FPRTemporary temp(this); + GPRReg resultReg = result.gpr(); + FPRReg tempReg = temp.fpr(); + + MacroAssembler::JumpList slowCases; + + slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()))); + + m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg); + slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg)); + boxDouble(tempReg, resultReg); + + addSlowPathGenerator( + slowPathCall( + slowCases, this, operationGetByValArrayInt, + result.gpr(), baseReg, propertyReg)); + + jsValueResult(resultReg, m_compileIndex); + break; + } + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { if (node.arrayMode().isInBounds()) { @@ -2789,6 +2867,7 @@ void SpeculativeJIT::compile(Node& node) GPRReg propertyReg = property.gpr(); switch (arrayMode.type()) { + case Array::Int32: case Array::Contiguous: { JSValueOperand value(this, child3); @@ -2796,8 +2875,15 @@ void SpeculativeJIT::compile(Node& node) if (!m_compileOkay) return; + + if (arrayMode.type() == Array::Int32 + && !isInt32Speculation(m_state.forNode(child3).m_type)) { + speculationCheck( + BadType, JSValueRegs(valueReg), child3, + m_jit.branch64(MacroAssembler::Below, valueReg, GPRInfo::tagTypeNumberRegister)); + } - if (Heap::isWriteBarrierEnabled()) { + if (arrayMode.type() == Array::Contiguous && Heap::isWriteBarrierEnabled()) { GPRTemporary scratch(this); writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratch.gpr()); } @@ -2857,6 +2943,11 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + compileDoublePutByVal(node, base, property); + break; + } + case Array::ArrayStorage: case Array::SlowPutArrayStorage: { JSValueOperand value(this, child3); @@ -3081,23 +3172,31 @@ void SpeculativeJIT::compile(Node& node) ASSERT(node.arrayMode().isJSArray()); SpeculateCellOperand base(this, node.child1()); - JSValueOperand value(this, node.child2()); GPRTemporary storageLength(this); GPRReg baseGPR = base.gpr(); - GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); - if (Heap::isWriteBarrierEnabled()) { - GPRTemporary scratch(this); - writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); - } - StorageOperand storage(this, node.child3()); GPRReg storageGPR = storage.gpr(); switch (node.arrayMode().type()) { + case Array::Int32: case Array::Contiguous: { + JSValueOperand value(this, node.child2()); + GPRReg valueGPR = value.gpr(); + + if (node.arrayMode().type() == Array::Int32 && !isInt32Speculation(m_state.forNode(node.child2()).m_type)) { + speculationCheck( + BadType, JSValueRegs(valueGPR), node.child2(), + m_jit.branch64(MacroAssembler::Below, valueGPR, GPRInfo::tagTypeNumberRegister)); + } + + if (node.arrayMode().type() != Array::Int32 && Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); m_jit.store64(valueGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); @@ -3114,7 +3213,43 @@ void SpeculativeJIT::compile(Node& node) break; } + case Array::Double: { + SpeculateDoubleOperand value(this, node.child2()); + FPRReg valueFPR = value.fpr(); + + if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR)); + } + + m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); + MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + m_jit.add32(TrustedImm32(1), storageLengthGPR); + m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); + m_jit.or64(GPRInfo::tagTypeNumberRegister, storageLengthGPR); + + addSlowPathGenerator( + slowPathCall( + slowPath, this, operationArrayPushDouble, NoResult, storageLengthGPR, + valueFPR, baseGPR)); + + jsValueResult(storageLengthGPR, m_compileIndex); + break; + } + case Array::ArrayStorage: { + JSValueOperand value(this, node.child2()); + GPRReg valueGPR = value.gpr(); + + if (Heap::isWriteBarrierEnabled()) { + GPRTemporary scratch(this); + writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR); + } + m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR); // Refuse to handle bizarre lengths. @@ -3152,13 +3287,17 @@ void SpeculativeJIT::compile(Node& node) StorageOperand storage(this, node.child2()); GPRTemporary value(this); GPRTemporary storageLength(this); + FPRTemporary temp(this); // This is kind of lame, since we don't always need it. I'm relying on the fact that we don't have FPR pressure, especially in code that uses pop(). GPRReg baseGPR = base.gpr(); GPRReg storageGPR = storage.gpr(); GPRReg valueGPR = value.gpr(); GPRReg storageLengthGPR = storageLength.gpr(); + FPRReg tempFPR = temp.fpr(); switch (node.arrayMode().type()) { + case Array::Int32: + case Array::Double: case Array::Contiguous: { m_jit.load32( MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR); @@ -3167,14 +3306,27 @@ void SpeculativeJIT::compile(Node& node) m_jit.sub32(TrustedImm32(1), storageLengthGPR); m_jit.store32( storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); - m_jit.load64( - MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), - valueGPR); - // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old - // length and the new length. - m_jit.store64( + MacroAssembler::Jump slowCase; + if (node.arrayMode().type() == Array::Double) { + m_jit.loadDouble( + MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), + tempFPR); + // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old + // length and the new length. + m_jit.store64( + MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); + slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR); + boxDouble(tempFPR, valueGPR); + } else { + m_jit.load64( + MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight), + valueGPR); + // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old + // length and the new length. + m_jit.store64( MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight)); - MacroAssembler::Jump slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR); + slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR); + } addSlowPathGenerator( slowPathMove( @@ -3184,6 +3336,7 @@ void SpeculativeJIT::compile(Node& node) slowPathCall( slowCase, this, operationArrayPopAndRecoverLength, valueGPR, baseGPR)); + // We can't know for sure that the result is an int because of the slow paths. :-/ jsValueResult(valueGPR, m_compileIndex); break; } @@ -3338,10 +3491,16 @@ void SpeculativeJIT::compile(Node& node) case NewArray: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); - ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType())); + Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()); + ASSERT(structure->indexingType() == node.indexingType()); + ASSERT( + hasUndecided(structure->indexingType()) + || hasInt32(structure->indexingType()) + || hasDouble(structure->indexingType()) + || hasContiguous(structure->indexingType())); unsigned numElements = node.numChildren(); @@ -3351,15 +3510,50 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(structure, resultGPR, storageGPR, numElements); // At this point, one way or another, resultGPR and storageGPR have pointers to // the JSArray and the Butterfly, respectively. + ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren()); + for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opGPR = operand.gpr(); - m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx)); + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + FPRReg opFPR = operand.fpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx)); + break; + } + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) { + speculationCheck( + BadType, JSValueRegs(opGPR), use.index(), + m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister)); + } + m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx)); + break; + } + default: + CRASH(); + break; + } } // Yuck, we should *really* have a way of also returning the storageGPR. But @@ -3376,7 +3570,7 @@ void SpeculativeJIT::compile(Node& node) if (!node.numChildren()) { flushRegisters(); GPRResult result(this); - callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructure()); + callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())); cellResult(result.gpr(), m_compileIndex); break; } @@ -3386,11 +3580,65 @@ void SpeculativeJIT::compile(Node& node) EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) { - JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]); - GPRReg opGPR = operand.gpr(); - operand.use(); - - m_jit.store64(opGPR, buffer + operandIdx); + // Need to perform the speculations that this node promises to perform. If we're + // emitting code here and the indexing type is not array storage then there is + // probably something hilarious going on and we're already failing at all the + // things, but at least we're going to be sound. + Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]; + switch (node.indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + CRASH(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + SpeculateDoubleOperand operand(this, use); + GPRTemporary scratch(this); + FPRReg opFPR = operand.fpr(); + GPRReg scratchGPR = scratch.gpr(); + if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) { + // FIXME: We need a way of profiling these, and we need to hoist them into + // SpeculateDoubleOperand. + speculationCheck( + BadType, JSValueRegs(), NoNode, + m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR)); + } + + m_jit.boxDouble(opFPR, scratchGPR); + m_jit.store64(scratchGPR, buffer + operandIdx); + break; + } + case ALL_INT32_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) { + speculationCheck( + BadType, JSValueRegs(opGPR), use.index(), + m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister)); + } + m_jit.store64(opGPR, buffer + operandIdx); + break; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + JSValueOperand operand(this, use); + GPRReg opGPR = operand.gpr(); + m_jit.store64(opGPR, buffer + operandIdx); + operand.use(); + break; + } + default: + CRASH(); + break; + } + } + + switch (node.indexingType()) { + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + useChildren(node); + break; + default: + break; } flushRegisters(); @@ -3406,7 +3654,7 @@ void SpeculativeJIT::compile(Node& node) GPRResult result(this); callOperation( - operationNewArray, result.gpr(), globalObject->arrayStructure(), + operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), static_cast<void*>(buffer), node.numChildren()); if (scratchSize) { @@ -3422,18 +3670,26 @@ void SpeculativeJIT::compile(Node& node) case NewArrayWithSize: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); SpeculateStrictInt32Operand size(this, node.child1()); GPRTemporary result(this); GPRTemporary storage(this); GPRTemporary scratch(this); + GPRTemporary scratch2; GPRReg sizeGPR = size.gpr(); GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); GPRReg scratchGPR = scratch.gpr(); + GPRReg scratch2GPR = InvalidGPRReg; + + if (hasDouble(node.indexingType())) { + GPRTemporary realScratch2(this, size); + scratch2.adopt(realScratch2); + scratch2GPR = scratch2.gpr(); + } MacroAssembler::JumpList slowCases; slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX))); @@ -3446,17 +3702,28 @@ void SpeculativeJIT::compile(Node& node) emitAllocateBasicStorage(resultGPR, storageGPR)); m_jit.subPtr(scratchGPR, storageGPR); emitAllocateBasicJSObject<JSArray, MarkedBlock::None>( - TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR, + TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR, storageGPR, sizeof(JSArray), slowCases); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength())); m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength())); + if (hasDouble(node.indexingType())) { + m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR); + m_jit.move(sizeGPR, scratch2GPR); + MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratch2GPR); + MacroAssembler::Label loop = m_jit.label(); + m_jit.sub32(TrustedImm32(1), scratch2GPR); + m_jit.store64(scratchGPR, MacroAssembler::BaseIndex(storageGPR, scratch2GPR, MacroAssembler::TimesEight)); + m_jit.branchTest32(MacroAssembler::NonZero, scratch2GPR).linkTo(loop, &m_jit); + done.link(&m_jit); + } + addSlowPathGenerator(adoptPtr( new CallArrayAllocatorWithVariableSizeSlowPathGenerator( slowCases, this, operationNewArrayWithSize, resultGPR, - globalObject->arrayStructure(), - globalObject->arrayStructureWithArrayStorage(), + globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), + globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage), sizeGPR))); cellResult(resultGPR, m_compileIndex); @@ -3470,10 +3737,10 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg structureGPR = selectScratchGPR(sizeGPR); MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)); - m_jit.move(TrustedImmPtr(globalObject->arrayStructure()), structureGPR); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR); MacroAssembler::Jump done = m_jit.jump(); bigLength.link(&m_jit); - m_jit.move(TrustedImmPtr(globalObject->arrayStructureWithArrayStorage()), structureGPR); + m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR); done.link(&m_jit); callOperation(operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR); cellResult(resultGPR, m_compileIndex); @@ -3520,7 +3787,8 @@ void SpeculativeJIT::compile(Node& node) case NewArrayBuffer: { JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin); - if (!globalObject->isHavingABadTime()) { + IndexingType indexingType = node.indexingType(); + if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) { globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint()); unsigned numElements = node.numConstants(); @@ -3531,13 +3799,23 @@ void SpeculativeJIT::compile(Node& node) GPRReg resultGPR = result.gpr(); GPRReg storageGPR = storage.gpr(); - emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements); + emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements); + ASSERT(indexingType & IsArray); JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant()); - for (unsigned index = 0; index < node.numConstants(); ++index) { - m_jit.store64( - Imm64(JSValue::encode(data[index])), - MacroAssembler::Address(storageGPR, sizeof(JSValue) * index)); + if (indexingType == ArrayWithDouble) { + for (unsigned index = 0; index < node.numConstants(); ++index) { + double value = data[index].asNumber(); + m_jit.store64( + Imm64(bitwise_cast<int64_t>(value)), + MacroAssembler::Address(storageGPR, sizeof(double) * index)); + } + } else { + for (unsigned index = 0; index < node.numConstants(); ++index) { + m_jit.store64( + Imm64(JSValue::encode(data[index])), + MacroAssembler::Address(storageGPR, sizeof(JSValue) * index)); + } } cellResult(resultGPR, m_compileIndex); @@ -3547,7 +3825,7 @@ void SpeculativeJIT::compile(Node& node) flushRegisters(); GPRResult result(this); - callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants()); + callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants()); cellResult(result.gpr(), m_compileIndex); break; @@ -3645,6 +3923,12 @@ void SpeculativeJIT::compile(Node& node) cellResult(resultGPR, m_compileIndex); break; } + + case InheritorIDWatchpoint: { + jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint()); + noResult(m_compileIndex); + break; + } case NewObject: { GPRTemporary result(this); @@ -3655,9 +3939,9 @@ void SpeculativeJIT::compile(Node& node) MacroAssembler::JumpList slowPath; - emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath); + emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath); - addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR)); + addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure())); cellResult(resultGPR, m_compileIndex); break; @@ -3813,7 +4097,7 @@ void SpeculativeJIT::compile(Node& node) case CheckFunction: { SpeculateCellOperand function(this, node.child1()); - speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); + speculationCheck(BadCache, JSValueRegs(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function())); noResult(m_compileIndex); break; } diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp index 22b9395b5..9d6060d60 100644 --- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp @@ -167,7 +167,7 @@ public: if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n", + dataLogF("Zeroing the structure to hoist for %s because the ratio is %lf.\n", m_graph.nameOfVariableAccessData(variable), variable->voteRatio()); #endif iter->value.m_structure = 0; @@ -200,7 +200,7 @@ public: JSValue value = m_graph.m_mustHandleValues[i]; if (!value || !value.isCell()) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n", + dataLogF("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n", m_graph.nameOfVariableAccessData(variable), value.description()); #endif iter->value.m_structure = 0; @@ -208,7 +208,7 @@ public: } if (value.asCell()->structure() != iter->value.m_structure) { #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n", + dataLogF("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n", m_graph.nameOfVariableAccessData(variable), value.asCell()->structure(), iter->value.m_structure); #endif iter->value.m_structure = 0; @@ -223,10 +223,10 @@ public: for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->value.m_structure) { - dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key)); + dataLogF("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key)); continue; } - dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key)); + dataLogF("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key)); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) diff --git a/Source/JavaScriptCore/dfg/DFGThunks.cpp b/Source/JavaScriptCore/dfg/DFGThunks.cpp index 74d1967a8..ac0b45f60 100644 --- a/Source/JavaScriptCore/dfg/DFGThunks.cpp +++ b/Source/JavaScriptCore/dfg/DFGThunks.cpp @@ -213,6 +213,18 @@ MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData* globalData) return linkForThunkGenerator(globalData, CodeForConstruct); } +// For closure optimizations, we only include calls, since if you're using closures for +// object construction then you're going to lose big time anyway. +MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData* globalData) +{ + CCallHelpers jit(globalData); + + slowPathFor(jit, globalData, operationLinkClosureCall); + + LinkBuffer patchBuffer(*globalData, &jit, GLOBAL_THUNK_ID); + return FINALIZE_CODE(patchBuffer, ("DFG link closure call slow path thunk")); +} + static MacroAssemblerCodeRef virtualForThunkGenerator( JSGlobalData* globalData, CodeSpecializationKind kind) { diff --git a/Source/JavaScriptCore/dfg/DFGThunks.h b/Source/JavaScriptCore/dfg/DFGThunks.h index 11a06d107..c97e3bfb6 100644 --- a/Source/JavaScriptCore/dfg/DFGThunks.h +++ b/Source/JavaScriptCore/dfg/DFGThunks.h @@ -45,6 +45,8 @@ MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(JSGlobalData*); MacroAssemblerCodeRef linkCallThunkGenerator(JSGlobalData*); MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData*); +MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData*); + MacroAssemblerCodeRef virtualCallThunkGenerator(JSGlobalData*); MacroAssemblerCodeRef virtualConstructThunkGenerator(JSGlobalData*); diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp index 2b26123d8..274b544b5 100644 --- a/Source/JavaScriptCore/dfg/DFGValidate.cpp +++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp @@ -45,9 +45,9 @@ public: #define VALIDATE(context, assertion) do { \ if (!(assertion)) { \ - dataLog("\n\n\nAt "); \ + dataLogF("\n\n\nAt "); \ reportValidationContext context; \ - dataLog(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \ + dataLogF(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \ dumpGraphIfAppropriate(); \ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \ CRASH(); \ @@ -56,13 +56,13 @@ public: #define V_EQUAL(context, left, right) do { \ if (left != right) { \ - dataLog("\n\n\nAt "); \ + dataLogF("\n\n\nAt "); \ reportValidationContext context; \ - dataLog(": validation (%s = ", #left); \ + dataLogF(": validation (%s = ", #left); \ dumpData(left); \ - dataLog(") == (%s = ", #right); \ + dataLogF(") == (%s = ", #right); \ dumpData(right); \ - dataLog(") (%s:%d) failed.\n", __FILE__, __LINE__); \ + dataLogF(") (%s:%d) failed.\n", __FILE__, __LINE__); \ dumpGraphIfAppropriate(); \ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #left " == " #right); \ CRASH(); \ @@ -290,60 +290,60 @@ private: void reportValidationContext(NodeIndex nodeIndex) { - dataLog("@%u", nodeIndex); + dataLogF("@%u", nodeIndex); } enum BlockTag { Block }; void reportValidationContext(BlockTag, BlockIndex blockIndex) { - dataLog("Block #%u", blockIndex); + dataLogF("Block #%u", blockIndex); } void reportValidationContext(NodeIndex nodeIndex, Edge edge) { - dataLog("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index()); + dataLogF("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index()); } void reportValidationContext( VirtualRegister local, BlockIndex sourceBlockIndex, BlockTag, BlockIndex destinationBlockIndex) { - dataLog("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex); + dataLogF("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex); } void reportValidationContext( VirtualRegister local, BlockIndex sourceBlockIndex, NodeIndex prevNodeIndex) { - dataLog("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex); + dataLogF("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex); } void reportValidationContext( NodeIndex nodeIndex, BlockIndex blockIndex) { - dataLog("@%u in Block #%u", nodeIndex, blockIndex); + dataLogF("@%u in Block #%u", nodeIndex, blockIndex); } void reportValidationContext( NodeIndex nodeIndex, NodeIndex nodeIndex2, BlockIndex blockIndex) { - dataLog("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex); + dataLogF("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex); } void reportValidationContext( NodeIndex nodeIndex, BlockIndex blockIndex, NodeIndex expectedNodeIndex, Edge incomingEdge) { - dataLog("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index()); + dataLogF("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index()); } void dumpData(unsigned value) { - dataLog("%u", value); + dataLogF("%u", value); } void dumpGraphIfAppropriate() { if (m_graphDumpMode == DontDumpGraph) return; - dataLog("Graph at time of failure:\n"); + dataLogF("Graph at time of failure:\n"); m_graph.dump(); } }; diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp index fa36ccdb5..7fa109b62 100644 --- a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp +++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp @@ -36,9 +36,9 @@ namespace JSC { namespace DFG { void VariableEventStream::logEvent(const VariableEvent& event) { - dataLog("seq#%u:", static_cast<unsigned>(size())); + dataLogF("seq#%u:", static_cast<unsigned>(size())); event.dump(WTF::dataFile()); - dataLog(" "); + dataLogF(" "); } struct MinifiedGenerationInfo { @@ -103,7 +103,7 @@ void VariableEventStream::reconstruct( startIndex--; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Computing OSR exit recoveries starting at seq#%u.\n", startIndex); + dataLogF("Computing OSR exit recoveries starting at seq#%u.\n", startIndex); #endif // Step 2: Create a mock-up of the DFG's state and execute the events. diff --git a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp index 86b33835d..eb3232e69 100644 --- a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp @@ -43,9 +43,9 @@ public: bool run() { #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Preserved vars: "); + dataLogF("Preserved vars: "); m_graph.m_preservedVars.dump(WTF::dataFile()); - dataLog("\n"); + dataLogF("\n"); #endif ScoreBoard scoreBoard(m_graph, m_graph.m_preservedVars); scoreBoard.assertClear(); @@ -62,8 +62,8 @@ public: NodeIndex nodeIndex = block->at(indexInBlock); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (needsNewLine) - dataLog("\n"); - dataLog(" @%u:", nodeIndex); + dataLogF("\n"); + dataLogF(" @%u:", nodeIndex); needsNewLine = true; #endif Node& node = m_graph[nodeIndex]; @@ -92,7 +92,7 @@ public: VirtualRegister virtualRegister = scoreBoard.allocate(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) - dataLog(" Assigning virtual register %u to node %u.", + dataLogF(" Assigning virtual register %u to node %u.", virtualRegister, nodeIndex); #endif node.setVirtualRegister(virtualRegister); @@ -105,7 +105,7 @@ public: } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (needsNewLine) - dataLog("\n"); + dataLogF("\n"); #endif // 'm_numCalleeRegisters' is the number of locals and temporaries allocated @@ -123,7 +123,7 @@ public: if ((unsigned)codeBlock()->m_numCalleeRegisters < calleeRegisters) codeBlock()->m_numCalleeRegisters = calleeRegisters; #if DFG_ENABLE(DEBUG_VERBOSE) - dataLog("Num callee registers: %u\n", calleeRegisters); + dataLogF("Num callee registers: %u\n", calleeRegisters); #endif return true; |
