/*
 * 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.
 */

#include <vector>

#include "cpu/beta_cpu/rename_map.hh"

using namespace std;

// Todo: Consider making functions inline.  Avoid having things that are
// using the zero register or misc registers from adding on the registers
// to the free list.  Possibly remove the direct communication between
// this and the freelist.  Considering making inline bool functions that
// determine if the register is a logical int, logical fp, physical int,
// physical fp, etc.

SimpleRenameMap::SimpleRenameMap(unsigned _numLogicalIntRegs,
                                 unsigned _numPhysicalIntRegs,
                                 unsigned _numLogicalFloatRegs,
                                 unsigned _numPhysicalFloatRegs,
                                 unsigned _numMiscRegs,
                                 RegIndex _intZeroReg,
                                 RegIndex _floatZeroReg)
    : numLogicalIntRegs(_numLogicalIntRegs),
      numPhysicalIntRegs(_numPhysicalIntRegs),
      numLogicalFloatRegs(_numLogicalFloatRegs),
      numPhysicalFloatRegs(_numPhysicalFloatRegs),
      numMiscRegs(_numMiscRegs),
      intZeroReg(_intZeroReg),
      floatZeroReg(_floatZeroReg)
{
    DPRINTF(Rename, "Rename: Creating rename map.  Phys: %i / %i, Float: "
            "%i / %i.\n", numLogicalIntRegs, numPhysicalIntRegs,
            numLogicalFloatRegs, numPhysicalFloatRegs);

    numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs;

    numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs;

    //Create the rename maps, and their scoreboards.
    intRenameMap = new RenameEntry[numLogicalIntRegs];
    floatRenameMap = new RenameEntry[numLogicalRegs];

    // Should combine this into one scoreboard.
    intScoreboard.resize(numPhysicalIntRegs);
    floatScoreboard.resize(numPhysicalRegs);
    miscScoreboard.resize(numPhysicalRegs + numMiscRegs);

    // Initialize the entries in the integer rename map to point to the
    // physical registers of the same index, and consider each register
    // ready until the first rename occurs.
    for (RegIndex index = 0; index < numLogicalIntRegs; ++index)
    {
        intRenameMap[index].physical_reg = index;
        intScoreboard[index] = 1;
    }

    // Initialize the rest of the physical registers (the ones that don't
    // directly map to a logical register) as unready.
    for (PhysRegIndex index = numLogicalIntRegs;
         index < numPhysicalIntRegs;
         ++index)
    {
        intScoreboard[index] = 0;
    }

    int float_reg_idx = numPhysicalIntRegs;

    // Initialize the entries in the floating point rename map to point to
    // the physical registers of the same index, and consider each register
    // ready until the first rename occurs.
    // 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 = float_reg_idx++;
    }

    for (PhysRegIndex index = numPhysicalIntRegs;
         index < numPhysicalIntRegs + numLogicalFloatRegs; ++index)
    {
        floatScoreboard[index] = 1;
    }

    // Initialize the rest of the physical registers (the ones that don't
    // directly map to a logical register) as unready.
    for (PhysRegIndex index = numPhysicalIntRegs + numLogicalFloatRegs;
         index < numPhysicalRegs;
         ++index)
    {
        floatScoreboard[index] = 0;
    }

    // Initialize the entries in the misc register scoreboard to be ready.
    for (PhysRegIndex index = numPhysicalRegs;
         index < numPhysicalRegs + numMiscRegs; ++index)
    {
        miscScoreboard[index] = 1;
    }
}

SimpleRenameMap::~SimpleRenameMap()
{
    // Delete the rename maps as they were allocated with new.
    delete [] intRenameMap;
    delete [] floatRenameMap;
}

void
SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr)
{
    //Setup the interface to the freelist.
    freeList = fl_ptr;
}


// Don't allow this stage to fault; force that check to the rename stage.
// Simply ask to rename a logical register and get back a new physical
// register index.
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 mark the register
        // as not ready.
        if (arch_reg != intZeroReg) {
            // Get a free physical register to rename to.
            renamed_reg = freeList->getIntReg();

            // Update the integer rename map.
            intRenameMap[arch_reg].physical_reg = renamed_reg;

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

            // Mark register as not ready.
            intScoreboard[renamed_reg] = false;
        } else {
            // Otherwise return the zero register so nothing bad happens.
            renamed_reg = intZeroReg;
        }
    } else if (arch_reg < numLogicalRegs) {
        // Subtract off the base offset for floating point registers.
//        arch_reg = arch_reg - numLogicalIntRegs;

        // 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 mark the register
        // as not ready.
        if (arch_reg != floatZeroReg) {
            // Get a free floating point register to rename to.
            renamed_reg = freeList->getFloatReg();

            // Update the floating point rename map.
            floatRenameMap[arch_reg].physical_reg = renamed_reg;

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

            // Mark register as not ready.
            floatScoreboard[renamed_reg] = false;
        } 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;

        // 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);

        miscScoreboard[renamed_reg] = false;
    }

    return RenameInfo(renamed_reg, prev_reg);
}

//Perhaps give this a pair as a return value, of the physical register
//and whether or not it's ready.
PhysRegIndex
SimpleRenameMap::lookup(RegIndex arch_reg)
{
    if (arch_reg < numLogicalIntRegs) {
        return intRenameMap[arch_reg].physical_reg;
    } else if (arch_reg < numLogicalRegs) {
        // Subtract off the base FP offset.
//        arch_reg = arch_reg - numLogicalIntRegs;

        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;
    }
}

bool
SimpleRenameMap::isReady(PhysRegIndex phys_reg)
{
    if (phys_reg < numPhysicalIntRegs) {
        return intScoreboard[phys_reg];
    } else if (phys_reg < numPhysicalRegs) {

        // Subtract off the base FP offset.
//        phys_reg = phys_reg - numPhysicalIntRegs;

        return floatScoreboard[phys_reg];
    } else {
        // Subtract off the misc registers offset.
//        phys_reg = phys_reg - numPhysicalRegs;

        return miscScoreboard[phys_reg];
    }
}

// In this implementation the miscellaneous registers do not actually rename,
// so this function does not allow you to try to change their mappings.
void
SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg)
{
    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 {
        assert(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;
    }
}

void
SimpleRenameMap::squash(vector<RegIndex> freed_regs,
                        vector<UnmapInfo> unmaps)
{
    panic("Not sure this function should be called.");

    // Not sure the rename map should be able to access the free list
    // like this.
    while (!freed_regs.empty()) {
        RegIndex free_register = freed_regs.back();

        if (free_register < numPhysicalIntRegs) {
            freeList->addIntReg(free_register);
        } else {
            // Subtract off the base FP dependence tag.
            free_register = free_register - numPhysicalIntRegs;
            freeList->addFloatReg(free_register);
        }

        freed_regs.pop_back();
    }

    // Take unmap info and roll back the rename map.
}

void
SimpleRenameMap::markAsReady(PhysRegIndex ready_reg)
{
    DPRINTF(Rename, "Rename map: Marking register %i as ready.\n",
            (int)ready_reg);

    if (ready_reg < numPhysicalIntRegs) {
        assert(ready_reg >= 0);

        intScoreboard[ready_reg] = 1;
    } else if (ready_reg < numPhysicalRegs) {

        // Subtract off the base FP offset.
//        ready_reg = ready_reg - numPhysicalIntRegs;

        floatScoreboard[ready_reg] = 1;
    } else {
        //Subtract off the misc registers offset.
//        ready_reg = ready_reg - numPhysicalRegs;

        miscScoreboard[ready_reg] = 1;
    }
}

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;
    }
}