/*
 * Copyright (C) 2013 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 "FTLCapabilities.h"

#if ENABLE(FTL_JIT)

namespace JSC { namespace FTL {

using namespace DFG;

inline bool canCompile(Node* node)
{
    switch (node->op()) {
    case JSConstant:
    case WeakJSConstant:
    case GetLocal:
    case SetLocal:
    case MovHintAndCheck:
    case MovHint:
    case ZombieHint:
    case Phantom:
    case Flush:
    case PhantomLocal:
    case SetArgument:
    case Return:
    case BitAnd:
    case BitOr:
    case BitXor:
    case BitRShift:
    case BitLShift:
    case BitURShift:
    case CheckStructure:
    case StructureTransitionWatchpoint:
    case ArrayifyToStructure:
    case PutStructure:
    case PhantomPutStructure:
    case GetButterfly:
    case GetByOffset:
    case PutByOffset:
    case GetGlobalVar:
    case PutGlobalVar:
    case ValueAdd:
    case ArithAdd:
    case ArithSub:
    case ArithMul:
    case ArithDiv:
    case ArithMod:
    case ArithMin:
    case ArithMax:
    case ArithAbs:
    case ArithNegate:
    case UInt32ToNumber:
    case Int32ToDouble:
    case CompareEqConstant:
    case CompareStrictEqConstant:
    case Jump:
    case ForceOSRExit:
    case Phi:
    case Upsilon:
        // These are OK.
        break;
    case GetArrayLength:
        switch (node->arrayMode().type()) {
        case Array::Int32:
        case Array::Double:
        case Array::Contiguous:
            break;
        default:
            return false;
        }
        break;
    case GetByVal:
        switch (node->arrayMode().type()) {
        case Array::ForceExit:
            return true;
        case Array::Int32:
        case Array::Double:
        case Array::Contiguous:
            break;
        default:
            return false;
        }
        switch (node->arrayMode().speculation()) {
        case Array::SaneChain:
        case Array::InBounds:
            break;
        default:
            return false;
        }
        break;
    case PutByVal:
    case PutByValAlias:
        switch (node->arrayMode().type()) {
        case Array::ForceExit:
            return true;
        case Array::Int32:
        case Array::Double:
        case Array::Contiguous:
            break;
        default:
            return false;
        }
        break;
    case CompareEq:
    case CompareStrictEq:
        if (node->isBinaryUseKind(Int32Use))
            break;
        if (node->isBinaryUseKind(NumberUse))
            break;
        if (node->isBinaryUseKind(ObjectUse))
            break;
        return false;
    case CompareLess:
    case CompareLessEq:
    case CompareGreater:
    case CompareGreaterEq:
        if (node->isBinaryUseKind(Int32Use))
            break;
        if (node->isBinaryUseKind(NumberUse))
            break;
        return false;
    case Branch:
    case LogicalNot:
        switch (node->child1().useKind()) {
        case BooleanUse:
        case Int32Use:
        case NumberUse:
        case ObjectOrOtherUse:
            break;
        default:
            return false;
        }
        break;
    case Switch:
        switch (node->switchData()->kind) {
        case SwitchImm:
        case SwitchChar:
            break;
        default:
            return false;
        }
        break;
    default:
        // Don't know how to handle anything else.
        return false;
    }
    return true;
}

bool canCompile(Graph& graph)
{
    for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) {
        BasicBlock* block = graph.block(blockIndex);
        if (!block)
            continue;
        
        // We don't care if we can compile blocks that the CFA hasn't visited.
        if (!block->cfaHasVisited)
            continue;
        
        for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
            Node* node = block->at(nodeIndex);
            
            for (unsigned childIndex = graph.numChildren(node); childIndex--;) {
                Edge edge = graph.child(node, childIndex);
                if (!edge)
                    continue;
                switch (edge.useKind()) {
                case UntypedUse:
                case Int32Use:
                case KnownInt32Use:
                case NumberUse:
                case KnownNumberUse:
                case RealNumberUse:
                case BooleanUse:
                case CellUse:
                case KnownCellUse:
                case ObjectUse:
                case ObjectOrOtherUse:
                case StringUse:
                    // These are OK.
                    break;
                default:
                    // Don't know how to handle anything else.
                    if (verboseCompilationEnabled()) {
                        dataLog("FTL rejecting node because of bad use kind: ", edge.useKind(), " in node:\n");
                        graph.dump(WTF::dataFile(), "    ", node);
                    }
                    return false;
                }
            }
            
            if (!canCompile(node)) {
                if (verboseCompilationEnabled()) {
                    dataLog("FTL rejecting node:\n");
                    graph.dump(WTF::dataFile(), "    ", node);
                }
                return false;
            }
            
            // We don't care if we can compile anything after a force-exit.
            if (node->op() == ForceOSRExit)
                break;
        }
    }
    
    return true;
}

} } // namespace JSC::FTL

#endif // ENABLE(FTL_JIT)

