/*
 * Copyright (C) 2012 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 DFGSlowPathGenerator_h
#define DFGSlowPathGenerator_h

#include <wtf/Platform.h>

#if ENABLE(DFG_JIT)

#include "DFGCommon.h"
#include "DFGSilentRegisterSavePlan.h"
#include "DFGSpeculativeJIT.h"
#include <wtf/FastAllocBase.h>
#include <wtf/PassOwnPtr.h>

namespace JSC { namespace DFG {

class SlowPathGenerator {
    WTF_MAKE_FAST_ALLOCATED;
public:
    SlowPathGenerator(SpeculativeJIT* jit)
        : m_currentNode(jit->m_currentNode)
    {
    }
    virtual ~SlowPathGenerator() { }
    void generate(SpeculativeJIT* jit)
    {
#if DFG_ENABLE(DEBUG_VERBOSE)
        dataLogF("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset());
#endif
        m_label = jit->m_jit.label();
        jit->m_currentNode = m_currentNode;
        generateInternal(jit);
#if !ASSERT_DISABLED
        jit->m_jit.breakpoint(); // make sure that the generator jumps back to somewhere
#endif
    }
    MacroAssembler::Label label() const { return m_label; }
    virtual MacroAssembler::Call call() const
    {
        RELEASE_ASSERT_NOT_REACHED(); // By default slow path generators don't have a call.
        return MacroAssembler::Call();
    }
protected:
    virtual void generateInternal(SpeculativeJIT*) = 0;
    MacroAssembler::Label m_label;
    Node* m_currentNode;
};

template<typename JumpType>
class JumpingSlowPathGenerator : public SlowPathGenerator {
public:
    JumpingSlowPathGenerator(JumpType from, SpeculativeJIT* jit)
        : SlowPathGenerator(jit)
        , m_from(from)
        , m_to(jit->m_jit.label())
    {
    }
    
protected:
    void linkFrom(SpeculativeJIT* jit)
    {
        m_from.link(&jit->m_jit);
    }
    
    void jumpTo(SpeculativeJIT* jit)
    {
        jit->m_jit.jump().linkTo(m_to, &jit->m_jit);
    }

    JumpType m_from;
    MacroAssembler::Label m_to;
};

template<typename JumpType, typename FunctionType, typename ResultType>
class CallSlowPathGenerator : public JumpingSlowPathGenerator<JumpType> {
public:
    CallSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result)
        : JumpingSlowPathGenerator<JumpType>(from, jit)
        , m_function(function)
        , m_spillMode(spillMode)
        , m_result(result)
    {
        if (m_spillMode == NeedToSpill)
            jit->silentSpillAllRegistersImpl(false, m_plans, extractResult(result));
    }
    
    virtual MacroAssembler::Call call() const
    {
        return m_call;
    }
    
protected:
    void setUp(SpeculativeJIT* jit)
    {
        this->linkFrom(jit);
        if (m_spillMode == NeedToSpill) {
            for (unsigned i = 0; i < m_plans.size(); ++i)
                jit->silentSpill(m_plans[i]);
        }
    }
    
    void recordCall(MacroAssembler::Call call)
    {
        m_call = call;
    }
    
    void tearDown(SpeculativeJIT* jit)
    {
        if (m_spillMode == NeedToSpill) {
            GPRReg canTrample = SpeculativeJIT::pickCanTrample(extractResult(m_result));
            for (unsigned i = m_plans.size(); i--;)
                jit->silentFill(m_plans[i], canTrample);
        }
        this->jumpTo(jit);
    }

    FunctionType m_function;
    SpillRegistersMode m_spillMode;
    ResultType m_result;
    MacroAssembler::Call m_call;
    Vector<SilentRegisterSavePlan, 2> m_plans;
};

template<typename JumpType, typename FunctionType, typename ResultType>
class CallResultAndNoArgumentsSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndNoArgumentsSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
    {
    }
    
protected:
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(jit->callOperation(this->m_function, extractResult(this->m_result)));
        this->tearDown(jit);
    }
};

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1>
class CallResultAndOneArgumentSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndOneArgumentSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result, ArgumentType1 argument1)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
        , m_argument1(argument1)
    {
    }
    
protected:
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(jit->callOperation(this->m_function, extractResult(this->m_result), m_argument1));
        this->tearDown(jit);
    }

    ArgumentType1 m_argument1;
};

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2>
class CallResultAndTwoArgumentsSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndTwoArgumentsSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result, ArgumentType1 argument1,
        ArgumentType2 argument2)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
        , m_argument1(argument1)
        , m_argument2(argument2)
    {
    }
    
protected:
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(jit->callOperation(this->m_function, extractResult(this->m_result), m_argument1, m_argument2));
        this->tearDown(jit);
    }

    ArgumentType1 m_argument1;
    ArgumentType2 m_argument2;
};

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3>
class CallResultAndThreeArgumentsSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndThreeArgumentsSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result, ArgumentType1 argument1,
        ArgumentType2 argument2, ArgumentType3 argument3)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
        , m_argument1(argument1)
        , m_argument2(argument2)
        , m_argument3(argument3)
    {
    }

protected:    
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(
            jit->callOperation(
                this->m_function, extractResult(this->m_result), m_argument1, m_argument2,
                m_argument3));
        this->tearDown(jit);
    }

    ArgumentType1 m_argument1;
    ArgumentType2 m_argument2;
    ArgumentType3 m_argument3;
};

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3,
    typename ArgumentType4>
class CallResultAndFourArgumentsSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndFourArgumentsSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result, ArgumentType1 argument1,
        ArgumentType2 argument2, ArgumentType3 argument3, ArgumentType4 argument4)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
        , m_argument1(argument1)
        , m_argument2(argument2)
        , m_argument3(argument3)
        , m_argument4(argument4)
    {
    }
    
protected:
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(
            jit->callOperation(
                this->m_function, extractResult(this->m_result), m_argument1, m_argument2,
                m_argument3, m_argument4));
        this->tearDown(jit);
    }

    ArgumentType1 m_argument1;
    ArgumentType2 m_argument2;
    ArgumentType3 m_argument3;
    ArgumentType4 m_argument4;
};

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3,
    typename ArgumentType4, typename ArgumentType5>
class CallResultAndFiveArgumentsSlowPathGenerator
    : public CallSlowPathGenerator<JumpType, FunctionType, ResultType> {
public:
    CallResultAndFiveArgumentsSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit, FunctionType function,
        SpillRegistersMode spillMode, ResultType result, ArgumentType1 argument1,
        ArgumentType2 argument2, ArgumentType3 argument3, ArgumentType4 argument4,
        ArgumentType5 argument5)
        : CallSlowPathGenerator<JumpType, FunctionType, ResultType>(
            from, jit, function, spillMode, result)
        , m_argument1(argument1)
        , m_argument2(argument2)
        , m_argument3(argument3)
        , m_argument4(argument4)
        , m_argument5(argument5)
    {
    }

protected:    
    void generateInternal(SpeculativeJIT* jit)
    {
        this->setUp(jit);
        this->recordCall(
            jit->callOperation(
                this->m_function, extractResult(this->m_result), m_argument1, m_argument2,
                m_argument3, m_argument4, m_argument5));
        this->tearDown(jit);
    }

    ArgumentType1 m_argument1;
    ArgumentType2 m_argument2;
    ArgumentType3 m_argument3;
    ArgumentType4 m_argument4;
    ArgumentType5 m_argument5;
};

template<typename JumpType, typename FunctionType, typename ResultType>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndNoArgumentsSlowPathGenerator<
            JumpType, FunctionType, ResultType>(
                from, jit, function, spillMode, result));
}

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, ArgumentType1 argument1,
    SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndOneArgumentSlowPathGenerator<
            JumpType, FunctionType, ResultType, ArgumentType1>(
                from, jit, function, spillMode, result, argument1));
}

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, ArgumentType1 argument1, ArgumentType2 argument2,
    SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndTwoArgumentsSlowPathGenerator<
            JumpType, FunctionType, ResultType, ArgumentType1, ArgumentType2>(
                from, jit, function, spillMode, result, argument1, argument2));
}

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, ArgumentType1 argument1, ArgumentType2 argument2,
    ArgumentType3 argument3, SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndThreeArgumentsSlowPathGenerator<
            JumpType, FunctionType, ResultType, ArgumentType1, ArgumentType2,
            ArgumentType3>(
                from, jit, function, spillMode, result, argument1, argument2,
                argument3));
}

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3,
    typename ArgumentType4>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, ArgumentType1 argument1, ArgumentType2 argument2,
    ArgumentType3 argument3, ArgumentType4 argument4,
    SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndFourArgumentsSlowPathGenerator<
            JumpType, FunctionType, ResultType, ArgumentType1, ArgumentType2,
            ArgumentType3, ArgumentType4>(
                from, jit, function, spillMode, result, argument1, argument2,
                argument3, argument4));
}

template<
    typename JumpType, typename FunctionType, typename ResultType,
    typename ArgumentType1, typename ArgumentType2, typename ArgumentType3,
    typename ArgumentType4, typename ArgumentType5>
inline PassOwnPtr<SlowPathGenerator> slowPathCall(
    JumpType from, SpeculativeJIT* jit, FunctionType function,
    ResultType result, ArgumentType1 argument1, ArgumentType2 argument2,
    ArgumentType3 argument3, ArgumentType4 argument4, ArgumentType5 argument5,
    SpillRegistersMode spillMode = NeedToSpill)
{
    return adoptPtr(
        new CallResultAndFiveArgumentsSlowPathGenerator<
            JumpType, FunctionType, ResultType, ArgumentType1, ArgumentType2,
            ArgumentType3, ArgumentType4, ArgumentType5>(
                from, jit, function, spillMode, result, argument1, argument2,
                argument3, argument4, argument5));
}

template<typename JumpType, typename DestinationType, typename SourceType, unsigned numberOfAssignments>
class AssigningSlowPathGenerator : public JumpingSlowPathGenerator<JumpType> {
public:
    AssigningSlowPathGenerator(
        JumpType from, SpeculativeJIT* jit,
        DestinationType destination[numberOfAssignments],
        SourceType source[numberOfAssignments])
        : JumpingSlowPathGenerator<JumpType>(from, jit)
    {
        for (unsigned i = numberOfAssignments; i--;) {
            m_destination[i] = destination[i];
            m_source[i] = source[i];
        }
    }

protected:
    virtual void generateInternal(SpeculativeJIT* jit)
    {
        this->linkFrom(jit);
        for (unsigned i = numberOfAssignments; i--;)
            jit->m_jit.move(m_source[i], m_destination[i]);
        this->jumpTo(jit);
    }

private:
    DestinationType m_destination[numberOfAssignments];
    SourceType m_source[numberOfAssignments];
};

template<typename JumpType, typename DestinationType, typename SourceType, unsigned numberOfAssignments>
inline PassOwnPtr<SlowPathGenerator> slowPathMove(
    JumpType from, SpeculativeJIT* jit, SourceType source[numberOfAssignments], DestinationType destination[numberOfAssignments])
{
    return adoptPtr(
        new AssigningSlowPathGenerator<
            JumpType, DestinationType, SourceType, numberOfAssignments>(
                from, jit, destination, source));
}

template<typename JumpType, typename DestinationType, typename SourceType>
inline PassOwnPtr<SlowPathGenerator> slowPathMove(
    JumpType from, SpeculativeJIT* jit, SourceType source, DestinationType destination)
{
    SourceType sourceArray[1] = { source };
    DestinationType destinationArray[1] = { destination };
    return adoptPtr(
        new AssigningSlowPathGenerator<
            JumpType, DestinationType, SourceType, 1>(
                from, jit, destinationArray, sourceArray));
}

template<typename JumpType, typename DestinationType, typename SourceType>
inline PassOwnPtr<SlowPathGenerator> slowPathMove(
    JumpType from, SpeculativeJIT* jit, SourceType source1, DestinationType destination1, SourceType source2, DestinationType destination2)
{
    SourceType sourceArray[2] = { source1, source2 };
    DestinationType destinationArray[2] = { destination1, destination2 };
    return adoptPtr(
        new AssigningSlowPathGenerator<
            JumpType, DestinationType, SourceType, 2>(
                from, jit, destinationArray, sourceArray));
}

} } // namespace JSC::DFG

#endif // ENABLD(DFG_JIT)

#endif // DFGSlowPathGenerator_h

