blob: 6f2a03bf65c267c0a888986dc6cf261f536428b7 [file] [log] [blame]
/*
* Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DFGClobberize_h
#define DFGClobberize_h
#include <wtf/Platform.h>
#if ENABLE(DFG_JIT)
#include "DFGAbstractHeap.h"
#include "DFGEdgeUsesStructure.h"
#include "DFGGraph.h"
namespace JSC { namespace DFG {
template<typename ReadFunctor, typename WriteFunctor>
void clobberizeForAllocation(ReadFunctor& read, WriteFunctor& write)
{
read(GCState);
read(BarrierState);
write(GCState);
write(BarrierState);
}
template<typename ReadFunctor, typename WriteFunctor>
void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write)
{
// Some notes:
//
// - We cannot hoist, or sink, anything that has effects. This means that the
// easiest way of indicating that something cannot be hoisted is to claim
// that it side-effects some miscellaneous thing.
//
// - We cannot hoist forward-exiting nodes without some additional effort. I
// believe that what it comes down to is that forward-exiting generally have
// their NodeExitsForward cleared upon hoist, except for forward-exiting
// nodes that take bogus state as their input. Those are substantially
// harder. We disable it for now. In the future we could enable it by having
// versions of those nodes that backward-exit instead, but I'm not convinced
// of the soundness.
//
// - Some nodes lie, and claim that they do not read the JSCell_structure.
// These are nodes that use the structure in a way that does not depend on
// things that change under structure transitions.
//
// - It's implicitly understood that OSR exits read the world. This is why we
// generally don't move or eliminate stores. Every node can exit, so the
// read set does not reflect things that would be read if we exited.
// Instead, the read set reflects what the node will have to read if it
// *doesn't* exit.
//
// - Broadly, we don't say that we're reading something if that something is
// immutable.
//
// - We try to make this work even prior to type inference, just so that we
// can use it for IR dumps. No promises on whether the answers are sound
// prior to type inference - though they probably could be if we did some
// small hacking.
if (edgesUseStructure(graph, node))
read(JSCell_structure);
switch (node->op()) {
case JSConstant:
case WeakJSConstant:
case Identity:
case Phantom:
case Breakpoint:
case ProfileWillCall:
case ProfileDidCall:
case BitAnd:
case BitOr:
case BitXor:
case BitLShift:
case BitRShift:
case BitURShift:
case ValueToInt32:
case ArithAdd:
case ArithSub:
case ArithNegate:
case ArithMul:
case ArithIMul:
case ArithDiv:
case ArithMod:
case ArithAbs:
case ArithMin:
case ArithMax:
case ArithSqrt:
case ArithSin:
case ArithCos:
case GetScope:
case SkipScope:
case CheckFunction:
case StringCharCodeAt:
case StringFromCharCode:
case CompareEqConstant:
case CompareStrictEqConstant:
case CompareStrictEq:
case IsUndefined:
case IsBoolean:
case IsNumber:
case IsString:
case LogicalNot:
case Int32ToDouble:
case ExtractOSREntryLocal:
case Int52ToDouble:
case Int52ToValue:
case CheckInBounds:
case ConstantStoragePointer:
case UInt32ToNumber:
case DoubleAsInt32:
case Check:
return;
case MovHint:
case ZombieHint:
case Upsilon:
case Phi:
case Flush:
case PhantomLocal:
case SetArgument:
case PhantomArguments:
case Jump:
case Branch:
case Switch:
case Throw:
case ForceOSRExit:
case Return:
case Unreachable:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
case CheckTierUpAndOSREnter:
case LoopHint:
case InvalidationPoint:
write(SideState);
return;
case VariableWatchpoint:
case TypedArrayWatchpoint:
read(Watchpoint_fire);
write(SideState);
return;
case NotifyWrite:
write(Watchpoint_fire);
write(SideState);
return;
case CreateActivation:
case CreateArguments:
clobberizeForAllocation(read, write);
write(SideState);
write(Watchpoint_fire);
return;
case FunctionReentryWatchpoint:
read(Watchpoint_fire);
return;
case ToThis:
case CreateThis:
read(MiscFields);
clobberizeForAllocation(read, write);
return;
case VarInjectionWatchpoint:
case AllocationProfileWatchpoint:
case IsObject:
case IsFunction:
case TypeOf:
read(MiscFields);
return;
case GetById:
case GetByIdFlush:
case PutById:
case PutByIdDirect:
case ArrayPush:
case ArrayPop:
case Call:
case Construct:
case ToPrimitive:
case In:
case GetMyArgumentsLengthSafe:
case GetMyArgumentByValSafe:
case ValueAdd:
read(World);
write(World);
return;
case GetCallee:
read(AbstractHeap(Variables, JSStack::Callee));
return;
case GetLocal:
case GetArgument:
read(AbstractHeap(Variables, node->local()));
return;
case SetLocal:
write(AbstractHeap(Variables, node->local()));
return;
case GetLocalUnlinked:
read(AbstractHeap(Variables, node->unlinkedLocal()));
return;
case GetByVal: {
ArrayMode mode = node->arrayMode();
switch (mode.type()) {
case Array::SelectUsingPredictions:
case Array::Unprofiled:
case Array::Undecided:
// Assume the worst since we don't have profiling yet.
read(World);
write(World);
return;
case Array::ForceExit:
write(SideState);
return;
case Array::Generic:
read(World);
write(World);
return;
case Array::String:
if (mode.isOutOfBounds()) {
read(World);
write(World);
return;
}
// This appears to read nothing because it's only reading immutable data.
return;
case Array::Arguments:
read(Arguments_registers);
read(Variables);
return;
case Array::Int32:
if (mode.isInBounds()) {
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedInt32Properties);
return;
}
read(World);
write(World);
return;
case Array::Double:
if (mode.isInBounds()) {
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedDoubleProperties);
return;
}
read(World);
write(World);
return;
case Array::Contiguous:
if (mode.isInBounds()) {
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedContiguousProperties);
return;
}
read(World);
write(World);
return;
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
// Give up on life for now.
read(World);
write(World);
return;
case Array::Int8Array:
case Array::Int16Array:
case Array::Int32Array:
case Array::Uint8Array:
case Array::Uint8ClampedArray:
case Array::Uint16Array:
case Array::Uint32Array:
case Array::Float32Array:
case Array::Float64Array:
read(TypedArrayProperties);
read(JSArrayBufferView_vector);
read(JSArrayBufferView_length);
return;
}
RELEASE_ASSERT_NOT_REACHED();
return;
}
case PutByValDirect:
case PutByVal:
case PutByValAlias: {
ArrayMode mode = node->arrayMode();
switch (mode.modeForPut().type()) {
case Array::SelectUsingPredictions:
case Array::Unprofiled:
case Array::Undecided:
case Array::String:
// Assume the worst since we don't have profiling yet.
read(World);
write(World);
return;
case Array::ForceExit:
write(SideState);
return;
case Array::Generic:
read(World);
write(World);
return;
case Array::Arguments:
read(Arguments_registers);
read(Arguments_numArguments);
read(Arguments_slowArguments);
write(Variables);
return;
case Array::Int32:
if (node->arrayMode().isOutOfBounds()) {
read(World);
write(World);
return;
}
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedInt32Properties);
write(IndexedInt32Properties);
return;
case Array::Double:
if (node->arrayMode().isOutOfBounds()) {
read(World);
write(World);
return;
}
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedDoubleProperties);
write(IndexedDoubleProperties);
return;
case Array::Contiguous:
if (node->arrayMode().isOutOfBounds()) {
read(World);
write(World);
return;
}
read(Butterfly_publicLength);
read(Butterfly_vectorLength);
read(IndexedContiguousProperties);
write(IndexedContiguousProperties);
return;
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
// Give up on life for now.
read(World);
write(World);
return;
case Array::Int8Array:
case Array::Int16Array:
case Array::Int32Array:
case Array::Uint8Array:
case Array::Uint8ClampedArray:
case Array::Uint16Array:
case Array::Uint32Array:
case Array::Float32Array:
case Array::Float64Array:
read(JSArrayBufferView_vector);
read(JSArrayBufferView_length);
write(TypedArrayProperties);
return;
}
RELEASE_ASSERT_NOT_REACHED();
return;
}
case CheckStructure:
case StructureTransitionWatchpoint:
case CheckArray:
case CheckHasInstance:
case InstanceOf:
read(JSCell_structure);
return;
case CheckExecutable:
read(JSFunction_executable);
return;
case PutStructure:
case PhantomPutStructure:
write(JSCell_structure);
return;
case AllocatePropertyStorage:
write(JSObject_butterfly);
clobberizeForAllocation(read, write);
return;
case ReallocatePropertyStorage:
read(JSObject_butterfly);
write(JSObject_butterfly);
clobberizeForAllocation(read, write);
return;
case GetButterfly:
read(JSObject_butterfly);
return;
case Arrayify:
case ArrayifyToStructure:
read(JSCell_structure);
read(JSObject_butterfly);
write(JSCell_structure);
write(JSObject_butterfly);
clobberizeForAllocation(read, write);
return;
case GetIndexedPropertyStorage:
if (node->arrayMode().type() == Array::String)
return;
read(JSArrayBufferView_vector);
return;
case GetTypedArrayByteOffset:
read(JSArrayBufferView_vector);
read(JSArrayBufferView_mode);
read(Butterfly_arrayBuffer);
read(ArrayBuffer_data);
return;
case GetByOffset:
read(AbstractHeap(NamedProperties, graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber));
return;
case PutByOffset:
write(AbstractHeap(NamedProperties, graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber));
return;
case GetArrayLength: {
ArrayMode mode = node->arrayMode();
switch (mode.type()) {
case Array::Int32:
case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
read(Butterfly_publicLength);
return;
case Array::String:
return;
case Array::Arguments:
read(Arguments_overrideLength);
read(Arguments_numArguments);
return;
default:
read(JSArrayBufferView_length);
return;
}
}
case GetMyScope:
read(AbstractHeap(Variables, JSStack::ScopeChain));
return;
case SkipTopScope:
read(AbstractHeap(Variables, graph.activationRegister()));
return;
case GetClosureRegisters:
read(JSVariableObject_registers);
return;
case GetClosureVar:
read(AbstractHeap(Variables, node->varNumber()));
return;
case PutClosureVar:
write(AbstractHeap(Variables, node->varNumber()));
return;
case GetGlobalVar:
read(AbstractHeap(Absolute, node->registerPointer()));
return;
case PutGlobalVar:
write(AbstractHeap(Absolute, node->registerPointer()));
return;
case NewObject:
case NewArray:
case NewArrayWithSize:
case NewArrayBuffer:
case NewRegexp:
case NewStringObject:
case MakeRope:
case NewFunctionNoCheck:
case NewFunction:
case NewFunctionExpression:
clobberizeForAllocation(read, write);
return;
case NewTypedArray:
clobberizeForAllocation(read, write);
switch (node->child1().useKind()) {
case Int32Use:
return;
case UntypedUse:
read(World);
write(World);
return;
default:
RELEASE_ASSERT_NOT_REACHED();
return;
}
case RegExpExec:
case RegExpTest:
read(RegExpState);
write(RegExpState);
return;
case StringCharAt:
if (node->arrayMode().isOutOfBounds()) {
read(World);
write(World);
return;
}
return;
case CompareEq:
case CompareLess:
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
if (!node->isBinaryUseKind(UntypedUse))
return;
read(World);
write(World);
return;
case ToString:
switch (node->child1().useKind()) {
case StringObjectUse:
case StringOrStringObjectUse:
return;
case CellUse:
case UntypedUse:
read(World);
write(World);
return;
default:
RELEASE_ASSERT_NOT_REACHED();
return;
}
case TearOffActivation:
write(JSVariableObject_registers);
return;
case TearOffArguments:
write(Arguments_registers);
return;
case GetMyArgumentsLength:
read(AbstractHeap(Variables, graph.argumentsRegisterFor(node->codeOrigin)));
read(AbstractHeap(Variables, JSStack::ArgumentCount));
return;
case GetMyArgumentByVal:
read(Variables);
return;
case CheckArgumentsNotCreated:
read(AbstractHeap(Variables, graph.argumentsRegisterFor(node->codeOrigin)));
return;
case ThrowReferenceError:
write(SideState);
clobberizeForAllocation(read, write);
return;
case CountExecution:
case CheckWatchdogTimer:
read(InternalState);
write(InternalState);
return;
case StoreBarrier:
case ConditionalStoreBarrier:
case StoreBarrierWithNullCheck:
read(BarrierState);
write(BarrierState);
return;
case LastNodeType:
RELEASE_ASSERT_NOT_REACHED();
return;
}
RELEASE_ASSERT_NOT_REACHED();
}
class NoOpClobberize {
public:
NoOpClobberize() { }
void operator()(AbstractHeap) { }
};
class CheckClobberize {
public:
CheckClobberize()
: m_result(false)
{
}
void operator()(AbstractHeap) { m_result = true; }
bool result() const { return m_result; }
private:
bool m_result;
};
bool doesWrites(Graph&, Node*);
class AbstractHeapOverlaps {
public:
AbstractHeapOverlaps(AbstractHeap heap)
: m_heap(heap)
, m_result(false)
{
}
void operator()(AbstractHeap otherHeap)
{
if (m_result)
return;
m_result = m_heap.overlaps(otherHeap);
}
bool result() const { return m_result; }
private:
AbstractHeap m_heap;
bool m_result;
};
bool writesOverlap(Graph&, Node*, AbstractHeap);
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
#endif // DFGClobberize_h