summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h')
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h2623
1 files changed, 1948 insertions, 675 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 3f68aced1..467679b56 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * 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
@@ -23,15 +23,19 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DFGAbstractInterpreterInlines_h
-#define DFGAbstractInterpreterInlines_h
-
-#include <wtf/Platform.h>
+#pragma once
#if ENABLE(DFG_JIT)
+#include "ArrayConstructor.h"
#include "DFGAbstractInterpreter.h"
+#include "DOMJITGetterSetter.h"
+#include "DOMJITSignature.h"
#include "GetByIdStatus.h"
+#include "GetterSetter.h"
+#include "HashMapImpl.h"
+#include "JITOperations.h"
+#include "MathCommon.h"
#include "Operations.h"
#include "PutByIdStatus.h"
#include "StringObject.h"
@@ -42,8 +46,11 @@ template<typename AbstractStateType>
AbstractInterpreter<AbstractStateType>::AbstractInterpreter(Graph& graph, AbstractStateType& state)
: m_codeBlock(graph.m_codeBlock)
, m_graph(graph)
+ , m_vm(m_graph.m_vm)
, m_state(state)
{
+ if (m_graph.m_form == SSA)
+ m_phiChildren = std::make_unique<PhiChildren>(m_graph);
}
template<typename AbstractStateType>
@@ -58,17 +65,23 @@ AbstractInterpreter<AbstractStateType>::booleanResult(
{
JSValue childConst = value.value();
if (childConst) {
- if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->codeOrigin)->globalExec()))
+ 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_currentKnownStructure.hasSingleton()) {
- Structure* structure = value.m_currentKnownStructure.singleton();
- if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin))
- && structure->typeInfo().type() != StringType)
+ if (isCellSpeculation(value.m_type) && !value.m_structure.isTop()) {
+ bool allTrue = true;
+ for (unsigned i = value.m_structure.size(); i--;) {
+ RegisteredStructure 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;
}
@@ -76,40 +89,49 @@ AbstractInterpreter<AbstractStateType>::booleanResult(
}
template<typename AbstractStateType>
-bool AbstractInterpreter<AbstractStateType>::startExecuting(Node* node)
+void AbstractInterpreter<AbstractStateType>::startExecuting()
{
ASSERT(m_state.block());
ASSERT(m_state.isValid());
m_state.setDidClobber(false);
-
- node->setCanExit(false);
-
- return node->shouldGenerate();
-}
-
-template<typename AbstractStateType>
-bool AbstractInterpreter<AbstractStateType>::startExecuting(unsigned indexInBlock)
-{
- return startExecuting(m_state.block()->at(indexInBlock));
}
template<typename AbstractStateType>
void AbstractInterpreter<AbstractStateType>::executeEdges(Node* node)
{
- DFG_NODE_DO_TO_CHILDREN(m_graph, node, filterEdgeByUse);
+ m_graph.doToChildren(
+ node,
+ [&] (Edge& edge) {
+ filterEdgeByUse(edge);
+ });
}
template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::executeEdges(unsigned indexInBlock)
+void AbstractInterpreter<AbstractStateType>::executeKnownEdgeTypes(Node* node)
{
- executeEdges(m_state.block()->at(indexInBlock));
+ // 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. But the existance of the edge is enough to prove that
+ // it is indeed the case. Taking advantage of this is not optional, since otherwise the DFG
+ // and FTL backends may emit checks in a node that lacks a valid exit origin.
+ m_graph.doToChildren(
+ node,
+ [&] (Edge& edge) {
+ if (mayHaveTypeCheck(edge.useKind()))
+ return;
+
+ filterEdgeByUse(edge);
+ });
}
template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::verifyEdge(Node*, Edge edge)
+void AbstractInterpreter<AbstractStateType>::verifyEdge(Node* node, Edge edge)
{
- RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind())));
+ 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>
@@ -118,64 +140,94 @@ void AbstractInterpreter<AbstractStateType>::verifyEdges(Node* node)
DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge);
}
+inline bool isToThisAnIdentity(bool isStrictMode, AbstractValue& valueForNode)
+{
+ // We look at the type first since that will cover most cases and does not require iterating all the structures.
+ if (isStrictMode) {
+ if (valueForNode.m_type && !(valueForNode.m_type & SpecObjectOther))
+ return true;
+ } else {
+ if (valueForNode.m_type && !(valueForNode.m_type & (~SpecObject | SpecObjectOther)))
+ return true;
+ }
+
+ if ((isStrictMode || (valueForNode.m_type && !(valueForNode.m_type & ~SpecObject))) && valueForNode.m_structure.isFinite()) {
+ bool overridesToThis = false;
+ valueForNode.m_structure.forEach([&](RegisteredStructure structure) {
+ TypeInfo type = structure->typeInfo();
+ ASSERT(type.isObject() || type.type() == StringType || type.type() == SymbolType);
+ if (!isStrictMode)
+ ASSERT(type.isObject());
+ // We don't need to worry about strings/symbols here since either:
+ // 1) We are in strict mode and strings/symbols are not wrapped
+ // 2) The AI has proven that the type of this is a subtype of object
+ if (type.isObject() && type.overridesToThis())
+ overridesToThis = true;
+ });
+ return !overridesToThis;
+ }
+
+ return false;
+}
+
template<typename AbstractStateType>
bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimit, Node* node)
{
- if (!ASSERT_DISABLED)
- verifyEdges(node);
+ verifyEdges(node);
m_state.createValueForNode(node);
switch (node->op()) {
case JSConstant:
- case WeakJSConstant:
- case PhantomArguments: {
- forNode(node).set(m_graph, m_graph.valueOfJSConstant(node));
+ case DoubleConstant:
+ case Int52Constant: {
+ setBuiltInConstant(node, *node->constant());
break;
}
-
- case Identity: {
- forNode(node) = forNode(node->child1());
+
+ case LazyJSConstant: {
+ LazyJSValue value = node->lazyJSValue();
+ switch (value.kind()) {
+ case LazyJSValue::KnownValue:
+ setConstant(node, value.value()->value());
+ break;
+ case LazyJSValue::SingleCharacterString:
+ case LazyJSValue::KnownStringImpl:
+ case LazyJSValue::NewStringImpl:
+ forNode(node).setType(m_graph, SpecString);
+ break;
+ }
break;
}
- case GetArgument: {
- ASSERT(m_graph.m_form == SSA);
- VariableAccessData* variable = node->variableAccessData();
- AbstractValue& value = m_state.variables().operand(variable->local().offset());
- ASSERT(value.isHeapTop());
- FiltrationResult result =
- value.filter(typeFilterFor(useKindFor(variable->flushFormat())));
- ASSERT_UNUSED(result, result == FiltrationOK);
- forNode(node) = value;
+ case Identity: {
+ forNode(node) = forNode(node->child1());
+ if (forNode(node).value())
+ m_state.setFoundConstants(true);
break;
}
case ExtractOSREntryLocal: {
- if (!(node->unlinkedLocal().isArgument())
- && m_graph.m_lazyVars.get(node->unlinkedLocal().toLocal())) {
- // This is kind of pessimistic - we could know in some cases that the
- // DFG code at the point of the OSR had already initialized the lazy
- // variable. But maybe this is fine, since we're inserting OSR
- // entrypoints very early in the pipeline - so any lazy initializations
- // ought to be hoisted out anyway.
- forNode(node).makeBytecodeTop();
- } else
- forNode(node).makeHeapTop();
+ forNode(node).makeBytecodeTop();
break;
}
case GetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
- if (variableAccessData->prediction() == SpecNone) {
- m_state.setIsValid(false);
- break;
- }
AbstractValue value = m_state.variables().operand(variableAccessData->local().offset());
- if (!variableAccessData->isCaptured()) {
- if (value.isClear())
- node->setCanExit(true);
- }
+ // 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;
@@ -191,7 +243,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
case SetLocal: {
- m_state.variables().operand(node->local().offset()) = forNode(node->child1());
+ m_state.variables().operand(node->local()) = forNode(node->child1());
+ break;
+ }
+
+ case PutStack: {
+ m_state.variables().operand(node->stackAccessData()->local) = forNode(node->child1());
break;
}
@@ -202,10 +259,31 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
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.
- ASSERT(!m_state.block()->valuesAtHead.operand(node->local()).isClear());
+ // 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(SpecInt32Only);
+ for (unsigned i = data->limit - 1; i--;)
+ m_state.variables().operand(data->start.offset() + i).makeHeapTop();
break;
+ }
case BitAnd:
case BitOr:
@@ -213,6 +291,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
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, SpecInt32Only);
+ break;
+ }
+
JSValue left = forNode(node->child1()).value();
JSValue right = forNode(node->child2()).value();
if (left && right && left.isInt32() && right.isInt32()) {
@@ -243,19 +327,36 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
break;
}
- forNode(node).setType(SpecInt32);
+
+ if (node->op() == BitAnd
+ && (isBoolInt32Speculation(forNode(node->child1()).m_type) ||
+ isBoolInt32Speculation(forNode(node->child2()).m_type))) {
+ forNode(node).setType(SpecBoolInt32);
+ break;
+ }
+
+ forNode(node).setType(SpecInt32Only);
break;
}
case UInt32ToNumber: {
JSValue child = forNode(node->child1()).value();
if (doesOverflow(node->arithMode())) {
+ if (enableInt52()) {
+ if (child && child.isAnyInt()) {
+ int64_t machineInt = child.asAnyInt();
+ setConstant(node, jsNumber(static_cast<uint32_t>(machineInt)));
+ break;
+ }
+ forNode(node).setType(SpecAnyInt);
+ break;
+ }
if (child && child.isInt32()) {
uint32_t value = child.asInt32();
setConstant(node, jsNumber(value));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(SpecAnyIntAsDouble);
break;
}
if (child && child.isInt32()) {
@@ -265,8 +366,27 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
+ 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;
}
@@ -280,8 +400,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- node->setCanExit(true);
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
}
@@ -296,55 +415,91 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
if (child.isBoolean()) {
- setConstant(node, JSValue(child.asBoolean()));
+ setConstant(node, jsNumber(child.asBoolean()));
+ break;
+ }
+ if (child.isUndefinedOrNull()) {
+ setConstant(node, jsNumber(0));
break;
}
}
- forNode(node).setType(SpecInt32);
+ if (isBooleanSpeculation(forNode(node->child1()).m_type)) {
+ forNode(node).setType(SpecBoolInt32);
+ break;
+ }
+
+ forNode(node).setType(SpecInt32Only);
break;
}
- case Int32ToDouble: {
+ case DoubleRep: {
JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, JSValue(JSValue::EncodeAsDouble, child.asNumber()));
+ if (std::optional<double> number = child.toNumberFromPrimitive()) {
+ setConstant(node, jsDoubleNumber(*number));
+ 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;
}
- if (isInt32Speculation(forNode(node->child1()).m_type))
- forNode(node).setType(SpecDoubleReal);
- else
- forNode(node).setType(SpecDouble);
+
+ case Int52RepUse:
+ case NumberUse:
+ case RealNumberUse:
+ break;
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ forNode(node).setType(type);
+ forNode(node).fixTypeForRepresentation(m_graph, node);
break;
}
- case Int52ToDouble: {
+ case Int52Rep: {
JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
+ if (child && child.isAnyInt()) {
setConstant(node, child);
break;
}
- forNode(node).setType(SpecDouble);
+
+ forNode(node).setType(SpecAnyInt);
break;
}
- case Int52ToValue: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, child);
+ case ValueRep: {
+ JSValue value = forNode(node->child1()).value();
+ if (value) {
+ setConstant(node, value);
break;
}
- SpeculatedType type = forNode(node->child1()).m_type;
- if (type & SpecInt52)
- type = (type | SpecInt32 | SpecInt52AsDouble) & ~SpecInt52;
- forNode(node).setType(type);
+
+ 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->codeOrigin, clobberLimit);
- forNode(node).setType(SpecString | SpecBytecodeNumber);
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, SpecString | SpecBytecodeNumber);
+ break;
+ }
+
+ case StrCat: {
+ forNode(node).setType(m_graph, SpecString);
break;
}
@@ -364,33 +519,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- if (shouldCheckOverflow(node->arithMode()))
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case MachineIntUse:
- if (left && right && left.isMachineInt() && right.isMachineInt()) {
- JSValue result = jsNumber(left.asMachineInt() + right.asMachineInt());
- if (result.isMachineInt()) {
+ case Int52RepUse:
+ if (left && right && left.isAnyInt() && right.isAnyInt()) {
+ JSValue result = jsNumber(left.asAnyInt() + right.asAnyInt());
+ if (result.isAnyInt()) {
setConstant(node, result);
break;
}
}
- forNode(node).setType(SpecInt52);
- if (!forNode(node->child1()).isType(SpecInt32)
- || !forNode(node->child2()).isType(SpecInt32))
- node->setCanExit(true);
+ forNode(node).setType(SpecAnyInt);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
- setConstant(node, jsNumber(left.asNumber() + right.asNumber()));
+ setConstant(node, jsDoubleNumber(left.asNumber() + right.asNumber()));
break;
}
- if (isFullRealNumberSpeculation(forNode(node->child1()).m_type)
- && isFullRealNumberSpeculation(forNode(node->child2()).m_type))
- forNode(node).setType(SpecDoubleReal);
- else
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(
+ typeOfDoubleSum(
+ forNode(node->child1()).m_type, forNode(node->child2()).m_type));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -398,10 +546,20 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
break;
}
-
+
+ case ArithClz32: {
+ JSValue operand = forNode(node->child1()).value();
+ if (std::optional<double> number = operand.toNumberFromPrimitive()) {
+ uint32_t value = toUInt32(*number);
+ setConstant(node, jsNumber(clz32(value)));
+ break;
+ }
+ forNode(node).setType(SpecInt32Only);
+ break;
+ }
+
case MakeRope: {
- node->setCanExit(true);
- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ forNode(node).set(m_graph, m_vm.stringStructure.get());
break;
}
@@ -421,29 +579,30 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- if (shouldCheckOverflow(node->arithMode()))
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case MachineIntUse:
- if (left && right && left.isMachineInt() && right.isMachineInt()) {
- JSValue result = jsNumber(left.asMachineInt() - right.asMachineInt());
- if (result.isMachineInt() || !shouldCheckOverflow(node->arithMode())) {
+ case Int52RepUse:
+ if (left && right && left.isAnyInt() && right.isAnyInt()) {
+ JSValue result = jsNumber(left.asAnyInt() - right.asAnyInt());
+ if (result.isAnyInt() || !shouldCheckOverflow(node->arithMode())) {
setConstant(node, result);
break;
}
}
- forNode(node).setType(SpecInt52);
- if (!forNode(node->child1()).isType(SpecInt32)
- || !forNode(node->child2()).isType(SpecInt32))
- node->setCanExit(true);
+ forNode(node).setType(SpecAnyInt);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
- setConstant(node, jsNumber(left.asNumber() - right.asNumber()));
+ setConstant(node, jsDoubleNumber(left.asNumber() - right.asNumber()));
break;
}
- forNode(node).setType(SpecDouble);
+ 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();
@@ -472,38 +631,35 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- if (shouldCheckOverflow(node->arithMode()))
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case MachineIntUse:
- if (child && child.isMachineInt()) {
+ case Int52RepUse:
+ if (child && child.isAnyInt()) {
double doubleResult;
if (shouldCheckNegativeZero(node->arithMode()))
doubleResult = -child.asNumber();
else
doubleResult = 0 - child.asNumber();
JSValue valueResult = jsNumber(doubleResult);
- if (valueResult.isMachineInt()) {
+ if (valueResult.isAnyInt()) {
setConstant(node, valueResult);
break;
}
}
- forNode(node).setType(SpecInt52);
- if (m_state.forNode(node->child1()).couldBeType(SpecInt52))
- node->setCanExit(true);
- if (shouldCheckNegativeZero(node->arithMode()))
- node->setCanExit(true);
+ forNode(node).setType(SpecAnyInt);
break;
- case NumberUse:
+ case DoubleRepUse:
if (child && child.isNumber()) {
- setConstant(node, jsNumber(-child.asNumber()));
+ setConstant(node, jsDoubleNumber(-child.asNumber()));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(
+ typeOfDoubleNegation(
+ forNode(node->child1()).m_type));
break;
default:
- RELEASE_ASSERT_NOT_REACHED();
+ DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse);
+ forNode(node).setType(SpecBytecodeNumber);
break;
}
break;
@@ -528,34 +684,33 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- if (shouldCheckOverflow(node->arithMode()))
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case MachineIntUse:
- if (left && right && left.isMachineInt() && right.isMachineInt()) {
+ case Int52RepUse:
+ if (left && right && left.isAnyInt() && right.isAnyInt()) {
double doubleResult = left.asNumber() * right.asNumber();
if (!shouldCheckNegativeZero(node->arithMode()))
doubleResult += 0;
JSValue valueResult = jsNumber(doubleResult);
- if (valueResult.isMachineInt()) {
+ if (valueResult.isAnyInt()) {
setConstant(node, valueResult);
break;
}
}
- forNode(node).setType(SpecInt52);
- node->setCanExit(true);
+ forNode(node).setType(SpecAnyInt);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
- setConstant(node, jsNumber(left.asNumber() * right.asNumber()));
+ setConstant(node, jsDoubleNumber(left.asNumber() * right.asNumber()));
break;
}
- if (isFullRealNumberSpeculation(forNode(node->child1()).m_type)
- || isFullRealNumberSpeculation(forNode(node->child2()).m_type))
- forNode(node).setType(SpecDoubleReal);
- else
- forNode(node).setType(SpecDouble);
+ 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();
@@ -581,15 +736,20 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
- setConstant(node, jsNumber(left.asNumber() / right.asNumber()));
+ setConstant(node, jsDoubleNumber(left.asNumber() / right.asNumber()));
break;
}
- forNode(node).setType(SpecDouble);
+ 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();
@@ -615,15 +775,16 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
- setConstant(node, jsNumber(fmod(left.asNumber(), right.asNumber())));
+ setConstant(node, jsDoubleNumber(fmod(left.asNumber(), right.asNumber())));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(
+ typeOfDoubleBinaryOp(
+ forNode(node->child1()).m_type, forNode(node->child2()).m_type));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -641,17 +802,18 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
setConstant(node, jsNumber(std::min(left.asInt32(), right.asInt32())));
break;
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
double a = left.asNumber();
double b = right.asNumber();
- setConstant(node, jsNumber(a < b ? a : (b <= a ? b : a + b)));
+ setConstant(node, jsDoubleNumber(a < b ? a : (b <= a ? b : a + b)));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(
+ typeOfDoubleMinMax(
+ forNode(node->child1()).m_type, forNode(node->child2()).m_type));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -669,17 +831,18 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
setConstant(node, jsNumber(std::max(left.asInt32(), right.asInt32())));
break;
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case NumberUse:
+ case DoubleRepUse:
if (left && right && left.isNumber() && right.isNumber()) {
double a = left.asNumber();
double b = right.asNumber();
- setConstant(node, jsNumber(a > b ? a : (b >= a ? b : a + b)));
+ setConstant(node, jsDoubleNumber(a > b ? a : (b >= a ? b : a + b)));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(
+ typeOfDoubleMinMax(
+ forNode(node->child1()).m_type, forNode(node->child2()).m_type));
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -692,60 +855,129 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
JSValue child = forNode(node->child1()).value();
switch (node->child1().useKind()) {
case Int32Use:
- if (child && child.isInt32()) {
- JSValue result = jsNumber(fabs(child.asNumber()));
+ if (std::optional<double> number = child.toNumberFromPrimitive()) {
+ JSValue result = jsNumber(fabs(*number));
if (result.isInt32()) {
setConstant(node, result);
break;
}
}
- forNode(node).setType(SpecInt32);
- node->setCanExit(true);
+ forNode(node).setType(SpecInt32Only);
break;
- case NumberUse:
- if (child && child.isNumber()) {
- setConstant(node, jsNumber(child.asNumber()));
+ case DoubleRepUse:
+ if (std::optional<double> number = child.toNumberFromPrimitive()) {
+ setConstant(node, jsDoubleNumber(fabs(*number)));
break;
}
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(typeOfDoubleAbs(forNode(node->child1()).m_type));
break;
default:
- RELEASE_ASSERT_NOT_REACHED();
+ DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse);
+ forNode(node).setType(SpecFullNumber);
break;
}
break;
}
-
- case ArithSqrt: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, jsNumber(sqrt(child.asNumber())));
- 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(SpecDouble);
+ forNode(node).setType(typeOfDoublePow(forNode(node->child1()).m_type, forNode(node->child2()).m_type));
break;
}
-
- case ArithSin: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, jsNumber(sin(child.asNumber())));
- break;
- }
- forNode(node).setType(SpecDouble);
+
+ case ArithRandom: {
+ forNode(node).setType(m_graph, SpecDoubleReal);
break;
}
-
- case ArithCos: {
- JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, jsNumber(cos(child.asNumber())));
- break;
+
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
+ case ArithTrunc: {
+ JSValue operand = forNode(node->child1()).value();
+ if (std::optional<double> number = operand.toNumberFromPrimitive()) {
+ double roundedValue = 0;
+ if (node->op() == ArithRound)
+ roundedValue = jsRound(*number);
+ else if (node->op() == ArithFloor)
+ roundedValue = floor(*number);
+ else if (node->op() == ArithCeil)
+ roundedValue = ceil(*number);
+ else {
+ ASSERT(node->op() == ArithTrunc);
+ roundedValue = trunc(*number);
+ }
+
+ if (node->child1().useKind() == UntypedUse) {
+ setConstant(node, jsNumber(roundedValue));
+ break;
+ }
+ 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 (node->child1().useKind() == DoubleRepUse) {
+ if (producesInteger(node->arithRoundingMode()))
+ forNode(node).setType(SpecInt32Only);
+ else if (node->child1().useKind() == DoubleRepUse)
+ forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
+ } else {
+ DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse);
+ forNode(node).setType(SpecFullNumber);
}
- forNode(node).setType(SpecDouble);
break;
}
+ case ArithSqrt:
+ executeDoubleUnaryOpEffects(node, sqrt);
+ break;
+
+ case ArithFRound:
+ executeDoubleUnaryOpEffects(node, [](double value) -> double { return static_cast<float>(value); });
+ break;
+
+ case ArithSin:
+ executeDoubleUnaryOpEffects(node, sin);
+ break;
+
+ case ArithCos:
+ executeDoubleUnaryOpEffects(node, cos);
+ break;
+
+ case ArithTan:
+ executeDoubleUnaryOpEffects(node, tan);
+ break;
+
+ case ArithLog:
+ executeDoubleUnaryOpEffects(node, log);
+ break;
+
case LogicalNot: {
switch (booleanResult(node, forNode(node->child1()))) {
case DefinitelyTrue:
@@ -755,60 +987,110 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
setConstant(node, jsBoolean(true));
break;
default:
- switch (node->child1().useKind()) {
- case BooleanUse:
- case Int32Use:
- case NumberUse:
- case UntypedUse:
- case StringUse:
- break;
- case ObjectOrOtherUse:
- node->setCanExit(true);
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
forNode(node).setType(SpecBoolean);
break;
}
break;
}
-
+
+ case MapHash: {
+ if (JSValue key = forNode(node->child1()).value()) {
+ if (std::optional<uint32_t> hash = concurrentJSMapHash(key)) {
+ // Although C++ code uses uint32_t for the hash, the closest type in DFG IR is Int32
+ // and that's what MapHash returns. So, we have to cast to int32_t to avoid large
+ // unsigned values becoming doubles. This casting between signed and unsigned
+ // happens in the assembly code we emit when we don't constant fold this node.
+ setConstant(node, jsNumber(static_cast<int32_t>(*hash)));
+ break;
+ }
+ }
+ forNode(node).setType(SpecInt32Only);
+ break;
+ }
+
+ case ToLowerCase: {
+ forNode(node).setType(m_graph, SpecString);
+ break;
+ }
+
+ case LoadFromJSMapBucket:
+ forNode(node).makeHeapTop();
+ break;
+
+ case GetMapBucket:
+ forNode(node).setType(m_graph, SpecCellOther);
+ break;
+
+ case IsNonEmptyMapBucket:
+ forNode(node).setType(SpecBoolean);
+ break;
+
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
- case IsString:
case IsObject:
- case IsFunction: {
- node->setCanExit(
- node->op() == IsUndefined
- && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->codeOrigin));
- JSValue child = forNode(node->child1()).value();
- if (child) {
+ case IsObjectOrNull:
+ case IsFunction:
+ case IsCellWithType:
+ case IsTypedArrayView: {
+ AbstractValue child = forNode(node->child1());
+ if (child.value()) {
bool constantWasSet = true;
switch (node->op()) {
+ case IsCellWithType:
+ setConstant(node, jsBoolean(child.value().isCell() && child.value().asCell()->type() == node->queriedType()));
+ break;
case IsUndefined:
setConstant(node, jsBoolean(
- child.isCell()
- ? child.asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin))
- : child.isUndefined()));
+ child.value().isCell()
+ ? child.value().asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))
+ : child.value().isUndefined()));
break;
case IsBoolean:
- setConstant(node, jsBoolean(child.isBoolean()));
+ setConstant(node, jsBoolean(child.value().isBoolean()));
break;
case IsNumber:
- setConstant(node, jsBoolean(child.isNumber()));
- break;
- case IsString:
- setConstant(node, jsBoolean(isJSString(child)));
+ setConstant(node, jsBoolean(child.value().isNumber()));
break;
case IsObject:
- if (child.isNull() || !child.isObject()) {
- setConstant(node, jsBoolean(child.isNull()));
- break;
- }
- constantWasSet = false;
+ 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;
+ case IsEmpty:
+ setConstant(node, jsBoolean(child.value().isEmpty()));
+ break;
+ case IsTypedArrayView:
+ setConstant(node, jsBoolean(child.value().isObject() && isTypedView(child.value().getObject()->classInfo(m_vm)->typedArrayStorageType)));
break;
default:
constantWasSet = false;
@@ -817,58 +1099,205 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
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 IsEmpty: {
+ if (child.m_type && !(child.m_type & SpecEmpty)) {
+ setConstant(node, jsBoolean(false));
+ constantWasSet = true;
+ break;
+ }
+
+ if (child.m_type && !(child.m_type & ~SpecEmpty)) {
+ setConstant(node, jsBoolean(true));
+ constantWasSet = true;
+ break;
+ }
+
+ break;
+ }
+ 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 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 | SpecProxyObject))) {
+ setConstant(node, jsBoolean(false));
+ constantWasSet = true;
+ break;
+ }
+ break;
+ case IsCellWithType:
+ if (!(child.m_type & ~node->speculatedTypeForQuery())) {
+ setConstant(node, jsBoolean(true));
+ constantWasSet = true;
+ break;
+ }
+ if (!(child.m_type & node->speculatedTypeForQuery())) {
+ setConstant(node, jsBoolean(false));
+ constantWasSet = true;
+ break;
+ }
+ break;
+
+ case IsTypedArrayView:
+ if (!(child.m_type & ~SpecTypedArrayView)) {
+ setConstant(node, jsBoolean(true));
+ constantWasSet = true;
+ break;
+ }
+ if (!(child.m_type & SpecTypedArrayView)) {
+ 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->codeOrigin), child);
- setConstant(node, typeString);
+ JSValue typeString = jsTypeStringForValue(m_vm, m_codeBlock->globalObjectFor(node->origin.semantic), child);
+ setConstant(node, *m_graph.freeze(typeString));
break;
}
if (isFullNumberSpeculation(abstractChild.m_type)) {
- setConstant(node, vm->smallStrings.numberString());
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.numberString()));
break;
}
if (isStringSpeculation(abstractChild.m_type)) {
- setConstant(node, vm->smallStrings.stringString());
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.stringString()));
break;
}
-
- if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) {
- setConstant(node, vm->smallStrings.objectString());
+
+ // 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 - SpecFunction))) {
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.objectString()));
break;
}
if (isFunctionSpeculation(abstractChild.m_type)) {
- setConstant(node, vm->smallStrings.functionString());
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.functionString()));
break;
}
if (isBooleanSpeculation(abstractChild.m_type)) {
- setConstant(node, vm->smallStrings.booleanString());
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.booleanString()));
break;
}
- switch (node->child1().useKind()) {
- case StringUse:
- case CellUse:
- node->setCanExit(true);
- break;
- case UntypedUse:
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
+ if (isSymbolSpeculation(abstractChild.m_type)) {
+ setConstant(node, *m_graph.freeze(m_vm.smallStrings.symbolString()));
break;
}
- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+
+ forNode(node).setType(m_graph, SpecStringIdent);
break;
}
@@ -876,8 +1305,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
- case CompareEq:
- case CompareEqConstant: {
+ case CompareEq: {
JSValue leftConst = forNode(node->child1()).value();
JSValue rightConst = forNode(node->child2()).value();
if (leftConst && rightConst) {
@@ -907,90 +1335,198 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
- if (node->op() == CompareEq && leftConst.isString() && rightConst.isString()) {
+ if (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)));
+ bool result;
+ if (node->op() == CompareEq)
+ result = WTF::equal(a, b);
+ else if (node->op() == CompareLess)
+ result = codePointCompare(a, b) < 0;
+ else if (node->op() == CompareLessEq)
+ result = codePointCompare(a, b) <= 0;
+ else if (node->op() == CompareGreater)
+ result = codePointCompare(a, b) > 0;
+ else if (node->op() == CompareGreaterEq)
+ result = codePointCompare(a, b) >= 0;
+ else
+ RELEASE_ASSERT_NOT_REACHED();
+ setConstant(node, jsBoolean(result));
break;
}
}
+
+ if (node->op() == CompareEq && leftConst.isSymbol() && rightConst.isSymbol()) {
+ setConstant(node, jsBoolean(asSymbol(leftConst) == asSymbol(rightConst)));
+ break;
+ }
}
- if (node->op() == CompareEqConstant || node->op() == CompareEq) {
+ if (node->op() == CompareEq) {
SpeculatedType leftType = forNode(node->child1()).m_type;
SpeculatedType rightType = forNode(node->child2()).m_type;
- if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType))
- || (isOtherSpeculation(leftType) && isInt32Speculation(rightType))) {
+ 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;
+ }
+ }
}
- forNode(node).setType(SpecBoolean);
+ 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;
+ }
+ }
- // This is overly conservative. But the only thing this prevents is store elimination,
- // and how likely is it, really, that you'll have redundant stores across a comparison
- // operation? Comparison operations are typically at the end of basic blocks, so
- // unless we have global store elimination (super unlikely given how unprofitable that
- // optimization is to begin with), you aren't going to be wanting to store eliminate
- // across an equality op.
- node->setCanExit(true);
+ forNode(node).setType(SpecBoolean);
break;
}
- case CompareStrictEq:
- case CompareStrictEqConstant: {
+ 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.isNumber() && right.isNumber()) {
- setConstant(node, jsBoolean(left.asNumber() == right.asNumber()));
- break;
- }
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 CompareEqPtr: {
+ Node* childNode = node->child1().node();
+ JSValue childValue = forNode(childNode).value();
+ if (childValue) {
+ setConstant(node, jsBoolean(childValue.isCell() && childValue.asCell() == node->cellOperand()->cell()));
+ break;
+ }
+
forNode(node).setType(SpecBoolean);
- node->setCanExit(true); // This is overly conservative.
break;
}
case StringCharCodeAt:
- node->setCanExit(true);
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case StringFromCharCode:
- forNode(node).setType(SpecString);
+ forNode(node).setType(m_graph, SpecString);
break;
case StringCharAt:
- node->setCanExit(true);
- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ forNode(node).set(m_graph, m_vm.stringStructure.get());
break;
case GetByVal: {
- node->setCanExit(true);
switch (node->arrayMode().type()) {
case Array::SelectUsingPredictions:
case Array::Unprofiled:
- case Array::Undecided:
+ 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->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
break;
case Array::String:
@@ -1005,27 +1541,28 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
// 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->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
} else
- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ forNode(node).set(m_graph, m_vm.stringStructure.get());
break;
- case Array::Arguments:
+ case Array::DirectArguments:
+ case Array::ScopedArguments:
forNode(node).makeHeapTop();
break;
case Array::Int32:
if (node->arrayMode().isOutOfBounds()) {
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
} else
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Double:
if (node->arrayMode().isOutOfBounds()) {
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
} else if (node->arrayMode().isSaneChain())
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(SpecBytecodeDouble);
else
forNode(node).setType(SpecDoubleReal);
break;
@@ -1033,40 +1570,40 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
break;
case Array::Int8Array:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Int16Array:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Int32Array:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Uint8Array:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Uint8ClampedArray:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Uint16Array:
- forNode(node).setType(SpecInt32);
+ forNode(node).setType(SpecInt32Only);
break;
case Array::Uint32Array:
if (node->shouldSpeculateInt32())
- forNode(node).setType(SpecInt32);
- else if (enableInt52() && node->shouldSpeculateMachineInt())
- forNode(node).setType(SpecInt52);
+ forNode(node).setType(SpecInt32Only);
+ else if (enableInt52() && node->shouldSpeculateAnyInt())
+ forNode(node).setType(SpecAnyInt);
else
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(SpecAnyIntAsDouble);
break;
case Array::Float32Array:
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(SpecFullDouble);
break;
case Array::Float64Array:
- forNode(node).setType(SpecDouble);
+ forNode(node).setType(SpecFullDouble);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -1078,30 +1615,29 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case PutByValDirect:
case PutByVal:
case PutByValAlias: {
- node->setCanExit(true);
switch (node->arrayMode().modeForPut().type()) {
case Array::ForceExit:
m_state.setIsValid(false);
break;
case Array::Generic:
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
case Array::Int32:
if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
case Array::Double:
if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
case Array::Contiguous:
case Array::ArrayStorage:
if (node->arrayMode().isOutOfBounds())
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
case Array::SlowPutArrayStorage:
if (node->arrayMode().mayStoreToHole())
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
default:
break;
@@ -1110,25 +1646,121 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
case ArrayPush:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBytecodeNumber);
break;
+
+ case ArraySlice: {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+
+ // FIXME: We could do better here if we prove that the
+ // incoming value has only a single structure.
+ RegisteredStructureSet structureSet;
+ structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithInt32)));
+ structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous)));
+ structureSet.add(m_graph.registerStructure(globalObject->originalArrayStructureForIndexingType(ArrayWithDouble)));
+
+ forNode(node).set(m_graph, structureSet);
+ break;
+ }
case ArrayPop:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
break;
+
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds: {
+ 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, or to return undefined. We could implement that eventually.
- case RegExpExec:
+ unsigned argumentIndex = index.asUInt32() + node->numberOfArgumentsToSkip();
+ if (inlineCallFrame) {
+ if (argumentIndex < inlineCallFrame->arguments.size() - 1) {
+ forNode(node) = m_state.variables().operand(
+ virtualRegisterForArgument(argumentIndex + 1) + inlineCallFrame->stackOffset);
+ m_state.setFoundConstants(true);
+ break;
+ }
+ } else {
+ if (argumentIndex < m_state.variables().numberOfArguments() - 1) {
+ forNode(node) = m_state.variables().argument(argumentIndex + 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 = 1 + node->numberOfArgumentsToSkip(); i < inlineCallFrame->arguments.size(); ++i) {
+ result.merge(
+ m_state.variables().operand(
+ virtualRegisterForArgument(i) + inlineCallFrame->stackOffset));
+ }
+
+ if (node->op() == GetMyArgumentByValOutOfBounds)
+ result.merge(SpecOther);
+
+ if (result.value())
+ m_state.setFoundConstants(true);
+
+ forNode(node) = result;
+ break;
+ }
+
forNode(node).makeHeapTop();
break;
+ }
+
+ case RegExpExec:
+ if (node->child2().useKind() == RegExpObjectUse
+ && node->child3().useKind() == StringUse) {
+ // This doesn't clobber the world since there are no conversions to perform.
+ } else
+ clobberWorld(node->origin.semantic, clobberLimit);
+ if (JSValue globalObjectValue = forNode(node->child1()).m_value) {
+ if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(m_vm, globalObjectValue)) {
+ if (!globalObject->isHavingABadTime()) {
+ m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
+ Structure* structure = globalObject->regExpMatchesArrayStructure();
+ m_graph.registerStructure(structure);
+ forNode(node).set(m_graph, structure);
+ forNode(node).merge(SpecOther);
+ break;
+ }
+ }
+ }
+ forNode(node).setType(m_graph, SpecOther | SpecArray);
+ break;
case RegExpTest:
+ if (node->child2().useKind() == RegExpObjectUse
+ && node->child3().useKind() == StringUse) {
+ // This doesn't clobber the world since there are no conversions to perform.
+ } else
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBoolean);
break;
+ case StringReplace:
+ case StringReplaceRegExp:
+ 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_vm.stringStructure.get());
+ break;
+
case Jump:
break;
@@ -1147,7 +1779,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
// constant propagation, but we can do better:
// We can specialize the source variable's value on each direction of
// the branch.
- node->setCanExit(true); // This is overly conservative.
m_state.setBranchDirection(TakeBoth);
break;
}
@@ -1161,11 +1792,18 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case Return:
m_state.setIsValid(false);
break;
+
+ case TailCall:
+ case DirectTailCall:
+ case TailCallVarargs:
+ case TailCallForwardVarargs:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ m_state.setIsValid(false);
+ break;
case Throw:
- case ThrowReferenceError:
+ case ThrowStaticError:
m_state.setIsValid(false);
- node->setCanExit(true);
break;
case ToPrimitive: {
@@ -1177,66 +1815,70 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
ASSERT(node->child1().useKind() == UntypedUse);
- AbstractValue& source = forNode(node->child1());
- AbstractValue& destination = forNode(node);
+ if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))) {
+ m_state.setFoundConstants(true);
+ forNode(node) = forNode(node->child1());
+ break;
+ }
- // NB. The more canonical way of writing this would have been:
- //
- // destination = source;
- // if (destination.m_type & !(SpecFullNumber | SpecString | SpecBoolean)) {
- // destination.filter(SpecFullNumber | SpecString | SpecBoolean);
- // AbstractValue string;
- // string.set(vm->stringStructure);
- // destination.merge(string);
- // }
- //
- // The reason why this would, in most other cases, have been better is that
- // then destination would preserve any non-SpeculatedType knowledge of source.
- // As it stands, the code below forgets any non-SpeculatedType knowledge that
- // source would have had. Fortunately, though, for things like strings and
- // numbers and booleans, we don't care about the non-SpeculatedType knowedge:
- // the structure won't tell us anything we don't already know, and neither
- // will ArrayModes. And if the source was a meaningful constant then we
- // would have handled that above. Unfortunately, this does mean that
- // ToPrimitive will currently forget string constants. But that's not a big
- // deal since we don't do any optimization on those currently.
-
- clobberWorld(node->codeOrigin, clobberLimit);
-
- SpeculatedType type = source.m_type;
- if (type & ~(SpecFullNumber | SpecString | SpecBoolean))
- type = (SpecHeapTop & ~SpecCell) | SpecString;
-
- destination.setType(type);
- if (destination.isClear())
- m_state.setIsValid(false);
+ clobberWorld(node->origin.semantic, clobberLimit);
+
+ forNode(node).setType(m_graph, SpecHeapTop & ~SpecObject);
+ break;
+ }
+
+ case ToNumber: {
+ 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 & ~SpecBytecodeNumber)) {
+ m_state.setFoundConstants(true);
+ forNode(node) = forNode(node->child1());
+ break;
+ }
+
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, SpecBytecodeNumber);
break;
}
- case ToString: {
+ 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->codeOrigin)->stringObjectStructure());
- node->setCanExit(true); // We could be more precise but it's likely not worth it.
+ m_graph.registerStructure(m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure()));
break;
case StringOrStringObjectUse:
- node->setCanExit(true); // We could be more precise but it's likely not worth it.
+ case Int32Use:
+ case Int52RepUse:
+ case DoubleRepUse:
+ case NotCellUse:
break;
case CellUse:
case UntypedUse:
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ forNode(node).set(m_graph, m_vm.stringStructure.get());
break;
}
+
+ case NumberToStringWithRadix:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+ break;
case NewStringObject: {
ASSERT(node->structure()->classInfo() == StringObject::info());
@@ -1245,25 +1887,40 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
case NewArray:
- node->setCanExit(true);
forNode(node).set(
m_graph,
- m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
- m_state.setHaveStructures(true);
+ m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
+ break;
+
+ case NewArrayWithSpread:
+ if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+ // We've compiled assuming we're not having a bad time, so to be consistent
+ // with StructureRegisterationPhase we must say we produce an original array
+ // allocation structure.
+ forNode(node).set(
+ m_graph,
+ m_graph.globalObjectFor(node->origin.semantic)->originalArrayStructureForIndexingType(ArrayWithContiguous));
+ } else {
+ forNode(node).set(
+ m_graph,
+ m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous));
+ }
+
+ break;
+
+ case Spread:
+ forNode(node).set(
+ m_graph, m_vm.fixedArrayStructure.get());
break;
case NewArrayBuffer:
- node->setCanExit(true);
forNode(node).set(
m_graph,
- m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
- m_state.setHaveStructures(true);
+ m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
break;
case NewArrayWithSize:
- node->setCanExit(true);
- forNode(node).setType(SpecArray);
- m_state.setHaveStructures(true);
+ forNode(node).setType(m_graph, SpecArray);
break;
case NewTypedArray:
@@ -1271,7 +1928,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case Int32Use:
break;
case UntypedUse:
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
default:
RELEASE_ASSERT_NOT_REACHED();
@@ -1279,21 +1936,26 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
forNode(node).set(
m_graph,
- m_graph.globalObjectFor(node->codeOrigin)->typedArrayStructure(
+ m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructureConcurrently(
node->typedArrayType()));
- m_state.setHaveStructures(true);
break;
-
+
case NewRegexp:
- forNode(node).set(m_graph, m_graph.globalObjectFor(node->codeOrigin)->regExpStructure());
- m_state.setHaveStructures(true);
+ 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 (m_graph.executableFor(node->codeOrigin)->isStrictMode())
+ bool strictMode = m_graph.executableFor(node->origin.semantic)->isStrictMode();
+
+ if (isToThisAnIdentity(strictMode, source)) {
+ m_state.setFoundConstants(true);
+ destination = source;
+ break;
+ }
+
+ if (strictMode)
destination.makeHeapTop();
else {
destination = source;
@@ -1303,248 +1965,415 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
}
case CreateThis: {
- forNode(node).setType(SpecFinalObject);
+ // FIXME: We can fold this to NewObject if the incoming callee is a constant.
+ forNode(node).setType(m_graph, SpecFinalObject);
break;
}
- case AllocationProfileWatchpoint:
- node->setCanExit(true);
- break;
-
case NewObject:
- ASSERT(node->structure());
+ ASSERT(!!node->structure().get());
forNode(node).set(m_graph, node->structure());
- m_state.setHaveStructures(true);
+ break;
+
+ case CallObjectConstructor: {
+ AbstractValue& source = forNode(node->child1());
+ AbstractValue& destination = forNode(node);
+
+ if (!(source.m_type & ~SpecObject)) {
+ m_state.setFoundConstants(true);
+ destination = source;
+ break;
+ }
+
+ forNode(node).setType(m_graph, SpecObject);
+ break;
+ }
+
+ case PhantomNewObject:
+ case PhantomNewFunction:
+ case PhantomNewGeneratorFunction:
+ case PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case PhantomDirectArguments:
+ case PhantomClonedArguments:
+ case PhantomCreateRest:
+ case PhantomSpread:
+ case PhantomNewArrayWithSpread:
+ case BottomValue:
+ m_state.setDidClobber(true); // Prevent constant folding.
+ // This claims to return bottom.
+ break;
+
+ case PutHint:
break;
+ case MaterializeNewObject: {
+ forNode(node).set(m_graph, node->structureSet());
+ break;
+ }
+
case CreateActivation:
+ case MaterializeCreateActivation:
forNode(node).set(
- m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->activationStructure());
- m_state.setHaveStructures(true);
+ m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->activationStructure());
break;
- case FunctionReentryWatchpoint:
- case TypedArrayWatchpoint:
+ case CreateDirectArguments:
+ forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->directArgumentsStructure());
break;
-
- case CreateArguments:
- forNode(node) = forNode(node->child1());
- forNode(node).filter(~SpecEmpty);
- forNode(node).merge(SpecArguments);
+
+ case CreateScopedArguments:
+ forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->scopedArgumentsStructure());
break;
- case TearOffActivation:
- case TearOffArguments:
- // Does nothing that is user-visible.
+ case CreateClonedArguments:
+ if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+ forNode(node).setType(m_graph, SpecObject);
+ break;
+ }
+ forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->clonedArgumentsStructure());
break;
- case CheckArgumentsNotCreated:
- if (isEmptySpeculation(
- m_state.variables().operand(
- m_graph.argumentsRegisterFor(node->codeOrigin).offset()).m_type))
- m_state.setFoundConstants(true);
- else
- node->setCanExit(true);
+ case NewGeneratorFunction:
+ forNode(node).set(
+ m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->generatorFunctionStructure());
break;
-
- case GetMyArgumentsLength:
- // We know that this executable does not escape its arguments, so we can optimize
- // the arguments a bit. Note that this is not sufficient to force constant folding
- // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation.
- // We perform further optimizations on this later on.
- if (node->codeOrigin.inlineCallFrame) {
- forNode(node).set(
- m_graph, jsNumber(node->codeOrigin.inlineCallFrame->arguments.size() - 1));
- } else
- forNode(node).setType(SpecInt32);
- node->setCanExit(
- !isEmptySpeculation(
- m_state.variables().operand(
- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type));
- break;
-
- case GetMyArgumentsLengthSafe:
- // This potentially clobbers all structures if the arguments object had a getter
- // installed on the length property.
- clobberWorld(node->codeOrigin, clobberLimit);
- // We currently make no guarantee about what this returns because it does not
- // speculate that the length property is actually a length.
- forNode(node).makeHeapTop();
+
+ case NewAsyncFunction:
+ forNode(node).set(
+ m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->asyncFunctionStructure());
+ break;
+
+ case NewFunction:
+ forNode(node).set(
+ m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure());
break;
- case GetMyArgumentByVal:
- node->setCanExit(true);
- // We know that this executable does not escape its arguments, so we can optimize
- // the arguments a bit. Note that this ends up being further optimized by the
- // ArgumentsSimplificationPhase.
- forNode(node).makeHeapTop();
+ case GetCallee:
+ if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_vm, 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 GetMyArgumentByValSafe:
- node->setCanExit(true);
- // This potentially clobbers all structures if the property we're accessing has
- // a getter. We don't speculate against this.
- clobberWorld(node->codeOrigin, clobberLimit);
- // And the result is unknown.
- forNode(node).makeHeapTop();
+ case GetArgumentCountIncludingThis:
+ forNode(node).setType(SpecInt32Only);
break;
- case NewFunction: {
- AbstractValue& value = forNode(node);
- value = forNode(node->child1());
+ case GetRestLength:
+ forNode(node).setType(SpecInt32Only);
+ break;
- if (!(value.m_type & SpecEmpty)) {
- m_state.setFoundConstants(true);
- 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;
+ }
}
-
- value.setType((value.m_type & ~SpecEmpty) | SpecFunction);
+
+ forNode(node).setType(m_graph, SpecObject);
break;
}
-
- case NewFunctionExpression:
- case NewFunctionNoCheck:
- forNode(node).set(
- m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->functionStructure());
- break;
- case GetCallee:
- forNode(node).setType(SpecFunction);
+ 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: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202
- case GetMyScope:
- case SkipTopScope:
- forNode(node).setType(SpecObjectOther);
+ case GetScope:
+ if (JSValue base = forNode(node->child1()).m_value) {
+ if (JSFunction* function = jsDynamicCast<JSFunction*>(m_vm, 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, JSValue(jsCast<JSScope*>(child.asCell())->next()));
+ setConstant(node, *m_graph.freeze(JSValue(jsCast<JSScope*>(child.asCell())->next())));
break;
}
- forNode(node).setType(SpecObjectOther);
+ forNode(node).setType(m_graph, SpecObjectOther);
break;
}
- case GetClosureRegisters:
- forNode(node).clear(); // The result is not a JS value.
+ case GetGlobalObject: {
+ JSValue child = forNode(node->child1()).value();
+ if (child) {
+ setConstant(node, *m_graph.freeze(JSValue(asObject(child)->globalObject())));
+ break;
+ }
+
+ if (forNode(node->child1()).m_structure.isFinite()) {
+ JSGlobalObject* globalObject = nullptr;
+ bool ok = true;
+ forNode(node->child1()).m_structure.forEach(
+ [&] (RegisteredStructure structure) {
+ if (!globalObject)
+ globalObject = structure->globalObject();
+ else if (globalObject != structure->globalObject())
+ ok = false;
+ });
+ if (globalObject && ok) {
+ setConstant(node, *m_graph.freeze(JSValue(globalObject)));
+ break;
+ }
+ }
+
+ forNode(node).setType(m_graph, SpecObjectOther);
break;
+ }
case GetClosureVar:
- forNode(node).makeHeapTop();
+ if (JSValue value = m_graph.tryGetConstantClosureVar(forNode(node->child1()), node->scopeOffset())) {
+ setConstant(node, *m_graph.freeze(value));
+ break;
+ }
+ forNode(node).makeBytecodeTop();
break;
case PutClosureVar:
- clobberCapturedVars(node->codeOrigin);
break;
-
+
+ case GetRegExpObjectLastIndex:
+ forNode(node).makeHeapTop();
+ break;
+
+ case SetRegExpObjectLastIndex:
+ case RecordRegExpCachedResult:
+ break;
+
+ case GetFromArguments:
+ forNode(node).makeHeapTop();
+ break;
+
+ case PutToArguments:
+ break;
+
+ case GetArgument:
+ forNode(node).makeHeapTop();
+ break;
+
+ case TryGetById:
+ // FIXME: This should constant fold at least as well as the normal GetById case.
+ // https://bugs.webkit.org/show_bug.cgi?id=156422
+ forNode(node).makeHeapTop();
+ break;
+
case GetById:
- case GetByIdFlush:
- node->setCanExit(true);
+ case GetByIdFlush: {
if (!node->prediction()) {
m_state.setIsValid(false);
break;
}
- if (isCellSpeculation(node->child1()->prediction())) {
- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
- GetByIdStatus status = GetByIdStatus::computeFor(
- m_graph.m_vm, structure,
- m_graph.identifiers()[node->identifierNumber()]);
- if (status.isSimple()) {
- // Assert things that we can't handle and that the computeFor() method
- // above won't be able to return.
- ASSERT(status.structureSet().size() == 1);
- ASSERT(!status.chain());
-
- if (status.specificValue())
- setConstant(node, status.specificValue());
- else
- forNode(node).makeHeapTop();
- filter(node->child1(), status.structureSet());
-
- m_state.setFoundConstants(true);
- m_state.setHaveStructures(true);
- 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.toStructureSet(), 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->codeOrigin, clobberLimit);
+
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
break;
-
- case GetArrayLength:
- node->setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough.
- forNode(node).setType(SpecInt32);
+ }
+
+ case GetByValWithThis:
+ case GetByIdWithThis:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).makeHeapTop();
break;
-
- case CheckExecutable: {
- // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks
- // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200
- // FIXME: We could eliminate these entirely if we know the exact value that flows into this.
- // https://bugs.webkit.org/show_bug.cgi?id=106201
- node->setCanExit(true);
+
+ 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(SpecInt32Only);
break;
}
+ case DeleteById:
+ case DeleteByVal: {
+ // FIXME: This could decide if the delete will be successful based on the set of structures that
+ // we get from our base value. https://bugs.webkit.org/show_bug.cgi?id=156611
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(SpecBoolean);
+ break;
+ }
+
case CheckStructure: {
- // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
AbstractValue& value = forNode(node->child1());
- ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
-
- StructureSet& set = node->structureSet();
- if (value.m_currentKnownStructure.isSubsetOf(set)) {
+ const RegisteredStructureSet& 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);
- break;
- }
- node->setCanExit(true);
- m_state.setHaveStructures(true);
-
- // If this structure check is attempting to prove knowledge already held in
- // the futurePossibleStructure set then the constant folding phase should
- // turn this into a watchpoint instead.
- if (value.m_futurePossibleStructure.isSubsetOf(set)
- && value.m_futurePossibleStructure.hasSingleton()) {
- m_state.setFoundConstants(true);
- filter(value, value.m_futurePossibleStructure.singleton());
+ 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);
+
+ filter(value, set, admittedTypes);
break;
}
- case StructureTransitionWatchpoint: {
+ 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());
-
- filter(value, node->structure());
- m_state.setHaveStructures(true);
- node->setCanExit(true);
+ const RegisteredStructureSet& set = node->structureSet();
+
+ if (value.value()) {
+ if (Structure* structure = jsDynamicCast<Structure*>(m_vm, value.value())) {
+ if (set.contains(m_graph.registerStructure(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*>(m_vm)) {
+ if (set.contains(m_graph.registerStructure(structure)))
+ return;
+ }
+ allGood = false;
+ });
+ if (allGood) {
+ m_state.setFoundConstants(true);
+ break;
+ }
+ }
+
+ if (RegisteredStructure structure = set.onlyStructure()) {
+ filterByValue(node->child1(), *m_graph.freeze(structure.get()));
+ break;
+ }
+
+ // Aw shucks, we can't do anything!
break;
}
-
+
case PutStructure:
- case PhantomPutStructure:
- if (!forNode(node->child1()).m_currentKnownStructure.isClear()) {
- clobberStructures(clobberLimit);
- forNode(node->child1()).set(m_graph, node->structureTransitionData().newStructure);
- m_state.setHaveStructures(true);
+ 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 AllocatePropertyStorage:
case ReallocatePropertyStorage:
+ case NukeStructureAndSetButterfly:
+ // FIXME: We don't model the fact that the structureID is nuked, simply because currently
+ // nobody would currently benefit from having that information. But it's a bug nonetheless.
forNode(node).clear(); // The result is not a JS value.
break;
+ case CheckDOM: {
+ JSValue constant = forNode(node->child1()).value();
+ if (constant) {
+ if (constant.isCell() && constant.asCell()->inherits(m_vm, node->classInfo())) {
+ m_state.setFoundConstants(true);
+ ASSERT(constant);
+ break;
+ }
+ }
+
+ AbstractValue& value = forNode(node->child1());
+
+ if (value.m_structure.isSubClassOf(node->classInfo()))
+ m_state.setFoundConstants(true);
+
+ filterClassInfo(value, node->classInfo());
+ break;
+ }
+ case CallDOMGetter: {
+ CallDOMGetterData* callDOMGetterData = node->callDOMGetterData();
+ DOMJIT::CallDOMGetterPatchpoint* patchpoint = callDOMGetterData->patchpoint;
+ if (patchpoint->effect.writes)
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, callDOMGetterData->domJIT->resultType());
+ break;
+ }
+ case CallDOM: {
+ const DOMJIT::Signature* signature = node->signature();
+ if (signature->effect.writes)
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, signature->result);
+ break;
+ }
case CheckArray: {
if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) {
m_state.setFoundConstants(true);
break;
}
- node->setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here.
switch (node->arrayMode().type()) {
case Array::String:
filter(node->child1(), SpecString);
@@ -1552,11 +2381,15 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
case Array::Int32:
case Array::Double:
case Array::Contiguous:
+ case Array::Undecided:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
break;
- case Array::Arguments:
- filter(node->child1(), SpecArguments);
+ case Array::DirectArguments:
+ filter(node->child1(), SpecDirectArguments);
+ break;
+ case Array::ScopedArguments:
+ filter(node->child1(), SpecScopedArguments);
break;
case Array::Int8Array:
filter(node->child1(), SpecInt8Array);
@@ -1585,12 +2418,14 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
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());
- m_state.setHaveStructures(true);
break;
}
case Arrayify: {
@@ -1598,59 +2433,269 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
m_state.setFoundConstants(true);
break;
}
- ASSERT(node->arrayMode().conversion() == Array::Convert
- || node->arrayMode().conversion() == Array::RageConvert);
- node->setCanExit(true);
+ ASSERT(node->arrayMode().conversion() == Array::Convert);
clobberStructures(clobberLimit);
filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering());
- m_state.setHaveStructures(true);
break;
}
case ArrayifyToStructure: {
AbstractValue& value = forNode(node->child1());
- StructureSet set = node->structure();
- if (value.m_futurePossibleStructure.isSubsetOf(set)
- || value.m_currentKnownStructure.isSubsetOf(set))
+ if (value.m_structure.isSubsetOf(RegisteredStructureSet(node->structure())))
m_state.setFoundConstants(true);
- node->setCanExit(true);
clobberStructures(clobberLimit);
- filter(value, set);
- m_state.setHaveStructures(true);
+
+ // 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 GetIndexedPropertyStorage:
case ConstantStoragePointer: {
forNode(node).clear();
break;
}
case GetTypedArrayByteOffset: {
- forNode(node).setType(SpecInt32);
+ JSArrayBufferView* view = m_graph.tryGetFoldableView(forNode(node->child1()).m_value);
+ if (view) {
+ setConstant(node, jsNumber(view->byteOffset()));
+ break;
+ }
+ forNode(node).setType(SpecInt32Only);
break;
}
case GetByOffset: {
- forNode(node).makeHeapTop();
+ 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*>(m_vm, result)) {
+ setConstant(node, *m_graph.freeze(result));
+ break;
+ }
+
+ forNode(node).set(m_graph, m_graph.globalObjectFor(node->origin.semantic)->getterSetterStructure());
+ 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());
+ RegisteredStructureSet baseSet;
+ AbstractValue result;
+ for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) {
+ RegisteredStructureSet 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: {
+ RegisteredStructureSet 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];
+ RegisteredStructureSet thisSet = *m_graph.addStructureSet(variant.oldStructure());
+ thisSet.filter(base);
+ if (thisSet.isEmpty())
+ continue;
+
+ AbstractValue thisValue = originalValue;
+ thisValue.filter(m_graph, variant.requiredType());
+ resultingValue.merge(thisValue);
- case CheckFunction: {
+ if (variant.kind() == PutByIdVariant::Transition) {
+ RegisteredStructure newStructure = m_graph.registerStructure(variant.newStructure());
+ if (thisSet.onlyStructure() != newStructure) {
+ transitions.append(
+ Transition(m_graph.registerStructure(variant.oldStructureForTransition()), newStructure));
+ } // else this is really a replace.
+ newSet.add(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*>(m_vm, 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->function()) {
+ if (value == node->cellOperand()->value()) {
m_state.setFoundConstants(true);
ASSERT(value);
break;
}
-
- node->setCanExit(true); // Lies! We can do better.
- filterByValue(node->child1(), node->function());
+ 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 CheckStringIdent: {
+ AbstractValue& value = forNode(node->child1());
+ UniquedStringImpl* uid = node->uidOperand();
+ ASSERT(!(value.m_type & ~SpecStringIdent)); // Edge filtering should have already ensured this.
+
+ JSValue childConstant = value.value();
+ if (childConstant) {
+ ASSERT(childConstant.isString());
+ if (asString(childConstant)->tryGetValueImpl() == uid) {
+ m_state.setFoundConstants(true);
+ break;
+ }
+ }
+
+ filter(value, SpecStringIdent);
+ break;
+ }
+
case CheckInBounds: {
JSValue left = forNode(node->child1()).value();
JSValue right = forNode(node->child2()).value();
@@ -1659,79 +2704,191 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
m_state.setFoundConstants(true);
break;
}
-
- node->setCanExit(true);
break;
}
case PutById:
- case PutByIdDirect:
- node->setCanExit(true);
- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
+ case PutByIdFlush:
+ case PutByIdDirect: {
+ AbstractValue& value = forNode(node->child1());
+ if (value.m_structure.isFinite()) {
PutByIdStatus status = PutByIdStatus::computeFor(
- m_graph.m_vm,
- m_graph.globalObjectFor(node->codeOrigin),
- structure,
+ m_graph.globalObjectFor(node->origin.semantic),
+ value.m_structure.toStructureSet(),
m_graph.identifiers()[node->identifierNumber()],
node->op() == PutByIdDirect);
- if (status.isSimpleReplace()) {
- filter(node->child1(), structure);
- m_state.setFoundConstants(true);
- m_state.setHaveStructures(true);
- break;
- }
- if (status.isSimpleTransition()) {
- clobberStructures(clobberLimit);
- forNode(node->child1()).set(m_graph, status.newStructure());
- m_state.setHaveStructures(true);
- m_state.setFoundConstants(true);
+
+ if (status.isSimple()) {
+ RegisteredStructureSet newSet;
+ TransitionVector transitions;
+
+ for (unsigned i = status.numVariants(); i--;) {
+ const PutByIdVariant& variant = status[i];
+ if (variant.kind() == PutByIdVariant::Transition) {
+ RegisteredStructure newStructure = m_graph.registerStructure(variant.newStructure());
+ transitions.append(
+ Transition(
+ m_graph.registerStructure(variant.oldStructureForTransition()), newStructure));
+ newSet.add(newStructure);
+ } else {
+ ASSERT(variant.kind() == PutByIdVariant::Replace);
+ newSet.merge(*m_graph.addStructureSet(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->codeOrigin, clobberLimit);
+
+ clobberWorld(node->origin.semantic, clobberLimit);
+ break;
+ }
+
+ case PutByValWithThis:
+ case PutByIdWithThis:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ break;
+
+ case PutGetterById:
+ case PutSetterById:
+ case PutGetterSetterById:
+ case PutGetterByVal:
+ case PutSetterByVal: {
+ clobberWorld(node->origin.semantic, clobberLimit);
+ break;
+ }
+
+ case DefineDataProperty:
+ case DefineAccessorProperty:
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
- case In:
+ case In: {
// FIXME: We can determine when the property definitely exists based on abstract
// value information.
- clobberWorld(node->codeOrigin, clobberLimit);
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBoolean);
break;
+ }
+
+ case HasOwnProperty: {
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(SpecBoolean);
+ break;
+ }
+ case GetEnumerableLength: {
+ forNode(node).setType(SpecInt32Only);
+ 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 VariableWatchpoint:
- case VarInjectionWatchpoint:
- node->setCanExit(true);
+
+ case GetGlobalLexicalVariable:
+ forNode(node).makeBytecodeTop();
break;
-
- case PutGlobalVar:
+
+ case GetDynamicVar:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).makeBytecodeTop();
+ break;
+
+ case PutDynamicVar:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ break;
+
+ case ResolveScope:
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, SpecObject);
+ break;
+
+ case PutGlobalVariable:
case NotifyWrite:
break;
- case CheckHasInstance:
- node->setCanExit(true);
- // Sadly, we don't propagate the fact that we've done CheckHasInstance
+ case OverridesHasInstance:
+ forNode(node).setType(SpecBoolean);
break;
case InstanceOf:
- node->setCanExit(true);
- // Again, sadly, we don't propagate the fact that we've done 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.
+ forNode(node) = forNode(NodeFlowProjection(node, NodeFlowProjection::Shadow));
+ // 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());
- AbstractValue& value = forNode(node->child1());
- forNode(node) = value;
- forNode(node->phi()) = value;
+ NodeFlowProjection shadow(node->phi(), NodeFlowProjection::Shadow);
+ if (shadow.isStillValid()) {
+ m_state.createValueForNode(shadow);
+ forNode(shadow) = forNode(node->child1());
+ }
break;
}
@@ -1740,62 +2897,125 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
case Call:
+ case TailCallInlinedCaller:
case Construct:
- node->setCanExit(true);
- clobberWorld(node->codeOrigin, clobberLimit);
+ case CallVarargs:
+ case CallForwardVarargs:
+ case TailCallVarargsInlinedCaller:
+ case ConstructVarargs:
+ case ConstructForwardVarargs:
+ case TailCallForwardVarargsInlinedCaller:
+ case CallEval:
+ case DirectCall:
+ case DirectConstruct:
+ case DirectTailCallInlinedCaller:
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).makeHeapTop();
break;
case ForceOSRExit:
- node->setCanExit(true);
+ case CheckBadCell:
m_state.setIsValid(false);
break;
case InvalidationPoint:
- node->setCanExit(true);
+ forAllValues(clobberLimit, AbstractValue::observeInvalidationPointFor);
+ m_state.setStructureClobberState(StructuresAreWatched);
break;
case CheckWatchdogTimer:
- node->setCanExit(true);
+ case LogShadowChickenPrologue:
+ case LogShadowChickenTail:
break;
- case Breakpoint:
- case ProfileWillCall:
- case ProfileDidCall:
+ case ProfileType:
+ case ProfileControlFlow:
case Phantom:
- case Check:
case CountExecution:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
+ case CheckTypeInfoFlags:
break;
- case ConditionalStoreBarrier: {
- if (!needsTypeCheck(node->child2().node(), ~SpecCell))
- m_state.setFoundConstants(true);
- filter(node->child1(), SpecCell);
+ case ParseInt: {
+ AbstractValue value = forNode(node->child1());
+ if (value.m_type && !(value.m_type & ~SpecInt32Only)) {
+ JSValue radix;
+ if (!node->child2())
+ radix = jsNumber(0);
+ else
+ radix = forNode(node->child2()).m_value;
+
+ if (radix.isNumber()
+ && (radix.asNumber() == 0 || radix.asNumber() == 10)) {
+ m_state.setFoundConstants(true);
+ forNode(node).setType(SpecInt32Only);
+ break;
+ }
+ }
+
+ if (node->child1().useKind() == UntypedUse)
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, SpecBytecodeNumber);
break;
}
- case StoreBarrier: {
- filter(node->child1(), SpecCell);
+ case CreateRest:
+ if (!m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+ // This means we're already having a bad time.
+ clobberWorld(node->origin.semantic, clobberLimit);
+ forNode(node).setType(m_graph, SpecArray);
+ break;
+ }
+ forNode(node).set(
+ m_graph,
+ m_graph.globalObjectFor(node->origin.semantic)->restParameterStructure());
+ 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 StoreBarrierWithNullCheck: {
+ case SetFunctionName: {
+ clobberWorld(node->origin.semantic, clobberLimit);
break;
}
+ case StoreBarrier:
+ case FencedStoreBarrier: {
+ filter(node->child1(), SpecCell);
+ break;
+ }
+
case CheckTierUpAndOSREnter:
case LoopHint:
- // We pretend that it can exit because it may want to get all state.
- node->setCanExit(true);
+ case ZombieHint:
+ case ExitOK:
break;
- case ZombieHint:
case Unreachable:
+ // It may be that during a previous run of AI we proved that something was unreachable, but
+ // during this run of AI we forget that it's unreachable. AI's proofs don't have to get
+ // monotonically stronger over time. So, we don't assert that AI doesn't reach the
+ // Unreachable. We have no choice but to take our past proof at face value. Otherwise we'll
+ // crash whenever AI fails to be as powerful on run K as it was on run K-1.
+ m_state.setIsValid(false);
+ break;
+
case LastNodeType:
case ArithIMul:
- RELEASE_ASSERT_NOT_REACHED();
+ case FiatInt52:
+ DFG_CRASH(m_graph, node, "Unexpected node type");
break;
}
@@ -1812,9 +3032,8 @@ template<typename AbstractStateType>
bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock)
{
Node* node = m_state.block()->at(indexInBlock);
- if (!startExecuting(node))
- return true;
-
+
+ startExecuting();
executeEdges(node);
return executeEffects(indexInBlock, node);
}
@@ -1822,79 +3041,99 @@ bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock)
template<typename AbstractStateType>
bool AbstractInterpreter<AbstractStateType>::execute(Node* node)
{
- if (!startExecuting(node))
- return true;
-
+ startExecuting();
executeEdges(node);
return executeEffects(UINT_MAX, node);
}
template<typename AbstractStateType>
void AbstractInterpreter<AbstractStateType>::clobberWorld(
- const CodeOrigin& codeOrigin, unsigned clobberLimit)
+ const CodeOrigin&, unsigned clobberLimit)
{
- clobberCapturedVars(codeOrigin);
clobberStructures(clobberLimit);
}
template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::clobberCapturedVars(const CodeOrigin& codeOrigin)
-{
- if (codeOrigin.inlineCallFrame) {
- const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars;
- for (size_t i = capturedVars.size(); i--;) {
- if (!capturedVars.quickGet(i))
- continue;
- m_state.variables().local(i).makeHeapTop();
- }
- } else {
- for (size_t i = m_codeBlock->m_numVars; i--;) {
- if (m_codeBlock->isCaptured(virtualRegisterForLocal(i)))
- m_state.variables().local(i).makeHeapTop();
- }
- }
-
- for (size_t i = m_state.variables().numberOfArguments(); i--;) {
- if (m_codeBlock->isCaptured(virtualRegisterForArgument(i)))
- m_state.variables().argument(i).makeHeapTop();
- }
-}
-
-template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit)
+template<typename Functor>
+void AbstractInterpreter<AbstractStateType>::forAllValues(
+ unsigned clobberLimit, Functor& functor)
{
- if (!m_state.haveStructures())
- return;
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--;)
- forNode(m_state.block()->at(i)).clobberStructures();
+ for (size_t i = clobberLimit; i--;) {
+ NodeFlowProjection::forEach(
+ m_state.block()->at(i),
+ [&] (NodeFlowProjection nodeProjection) {
+ functor(forNode(nodeProjection));
+ });
+ }
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)
- forNode(*iter).clobberStructures();
+ for (NodeFlowProjection node : m_state.block()->ssa->liveAtHead) {
+ if (node.isStillValid())
+ functor(forNode(node));
+ }
}
for (size_t i = m_state.variables().numberOfArguments(); i--;)
- m_state.variables().argument(i).clobberStructures();
+ functor(m_state.variables().argument(i));
for (size_t i = m_state.variables().numberOfLocals(); i--;)
- m_state.variables().local(i).clobberStructures();
- m_state.setHaveStructures(true);
+ functor(m_state.variables().local(i));
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit)
+{
+ forAllValues(clobberLimit, AbstractValue::clobberStructuresFor);
+ setDidClobber();
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::observeTransition(
+ unsigned clobberLimit, RegisteredStructure from, RegisteredStructure 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<NodeFlowProjection> 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;
+ for (NodeFlowProjection node : m_state.block()->ssa->liveAtHead) {
+ seen.add(node);
AbstractValue& value = forNode(node);
if (value.isClear())
continue;
@@ -1902,19 +3141,32 @@ void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out)
}
}
for (size_t i = 0; i < m_state.block()->size(); ++i) {
- Node* node = m_state.block()->at(i);
- AbstractValue& value = forNode(node);
- if (value.isClear())
- continue;
- out.print(comma, node, ":", value);
+ NodeFlowProjection::forEach(
+ m_state.block()->at(i), [&] (NodeFlowProjection nodeProjection) {
+ seen.add(nodeProjection);
+ AbstractValue& value = forNode(nodeProjection);
+ if (value.isClear())
+ return;
+ out.print(comma, nodeProjection, ":", value);
+ });
+ }
+ if (m_graph.m_form == SSA) {
+ for (NodeFlowProjection node : m_state.block()->ssa->liveAtTail) {
+ 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)
+ AbstractValue& value, const RegisteredStructureSet& set, SpeculatedType admittedTypes)
{
- if (value.filter(m_graph, set) == FiltrationOK)
+ if (value.filter(m_graph, set, admittedTypes) == FiltrationOK)
return FiltrationOK;
m_state.setIsValid(false);
return Contradiction;
@@ -1942,7 +3194,7 @@ FiltrationResult AbstractInterpreter<AbstractStateType>::filter(
template<typename AbstractStateType>
FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue(
- AbstractValue& abstractValue, JSValue concreteValue)
+ AbstractValue& abstractValue, FrozenValue concreteValue)
{
if (abstractValue.filterByValue(concreteValue) == FiltrationOK)
return FiltrationOK;
@@ -1950,9 +3202,30 @@ FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue(
return Contradiction;
}
-} } // namespace JSC::DFG
+template<typename AbstractStateType>
+FiltrationResult AbstractInterpreter<AbstractStateType>::filterClassInfo(
+ AbstractValue& value, const ClassInfo* classInfo)
+{
+ if (value.filterClassInfo(m_graph, classInfo) == FiltrationOK)
+ return FiltrationOK;
+ m_state.setIsValid(false);
+ return Contradiction;
+}
-#endif // ENABLE(DFG_JIT)
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::executeDoubleUnaryOpEffects(Node* node, double(*equivalentFunction)(double))
+{
+ JSValue child = forNode(node->child1()).value();
+ if (std::optional<double> number = child.toNumberFromPrimitive()) {
+ setConstant(node, jsDoubleNumber(equivalentFunction(*number)));
+ return;
+ }
+ SpeculatedType type = SpecFullNumber;
+ if (node->child1().useKind() == DoubleRepUse)
+ type = typeOfDoubleUnaryOp(forNode(node->child1()).m_type);
+ forNode(node).setType(type);
+}
-#endif // DFGAbstractInterpreterInlines_h
+} } // namespace JSC::DFG
+#endif // ENABLE(DFG_JIT)