// We don't expose this because we don't want anyone relying on the fact that this method currently // just returns string constants. static const char* speculationToAbbreviatedString(SpeculatedType prediction) { if (isFinalObjectSpeculation(prediction)) return "<Final>"; if (isArraySpeculation(prediction)) return "<Array>"; if (isStringIdentSpeculation(prediction)) return "<StringIdent>"; if (isStringSpeculation(prediction)) return "<String>"; if (isFunctionSpeculation(prediction)) return "<Function>"; if (isInt8ArraySpeculation(prediction)) return "<Int8array>"; if (isInt16ArraySpeculation(prediction)) return "<Int16array>"; if (isInt32ArraySpeculation(prediction)) return "<Int32array>"; if (isUint8ArraySpeculation(prediction)) return "<Uint8array>"; if (isUint16ArraySpeculation(prediction)) return "<Uint16array>"; if (isUint32ArraySpeculation(prediction)) return "<Uint32array>"; if (isFloat32ArraySpeculation(prediction)) return "<Float32array>"; if (isFloat64ArraySpeculation(prediction)) return "<Float64array>"; if (isArgumentsSpeculation(prediction)) return "<Arguments>"; if (isStringObjectSpeculation(prediction)) return "<StringObject>"; if (isStringOrStringObjectSpeculation(prediction)) return "<StringOrStringObject>"; if (isObjectSpeculation(prediction)) return "<Object>"; if (isCellSpeculation(prediction)) return "<Cell>"; if (isInt32Speculation(prediction)) return "<Int32>"; if (isInt52AsDoubleSpeculation(prediction)) return "<Int52AsDouble>"; if (isInt52Speculation(prediction)) return "<Int52>"; if (isMachineIntSpeculation(prediction)) return "<MachineInt>"; if (isDoubleSpeculation(prediction)) return "<Double>"; if (isFullNumberSpeculation(prediction)) return "<Number>"; if (isBooleanSpeculation(prediction)) return "<Boolean>"; if (isOtherSpeculation(prediction)) return "<Other>"; if (isMiscSpeculation(prediction)) return "<Misc>"; return ""; }
void fixupNode(Node& node) { if (!node.shouldGenerate()) return; NodeType op = node.op(); #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex); #endif switch (op) { case GetById: { if (!isInt32Speculation(m_graph[m_compileIndex].prediction())) break; if (codeBlock()->identifier(node.identifierNumber()) != globalData().propertyNames->length) break; bool isArray = isArraySpeculation(m_graph[node.child1()].prediction()); bool isArguments = isArgumentsSpeculation(m_graph[node.child1()].prediction()); bool isString = isStringSpeculation(m_graph[node.child1()].prediction()); bool isInt8Array = m_graph[node.child1()].shouldSpeculateInt8Array(); bool isInt16Array = m_graph[node.child1()].shouldSpeculateInt16Array(); bool isInt32Array = m_graph[node.child1()].shouldSpeculateInt32Array(); bool isUint8Array = m_graph[node.child1()].shouldSpeculateUint8Array(); bool isUint8ClampedArray = m_graph[node.child1()].shouldSpeculateUint8ClampedArray(); bool isUint16Array = m_graph[node.child1()].shouldSpeculateUint16Array(); bool isUint32Array = m_graph[node.child1()].shouldSpeculateUint32Array(); bool isFloat32Array = m_graph[node.child1()].shouldSpeculateFloat32Array(); bool isFloat64Array = m_graph[node.child1()].shouldSpeculateFloat64Array(); if (!isArray && !isArguments && !isString && !isInt8Array && !isInt16Array && !isInt32Array && !isUint8Array && !isUint8ClampedArray && !isUint16Array && !isUint32Array && !isFloat32Array && !isFloat64Array) break; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog(" @%u -> %s", m_compileIndex, isArray ? "GetArrayLength" : "GetStringLength"); #endif if (isArray) { node.setOp(GetArrayLength); ASSERT(node.flags() & NodeMustGenerate); node.clearFlags(NodeMustGenerate); m_graph.deref(m_compileIndex); ArrayProfile* arrayProfile = m_graph.baselineCodeBlockFor(node.codeOrigin)->getArrayProfile( node.codeOrigin.bytecodeIndex); if (!arrayProfile) break; arrayProfile->computeUpdatedPrediction(); if (!arrayProfile->hasDefiniteStructure()) break; m_graph.ref(node.child1()); Node checkStructure(CheckStructure, node.codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), node.child1().index()); checkStructure.ref(); NodeIndex checkStructureIndex = m_graph.size(); m_graph.append(checkStructure); m_insertionSet.append(m_indexInBlock, checkStructureIndex); break; } if (isArguments) node.setOp(GetArgumentsLength); else if (isString) node.setOp(GetStringLength); else if (isInt8Array) node.setOp(GetInt8ArrayLength); else if (isInt16Array) node.setOp(GetInt16ArrayLength); else if (isInt32Array) node.setOp(GetInt32ArrayLength); else if (isUint8Array) node.setOp(GetUint8ArrayLength); else if (isUint8ClampedArray) node.setOp(GetUint8ClampedArrayLength); else if (isUint16Array) node.setOp(GetUint16ArrayLength); else if (isUint32Array) node.setOp(GetUint32ArrayLength); else if (isFloat32Array) node.setOp(GetFloat32ArrayLength); else if (isFloat64Array) node.setOp(GetFloat64ArrayLength); else ASSERT_NOT_REACHED(); // No longer MustGenerate ASSERT(node.flags() & NodeMustGenerate); node.clearFlags(NodeMustGenerate); m_graph.deref(m_compileIndex); break; } case GetIndexedPropertyStorage: { if (!m_graph[node.child1()].prediction() || !m_graph[node.child2()].shouldSpeculateInteger() || m_graph[node.child1()].shouldSpeculateArguments()) { node.setOpAndDefaultFlags(Nop); m_graph.clearAndDerefChild1(node); m_graph.clearAndDerefChild2(node); m_graph.clearAndDerefChild3(node); node.setRefCount(0); } break; } case GetByVal: case StringCharAt: case StringCharCodeAt: { if (!!node.child3() && m_graph[node.child3()].op() == Nop) node.children.child3() = Edge(); break; } case ValueToInt32: { if (m_graph[node.child1()].shouldSpeculateNumber() && node.mustGenerate()) { node.clearFlags(NodeMustGenerate); m_graph.deref(m_compileIndex); } break; } case BitAnd: case BitOr: case BitXor: case BitRShift: case BitLShift: case BitURShift: { fixIntEdge(node.children.child1()); fixIntEdge(node.children.child2()); break; } case CompareEq: case CompareLess: case CompareLessEq: case CompareGreater: case CompareGreaterEq: case CompareStrictEq: { if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()])) break; if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) break; fixDoubleEdge(0); fixDoubleEdge(1); break; } case LogicalNot: { if (m_graph[node.child1()].shouldSpeculateInteger()) break; if (!m_graph[node.child1()].shouldSpeculateNumber()) break; fixDoubleEdge(0); break; } case Branch: { if (!m_graph[node.child1()].shouldSpeculateInteger() && m_graph[node.child1()].shouldSpeculateNumber()) fixDoubleEdge(0); Node& myNode = m_graph[m_compileIndex]; // reload because the graph may have changed Edge logicalNotEdge = myNode.child1(); Node& logicalNot = m_graph[logicalNotEdge]; if (logicalNot.op() == LogicalNot && logicalNot.adjustedRefCount() == 1) { Edge newChildEdge = logicalNot.child1(); if (m_graph[newChildEdge].hasBooleanResult()) { m_graph.ref(newChildEdge); m_graph.deref(logicalNotEdge); myNode.children.setChild1(newChildEdge); BlockIndex toBeTaken = myNode.notTakenBlockIndex(); BlockIndex toBeNotTaken = myNode.takenBlockIndex(); myNode.setTakenBlockIndex(toBeTaken); myNode.setNotTakenBlockIndex(toBeNotTaken); } } break; } case SetLocal: { if (node.variableAccessData()->isCaptured()) break; if (!node.variableAccessData()->shouldUseDoubleFormat()) break; fixDoubleEdge(0); break; } case ArithAdd: case ValueAdd: { if (m_graph.addShouldSpeculateInteger(node)) break; if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) break; fixDoubleEdge(0); fixDoubleEdge(1); break; } case ArithSub: { if (m_graph.addShouldSpeculateInteger(node) && node.canSpeculateInteger()) break; fixDoubleEdge(0); fixDoubleEdge(1); break; } case ArithNegate: { if (m_graph.negateShouldSpeculateInteger(node)) break; fixDoubleEdge(0); break; } case ArithMin: case ArithMax: case ArithMod: { if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) break; fixDoubleEdge(0); fixDoubleEdge(1); break; } case ArithMul: { if (m_graph.mulShouldSpeculateInteger(node)) break; fixDoubleEdge(0); fixDoubleEdge(1); break; } case ArithDiv: { if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) { if (isX86()) break; fixDoubleEdge(0); fixDoubleEdge(1); Node& oldDivision = m_graph[m_compileIndex]; Node newDivision = oldDivision; newDivision.setRefCount(2); newDivision.predict(SpecDouble); NodeIndex newDivisionIndex = m_graph.size(); oldDivision.setOp(DoubleAsInt32); oldDivision.children.initialize(Edge(newDivisionIndex, DoubleUse), Edge(), Edge()); m_graph.append(newDivision); m_insertionSet.append(m_indexInBlock, newDivisionIndex); break; } fixDoubleEdge(0); fixDoubleEdge(1); break; } case ArithAbs: { if (m_graph[node.child1()].shouldSpeculateInteger() && node.canSpeculateInteger()) break; fixDoubleEdge(0); break; } case ArithSqrt: { fixDoubleEdge(0); break; } case PutByVal: case PutByValSafe: { Edge child1 = m_graph.varArgChild(node, 0); Edge child2 = m_graph.varArgChild(node, 1); Edge child3 = m_graph.varArgChild(node, 2); if (!m_graph[child1].prediction() || !m_graph[child2].prediction()) break; if (!m_graph[child2].shouldSpeculateInteger()) break; if (isActionableIntMutableArraySpeculation(m_graph[child1].prediction())) { if (m_graph[child3].isConstant()) break; if (m_graph[child3].shouldSpeculateInteger()) break; fixDoubleEdge(2); break; } if (isActionableFloatMutableArraySpeculation(m_graph[child1].prediction())) { fixDoubleEdge(2); break; } break; } default: break; } #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) if (!(node.flags() & NodeHasVarArgs)) { dataLog("new children: "); node.dumpChildren(WTF::dataFile()); } dataLog("\n"); #endif }
ArrayMode ArrayMode::refine( Graph& graph, CodeOrigin codeOrigin, SpeculatedType base, SpeculatedType index, SpeculatedType value, NodeFlags flags) const { if (!base || !index) { // It can be that we had a legitimate arrayMode but no incoming predictions. That'll // happen if we inlined code based on, say, a global variable watchpoint, but later // realized that the callsite could not have possibly executed. It may be worthwhile // to fix that, but for now I'm leaving it as-is. return ArrayMode(Array::ForceExit); } if (!isInt32Speculation(index)) return ArrayMode(Array::Generic); // Note: our profiling currently doesn't give us good information in case we have // an unlikely control flow path that sets the base to a non-cell value. Value // profiling and prediction propagation will probably tell us that the value is // either a cell or not, but that doesn't tell us which is more likely: that this // is an array access on a cell (what we want and can optimize) or that the user is // doing a crazy by-val access on a primitive (we can't easily optimize this and // don't want to). So, for now, we assume that if the base is not a cell according // to value profiling, but the array profile tells us something else, then we // should just trust the array profile. switch (type()) { case Array::Unprofiled: return ArrayMode(Array::ForceExit); case Array::Undecided: if (!value) return withType(Array::ForceExit); if (isInt32Speculation(value)) return withTypeAndConversion(Array::Int32, Array::Convert); if (isFullNumberSpeculation(value)) return withTypeAndConversion(Array::Double, Array::Convert); return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Int32: if (!value || isInt32Speculation(value)) return *this; if (isFullNumberSpeculation(value)) return withTypeAndConversion(Array::Double, Array::Convert); return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Double: if (flags & NodeBytecodeUsesAsInt) return withTypeAndConversion(Array::Contiguous, Array::RageConvert); if (!value || isFullNumberSpeculation(value)) return *this; return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Contiguous: if (doesConversion() && (flags & NodeBytecodeUsesAsInt)) return withConversion(Array::RageConvert); return *this; case Array::SelectUsingPredictions: { base &= ~SpecOther; if (isStringSpeculation(base)) return withType(Array::String); if (isArgumentsSpeculation(base)) return withType(Array::Arguments); ArrayMode result; if (graph.hasExitSite(codeOrigin, OutOfBounds) || !isInBounds()) result = withSpeculation(Array::OutOfBounds); else result = withSpeculation(Array::InBounds); if (isInt8ArraySpeculation(base)) return result.withType(Array::Int8Array); if (isInt16ArraySpeculation(base)) return result.withType(Array::Int16Array); if (isInt32ArraySpeculation(base)) return result.withType(Array::Int32Array); if (isUint8ArraySpeculation(base)) return result.withType(Array::Uint8Array); if (isUint8ClampedArraySpeculation(base)) return result.withType(Array::Uint8ClampedArray); if (isUint16ArraySpeculation(base)) return result.withType(Array::Uint16Array); if (isUint32ArraySpeculation(base)) return result.withType(Array::Uint32Array); if (isFloat32ArraySpeculation(base)) return result.withType(Array::Float32Array); if (isFloat64ArraySpeculation(base)) return result.withType(Array::Float64Array); return ArrayMode(Array::Generic); } default: return *this; } }
ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value, NodeFlags flags) const { if (!base || !index) { // It can be that we had a legitimate arrayMode but no incoming predictions. That'll // happen if we inlined code based on, say, a global variable watchpoint, but later // realized that the callsite could not have possibly executed. It may be worthwhile // to fix that, but for now I'm leaving it as-is. return ArrayMode(Array::ForceExit); } if (!isInt32Speculation(index) || !isCellSpeculation(base)) return ArrayMode(Array::Generic); switch (type()) { case Array::Unprofiled: return ArrayMode(Array::ForceExit); 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 (flags & NodeUsedAsIntLocally) return withTypeAndConversion(Array::Contiguous, Array::RageConvert); if (!value || isNumberSpeculation(value)) return *this; return withTypeAndConversion(Array::Contiguous, Array::Convert); case Array::Contiguous: if (doesConversion() && (flags & NodeUsedAsIntLocally)) return withConversion(Array::RageConvert); return *this; 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; } }