/*
 * Copyright (C) 2019 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.
 */

#pragma once

#include "BytecodeGeneratorBase.h"

#include "RegisterID.h"
#include "StackAlignment.h"

namespace JSC {

template<typename T>
static inline void shrinkToFit(T& segmentedVector)
{
    while (segmentedVector.size() && !segmentedVector.last().refCount())
        segmentedVector.removeLast();
}

template<typename Traits>
BytecodeGeneratorBase<Traits>::BytecodeGeneratorBase(typename Traits::CodeBlock codeBlock, uint32_t virtualRegisterCountForCalleeSaves)
    : m_codeBlock(WTFMove(codeBlock))
{
    allocateCalleeSaveSpace(virtualRegisterCountForCalleeSaves);
}

template<typename Traits>
Ref<GenericLabel<Traits>> BytecodeGeneratorBase<Traits>::newLabel()
{
    shrinkToFit(m_labels);

    // Allocate new label ID.
    m_labels.append();
    return m_labels.last();
}

template<typename Traits>
Ref<GenericLabel<Traits>> BytecodeGeneratorBase<Traits>::newEmittedLabel()
{
    auto label = newLabel();
    emitLabel(label.get());
    return label;
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::reclaimFreeRegisters()
{
    shrinkToFit(m_calleeLocals);
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::emitLabel(GenericLabel<Traits>& label)
{
    unsigned newLabelIndex = m_writer.position();
    label.setLocation(*this, newLabelIndex);

    if (m_codeBlock->numberOfJumpTargets()) {
        unsigned lastLabelIndex = m_codeBlock->lastJumpTarget();
        ASSERT(lastLabelIndex <= newLabelIndex);
        if (newLabelIndex == lastLabelIndex) {
            // Peephole optimizations have already been disabled by emitting the last label
            return;
        }
    }

    m_codeBlock->addJumpTarget(newLabelIndex);

    m_lastOpcodeID = Traits::opcodeForDisablingOptimizations;
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::recordOpcode(typename Traits::OpcodeID opcodeID)
{
    ASSERT(m_lastOpcodeID == Traits::opcodeForDisablingOptimizations || (m_lastOpcodeID == m_lastInstruction->opcodeID() && m_writer.position() == m_lastInstruction.offset() + m_lastInstruction->size()));
    m_lastInstruction = m_writer.ref();
    m_lastOpcodeID = opcodeID;
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::alignWideOpcode16()
{
#if CPU(NEEDS_ALIGNED_ACCESS)
    static_assert(Traits::OpcodeTraits::maxOpcodeIDWidth == OpcodeSize::Narrow);
    size_t opcodeSize = 1;
    size_t prefixAndOpcodeSize = opcodeSize + PaddingBySize<OpcodeSize::Wide16>::value;
    while ((m_writer.position() + prefixAndOpcodeSize) % OpcodeSize::Wide16)
        Traits::OpNop::template emit<OpcodeSize::Narrow>(this);
#endif
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::alignWideOpcode32()
{
#if CPU(NEEDS_ALIGNED_ACCESS)
    static_assert(Traits::OpcodeTraits::maxOpcodeIDWidth == OpcodeSize::Narrow);
    size_t opcodeSize = 1;
    size_t prefixAndOpcodeSize = opcodeSize + PaddingBySize<OpcodeSize::Wide32>::value;
    while ((m_writer.position() + prefixAndOpcodeSize) % OpcodeSize::Wide32)
        Traits::OpNop::template emit<OpcodeSize::Narrow>(this);
#endif
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(uint8_t b)
{
    m_writer.write(b);
}


template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(uint16_t h)
{
    m_writer.write(h);
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(uint32_t i)
{
    m_writer.write(i);
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(int8_t b)
{
    m_writer.write(static_cast<uint8_t>(b));
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(int16_t h)
{
    m_writer.write(static_cast<uint16_t>(h));
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::write(int32_t i)
{
    m_writer.write(static_cast<uint32_t>(i));
}

template<typename Traits>
RegisterID* BytecodeGeneratorBase<Traits>::newRegister()
{
    m_calleeLocals.append(virtualRegisterForLocal(m_calleeLocals.size()));
    size_t numCalleeLocals = std::max<size_t>(m_codeBlock->numCalleeLocals(), m_calleeLocals.size());
    numCalleeLocals = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numCalleeLocals);
    m_codeBlock->setNumCalleeLocals(static_cast<unsigned>(numCalleeLocals));
    RELEASE_ASSERT(numCalleeLocals == m_codeBlock->numCalleeLocals());
    return &m_calleeLocals.last();
}

template<typename Traits>
RegisterID* BytecodeGeneratorBase<Traits>::newTemporary()
{
    reclaimFreeRegisters();

    RegisterID* result = newRegister();
    result->setTemporary();
    return result;
}

template<typename Traits>
template<typename Functor>
void BytecodeGeneratorBase<Traits>::newTemporaries(size_t count, const Functor& func)
{
    reclaimFreeRegisters();
    for (size_t index = 0; index < count; ++index) {
        RegisterID* result = newRegister();
        result->setTemporary();
        func(result);
    }
}

// Adds an anonymous local var slot. To give this slot a name, add it to symbolTable().
template<typename Traits>
RegisterID* BytecodeGeneratorBase<Traits>::addVar()
{
    int numVars = m_codeBlock->numVars();
    m_codeBlock->setNumVars(numVars + 1);
    RegisterID* result = newRegister();
    ASSERT(VirtualRegister(result->index()).toLocal() == numVars);
    result->ref(); // We should never free this slot.
    return result;
}

template<typename Traits>
void BytecodeGeneratorBase<Traits>::allocateCalleeSaveSpace(uint32_t virtualRegisterCountForCalleeSaves)
{
    for (size_t i = 0; i < virtualRegisterCountForCalleeSaves; i++)
        addVar();
}

} // namespace JSC
