/*!
* \file TS1VLIWPacking.cpp
*
* \brief Packing Pass to allow correct register allocation of parallel executable
         instructions
*
* The register allocation is working on the sequential list of machine 
* instructions, and is not able to handle parallel instructions.
* To allow correct register allocation of parallel instructions a packing pass
* is used. The pass packs the operands of parallel instructions in one PACK 
* instruction and deletes the original instructions.
* Some instructions are not allowed to be packed, because this would result in
* errors.
*/
#include <sstream>
#include <string>
#include <vector>

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

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

using namespace llvm;

namespace llvm {

  struct TS1VLIWPacking : public MachineFunctionPass{

  public:
    static char ID;
    TS1TargetMachine &TM;

    TS1VLIWPacking(TS1TargetMachine &tm)
      : MachineFunctionPass (ID), TM(tm) {}
    virtual const char *getPassName() const {
      return "TS1 VLIW Packing Pass";
    }

    bool runOnMachineBasicBlock (MachineBasicBlock &mbb);

    virtual bool runOnMachineFunction (MachineFunction &F) override;

  private:

    /**
    * Test an instruction, if it is allowed to be packed.
    * \param[in] opcode  The opcode of the instruction to be tested.
    * \return True, if the instruction is NOT allowed to be packed.
    */
    bool isForbiddenInstr (const MachineInstr* instr) const;

    void packListOfInstr (MachineBasicBlock& mbb,
                          MachineBasicBlock::iterator insertPoint,
                          std::vector<MachineInstr*>& instrList);

  };
  char TS1VLIWPacking::ID = 0;
}

FunctionPass *llvm::createTS1VLIWPacking (TS1TargetMachine &tm)
{
  return new TS1VLIWPacking(tm);
}

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

	assert(TS1FI->getRepresentation() == TS1FunctionInfo::Representation_Unpacked);

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

	TS1FI->setRepresentation(TS1FunctionInfo::Representation_Packed);

	return Changed;
}

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

  bool changed = false;
  //bool isNextInstr = false;
  bool needNewPackInstr = true;
  MachineInstrBuilder mib;
  MachineInstr* currPackInstr = NULL;

#if 0
  errs() << "Instructions before Packing\n";
  errs() << "-------------------------------------------\n";
#endif

  std::vector<MachineInstr*> instrsToPack;

  for (MachineBasicBlock::iterator I = mbb.begin(); I != mbb.end();)
  {
    MachineBasicBlock::iterator I2 = I++;
    MachineInstr* curInstr = I2;

#if 0
    errs() << curInstr->getDesc().getName() << ":: ";

    // prints out instructions before they are packed
    for (unsigned i = 0; i < curInstr->getNumOperands(); ++i)
    {
      MachineOperand MO = curInstr->getOperand(i);
      errs() << MO << " ";
    }
    errs() << "\n";
#endif

    // Though PHI are forbidden instructions they have to be at the beginning
    // of a machine basic block. Currently they are. So don't mess this up by
    // moving them at the end of a Bundle
    if (curInstr->getOpcode() == TS1::PHI)
      continue;

    // Remember instructions that need to be packed
    if (!isForbiddenInstr(curInstr))
      instrsToPack.push_back(curInstr);

    // End of bundle reached or last instruction of basic block reached
    if ( curInstr->getOpcode() == TS1::NEXT
         || curInstr->getDesc().isTerminator())
    {
      // if there is only one instruction packable it's not necessary doing it
      if (instrsToPack.size() > 1)
        packListOfInstr(mbb, curInstr, instrsToPack);
      instrsToPack.clear();

      if (curInstr->getOpcode() == TS1::NEXT)
        curInstr->eraseFromParent();
    }
  }

  // If the last Bundle of a basic block has no instruction with terminator flag
  // insert a PACK instruction at the end of the basic block
  if (instrsToPack.size() > 1)
    packListOfInstr(mbb, mbb.end(), instrsToPack);


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

  return changed;
}

// To test Opcodes if they are allowed to be packed
// Return true if they cant be packed.
bool TS1VLIWPacking::isForbiddenInstr(const MachineInstr* instr) const
{
  int opcode = instr->getOpcode();

  // NEXT is also tagged as branch but do it explicitly
  if (opcode == TS1::NEXT)
    return true;

  // BR_i_1 has to be excluded, because for the register allocation it has to be
  // known. MOV instructions are inserted at the beginning of the register allocation
  // and they have to be inserted before the BR. When BR is packed, register
  // allocation doesn't detect it.
  if (TM.getInstrInfo()->get(opcode).isBranch())
    return true;

  if (TM.getInstrInfo()->get(opcode).isReturn())
    return true;

  switch (opcode)
  {
    case TS1::PHI:
    case TS1::IMPLICIT_DEF:
    case TS1::COPY:
    case TS1::DBG_VALUE:
    case TS1::ADJCALLSTACKUP:
    case TS1::ADJCALLSTACKDOWN:
    case TS1::INLINEASM:
      return true;

    default:
      return false;
  }
}



void TS1VLIWPacking::packListOfInstr (MachineBasicBlock& mbb,
                                      MachineBasicBlock::iterator insertPoint,
                                      std::vector<MachineInstr*>& instrList)
{
  if (instrList.empty())
    return;

  MachineInstrBuilder mib;
  // Do the packing
  // Create new Pack instruction
  mib = BuildMI(mbb, insertPoint, DebugLoc(), TM.getInstrInfo()->get(TS1::PACK));

  for (unsigned i = 0; i < instrList.size(); ++i)
  {
    MachineInstr* curInstr = instrList[i];

    mib.addImm(curInstr->getOpcode());

    // add all operands. implicit operands also have to be added, because the
    // register allocation need to have knowledge about them.
    for (unsigned i = 0; i < curInstr->getNumOperands(); ++i)
    {
      mib.addOperand(curInstr->getOperand(i));
    }
	
	mib.addExternalSymbol("MemRefs");
	int memOpsCnt = 0;

	for (MachineInstr::mmo_iterator mmoi = curInstr->memoperands_begin(),
		mmoe = curInstr->memoperands_end(); mmoi != mmoe; ++mmoi)
	{
		MachineMemOperand* mmo = *mmoi;
		mib.addMemOperand(mmo);
		++memOpsCnt;
	}

	mib.addImm(memOpsCnt);
	//mib.addMemOperand(MachineMemOperand(Value(Value::MDNodeVal,s))))
	//MachineMemOperand* memOp = mbb.getParent()->getMachineMemOperand(0,0,0,0,0);

    mib.addExternalSymbol("EndOfOp");

    curInstr->eraseFromParent();
  }
}