blob: 6e0115526def3b4db62af1e9c5816e78a80fcae6 [file] [log] [blame]
/*
* Copyright (C) 2013-2021 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;
}
void Node::convertToRegExpTestInline(FrozenValue* globalObject, FrozenValue* regExp)
{
ASSERT(op() == RegExpTest);
setOpAndDefaultFlags(RegExpTestInline);
children.child1() = Edge(children.child1().node(), KnownCellUse);
children.child2() = Edge(children.child2().node(), RegExpObjectUse);
// We keep the existing child3.
m_opInfo = globalObject;
m_opInfo2 = 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)