diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h | 2795 |
1 files changed, 2795 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h new file mode 100644 index 000000000..338a90381 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h @@ -0,0 +1,2795 @@ +/* + * Copyright (C) 2013-2016 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 DFGAbstractInterpreterInlines_h +#define DFGAbstractInterpreterInlines_h + +#if ENABLE(DFG_JIT) + +#include "DFGAbstractInterpreter.h" +#include "GetByIdStatus.h" +#include "GetterSetter.h" +#include "JITOperations.h" +#include "MathCommon.h" +#include "Operations.h" +#include "PutByIdStatus.h" +#include "StringObject.h" + +namespace JSC { namespace DFG { + +template<typename AbstractStateType> +AbstractInterpreter<AbstractStateType>::AbstractInterpreter(Graph& graph, AbstractStateType& state) + : m_codeBlock(graph.m_codeBlock) + , m_graph(graph) + , m_state(state) +{ + if (m_graph.m_form == SSA) + m_phiChildren = std::make_unique<PhiChildren>(m_graph); +} + +template<typename AbstractStateType> +AbstractInterpreter<AbstractStateType>::~AbstractInterpreter() +{ +} + +template<typename AbstractStateType> +typename AbstractInterpreter<AbstractStateType>::BooleanResult +AbstractInterpreter<AbstractStateType>::booleanResult( + Node* node, AbstractValue& value) +{ + JSValue childConst = value.value(); + if (childConst) { + if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->origin.semantic)->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_structure.isTop()) { + bool allTrue = true; + for (unsigned i = value.m_structure.size(); i--;) { + Structure* structure = value.m_structure[i]; + if (structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) + || structure->typeInfo().type() == StringType) { + allTrue = false; + break; + } + } + if (allTrue) + return DefinitelyTrue; + } + + return UnknownBooleanResult; +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::startExecuting() +{ + ASSERT(m_state.block()); + ASSERT(m_state.isValid()); + + m_state.setDidClobber(false); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::executeEdges(Node* node) +{ + DFG_NODE_DO_TO_CHILDREN(m_graph, node, filterEdgeByUse); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::executeEdges(unsigned indexInBlock) +{ + executeEdges(m_state.block()->at(indexInBlock)); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::verifyEdge(Node* node, Edge edge) +{ + // Some use kinds are required to not have checks, because we know somehow that the incoming + // value will already have the type we want. In those cases, AI may not be smart enough to + // prove that this is indeed the case. + if (shouldNotHaveTypeCheck(edge.useKind())) + return; + + if (!(forNode(edge).m_type & ~typeFilterFor(edge.useKind()))) + return; + + DFG_CRASH(m_graph, node, toCString("Edge verification error: ", node, "->", edge, " was expected to have type ", SpeculationDump(typeFilterFor(edge.useKind())), " but has type ", SpeculationDump(forNode(edge).m_type), " (", forNode(edge).m_type, ")").data()); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::verifyEdges(Node* node) +{ + DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge); +} + +template<typename AbstractStateType> +bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node) +{ + if (!ASSERT_DISABLED) + verifyEdges(node); + + m_state.createValueForNode(node); + + switch (node->op()) { + case JSConstant: + case DoubleConstant: + case Int52Constant: { + setBuiltInConstant(node, *node->constant()); + break; + } + + case Identity: { + forNode(node) = forNode(node->child1()); + if (forNode(node).value()) + m_state.setFoundConstants(true); + break; + } + + case ExtractOSREntryLocal: { + forNode(node).makeBytecodeTop(); + break; + } + + case GetLocal: { + VariableAccessData* variableAccessData = node->variableAccessData(); + AbstractValue value = m_state.variables().operand(variableAccessData->local().offset()); + // The value in the local should already be checked. + DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(variableAccessData->flushFormat()))); + if (value.value()) + m_state.setFoundConstants(true); + forNode(node) = value; + break; + } + + case GetStack: { + StackAccessData* data = node->stackAccessData(); + AbstractValue value = m_state.variables().operand(data->local); + // The value in the local should already be checked. + DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(data->format))); + if (value.value()) + m_state.setFoundConstants(true); + forNode(node) = value; + break; + } + + case GetLocalUnlinked: { + AbstractValue value = m_state.variables().operand(node->unlinkedLocal().offset()); + if (value.value()) + m_state.setFoundConstants(true); + forNode(node) = value; + break; + } + + case SetLocal: { + m_state.variables().operand(node->local()) = forNode(node->child1()); + break; + } + + case PutStack: { + m_state.variables().operand(node->stackAccessData()->local) = forNode(node->child1()); + break; + } + + case MovHint: { + // Don't need to do anything. A MovHint only informs us about what would have happened + // in bytecode, but this code is just concerned with what is actually happening during + // DFG execution. + break; + } + + case KillStack: { + // This is just a hint telling us that the OSR state of the local is no longer inside the + // flushed data. + break; + } + + case SetArgument: + // Assert that the state of arguments has been set. SetArgument means that someone set + // the argument values out-of-band, and currently this always means setting to a + // non-clear value. + ASSERT(!m_state.variables().operand(node->local()).isClear()); + break; + + case LoadVarargs: + case ForwardVarargs: { + // FIXME: ForwardVarargs should check if the count becomes known, and if it does, it should turn + // itself into a straight-line sequence of GetStack/PutStack. + // https://bugs.webkit.org/show_bug.cgi?id=143071 + clobberWorld(node->origin.semantic, clobberLimit); + LoadVarargsData* data = node->loadVarargsData(); + m_state.variables().operand(data->count).setType(SpecInt32); + for (unsigned i = data->limit - 1; i--;) + m_state.variables().operand(data->start.offset() + i).makeHeapTop(); + break; + } + + case BitAnd: + case BitOr: + case BitXor: + case BitRShift: + case BitLShift: + case BitURShift: { + if (node->child1().useKind() == UntypedUse || node->child2().useKind() == UntypedUse) { + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(m_graph, SpecInt32); + break; + } + + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + if (left && right && left.isInt32() && right.isInt32()) { + int32_t a = left.asInt32(); + int32_t b = right.asInt32(); + switch (node->op()) { + case BitAnd: + setConstant(node, JSValue(a & b)); + break; + case BitOr: + setConstant(node, JSValue(a | b)); + break; + case BitXor: + setConstant(node, JSValue(a ^ b)); + break; + case BitRShift: + setConstant(node, JSValue(a >> static_cast<uint32_t>(b))); + break; + case BitLShift: + setConstant(node, JSValue(a << static_cast<uint32_t>(b))); + break; + case BitURShift: + setConstant(node, JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b))); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + if (node->op() == BitAnd + && (isBoolInt32Speculation(forNode(node->child1()).m_type) || + isBoolInt32Speculation(forNode(node->child2()).m_type))) { + forNode(node).setType(SpecBoolInt32); + break; + } + + forNode(node).setType(SpecInt32); + break; + } + + case UInt32ToNumber: { + JSValue child = forNode(node->child1()).value(); + if (doesOverflow(node->arithMode())) { + if (child && child.isInt32()) { + uint32_t value = child.asInt32(); + setConstant(node, jsNumber(value)); + break; + } + forNode(node).setType(SpecInt52AsDouble); + break; + } + if (child && child.isInt32()) { + int32_t value = child.asInt32(); + if (value >= 0) { + setConstant(node, jsNumber(value)); + break; + } + } + forNode(node).setType(SpecInt32); + break; + } + + case BooleanToNumber: { + JSValue concreteValue = forNode(node->child1()).value(); + if (concreteValue) { + if (concreteValue.isBoolean()) + setConstant(node, jsNumber(concreteValue.asBoolean())); + else + setConstant(node, *m_graph.freeze(concreteValue)); + break; + } + AbstractValue& value = forNode(node); + value = forNode(node->child1()); + if (node->child1().useKind() == UntypedUse && !(value.m_type & ~SpecBoolean)) + m_state.setFoundConstants(true); + if (value.m_type & SpecBoolean) { + value.merge(SpecBoolInt32); + value.filter(~SpecBoolean); + } + break; + } + + case DoubleAsInt32: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + double asDouble = child.asNumber(); + int32_t asInt = JSC::toInt32(asDouble); + if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble)) { + setConstant(node, JSValue(asInt)); + break; + } + } + forNode(node).setType(SpecInt32); + break; + } + + case ValueToInt32: { + JSValue child = forNode(node->child1()).value(); + if (child) { + if (child.isNumber()) { + if (child.isInt32()) + setConstant(node, child); + else + setConstant(node, JSValue(JSC::toInt32(child.asDouble()))); + break; + } + if (child.isBoolean()) { + setConstant(node, jsNumber(child.asBoolean())); + break; + } + if (child.isUndefinedOrNull()) { + setConstant(node, jsNumber(0)); + break; + } + } + + if (isBooleanSpeculation(forNode(node->child1()).m_type)) { + forNode(node).setType(SpecBoolInt32); + break; + } + + forNode(node).setType(SpecInt32); + break; + } + + case DoubleRep: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(child.asNumber())); + break; + } + + SpeculatedType type = forNode(node->child1()).m_type; + switch (node->child1().useKind()) { + case NotCellUse: { + if (type & SpecOther) { + type &= ~SpecOther; + type |= SpecDoublePureNaN | SpecBoolInt32; // Null becomes zero, undefined becomes NaN. + } + if (type & SpecBoolean) { + type &= ~SpecBoolean; + type |= SpecBoolInt32; // True becomes 1, false becomes 0. + } + type &= SpecBytecodeNumber; + break; + } + + case Int52RepUse: + case NumberUse: + case RealNumberUse: + break; + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + forNode(node).setType(type); + forNode(node).fixTypeForRepresentation(m_graph, node); + break; + } + + case Int52Rep: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isMachineInt()) { + setConstant(node, child); + break; + } + + forNode(node).setType(SpecInt32); + break; + } + + case ValueRep: { + JSValue value = forNode(node->child1()).value(); + if (value) { + setConstant(node, value); + break; + } + + forNode(node).setType(m_graph, forNode(node->child1()).m_type & ~SpecDoubleImpureNaN); + forNode(node).fixTypeForRepresentation(m_graph, node); + break; + } + + case ValueAdd: { + ASSERT(node->binaryUseKind() == UntypedUse); + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(m_graph, SpecString | SpecBytecodeNumber); + break; + } + + case StrCat: { + forNode(node).setType(m_graph, SpecString); + break; + } + + case ArithAdd: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + if (!shouldCheckOverflow(node->arithMode())) { + setConstant(node, jsNumber(left.asInt32() + right.asInt32())); + break; + } + JSValue result = jsNumber(left.asNumber() + right.asNumber()); + if (result.isInt32()) { + setConstant(node, result); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case Int52RepUse: + if (left && right && left.isMachineInt() && right.isMachineInt()) { + JSValue result = jsNumber(left.asMachineInt() + right.asMachineInt()); + if (result.isMachineInt()) { + setConstant(node, result); + break; + } + } + forNode(node).setType(SpecMachineInt); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + setConstant(node, jsDoubleNumber(left.asNumber() + right.asNumber())); + break; + } + forNode(node).setType( + typeOfDoubleSum( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithClz32: { + JSValue operand = forNode(node->child1()).value(); + if (operand && operand.isNumber()) { + uint32_t value = toUInt32(operand.asNumber()); + setConstant(node, jsNumber(clz32(value))); + break; + } + forNode(node).setType(SpecInt32); + break; + } + + case MakeRope: { + forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); + break; + } + + case ArithSub: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + if (!shouldCheckOverflow(node->arithMode())) { + setConstant(node, jsNumber(left.asInt32() - right.asInt32())); + break; + } + JSValue result = jsNumber(left.asNumber() - right.asNumber()); + if (result.isInt32()) { + setConstant(node, result); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case Int52RepUse: + if (left && right && left.isMachineInt() && right.isMachineInt()) { + JSValue result = jsNumber(left.asMachineInt() - right.asMachineInt()); + if (result.isMachineInt() || !shouldCheckOverflow(node->arithMode())) { + setConstant(node, result); + break; + } + } + forNode(node).setType(SpecMachineInt); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + setConstant(node, jsDoubleNumber(left.asNumber() - right.asNumber())); + break; + } + forNode(node).setType( + typeOfDoubleDifference( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + case UntypedUse: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(m_graph, SpecBytecodeNumber); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithNegate: { + JSValue child = forNode(node->child1()).value(); + switch (node->child1().useKind()) { + case Int32Use: + if (child && child.isInt32()) { + if (!shouldCheckOverflow(node->arithMode())) { + setConstant(node, jsNumber(-child.asInt32())); + break; + } + double doubleResult; + if (shouldCheckNegativeZero(node->arithMode())) + doubleResult = -child.asNumber(); + else + doubleResult = 0 - child.asNumber(); + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isInt32()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case Int52RepUse: + if (child && child.isMachineInt()) { + double doubleResult; + if (shouldCheckNegativeZero(node->arithMode())) + doubleResult = -child.asNumber(); + else + doubleResult = 0 - child.asNumber(); + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isMachineInt()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecMachineInt); + break; + case DoubleRepUse: + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(-child.asNumber())); + break; + } + forNode(node).setType( + typeOfDoubleNegation( + forNode(node->child1()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithMul: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + if (!shouldCheckOverflow(node->arithMode())) { + setConstant(node, jsNumber(left.asInt32() * right.asInt32())); + break; + } + double doubleResult = left.asNumber() * right.asNumber(); + if (!shouldCheckNegativeZero(node->arithMode())) + doubleResult += 0; // Sanitizes zero. + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isInt32()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case Int52RepUse: + if (left && right && left.isMachineInt() && right.isMachineInt()) { + double doubleResult = left.asNumber() * right.asNumber(); + if (!shouldCheckNegativeZero(node->arithMode())) + doubleResult += 0; + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isMachineInt()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecMachineInt); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + setConstant(node, jsDoubleNumber(left.asNumber() * right.asNumber())); + break; + } + forNode(node).setType( + typeOfDoubleProduct( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + case UntypedUse: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(m_graph, SpecBytecodeNumber); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithDiv: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + double doubleResult = left.asNumber() / right.asNumber(); + if (!shouldCheckOverflow(node->arithMode())) + doubleResult = toInt32(doubleResult); + else if (!shouldCheckNegativeZero(node->arithMode())) + doubleResult += 0; // Sanitizes zero. + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isInt32()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber())); + break; + } + forNode(node).setType( + typeOfDoubleQuotient( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + case UntypedUse: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(m_graph, SpecBytecodeNumber); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithMod: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + double doubleResult = fmod(left.asNumber(), right.asNumber()); + if (!shouldCheckOverflow(node->arithMode())) + doubleResult = toInt32(doubleResult); + else if (!shouldCheckNegativeZero(node->arithMode())) + doubleResult += 0; // Sanitizes zero. + JSValue valueResult = jsNumber(doubleResult); + if (valueResult.isInt32()) { + setConstant(node, valueResult); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber()))); + break; + } + forNode(node).setType( + typeOfDoubleBinaryOp( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithMin: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + setConstant(node, jsNumber(std::min(left.asInt32(), right.asInt32()))); + break; + } + forNode(node).setType(SpecInt32); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + double a = left.asNumber(); + double b = right.asNumber(); + setConstant(node, jsDoubleNumber(a < b ? a : (b <= a ? b : a + b))); + break; + } + forNode(node).setType( + typeOfDoubleMinMax( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithMax: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + switch (node->binaryUseKind()) { + case Int32Use: + if (left && right && left.isInt32() && right.isInt32()) { + setConstant(node, jsNumber(std::max(left.asInt32(), right.asInt32()))); + break; + } + forNode(node).setType(SpecInt32); + break; + case DoubleRepUse: + if (left && right && left.isNumber() && right.isNumber()) { + double a = left.asNumber(); + double b = right.asNumber(); + setConstant(node, jsDoubleNumber(a > b ? a : (b >= a ? b : a + b))); + break; + } + forNode(node).setType( + typeOfDoubleMinMax( + forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithAbs: { + JSValue child = forNode(node->child1()).value(); + switch (node->child1().useKind()) { + case Int32Use: + if (child && child.isInt32()) { + JSValue result = jsNumber(fabs(child.asNumber())); + if (result.isInt32()) { + setConstant(node, result); + break; + } + } + forNode(node).setType(SpecInt32); + break; + case DoubleRepUse: + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(fabs(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleAbs(forNode(node->child1()).m_type)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case ArithPow: { + JSValue childY = forNode(node->child2()).value(); + if (childY && childY.isNumber()) { + if (!childY.asNumber()) { + setConstant(node, jsDoubleNumber(1)); + break; + } + + JSValue childX = forNode(node->child1()).value(); + if (childX && childX.isNumber()) { + setConstant(node, jsDoubleNumber(operationMathPow(childX.asNumber(), childY.asNumber()))); + break; + } + } + forNode(node).setType(typeOfDoublePow(forNode(node->child1()).m_type, forNode(node->child2()).m_type)); + break; + } + + case ArithRandom: { + forNode(node).setType(m_graph, SpecDoubleReal); + break; + } + + case ArithRound: + case ArithFloor: + case ArithCeil: { + JSValue operand = forNode(node->child1()).value(); + if (operand && operand.isNumber()) { + double roundedValue = 0; + if (node->op() == ArithRound) + roundedValue = jsRound(operand.asNumber()); + else if (node->op() == ArithFloor) + roundedValue = floor(operand.asNumber()); + else { + ASSERT(node->op() == ArithCeil); + roundedValue = ceil(operand.asNumber()); + } + + if (producesInteger(node->arithRoundingMode())) { + int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue); + if (roundedValueAsInt32 == roundedValue) { + if (shouldCheckNegativeZero(node->arithRoundingMode())) { + if (roundedValueAsInt32 || !std::signbit(roundedValue)) { + setConstant(node, jsNumber(roundedValueAsInt32)); + break; + } + } else { + setConstant(node, jsNumber(roundedValueAsInt32)); + break; + } + } + } else { + setConstant(node, jsDoubleNumber(roundedValue)); + break; + } + } + if (producesInteger(node->arithRoundingMode())) + forNode(node).setType(SpecInt32); + else + forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type)); + break; + } + + case ArithSqrt: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(sqrt(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type)); + break; + } + + case ArithFRound: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(static_cast<float>(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type)); + break; + } + + case ArithSin: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(sin(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type)); + break; + } + + case ArithCos: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(cos(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type)); + break; + } + + case ArithLog: { + JSValue child = forNode(node->child1()).value(); + if (child && child.isNumber()) { + setConstant(node, jsDoubleNumber(log(child.asNumber()))); + break; + } + forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type)); + break; + } + + case LogicalNot: { + switch (booleanResult(node, forNode(node->child1()))) { + case DefinitelyTrue: + setConstant(node, jsBoolean(false)); + break; + case DefinitelyFalse: + setConstant(node, jsBoolean(true)); + break; + default: + forNode(node).setType(SpecBoolean); + break; + } + break; + } + + case IsUndefined: + case IsBoolean: + case IsNumber: + case IsString: + case IsObject: + case IsObjectOrNull: + case IsFunction: { + AbstractValue child = forNode(node->child1()); + if (child.value()) { + bool constantWasSet = true; + switch (node->op()) { + case IsUndefined: + setConstant(node, jsBoolean( + child.value().isCell() + ? child.value().asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) + : child.value().isUndefined())); + break; + case IsBoolean: + setConstant(node, jsBoolean(child.value().isBoolean())); + break; + case IsNumber: + setConstant(node, jsBoolean(child.value().isNumber())); + break; + case IsString: + setConstant(node, jsBoolean(isJSString(child.value()))); + break; + case IsObject: + setConstant(node, jsBoolean(child.value().isObject())); + break; + case IsObjectOrNull: + if (child.value().isObject()) { + JSObject* object = asObject(child.value()); + if (object->type() == JSFunctionType) + setConstant(node, jsBoolean(false)); + else if (!(object->inlineTypeFlags() & TypeOfShouldCallGetCallData)) + setConstant(node, jsBoolean(!child.value().asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)))); + else { + // FIXME: This could just call getCallData. + // https://bugs.webkit.org/show_bug.cgi?id=144457 + constantWasSet = false; + } + } else + setConstant(node, jsBoolean(child.value().isNull())); + break; + case IsFunction: + if (child.value().isObject()) { + JSObject* object = asObject(child.value()); + if (object->type() == JSFunctionType) + setConstant(node, jsBoolean(true)); + else if (!(object->inlineTypeFlags() & TypeOfShouldCallGetCallData)) + setConstant(node, jsBoolean(false)); + else { + // FIXME: This could just call getCallData. + // https://bugs.webkit.org/show_bug.cgi?id=144457 + constantWasSet = false; + } + } else + setConstant(node, jsBoolean(false)); + break; + default: + constantWasSet = false; + break; + } + if (constantWasSet) + break; + } + + // FIXME: This code should really use AbstractValue::isType() and + // AbstractValue::couldBeType(). + // https://bugs.webkit.org/show_bug.cgi?id=146870 + + bool constantWasSet = false; + switch (node->op()) { + case IsUndefined: + // FIXME: Use the masquerades-as-undefined watchpoint thingy. + // https://bugs.webkit.org/show_bug.cgi?id=144456 + + if (!(child.m_type & (SpecOther | SpecObjectOther))) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsBoolean: + if (!(child.m_type & ~SpecBoolean)) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + if (!(child.m_type & SpecBoolean)) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsNumber: + if (!(child.m_type & ~SpecFullNumber)) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + if (!(child.m_type & SpecFullNumber)) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsString: + if (!(child.m_type & ~SpecString)) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + if (!(child.m_type & SpecString)) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsObject: + if (!(child.m_type & ~SpecObject)) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + if (!(child.m_type & SpecObject)) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsObjectOrNull: + // FIXME: Use the masquerades-as-undefined watchpoint thingy. + // https://bugs.webkit.org/show_bug.cgi?id=144456 + + // These expressions are complicated to parse. A helpful way to parse this is that + // "!(T & ~S)" means "T is a subset of S". Conversely, "!(T & S)" means "T is a + // disjoint set from S". Things like "T - S" means that, provided that S is a + // subset of T, it's the "set of all things in T but not in S". Things like "T | S" + // mean the "union of T and S". + + // Is the child's type an object that isn't an other-object (i.e. object that could + // have masquaredes-as-undefined traps) and isn't a function? Then: we should fold + // this to true. + if (!(child.m_type & ~(SpecObject - SpecObjectOther - SpecFunction))) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + // Is the child's type definitely not either of: an object that isn't a function, + // or either undefined or null? Then: we should fold this to false. This means + // for example that if it's any non-function object, including those that have + // masquerades-as-undefined traps, then we don't fold. It also means we won't fold + // if it's undefined-or-null, since the type bits don't distinguish between + // undefined (which should fold to false) and null (which should fold to true). + if (!(child.m_type & ((SpecObject - SpecFunction) | SpecOther))) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + + break; + case IsFunction: + if (!(child.m_type & ~SpecFunction)) { + setConstant(node, jsBoolean(true)); + constantWasSet = true; + break; + } + + if (!(child.m_type & (SpecFunction | SpecObjectOther))) { + setConstant(node, jsBoolean(false)); + constantWasSet = true; + break; + } + break; + default: + break; + } + if (constantWasSet) + break; + + forNode(node).setType(SpecBoolean); + break; + } + + case TypeOf: { + VM* vm = m_codeBlock->vm(); + JSValue child = forNode(node->child1()).value(); + AbstractValue& abstractChild = forNode(node->child1()); + if (child) { + JSValue typeString = jsTypeStringForValue(*vm, m_codeBlock->globalObjectFor(node->origin.semantic), child); + setConstant(node, *m_graph.freeze(typeString)); + break; + } + + if (isFullNumberSpeculation(abstractChild.m_type)) { + setConstant(node, *m_graph.freeze(vm->smallStrings.numberString())); + break; + } + + if (isStringSpeculation(abstractChild.m_type)) { + setConstant(node, *m_graph.freeze(vm->smallStrings.stringString())); + break; + } + + // FIXME: We could use the masquerades-as-undefined watchpoint here. + // https://bugs.webkit.org/show_bug.cgi?id=144456 + if (!(abstractChild.m_type & ~(SpecObject - SpecObjectOther))) { + setConstant(node, *m_graph.freeze(vm->smallStrings.objectString())); + break; + } + + if (isFunctionSpeculation(abstractChild.m_type)) { + setConstant(node, *m_graph.freeze(vm->smallStrings.functionString())); + break; + } + + if (isBooleanSpeculation(abstractChild.m_type)) { + setConstant(node, *m_graph.freeze(vm->smallStrings.booleanString())); + break; + } + + if (isSymbolSpeculation(abstractChild.m_type)) { + setConstant(node, *m_graph.freeze(vm->smallStrings.symbolString())); + break; + } + + forNode(node).setType(m_graph, SpecStringIdent); + break; + } + + case CompareLess: + case CompareLessEq: + case CompareGreater: + case CompareGreaterEq: + case CompareEq: { + JSValue leftConst = forNode(node->child1()).value(); + JSValue rightConst = forNode(node->child2()).value(); + if (leftConst && rightConst) { + if (leftConst.isNumber() && rightConst.isNumber()) { + double a = leftConst.asNumber(); + double b = rightConst.asNumber(); + switch (node->op()) { + case CompareLess: + setConstant(node, jsBoolean(a < b)); + break; + case CompareLessEq: + setConstant(node, jsBoolean(a <= b)); + break; + case CompareGreater: + setConstant(node, jsBoolean(a > b)); + break; + case CompareGreaterEq: + setConstant(node, jsBoolean(a >= b)); + break; + case CompareEq: + setConstant(node, jsBoolean(a == b)); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + if (node->op() == CompareEq && leftConst.isString() && rightConst.isString()) { + const StringImpl* a = asString(leftConst)->tryGetValueImpl(); + const StringImpl* b = asString(rightConst)->tryGetValueImpl(); + if (a && b) { + setConstant(node, jsBoolean(WTF::equal(a, b))); + break; + } + } + + if (node->op() == CompareEq && leftConst.isSymbol() && rightConst.isSymbol()) { + setConstant(node, jsBoolean(asSymbol(leftConst)->privateName() == asSymbol(rightConst)->privateName())); + break; + } + } + + if (node->op() == CompareEq) { + SpeculatedType leftType = forNode(node->child1()).m_type; + SpeculatedType rightType = forNode(node->child2()).m_type; + if (!valuesCouldBeEqual(leftType, rightType)) { + setConstant(node, jsBoolean(false)); + break; + } + + if (leftType == SpecOther) + std::swap(leftType, rightType); + if (rightType == SpecOther) { + // Undefined and Null are always equal when compared to eachother. + if (!(leftType & ~SpecOther)) { + setConstant(node, jsBoolean(true)); + break; + } + + // Any other type compared to Null or Undefined is always false + // as long as the MasqueradesAsUndefined watchpoint is valid. + // + // MasqueradesAsUndefined only matters for SpecObjectOther, other + // cases are always "false". + if (!(leftType & (SpecObjectOther | SpecOther))) { + setConstant(node, jsBoolean(false)); + break; + } + + if (!(leftType & SpecOther) && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->origin.semantic)) { + JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); + m_graph.watchpoints().addLazily(globalObject->masqueradesAsUndefinedWatchpoint()); + setConstant(node, jsBoolean(false)); + break; + } + } + } + + if (node->child1() == node->child2()) { + if (node->isBinaryUseKind(Int32Use) || + node->isBinaryUseKind(Int52RepUse) || + node->isBinaryUseKind(StringUse) || + node->isBinaryUseKind(BooleanUse) || + node->isBinaryUseKind(SymbolUse) || + node->isBinaryUseKind(StringIdentUse) || + node->isBinaryUseKind(ObjectUse) || + node->isBinaryUseKind(ObjectUse, ObjectOrOtherUse) || + node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse)) { + switch (node->op()) { + case CompareLess: + case CompareGreater: + setConstant(node, jsBoolean(false)); + break; + case CompareLessEq: + case CompareGreaterEq: + case CompareEq: + setConstant(node, jsBoolean(true)); + break; + default: + DFG_CRASH(m_graph, node, "Unexpected node type"); + break; + } + break; + } + } + + forNode(node).setType(SpecBoolean); + break; + } + + case CompareStrictEq: { + Node* leftNode = node->child1().node(); + Node* rightNode = node->child2().node(); + JSValue left = forNode(leftNode).value(); + JSValue right = forNode(rightNode).value(); + if (left && right) { + if (left.isString() && right.isString()) { + // We need this case because JSValue::strictEqual is otherwise too racy for + // string comparisons. + const StringImpl* a = asString(left)->tryGetValueImpl(); + const StringImpl* b = asString(right)->tryGetValueImpl(); + if (a && b) { + setConstant(node, jsBoolean(WTF::equal(a, b))); + break; + } + } else { + setConstant(node, jsBoolean(JSValue::strictEqual(0, left, right))); + break; + } + } + + SpeculatedType leftLUB = leastUpperBoundOfStrictlyEquivalentSpeculations(forNode(leftNode).m_type); + SpeculatedType rightLUB = leastUpperBoundOfStrictlyEquivalentSpeculations(forNode(rightNode).m_type); + if (!(leftLUB & rightLUB)) { + setConstant(node, jsBoolean(false)); + break; + } + + if (node->child1() == node->child2()) { + if (node->isBinaryUseKind(BooleanUse) || + node->isBinaryUseKind(Int32Use) || + node->isBinaryUseKind(Int52RepUse) || + node->isBinaryUseKind(StringUse) || + node->isBinaryUseKind(StringIdentUse) || + node->isBinaryUseKind(SymbolUse) || + node->isBinaryUseKind(ObjectUse) || + node->isBinaryUseKind(MiscUse, UntypedUse) || + node->isBinaryUseKind(UntypedUse, MiscUse) || + node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || + node->isBinaryUseKind(NotStringVarUse, StringIdentUse) || + node->isBinaryUseKind(StringUse, UntypedUse) || + node->isBinaryUseKind(UntypedUse, StringUse)) { + setConstant(node, jsBoolean(true)); + break; + } + } + + forNode(node).setType(SpecBoolean); + break; + } + + case StringCharCodeAt: + forNode(node).setType(SpecInt32); + break; + + case StringFromCharCode: + forNode(node).setType(m_graph, SpecString); + break; + + case StringCharAt: + forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); + break; + + case GetByVal: { + switch (node->arrayMode().type()) { + case Array::SelectUsingPredictions: + case Array::Unprofiled: + case Array::SelectUsingArguments: + RELEASE_ASSERT_NOT_REACHED(); + break; + case Array::ForceExit: + m_state.setIsValid(false); + break; + case Array::Undecided: { + JSValue index = forNode(node->child2()).value(); + if (index && index.isInt32() && index.asInt32() >= 0) { + setConstant(node, jsUndefined()); + break; + } + forNode(node).setType(SpecOther); + break; + } + case Array::Generic: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + case Array::String: + if (node->arrayMode().isOutOfBounds()) { + // If the watchpoint was still valid we could totally set this to be + // SpecString | SpecOther. Except that we'd have to be careful. If we + // tested the watchpoint state here then it could change by the time + // we got to the backend. So to do this right, we'd have to get the + // fixup phase to check the watchpoint state and then bake into the + // GetByVal operation the fact that we're using a watchpoint, using + // something like Array::SaneChain (except not quite, because that + // implies an in-bounds access). None of this feels like it's worth it, + // so we're going with TOP for now. The same thing applies to + // clobbering the world. + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + } else + forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); + break; + case Array::DirectArguments: + case Array::ScopedArguments: + forNode(node).makeHeapTop(); + break; + case Array::Int32: + if (node->arrayMode().isOutOfBounds()) { + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + } else + forNode(node).setType(SpecInt32); + break; + case Array::Double: + if (node->arrayMode().isOutOfBounds()) { + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + } else if (node->arrayMode().isSaneChain()) + forNode(node).setType(SpecBytecodeDouble); + else + forNode(node).setType(SpecDoubleReal); + break; + case Array::Contiguous: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: + if (node->arrayMode().isOutOfBounds()) + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + case Array::Int8Array: + forNode(node).setType(SpecInt32); + break; + case Array::Int16Array: + forNode(node).setType(SpecInt32); + break; + case Array::Int32Array: + forNode(node).setType(SpecInt32); + break; + case Array::Uint8Array: + forNode(node).setType(SpecInt32); + break; + case Array::Uint8ClampedArray: + forNode(node).setType(SpecInt32); + break; + case Array::Uint16Array: + forNode(node).setType(SpecInt32); + break; + case Array::Uint32Array: + if (node->shouldSpeculateInt32()) + forNode(node).setType(SpecInt32); + else if (enableInt52() && node->shouldSpeculateMachineInt()) + forNode(node).setType(SpecInt52); + else + forNode(node).setType(SpecInt52AsDouble); + break; + case Array::Float32Array: + forNode(node).setType(SpecFullDouble); + break; + case Array::Float64Array: + forNode(node).setType(SpecFullDouble); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + break; + } + + case PutByValDirect: + case PutByVal: + case PutByValAlias: { + switch (node->arrayMode().modeForPut().type()) { + case Array::ForceExit: + m_state.setIsValid(false); + break; + case Array::Generic: + clobberWorld(node->origin.semantic, clobberLimit); + break; + case Array::Int32: + if (node->arrayMode().isOutOfBounds()) + clobberWorld(node->origin.semantic, clobberLimit); + break; + case Array::Double: + if (node->arrayMode().isOutOfBounds()) + clobberWorld(node->origin.semantic, clobberLimit); + break; + case Array::Contiguous: + case Array::ArrayStorage: + if (node->arrayMode().isOutOfBounds()) + clobberWorld(node->origin.semantic, clobberLimit); + break; + case Array::SlowPutArrayStorage: + if (node->arrayMode().mayStoreToHole()) + clobberWorld(node->origin.semantic, clobberLimit); + break; + default: + break; + } + break; + } + + case ArrayPush: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(SpecBytecodeNumber); + break; + + case ArrayPop: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + + case GetMyArgumentByVal: { + JSValue index = forNode(node->child2()).m_value; + InlineCallFrame* inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame; + + if (index && index.isInt32()) { + // This pretends to return TOP for accesses that are actually proven out-of-bounds because + // that's the conservative thing to do. Otherwise we'd need to write more code to mark such + // paths as unreachable, and it's almost certainly not worth the effort. + + if (inlineCallFrame) { + if (index.asUInt32() < inlineCallFrame->arguments.size() - 1) { + forNode(node) = m_state.variables().operand( + virtualRegisterForArgument(index.asInt32() + 1) + inlineCallFrame->stackOffset); + m_state.setFoundConstants(true); + break; + } + } else { + if (index.asUInt32() < m_state.variables().numberOfArguments() - 1) { + forNode(node) = m_state.variables().argument(index.asInt32() + 1); + m_state.setFoundConstants(true); + break; + } + } + } + + if (inlineCallFrame) { + // We have a bound on the types even though it's random access. Take advantage of this. + + AbstractValue result; + for (unsigned i = inlineCallFrame->arguments.size(); i-- > 1;) { + result.merge( + m_state.variables().operand( + virtualRegisterForArgument(i) + inlineCallFrame->stackOffset)); + } + + if (result.value()) + m_state.setFoundConstants(true); + forNode(node) = result; + break; + } + + forNode(node).makeHeapTop(); + break; + } + + case RegExpExec: + forNode(node).makeHeapTop(); + break; + + case RegExpTest: + forNode(node).setType(SpecBoolean); + break; + + case StringReplace: + if (node->child1().useKind() == StringUse + && node->child2().useKind() == RegExpObjectUse + && node->child3().useKind() == StringUse) { + // This doesn't clobber the world. It just reads and writes regexp state. + } else + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); + break; + + case Jump: + break; + + case Branch: { + Node* child = node->child1().node(); + BooleanResult result = booleanResult(node, forNode(child)); + if (result == DefinitelyTrue) { + m_state.setBranchDirection(TakeTrue); + break; + } + if (result == DefinitelyFalse) { + m_state.setBranchDirection(TakeFalse); + break; + } + // FIXME: The above handles the trivial cases of sparse conditional + // constant propagation, but we can do better: + // We can specialize the source variable's value on each direction of + // the branch. + m_state.setBranchDirection(TakeBoth); + break; + } + + case Switch: { + // Nothing to do for now. + // FIXME: Do sparse conditional things. + break; + } + + case Return: + m_state.setIsValid(false); + break; + + case TailCall: + case TailCallVarargs: + case TailCallForwardVarargs: + clobberWorld(node->origin.semantic, clobberLimit); + m_state.setIsValid(false); + break; + + case Throw: + case ThrowReferenceError: + m_state.setIsValid(false); + break; + + case ToPrimitive: { + JSValue childConst = forNode(node->child1()).value(); + if (childConst && childConst.isNumber()) { + setConstant(node, childConst); + break; + } + + ASSERT(node->child1().useKind() == UntypedUse); + + if (!forNode(node->child1()).m_type) { + m_state.setIsValid(false); + break; + } + + if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))) { + m_state.setFoundConstants(true); + forNode(node) = forNode(node->child1()); + break; + } + + clobberWorld(node->origin.semantic, clobberLimit); + + forNode(node).setType(m_graph, SpecHeapTop & ~SpecObject); + break; + } + + case ToString: + case CallStringConstructor: { + switch (node->child1().useKind()) { + case StringObjectUse: + // This also filters that the StringObject has the primordial StringObject + // structure. + filter( + node->child1(), + m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure()); + break; + case StringOrStringObjectUse: + break; + case CellUse: + case UntypedUse: + clobberWorld(node->origin.semantic, clobberLimit); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); + break; + } + + case NewStringObject: { + ASSERT(node->structure()->classInfo() == StringObject::info()); + forNode(node).set(m_graph, node->structure()); + break; + } + + case NewArray: + forNode(node).set( + m_graph, + m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); + break; + + case NewArrayBuffer: + forNode(node).set( + m_graph, + m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); + break; + + case NewArrayWithSize: + forNode(node).setType(m_graph, SpecArray); + break; + + case NewTypedArray: + switch (node->child1().useKind()) { + case Int32Use: + break; + case UntypedUse: + clobberWorld(node->origin.semantic, clobberLimit); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + forNode(node).set( + m_graph, + m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructure( + node->typedArrayType())); + break; + + case NewRegexp: + forNode(node).set(m_graph, m_graph.globalObjectFor(node->origin.semantic)->regExpStructure()); + break; + + case ToThis: { + AbstractValue& source = forNode(node->child1()); + AbstractValue& destination = forNode(node); + + if (source.m_type == SpecStringObject) { + m_state.setFoundConstants(true); + destination = source; + break; + } + + if (m_graph.executableFor(node->origin.semantic)->isStrictMode()) { + if (!(source.m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))) { + m_state.setFoundConstants(true); + destination = source; + break; + } + destination.makeHeapTop(); + } else { + destination = source; + destination.merge(SpecObject); + } + break; + } + + case CreateThis: { + // FIXME: We can fold this to NewObject if the incoming callee is a constant. + forNode(node).setType(m_graph, SpecFinalObject); + break; + } + + case NewObject: + ASSERT(node->structure()); + forNode(node).set(m_graph, node->structure()); + break; + + case PhantomNewObject: + case PhantomNewFunction: + case PhantomNewGeneratorFunction: + case PhantomCreateActivation: + case PhantomDirectArguments: + case PhantomClonedArguments: + case BottomValue: + m_state.setDidClobber(true); // Prevent constant folding. + // This claims to return bottom. + break; + + case PutHint: + break; + + case MaterializeNewObject: { + StructureSet set; + + m_phiChildren->forAllTransitiveIncomingValues( + m_graph.varArgChild(node, 0).node(), + [&] (Node* incoming) { + set.add(incoming->castConstant<Structure*>()); + }); + + forNode(node).set(m_graph, set); + break; + } + + case CreateActivation: + case MaterializeCreateActivation: + forNode(node).set( + m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->activationStructure()); + break; + + case CreateDirectArguments: + forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->directArgumentsStructure()); + break; + + case CreateScopedArguments: + forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->scopedArgumentsStructure()); + break; + + case CreateClonedArguments: + forNode(node).setType(m_graph, SpecObjectOther); + break; + + case NewArrowFunction: + forNode(node).set( + m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure()); + break; + + case NewGeneratorFunction: + forNode(node).set( + m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->generatorFunctionStructure()); + break; + + case NewFunction: + forNode(node).set( + m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure()); + break; + + case GetCallee: + if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_codeBlock->ownerExecutable())) { + InferredValue* singleton = executable->singletonFunction(); + if (JSValue value = singleton->inferredValue()) { + m_graph.watchpoints().addLazily(singleton); + JSFunction* function = jsCast<JSFunction*>(value); + setConstant(node, *m_graph.freeze(function)); + break; + } + } + forNode(node).setType(m_graph, SpecFunction); + break; + + case GetArgumentCount: + forNode(node).setType(SpecInt32); + break; + + case GetRestLength: + forNode(node).setType(SpecInt32); + break; + + case GetGetter: { + JSValue base = forNode(node->child1()).m_value; + if (base) { + GetterSetter* getterSetter = jsCast<GetterSetter*>(base); + if (!getterSetter->isGetterNull()) { + setConstant(node, *m_graph.freeze(getterSetter->getterConcurrently())); + break; + } + } + + forNode(node).setType(m_graph, SpecObject); + break; + } + + case GetSetter: { + JSValue base = forNode(node->child1()).m_value; + if (base) { + GetterSetter* getterSetter = jsCast<GetterSetter*>(base); + if (!getterSetter->isSetterNull()) { + setConstant(node, *m_graph.freeze(getterSetter->setterConcurrently())); + break; + } + } + + forNode(node).setType(m_graph, SpecObject); + break; + } + + case GetScope: + if (JSValue base = forNode(node->child1()).m_value) { + if (JSFunction* function = jsDynamicCast<JSFunction*>(base)) { + setConstant(node, *m_graph.freeze(function->scope())); + break; + } + } + forNode(node).setType(m_graph, SpecObjectOther); + break; + + case SkipScope: { + JSValue child = forNode(node->child1()).value(); + if (child) { + setConstant(node, *m_graph.freeze(JSValue(jsCast<JSScope*>(child.asCell())->next()))); + break; + } + forNode(node).setType(m_graph, SpecObjectOther); + break; + } + + case GetClosureVar: + if (JSValue value = m_graph.tryGetConstantClosureVar(forNode(node->child1()), node->scopeOffset())) { + setConstant(node, *m_graph.freeze(value)); + break; + } + forNode(node).makeBytecodeTop(); + break; + + case PutClosureVar: + break; + + case GetFromArguments: + forNode(node).makeHeapTop(); + break; + + case PutToArguments: + break; + + case GetById: + case GetByIdFlush: { + if (!node->prediction()) { + m_state.setIsValid(false); + break; + } + + AbstractValue& value = forNode(node->child1()); + if (value.m_structure.isFinite() + && (node->child1().useKind() == CellUse || !(value.m_type & ~SpecCell))) { + UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()]; + GetByIdStatus status = GetByIdStatus::computeFor(value.m_structure.set(), uid); + if (status.isSimple()) { + // Figure out what the result is going to be - is it TOP, a constant, or maybe + // something more subtle? + AbstractValue result; + for (unsigned i = status.numVariants(); i--;) { + // This thing won't give us a variant that involves prototypes. If it did, we'd + // have more work to do here. + DFG_ASSERT(m_graph, node, status[i].conditionSet().isEmpty()); + + result.merge( + m_graph.inferredValueForProperty( + value, uid, status[i].offset(), m_state.structureClobberState())); + } + m_state.setFoundConstants(true); + forNode(node) = result; + break; + } + } + + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + } + + case GetArrayLength: { + JSArrayBufferView* view = m_graph.tryGetFoldableView( + forNode(node->child1()).m_value, node->arrayMode()); + if (view) { + setConstant(node, jsNumber(view->length())); + break; + } + forNode(node).setType(SpecInt32); + break; + } + + case CheckStructure: { + AbstractValue& value = forNode(node->child1()); + + StructureSet& set = node->structureSet(); + + // It's interesting that we could have proven that the object has a larger structure set + // that includes the set we're testing. In that case we could make the structure check + // more efficient. We currently don't. + + if (value.m_structure.isSubsetOf(set)) + m_state.setFoundConstants(true); + + SpeculatedType admittedTypes = SpecNone; + switch (node->child1().useKind()) { + case CellUse: + case KnownCellUse: + admittedTypes = SpecNone; + break; + case CellOrOtherUse: + admittedTypes = SpecOther; + break; + default: + DFG_CRASH(m_graph, node, "Bad use kind"); + break; + } + + filter(value, set, admittedTypes); + break; + } + + case CheckStructureImmediate: { + // FIXME: This currently can only reason about one structure at a time. + // https://bugs.webkit.org/show_bug.cgi?id=136988 + + AbstractValue& value = forNode(node->child1()); + StructureSet& set = node->structureSet(); + + if (value.value()) { + if (Structure* structure = jsDynamicCast<Structure*>(value.value())) { + if (set.contains(structure)) { + m_state.setFoundConstants(true); + break; + } + } + m_state.setIsValid(false); + break; + } + + if (m_phiChildren) { + bool allGood = true; + m_phiChildren->forAllTransitiveIncomingValues( + node, + [&] (Node* incoming) { + if (Structure* structure = incoming->dynamicCastConstant<Structure*>()) { + if (set.contains(structure)) + return; + } + allGood = false; + }); + if (allGood) { + m_state.setFoundConstants(true); + break; + } + } + + if (Structure* structure = set.onlyStructure()) { + filterByValue(node->child1(), *m_graph.freeze(structure)); + break; + } + + // Aw shucks, we can't do anything! + break; + } + + case PutStructure: + if (!forNode(node->child1()).m_structure.isClear()) { + if (forNode(node->child1()).m_structure.onlyStructure() == node->transition()->next) + m_state.setFoundConstants(true); + else { + observeTransition( + clobberLimit, node->transition()->previous, node->transition()->next); + forNode(node->child1()).changeStructure(m_graph, node->transition()->next); + } + } + break; + case GetButterfly: + case GetButterflyReadOnly: + case AllocatePropertyStorage: + case ReallocatePropertyStorage: + forNode(node).clear(); // The result is not a JS value. + break; + case CheckArray: { + if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { + m_state.setFoundConstants(true); + break; + } + switch (node->arrayMode().type()) { + case Array::String: + filter(node->child1(), SpecString); + break; + case Array::Int32: + case Array::Double: + case Array::Contiguous: + case Array::Undecided: + case Array::ArrayStorage: + case Array::SlowPutArrayStorage: + break; + case Array::DirectArguments: + filter(node->child1(), SpecDirectArguments); + break; + case Array::ScopedArguments: + filter(node->child1(), SpecScopedArguments); + break; + case Array::Int8Array: + filter(node->child1(), SpecInt8Array); + break; + case Array::Int16Array: + filter(node->child1(), SpecInt16Array); + break; + case Array::Int32Array: + filter(node->child1(), SpecInt32Array); + break; + case Array::Uint8Array: + filter(node->child1(), SpecUint8Array); + break; + case Array::Uint8ClampedArray: + filter(node->child1(), SpecUint8ClampedArray); + break; + case Array::Uint16Array: + filter(node->child1(), SpecUint16Array); + break; + case Array::Uint32Array: + filter(node->child1(), SpecUint32Array); + break; + case Array::Float32Array: + filter(node->child1(), SpecFloat32Array); + break; + case Array::Float64Array: + filter(node->child1(), SpecFloat64Array); + break; + case Array::AnyTypedArray: + filter(node->child1(), SpecTypedArrayView); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); + break; + } + case Arrayify: { + if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { + m_state.setFoundConstants(true); + break; + } + ASSERT(node->arrayMode().conversion() == Array::Convert); + clobberStructures(clobberLimit); + filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); + break; + } + case ArrayifyToStructure: { + AbstractValue& value = forNode(node->child1()); + if (value.m_structure.isSubsetOf(StructureSet(node->structure()))) + m_state.setFoundConstants(true); + clobberStructures(clobberLimit); + + // We have a bunch of options of how to express the abstract set at this point. Let set S + // be the set of structures that the value had before clobbering and assume that all of + // them are watchable. The new value should be the least expressible upper bound of the + // intersection of "values that currently have structure = node->structure()" and "values + // that have structure in S plus any structure transition-reachable from S". Assume that + // node->structure() is not in S but it is transition-reachable from S. Then we would + // like to say that the result is "values that have structure = node->structure() until + // we invalidate", but there is no way to express this using the AbstractValue syntax. So + // we must choose between: + // + // 1) "values that currently have structure = node->structure()". This is a valid + // superset of the value that we really want, and it's specific enough to satisfy the + // preconditions of the array access that this is guarding. It's also specific enough + // to allow relevant optimizations in the case that we didn't have a contradiction + // like in this example. Notice that in the abscence of any contradiction, this result + // is precise rather than being a conservative LUB. + // + // 2) "values that currently hava structure in S plus any structure transition-reachable + // from S". This is also a valid superset of the value that we really want, but it's + // not specific enough to satisfy the preconditions of the array access that this is + // guarding - so playing such shenanigans would preclude us from having assertions on + // the typing preconditions of any array accesses. This would also not be a desirable + // answer in the absence of a contradiction. + // + // Note that it's tempting to simply say that the resulting value is BOTTOM because of + // the contradiction. That would be wrong, since we haven't hit an invalidation point, + // yet. + value.set(m_graph, node->structure()); + break; + } + case GetIndexedPropertyStorage: { + JSArrayBufferView* view = m_graph.tryGetFoldableView( + forNode(node->child1()).m_value, node->arrayMode()); + if (view) + m_state.setFoundConstants(true); + forNode(node).clear(); + break; + } + case ConstantStoragePointer: { + forNode(node).clear(); + break; + } + + case GetTypedArrayByteOffset: { + JSArrayBufferView* view = m_graph.tryGetFoldableView(forNode(node->child1()).m_value); + if (view) { + setConstant(node, jsNumber(view->byteOffset())); + break; + } + forNode(node).setType(SpecInt32); + break; + } + + case GetByOffset: { + StorageAccessData& data = node->storageAccessData(); + UniquedStringImpl* uid = m_graph.identifiers()[data.identifierNumber]; + + // FIXME: The part of this that handles inferred property types relies on AI knowing the structure + // right now. That's probably not optimal. In some cases, we may perform an optimization (usually + // by something other than AI, maybe by CSE for example) that obscures AI's view of the structure + // at the point where GetByOffset runs. Currently, when that happens, we'll have to rely entirely + // on the type that ByteCodeParser was able to prove. + AbstractValue value = m_graph.inferredValueForProperty( + forNode(node->child2()), uid, data.offset, m_state.structureClobberState()); + + // It's possible that the type that ByteCodeParser came up with is better. + AbstractValue typeFromParsing; + typeFromParsing.set(m_graph, data.inferredType, m_state.structureClobberState()); + value.filter(typeFromParsing); + + // If we decide that there does not exist any value that this can return, then it's probably + // because the compilation was already invalidated. + if (value.isClear()) + m_state.setIsValid(false); + + forNode(node) = value; + if (value.m_value) + m_state.setFoundConstants(true); + break; + } + + case GetGetterSetterByOffset: { + StorageAccessData& data = node->storageAccessData(); + JSValue result = m_graph.tryGetConstantProperty(forNode(node->child2()), data.offset); + if (result && jsDynamicCast<GetterSetter*>(result)) { + setConstant(node, *m_graph.freeze(result)); + break; + } + + forNode(node).set(m_graph, m_graph.m_vm.getterSetterStructure.get()); + break; + } + + case MultiGetByOffset: { + // This code will filter the base value in a manner that is possibly different (either more + // or less precise) than the way it would be filtered if this was strength-reduced to a + // CheckStructure. This is fine. It's legal for different passes over the code to prove + // different things about the code, so long as all of them are sound. That even includes + // one guy proving that code should never execute (due to a contradiction) and another guy + // not finding that contradiction. If someone ever proved that there would be a + // contradiction then there must always be a contradiction even if subsequent passes don't + // realize it. This is the case here. + + // Ordinarily you have to be careful with calling setFoundConstants() + // because of the effect on compile times, but this node is FTL-only. + m_state.setFoundConstants(true); + + UniquedStringImpl* uid = m_graph.identifiers()[node->multiGetByOffsetData().identifierNumber]; + + AbstractValue base = forNode(node->child1()); + StructureSet baseSet; + AbstractValue result; + for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) { + StructureSet set = getCase.set(); + set.filter(base); + if (set.isEmpty()) + continue; + baseSet.merge(set); + + switch (getCase.method().kind()) { + case GetByOffsetMethod::Constant: { + AbstractValue thisResult; + thisResult.set( + m_graph, + *getCase.method().constant(), + m_state.structureClobberState()); + result.merge(thisResult); + break; + } + + case GetByOffsetMethod::Load: { + result.merge( + m_graph.inferredValueForProperty( + set, uid, m_state.structureClobberState())); + break; + } + + default: { + result.makeHeapTop(); + break; + } } + } + + if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction) + m_state.setIsValid(false); + + forNode(node) = result; + break; + } + + case PutByOffset: { + break; + } + + case MultiPutByOffset: { + StructureSet newSet; + TransitionVector transitions; + + // Ordinarily you have to be careful with calling setFoundConstants() + // because of the effect on compile times, but this node is FTL-only. + m_state.setFoundConstants(true); + + AbstractValue base = forNode(node->child1()); + AbstractValue originalValue = forNode(node->child2()); + AbstractValue resultingValue; + + for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { + const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; + StructureSet thisSet = variant.oldStructure(); + thisSet.filter(base); + if (thisSet.isEmpty()) + continue; + + AbstractValue thisValue = originalValue; + thisValue.filter(m_graph, variant.requiredType()); + resultingValue.merge(thisValue); + + if (variant.kind() == PutByIdVariant::Transition) { + if (thisSet.onlyStructure() != variant.newStructure()) { + transitions.append( + Transition(variant.oldStructureForTransition(), variant.newStructure())); + } // else this is really a replace. + newSet.add(variant.newStructure()); + } else { + ASSERT(variant.kind() == PutByIdVariant::Replace); + newSet.merge(thisSet); + } + } + + observeTransitions(clobberLimit, transitions); + if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction) + m_state.setIsValid(false); + forNode(node->child2()) = resultingValue; + if (!!originalValue && !resultingValue) + m_state.setIsValid(false); + break; + } + + case GetExecutable: { + JSValue value = forNode(node->child1()).value(); + if (value) { + JSFunction* function = jsDynamicCast<JSFunction*>(value); + if (function) { + setConstant(node, *m_graph.freeze(function->executable())); + break; + } + } + forNode(node).setType(m_graph, SpecCellOther); + break; + } + + case CheckCell: { + JSValue value = forNode(node->child1()).value(); + if (value == node->cellOperand()->value()) { + m_state.setFoundConstants(true); + ASSERT(value); + break; + } + filterByValue(node->child1(), *node->cellOperand()); + break; + } + + case CheckNotEmpty: { + AbstractValue& value = forNode(node->child1()); + if (!(value.m_type & SpecEmpty)) { + m_state.setFoundConstants(true); + break; + } + + filter(value, ~SpecEmpty); + break; + } + + case CheckIdent: { + AbstractValue& value = forNode(node->child1()); + UniquedStringImpl* uid = node->uidOperand(); + ASSERT(uid->isSymbol() ? !(value.m_type & ~SpecSymbol) : !(value.m_type & ~SpecStringIdent)); // Edge filtering should have already ensured this. + + JSValue childConstant = value.value(); + if (childConstant) { + if (uid->isSymbol()) { + ASSERT(childConstant.isSymbol()); + if (asSymbol(childConstant)->privateName().uid() == uid) { + m_state.setFoundConstants(true); + break; + } + } else { + ASSERT(childConstant.isString()); + if (asString(childConstant)->tryGetValueImpl() == uid) { + m_state.setFoundConstants(true); + break; + } + } + } + + filter(value, uid->isSymbol() ? SpecSymbol : SpecStringIdent); + break; + } + + case CheckInBounds: { + JSValue left = forNode(node->child1()).value(); + JSValue right = forNode(node->child2()).value(); + if (left && right && left.isInt32() && right.isInt32() + && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) { + m_state.setFoundConstants(true); + break; + } + break; + } + + case PutById: + case PutByIdFlush: + case PutByIdDirect: { + AbstractValue& value = forNode(node->child1()); + if (value.m_structure.isFinite()) { + PutByIdStatus status = PutByIdStatus::computeFor( + m_graph.globalObjectFor(node->origin.semantic), + value.m_structure.set(), + m_graph.identifiers()[node->identifierNumber()], + node->op() == PutByIdDirect); + + if (status.isSimple()) { + StructureSet newSet; + TransitionVector transitions; + + for (unsigned i = status.numVariants(); i--;) { + const PutByIdVariant& variant = status[i]; + if (variant.kind() == PutByIdVariant::Transition) { + transitions.append( + Transition( + variant.oldStructureForTransition(), variant.newStructure())); + m_graph.registerStructure(variant.newStructure()); + newSet.add(variant.newStructure()); + } else { + ASSERT(variant.kind() == PutByIdVariant::Replace); + newSet.merge(variant.oldStructure()); + } + } + + if (status.numVariants() == 1 || isFTL(m_graph.m_plan.mode)) + m_state.setFoundConstants(true); + + observeTransitions(clobberLimit, transitions); + if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction) + m_state.setIsValid(false); + break; + } + } + + clobberWorld(node->origin.semantic, clobberLimit); + break; + } + + case PutGetterById: + case PutSetterById: + case PutGetterSetterById: + case PutGetterByVal: + case PutSetterByVal: { + clobberWorld(node->origin.semantic, clobberLimit); + break; + } + + case In: { + // FIXME: We can determine when the property definitely exists based on abstract + // value information. + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(SpecBoolean); + break; + } + + case GetEnumerableLength: { + forNode(node).setType(SpecInt32); + break; + } + case HasGenericProperty: { + forNode(node).setType(SpecBoolean); + break; + } + case HasStructureProperty: { + forNode(node).setType(SpecBoolean); + break; + } + case HasIndexedProperty: { + ArrayMode mode = node->arrayMode(); + switch (mode.type()) { + case Array::Int32: + case Array::Double: + case Array::Contiguous: + case Array::ArrayStorage: { + break; + } + default: { + clobberWorld(node->origin.semantic, clobberLimit); + break; + } + } + forNode(node).setType(SpecBoolean); + break; + } + case GetDirectPname: { + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + } + case GetPropertyEnumerator: { + forNode(node).setType(m_graph, SpecCell); + break; + } + case GetEnumeratorStructurePname: { + forNode(node).setType(m_graph, SpecString | SpecOther); + break; + } + case GetEnumeratorGenericPname: { + forNode(node).setType(m_graph, SpecString | SpecOther); + break; + } + case ToIndexString: { + forNode(node).setType(m_graph, SpecString); + break; + } + + case GetGlobalVar: + forNode(node).makeHeapTop(); + break; + case GetGlobalLexicalVariable: + forNode(node).makeBytecodeTop(); + break; + + case VarInjectionWatchpoint: + case PutGlobalVariable: + case NotifyWrite: + break; + + case OverridesHasInstance: + forNode(node).setType(SpecBoolean); + break; + + case InstanceOf: + // Sadly, we don't propagate the fact that we've done InstanceOf + forNode(node).setType(SpecBoolean); + break; + + case InstanceOfCustom: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).setType(SpecBoolean); + break; + + case Phi: + RELEASE_ASSERT(m_graph.m_form == SSA); + // The state of this node would have already been decided, but it may have become a + // constant, in which case we'd like to know. + if (forNode(node).m_value) + m_state.setFoundConstants(true); + break; + + case Upsilon: { + m_state.createValueForNode(node->phi()); + forNode(node->phi()) = forNode(node->child1()); + break; + } + + case Flush: + case PhantomLocal: + break; + + case Call: + case TailCallInlinedCaller: + case Construct: + case CallVarargs: + case CallForwardVarargs: + case TailCallVarargsInlinedCaller: + case ConstructVarargs: + case ConstructForwardVarargs: + case TailCallForwardVarargsInlinedCaller: + clobberWorld(node->origin.semantic, clobberLimit); + forNode(node).makeHeapTop(); + break; + + case ForceOSRExit: + case CheckBadCell: + m_state.setIsValid(false); + break; + + case InvalidationPoint: + forAllValues(clobberLimit, AbstractValue::observeInvalidationPointFor); + m_state.setStructureClobberState(StructuresAreWatched); + break; + + case CheckWatchdogTimer: + break; + + case Breakpoint: + case ProfileWillCall: + case ProfileDidCall: + case ProfileType: + case ProfileControlFlow: + case Phantom: + case CountExecution: + case CheckTierUpInLoop: + case CheckTierUpAtReturn: + case CheckTypeInfoFlags: + break; + + case CopyRest: + break; + + case Check: { + // Simplify out checks that don't actually do checking. + for (unsigned i = 0; i < AdjacencyList::Size; ++i) { + Edge edge = node->children.child(i); + if (!edge) + break; + if (edge.isProved() || edge.willNotHaveCheck()) { + m_state.setFoundConstants(true); + break; + } + } + break; + } + + case StoreBarrier: { + filter(node->child1(), SpecCell); + break; + } + + case CheckTierUpAndOSREnter: + case CheckTierUpWithNestedTriggerAndOSREnter: + case LoopHint: + case ZombieHint: + case ExitOK: + break; + + case Unreachable: + case LastNodeType: + case ArithIMul: + case FiatInt52: + DFG_CRASH(m_graph, node, "Unexpected node type"); + break; + } + + return m_state.isValid(); +} + +template<typename AbstractStateType> +bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned indexInBlock) +{ + return executeEffects(indexInBlock, m_state.block()->at(indexInBlock)); +} + +template<typename AbstractStateType> +bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock) +{ + Node* node = m_state.block()->at(indexInBlock); + + startExecuting(); + executeEdges(node); + return executeEffects(indexInBlock, node); +} + +template<typename AbstractStateType> +bool AbstractInterpreter<AbstractStateType>::execute(Node* node) +{ + startExecuting(); + executeEdges(node); + return executeEffects(UINT_MAX, node); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::clobberWorld( + const CodeOrigin&, unsigned clobberLimit) +{ + clobberStructures(clobberLimit); +} + +template<typename AbstractStateType> +template<typename Functor> +void AbstractInterpreter<AbstractStateType>::forAllValues( + unsigned clobberLimit, Functor& functor) +{ + SamplingRegion samplingRegion("DFG AI For All Values"); + if (clobberLimit >= m_state.block()->size()) + clobberLimit = m_state.block()->size(); + else + clobberLimit++; + ASSERT(clobberLimit <= m_state.block()->size()); + for (size_t i = clobberLimit; i--;) + functor(forNode(m_state.block()->at(i))); + if (m_graph.m_form == SSA) { + HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtHead.begin(); + HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtHead.end(); + for (; iter != end; ++iter) + functor(forNode(*iter)); + } + for (size_t i = m_state.variables().numberOfArguments(); i--;) + functor(m_state.variables().argument(i)); + for (size_t i = m_state.variables().numberOfLocals(); i--;) + functor(m_state.variables().local(i)); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit) +{ + SamplingRegion samplingRegion("DFG AI Clobber Structures"); + forAllValues(clobberLimit, AbstractValue::clobberStructuresFor); + setDidClobber(); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::observeTransition( + unsigned clobberLimit, Structure* from, Structure* to) +{ + AbstractValue::TransitionObserver transitionObserver(from, to); + forAllValues(clobberLimit, transitionObserver); + + ASSERT(!from->dfgShouldWatch()); // We don't need to claim to be in a clobbered state because 'from' was never watchable (during the time we were compiling), hence no constants ever introduced into the DFG IR that ever had a watchable structure would ever have the same structure as from. +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::observeTransitions( + unsigned clobberLimit, const TransitionVector& vector) +{ + AbstractValue::TransitionsObserver transitionsObserver(vector); + forAllValues(clobberLimit, transitionsObserver); + + if (!ASSERT_DISABLED) { + // We don't need to claim to be in a clobbered state because none of the Transition::previous structures are watchable. + for (unsigned i = vector.size(); i--;) + ASSERT(!vector[i].previous->dfgShouldWatch()); + } +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::setDidClobber() +{ + m_state.setDidClobber(true); + m_state.setStructureClobberState(StructuresAreClobbered); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) const +{ + const_cast<AbstractInterpreter<AbstractStateType>*>(this)->dump(out); +} + +template<typename AbstractStateType> +void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) +{ + CommaPrinter comma(" "); + HashSet<Node*> seen; + if (m_graph.m_form == SSA) { + HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtHead.begin(); + HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtHead.end(); + for (; iter != end; ++iter) { + Node* node = *iter; + seen.add(node); + AbstractValue& value = forNode(node); + if (value.isClear()) + continue; + out.print(comma, node, ":", value); + } + } + for (size_t i = 0; i < m_state.block()->size(); ++i) { + Node* node = m_state.block()->at(i); + seen.add(node); + AbstractValue& value = forNode(node); + if (value.isClear()) + continue; + out.print(comma, node, ":", value); + } + if (m_graph.m_form == SSA) { + HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtTail.begin(); + HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtTail.end(); + for (; iter != end; ++iter) { + Node* node = *iter; + if (seen.contains(node)) + continue; + AbstractValue& value = forNode(node); + if (value.isClear()) + continue; + out.print(comma, node, ":", value); + } + } +} + +template<typename AbstractStateType> +FiltrationResult AbstractInterpreter<AbstractStateType>::filter( + AbstractValue& value, const StructureSet& set, SpeculatedType admittedTypes) +{ + if (value.filter(m_graph, set, admittedTypes) == FiltrationOK) + return FiltrationOK; + m_state.setIsValid(false); + return Contradiction; +} + +template<typename AbstractStateType> +FiltrationResult AbstractInterpreter<AbstractStateType>::filterArrayModes( + AbstractValue& value, ArrayModes arrayModes) +{ + if (value.filterArrayModes(arrayModes) == FiltrationOK) + return FiltrationOK; + m_state.setIsValid(false); + return Contradiction; +} + +template<typename AbstractStateType> +FiltrationResult AbstractInterpreter<AbstractStateType>::filter( + AbstractValue& value, SpeculatedType type) +{ + if (value.filter(type) == FiltrationOK) + return FiltrationOK; + m_state.setIsValid(false); + return Contradiction; +} + +template<typename AbstractStateType> +FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue( + AbstractValue& abstractValue, FrozenValue concreteValue) +{ + if (abstractValue.filterByValue(concreteValue) == FiltrationOK) + return FiltrationOK; + m_state.setIsValid(false); + return Contradiction; +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + +#endif // DFGAbstractInterpreterInlines_h + |