/*
 * Copyright (c) 2004-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * 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;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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.
 *
 * Authors: Kevin Lim
 */

#include <vector>

#include "cpu/o3/rename_map.hh"

using namespace std;

// @todo: Consider making inline bool functions that determine if the
// register is a logical int, logical fp, physical int, physical fp,
// etc.

SimpleRenameMap::~SimpleRenameMap()
{
}

void
SimpleRenameMap::init(unsigned _numLogicalIntRegs,
                      unsigned _numPhysicalIntRegs,
                      PhysRegIndex &ireg_idx,

                      unsigned _numLogicalFloatRegs,
                      unsigned _numPhysicalFloatRegs,
                      PhysRegIndex &freg_idx,

                      unsigned _numMiscRegs,

                      RegIndex _intZeroReg,
                      RegIndex _floatZeroReg,

                      int map_id,
                      bool bindRegs)
{
    id = map_id;

    numLogicalIntRegs = _numLogicalIntRegs;

    numLogicalFloatRegs = _numLogicalFloatRegs;

    numPhysicalIntRegs = _numPhysicalIntRegs;

    numPhysicalFloatRegs = _numPhysicalFloatRegs;

    numMiscRegs = _numMiscRegs;

    intZeroReg = _intZeroReg;
    floatZeroReg = _floatZeroReg;

    DPRINTF(Rename, "Creating rename map %i.  Phys: %i / %i, Float: "
            "%i / %i.\n", id, numLogicalIntRegs, numPhysicalIntRegs,
            numLogicalFloatRegs, numPhysicalFloatRegs);

    numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs;

    numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs;

    //Create the rename maps
    intRenameMap.resize(numLogicalIntRegs);
    floatRenameMap.resize(numLogicalRegs);

    if (bindRegs) {
        DPRINTF(Rename, "Binding registers into rename map %i",id);

        // Initialize the entries in the integer rename map to point to the
        // physical registers of the same index
        for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
        {
            intRenameMap[index].physical_reg = ireg_idx++;
        }

        // Initialize the entries in the floating point rename map to point to
        // the physical registers of the same index
        // Although the index refers purely to architected registers, because
        // the floating reg indices come after the integer reg indices, they
        // may exceed the size of a normal RegIndex (short).
        for (PhysRegIndex index = numLogicalIntRegs;
             index < numLogicalRegs; ++index)
        {
            floatRenameMap[index].physical_reg = freg_idx++;
        }
    } else {
        DPRINTF(Rename, "Binding registers into rename map %i",id);

        PhysRegIndex temp_ireg = ireg_idx;

        for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
        {
            intRenameMap[index].physical_reg = temp_ireg++;
        }

        PhysRegIndex temp_freg = freg_idx;

        for (PhysRegIndex index = numLogicalIntRegs;
             index < numLogicalRegs; ++index)
        {
            floatRenameMap[index].physical_reg = temp_freg++;
        }
    }
}

void
SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr)
{
    freeList = fl_ptr;
}


SimpleRenameMap::RenameInfo
SimpleRenameMap::rename(RegIndex arch_reg)
{
    PhysRegIndex renamed_reg;
    PhysRegIndex prev_reg;

    if (arch_reg < numLogicalIntRegs) {

        // Record the current physical register that is renamed to the
        // requested architected register.
        prev_reg = intRenameMap[arch_reg].physical_reg;

        // If it's not referencing the zero register, then rename the
        // register.
        if (arch_reg != intZeroReg) {
            renamed_reg = freeList->getIntReg();

            intRenameMap[arch_reg].physical_reg = renamed_reg;

            assert(renamed_reg >= 0 && renamed_reg < numPhysicalIntRegs);

        } else {
            // Otherwise return the zero register so nothing bad happens.
            renamed_reg = intZeroReg;
        }
    } else if (arch_reg < numLogicalRegs) {
        // Record the current physical register that is renamed to the
        // requested architected register.
        prev_reg = floatRenameMap[arch_reg].physical_reg;

        // If it's not referencing the zero register, then rename the
        // register.
        if (arch_reg != floatZeroReg) {
            renamed_reg = freeList->getFloatReg();

            floatRenameMap[arch_reg].physical_reg = renamed_reg;

            assert(renamed_reg < numPhysicalRegs &&
                   renamed_reg >= numPhysicalIntRegs);
        } else {
            // Otherwise return the zero register so nothing bad happens.
            renamed_reg = floatZeroReg;
        }
    } else {
        // Subtract off the base offset for miscellaneous registers.
        arch_reg = arch_reg - numLogicalRegs;

        DPRINTF(Rename, "Renamed misc reg %d\n", arch_reg);

        // No renaming happens to the misc. registers.  They are
        // simply the registers that come after all the physical
        // registers; thus take the base architected register and add
        // the physical registers to it.
        renamed_reg = arch_reg + numPhysicalRegs;

        // Set the previous register to the same register; mainly it must be
        // known that the prev reg was outside the range of normal registers
        // so the free list can avoid adding it.
        prev_reg = renamed_reg;

        assert(renamed_reg < numPhysicalRegs + numMiscRegs);
    }

    DPRINTF(Rename, "Renamed reg %d to physical reg %d old mapping was %d\n",
            arch_reg, renamed_reg, prev_reg);

    return RenameInfo(renamed_reg, prev_reg);
}

PhysRegIndex
SimpleRenameMap::lookup(RegIndex arch_reg)
{
    if (arch_reg < numLogicalIntRegs) {
        return intRenameMap[arch_reg].physical_reg;
    } else if (arch_reg < numLogicalRegs) {
        return floatRenameMap[arch_reg].physical_reg;
    } else {
        // Subtract off the misc registers offset.
        arch_reg = arch_reg - numLogicalRegs;

        // Misc. regs don't rename, so simply add the base arch reg to
        // the number of physical registers.
        return numPhysicalRegs + arch_reg;
    }
}

void
SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg)
{
    // In this implementation the miscellaneous registers do not
    // actually rename, so this function does not allow you to try to
    // change their mappings.
    if (arch_reg < numLogicalIntRegs) {
        DPRINTF(Rename, "Rename Map: Integer register %i being set to %i.\n",
                (int)arch_reg, renamed_reg);

        intRenameMap[arch_reg].physical_reg = renamed_reg;
    } else if (arch_reg < numLogicalIntRegs + numLogicalFloatRegs) {
        DPRINTF(Rename, "Rename Map: Float register %i being set to %i.\n",
                (int)arch_reg - numLogicalIntRegs, renamed_reg);

        floatRenameMap[arch_reg].physical_reg = renamed_reg;
    }
}

int
SimpleRenameMap::numFreeEntries()
{
    int free_int_regs = freeList->numFreeIntRegs();
    int free_float_regs = freeList->numFreeFloatRegs();

    if (free_int_regs < free_float_regs) {
        return free_int_regs;
    } else {
        return free_float_regs;
    }
}