|  | /* | 
|  | * Copyright (C) 2013-2020 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "DFGNode.h" | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "DFGGraph.h" | 
|  | #include "DFGPromotedHeapLocation.h" | 
|  | #include "DOMJITSignature.h" | 
|  | #include "JSImmutableButterfly.h" | 
|  |  | 
|  | namespace JSC { namespace DFG { | 
|  |  | 
|  | const char Node::HashSetTemplateInstantiationString[] = "::JSC::DFG::Node*"; | 
|  |  | 
|  | DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(DFGNode); | 
|  |  | 
|  | bool MultiPutByOffsetData::writesStructures() const | 
|  | { | 
|  | for (unsigned i = variants.size(); i--;) { | 
|  | if (variants[i].writesStructures()) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool MultiPutByOffsetData::reallocatesStorage() const | 
|  | { | 
|  | for (unsigned i = variants.size(); i--;) { | 
|  | if (variants[i].reallocatesStorage()) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool MultiDeleteByOffsetData::writesStructures() const | 
|  | { | 
|  | for (unsigned i = variants.size(); i--;) { | 
|  | if (variants[i].writesStructures()) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool MultiDeleteByOffsetData::allVariantsStoreEmpty() const | 
|  | { | 
|  | for (unsigned i = variants.size(); i--;) { | 
|  | if (!variants[i].newStructure()) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void BranchTarget::dump(PrintStream& out) const | 
|  | { | 
|  | if (!block) | 
|  | return; | 
|  |  | 
|  | out.print(*block); | 
|  |  | 
|  | if (count == count) // If the count is not NaN, then print it. | 
|  | out.print("/w:", count); | 
|  | } | 
|  |  | 
|  | bool Node::hasVariableAccessData(Graph& graph) | 
|  | { | 
|  | switch (op()) { | 
|  | case Phi: | 
|  | return graph.m_form != SSA; | 
|  | case GetLocal: | 
|  | case SetLocal: | 
|  | case SetArgumentDefinitely: | 
|  | case SetArgumentMaybe: | 
|  | case Flush: | 
|  | case PhantomLocal: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Node::remove(Graph& graph) | 
|  | { | 
|  | switch (op()) { | 
|  | case MultiGetByOffset: { | 
|  | MultiGetByOffsetData& data = multiGetByOffsetData(); | 
|  | StructureSet set; | 
|  | for (MultiGetByOffsetCase& getCase : data.cases) { | 
|  | getCase.set().forEach( | 
|  | [&] (RegisteredStructure structure) { | 
|  | set.add(structure.get()); | 
|  | }); | 
|  | } | 
|  | convertToCheckStructure(graph.addStructureSet(set)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | case MatchStructure: { | 
|  | MatchStructureData& data = matchStructureData(); | 
|  | RegisteredStructureSet set; | 
|  | for (MatchStructureVariant& variant : data.variants) | 
|  | set.add(variant.structure); | 
|  | convertToCheckStructure(graph.addStructureSet(set)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | default: | 
|  | if (flags() & NodeHasVarArgs) { | 
|  | unsigned targetIndex = 0; | 
|  | for (unsigned i = 0; i < numChildren(); ++i) { | 
|  | Edge& edge = graph.varArgChild(this, i); | 
|  | if (!edge) | 
|  | continue; | 
|  | if (edge.willHaveCheck()) { | 
|  | Edge& dst = graph.varArgChild(this, targetIndex++); | 
|  | std::swap(dst, edge); | 
|  | continue; | 
|  | } | 
|  | edge = Edge(); | 
|  | } | 
|  | setOpAndDefaultFlags(CheckVarargs); | 
|  | children.setNumChildren(targetIndex); | 
|  | } else { | 
|  | children = children.justChecks(); | 
|  | setOpAndDefaultFlags(Check); | 
|  | } | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Node::removeWithoutChecks() | 
|  | { | 
|  | children = AdjacencyList(); | 
|  | setOpAndDefaultFlags(Check); | 
|  | } | 
|  |  | 
|  | void Node::replaceWith(Graph& graph, Node* other) | 
|  | { | 
|  | remove(graph); | 
|  | setReplacement(other); | 
|  | } | 
|  |  | 
|  | void Node::replaceWithWithoutChecks(Node* other) | 
|  | { | 
|  | removeWithoutChecks(); | 
|  | setReplacement(other); | 
|  | } | 
|  |  | 
|  | void Node::convertToIdentity() | 
|  | { | 
|  | RELEASE_ASSERT(child1()); | 
|  | RELEASE_ASSERT(!child2()); | 
|  | NodeFlags result = canonicalResultRepresentation(this->result()); | 
|  | setOpAndDefaultFlags(Identity); | 
|  | setResult(result); | 
|  | } | 
|  |  | 
|  | void Node::convertToIdentityOn(Node* child) | 
|  | { | 
|  | children.reset(); | 
|  | clearFlags(NodeHasVarArgs); | 
|  | child1() = child->defaultEdge(); | 
|  | NodeFlags output = canonicalResultRepresentation(this->result()); | 
|  | NodeFlags input = canonicalResultRepresentation(child->result()); | 
|  | if (output == input) { | 
|  | setOpAndDefaultFlags(Identity); | 
|  | setResult(output); | 
|  | return; | 
|  | } | 
|  | switch (output) { | 
|  | case NodeResultDouble: | 
|  | setOpAndDefaultFlags(DoubleRep); | 
|  | switch (input) { | 
|  | case NodeResultInt52: | 
|  | child1().setUseKind(Int52RepUse); | 
|  | return; | 
|  | case NodeResultJS: | 
|  | child1().setUseKind(NumberUse); | 
|  | return; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | case NodeResultInt52: | 
|  | setOpAndDefaultFlags(Int52Rep); | 
|  | switch (input) { | 
|  | case NodeResultDouble: | 
|  | child1().setUseKind(DoubleRepAnyIntUse); | 
|  | return; | 
|  | case NodeResultJS: | 
|  | child1().setUseKind(AnyIntUse); | 
|  | return; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | case NodeResultJS: | 
|  | setOpAndDefaultFlags(ValueRep); | 
|  | switch (input) { | 
|  | case NodeResultDouble: | 
|  | child1().setUseKind(DoubleRepUse); | 
|  | return; | 
|  | case NodeResultInt52: | 
|  | child1().setUseKind(Int52RepUse); | 
|  | return; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Node::convertToLazyJSConstant(Graph& graph, LazyJSValue value) | 
|  | { | 
|  | m_op = LazyJSConstant; | 
|  | m_flags &= ~NodeMustGenerate; | 
|  | m_opInfo = graph.m_lazyJSValues.add(value); | 
|  | children.reset(); | 
|  | } | 
|  |  | 
|  | void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly) | 
|  | { | 
|  | setOpAndDefaultFlags(NewArrayBuffer); | 
|  | NewArrayBufferData data { }; | 
|  | data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode(); | 
|  | data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength(); | 
|  | children.reset(); | 
|  | m_opInfo = immutableButterfly; | 
|  | m_opInfo2 = data.asQuadWord; | 
|  | } | 
|  |  | 
|  | void Node::convertToDirectCall(FrozenValue* executable) | 
|  | { | 
|  | NodeType newOp = LastNodeType; | 
|  | switch (op()) { | 
|  | case Call: | 
|  | newOp = DirectCall; | 
|  | break; | 
|  | case Construct: | 
|  | newOp = DirectConstruct; | 
|  | break; | 
|  | case TailCallInlinedCaller: | 
|  | newOp = DirectTailCallInlinedCaller; | 
|  | break; | 
|  | case TailCall: | 
|  | newOp = DirectTailCall; | 
|  | break; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | m_op = newOp; | 
|  | m_opInfo = executable; | 
|  | } | 
|  |  | 
|  | void Node::convertToCallDOM(Graph& graph) | 
|  | { | 
|  | ASSERT(op() == Call); | 
|  | ASSERT(signature()); | 
|  |  | 
|  | Edge edges[3]; | 
|  | // Skip the first one. This is callee. | 
|  | RELEASE_ASSERT(numChildren() <= 4); | 
|  | for (unsigned i = 1; i < numChildren(); ++i) | 
|  | edges[i - 1] = graph.varArgChild(this, i); | 
|  |  | 
|  | setOpAndDefaultFlags(CallDOM); | 
|  | children.setChild1(edges[0]); | 
|  | children.setChild2(edges[1]); | 
|  | children.setChild3(edges[2]); | 
|  |  | 
|  | if (!signature()->effect.mustGenerate()) | 
|  | clearFlags(NodeMustGenerate); | 
|  | } | 
|  |  | 
|  | void Node::convertToRegExpExecNonGlobalOrStickyWithoutChecks(FrozenValue* regExp) | 
|  | { | 
|  | ASSERT(op() == RegExpExec); | 
|  | setOpAndDefaultFlags(RegExpExecNonGlobalOrSticky); | 
|  | children.child1() = Edge(children.child1().node(), KnownCellUse); | 
|  | children.child2() = Edge(children.child3().node(), KnownStringUse); | 
|  | children.child3() = Edge(); | 
|  | m_opInfo = regExp; | 
|  | } | 
|  |  | 
|  | void Node::convertToRegExpMatchFastGlobalWithoutChecks(FrozenValue* regExp) | 
|  | { | 
|  | ASSERT(op() == RegExpMatchFast); | 
|  | setOpAndDefaultFlags(RegExpMatchFastGlobal); | 
|  | children.child1() = Edge(children.child1().node(), KnownCellUse); | 
|  | children.child2() = Edge(children.child3().node(), KnownStringUse); | 
|  | children.child3() = Edge(); | 
|  | m_opInfo = regExp; | 
|  | } | 
|  |  | 
|  | String Node::tryGetString(Graph& graph) | 
|  | { | 
|  | if (hasConstant()) | 
|  | return constant()->tryGetString(graph); | 
|  | if (hasLazyJSValue()) | 
|  | return lazyJSValue().tryGetString(graph); | 
|  | return String(); | 
|  | } | 
|  |  | 
|  | PromotedLocationDescriptor Node::promotedLocationDescriptor() | 
|  | { | 
|  | return PromotedLocationDescriptor(static_cast<PromotedLocationKind>(m_opInfo.as<uint32_t>()), m_opInfo2.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | } } // namespace JSC::DFG | 
|  |  | 
|  | namespace WTF { | 
|  |  | 
|  | using namespace JSC; | 
|  | using namespace JSC::DFG; | 
|  |  | 
|  | void printInternal(PrintStream& out, SwitchKind kind) | 
|  | { | 
|  | switch (kind) { | 
|  | case SwitchImm: | 
|  | out.print("SwitchImm"); | 
|  | return; | 
|  | case SwitchChar: | 
|  | out.print("SwitchChar"); | 
|  | return; | 
|  | case SwitchString: | 
|  | out.print("SwitchString"); | 
|  | return; | 
|  | case SwitchCell: | 
|  | out.print("SwitchCell"); | 
|  | return; | 
|  | } | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  |  | 
|  | void printInternal(PrintStream& out, Node* node) | 
|  | { | 
|  | if (!node) { | 
|  | out.print("-"); | 
|  | return; | 
|  | } | 
|  | out.print("D@", node->index()); | 
|  | if (node->hasDoubleResult()) | 
|  | out.print("<Double>"); | 
|  | else if (node->hasInt52Result()) | 
|  | out.print("<Int52>"); | 
|  | } | 
|  |  | 
|  | } // namespace WTF | 
|  |  | 
|  | #endif // ENABLE(DFG_JIT) | 
|  |  |