/*!
* \file TS1VLIWUnpacking.cpp
*
* \brief Unpacking Pass to allow correct register allocation of parallel executable
instructions
*
* \remark Only use that pass, when using the Packing Pass before register allocation.
*
* The Packing Pass groups together instructions that can be executed in parallel.
* After register allocation the original instructions have to be recovered. This
* Unpacking Pass reads out the Operands of the PACK instructions and generates 
* the original instructions.
*/
#include <string>
#include <cstring>

#include "TS1.h"
#include "TS1TargetMachine.h"
#include "TS1InstrInfo.h"
#include "TS1MachineFunctionInfo.h"

#include "llvm/Function.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace llvm {

    struct TS1VLIWUnpacking : public MachineFunctionPass{

    public:
      static char ID;
      TS1TargetMachine &TM;
      bool m_afterReScheduling;

      TS1VLIWUnpacking(TS1TargetMachine &tm, bool afterReSched) 
        : MachineFunctionPass (ID), TM(tm), m_afterReScheduling(afterReSched) {}

      virtual const char *getPassName() const {
        return "TS1 Unpacking Pass";
      }

      bool runOnMachineBasicBlock (MachineBasicBlock &mbb);

      virtual bool runOnMachineFunction (MachineFunction &F);

    };
    char TS1VLIWUnpacking::ID = 0;
}

FunctionPass *llvm::createTS1VLIWUnpacking (TS1TargetMachine &tm, bool afterReSched)
{
  return new TS1VLIWUnpacking(tm, afterReSched);
}

bool TS1VLIWUnpacking::runOnMachineFunction (MachineFunction &F) {
	bool Changed = false;
	TS1FunctionInfo *TS1FI = F.getInfo<TS1FunctionInfo>(); 

	assert(TS1FI->getRepresentation() == TS1FunctionInfo::Representation_Packed 
		|| TS1FI->getRepresentation() == TS1FunctionInfo::Representation_Mixed);

	for (MachineFunction::iterator FI = F.begin(), FE = F.end(); FI != FE; ++FI) {
		Changed |= runOnMachineBasicBlock(*FI);
	}

	TS1FI->setRepresentation(TS1FunctionInfo::Representation_Unpacked);

	return Changed;
}


bool TS1VLIWUnpacking::runOnMachineBasicBlock (MachineBasicBlock &mbb)
{
  if (CmdBundlingDebugLevel >= 1)
    errs() << "------ VLIW-UnPacking: MBB " << mbb.getNumber() << " ------\n";

  bool changed = false;

#if 0
  // print out instructions before unpacking
  errs() << "\nMachineBasicBlock "<< mbb.getNumber() << " : " << "before UNpacking\n";
  errs() << "-----------------------------------\n";
  for (MachineBasicBlock::iterator I = mbb.begin(), E = mbb.end(); I != E; ++I)
  {
    MachineInstr* currentInstruction = I;
    errs() << currentInstruction->getDesc().getName() << ":: ";
    unsigned int operandNumber = currentInstruction->getNumOperands();
    for (unsigned i = 0; i < operandNumber; ++i)
    {
      MachineOperand &MO = currentInstruction->getOperand(i);
      errs() << MO << " ";

    }
    errs() << "\n";
  }
  errs() << "\n";
#endif

  for (MachineBasicBlock::iterator I = mbb.begin(); I != mbb.end();)
  {
    MachineBasicBlock::iterator I2 = I++;
    MachineInstr* currentInstruction = I2;
    TargetInstrDesc currentInstrDesc = currentInstruction->getDesc();
    const char* currentInstrName = currentInstrDesc.getName();

    static int count = 0;
    count += 1;

    // Unpacking used with re-scheduling: Don't insert NEXT, because
    // Re-Scheduling did it
    if (!m_afterReScheduling)
      // Put each instruction in its own bundle
      if (I != mbb.end())
        BuildMI(mbb, I,DebugLoc(), TM.getInstrInfo()->get(TS1::NEXT));

    // unpack all PACK instructions
    if (currentInstrName == TM.getInstrInfo()->get(TS1::PACK).getName())
    {
      unsigned int currentOperand = 0;
	  MachineInstr::mmo_iterator curMemOpIt = currentInstruction->memoperands_begin();

      // Create instructions from the operands of the pack instruction. After this,
      // copy all explicit Operands into the unpacked instruction
      // implicit operands are already added, when calling the BuildMI-Function
      while(currentOperand < currentInstruction->getNumOperands())
      {
        assert(!currentInstruction->getOperand(currentOperand).isReg() && "Implicit register at this place indicates that some alias register are missing in use/defs lists and added later by register allocation");
        unsigned int opcode = currentInstruction->getOperand(currentOperand++).getImm();
		const TargetInstrDesc& TID = TM.getInstrInfo()->get(opcode);

        MachineInstrBuilder mib = BuildMI(mbb, I2, DebugLoc(), TID);
		
		bool memRefFlag = false;
        for (;;) {
          // resembles a pop command
          MachineOperand MO = currentInstruction->getOperand(currentOperand++);
          
		  // Add the mem Refs encoded in the Pack instruction
		  if (memRefFlag)
		  {
			unsigned memRefCnt = MO.getImm();
			for (unsigned i = 0; i < memRefCnt; ++i)
			{
					MachineMemOperand* curMMO = *curMemOpIt;
					mib.addMemOperand(curMMO);
					++curMemOpIt;
			}
			memRefFlag = false;
			continue;
		  }

		  
		  if (MO.isSymbol() && std::strcmp(MO.getSymbolName(), "MemRefs") == 0)
		  {
			memRefFlag = true;
			continue;
		  }

		  if (MO.isSymbol() && std::strcmp(MO.getSymbolName(), "EndOfOp") == 0)
            break;

          if (MO.isReg())
          {
            if (!MO.isImplicit())
              mib.addOperand(MO);
          }else{
            mib.addOperand(MO);
          }
        }

		TM.getInstrInfo()->updateTargetFlags(mib);
      }

      // erase PACK-instruction
      currentInstruction->eraseFromParent();
    }
  }

#if 0
  // prints out the instructions after UNPacking. prints out the name and operands
  errs() << "\n\nInstructions after UnPacking\n";
  errs() << "-----------------------------------\n";
  for (MachineBasicBlock::iterator I = mbb.begin(), E = mbb.end(); I != E; ++I)
  {
    MachineInstr* currentInstruction = I;
    errs() << currentInstruction->getDesc().getName() << ":: ";
    unsigned int operandNumber = currentInstruction->getNumOperands();
    for (unsigned i = 0; i < operandNumber; ++i)
    {
      MachineOperand &MO = currentInstruction->getOperand(i);
      errs() << MO << " ";
    }
    errs() << "\n";
  }
  errs() << "\n";
#endif

  return changed;
}
