/*******************************************************\
* Copyright (C) 2006, ApS s.r.o Brno, AllRightsReserved *
\*******************************************************/

#define DEBUG_TYPE "codasip-asm-printer"

#include "llvm/MC/MCSymbol.h"
#include "llvm/Function.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Module.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Target/Mangler.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Analysis/DebugInfo.h"
#include "llvm/Type.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/LLVMContext.h"

#include "Codasip.h"
#include "CodasipInstrInfo.h"
#include "CodasipTargetMachine.h"
#include "CodasipMachineFunction.h"

#include <iostream>
#include <cstdio>

using namespace llvm;

namespace {
  
  class CodasipAsmPrinter: public AsmPrinter {
    
    public:
      explicit CodasipAsmPrinter(TargetMachine &TM, MCStreamer &MS): AsmPrinter(TM,MS) {}
      
      const char *getPassName() const {return "Codasip Assembly Printer";}
      
      static const char* getRegisterName(unsigned RegNo);  // (def in the inc)
      const char* getInstructionName(unsigned Opcode);     // (def in the inc)
      
      void EmitStartOfAsmFile(Module &M);
      void EmitFunctionBodyStart();
      
    private:
      // auxiliary buffer for printing
      SmallString<128> pbuf;
      #define EMIT {OutStreamer.EmitRawText(O.str()); pbuf = "";}
      
      void printInstruction(const MachineInstr *inst, raw_ostream &O);       // (def in the inc)
      void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O);  // (called from the inc)
      void EmitInstruction(const MachineInstr *MI);
      
      bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode, raw_ostream &O);
      bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode, raw_ostream &O);
      
      /// Prints the .loc directive
      void PrintDebugLoc(const DebugLoc &dl);
  };
  
}

// called from llc::main
extern "C" void LLVMInitializeCodasipAsmPrinter() {
  RegisterAsmPrinter<CodasipAsmPrinter> X(TheCodasipTarget);
}

#define GET_INSTRUCTION_NAME
#include "CodasipGenAsmWriter.inc"

#define SHIFT_FLAG 0x1
#define MASK_FLAG  0x2

void CodasipAsmPrinter::printOperand(const MachineInstr *MI, int opNum, raw_ostream &O)
{
  const MachineOperand &MO = MI->getOperand(opNum);
  bool offset = false; // can it have offset?
  switch (MO.getType())
  {
    case MachineOperand::MO_Register:
      O << LowercaseString(getRegisterName(MO.getReg()));
      break;
    case MachineOperand::MO_Immediate:
      if (MO.getImm()>0) O << '+';
      O << MO.getImm();
      break;
    case MachineOperand::MO_MachineBasicBlock:
      MO.getMBB()->getSymbol()->print(O);
      offset = true;
      return;
    case MachineOperand::MO_GlobalAddress:
      Mang->getSymbol(MO.getGlobal())->print(O);
      offset = true;
      break;
    case MachineOperand::MO_ExternalSymbol:
      O << '$' << MO.getSymbolName();
      offset = true;
      break;
    case MachineOperand::MO_JumpTableIndex:
      O << MAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
        << '_' << MO.getIndex();
      break;
    case MachineOperand::MO_ConstantPoolIndex:
      O << MAI->getPrivateGlobalPrefix() << "CPI"
        << getFunctionNumber() << '_' << MO.getIndex();
      offset = true;
      break;
    case MachineOperand::MO_BlockAddress:
      O << *GetBlockAddressSymbol(MO.getBlockAddress());
      break;
    default:
      assert(false && "<unknown operand type>");
  }
  if (MO.getTargetFlags()&SHIFT_FLAG) O << ">>16";
  if (MO.getTargetFlags()&MASK_FLAG)  O << " &0xffff";
  if (offset && MO.getOffset()) O << "+" << MO.getOffset();
}

void CodasipAsmPrinter::EmitInstruction(const MachineInstr *MI)
{
  // debug info?
  PrintDebugLoc(MI->getDebugLoc());
  // the instr itself
  raw_svector_ostream O(pbuf);
  printInstruction(MI,O);
  EMIT;
}

bool CodasipAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode, raw_ostream &O)
{
  // Does this asm operand have a single letter operand modifier?
  if (ExtraCode && ExtraCode[0]) return true; // Unknown modifier.
  // print it
  printOperand(MI,OpNo,O);
  return false;
}

bool CodasipAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, unsigned AsmVariant, const char *ExtraCode, raw_ostream &O)
{
  assert(false);
  O << "XYZ";
  return false;
}


void CodasipAsmPrinter::PrintDebugLoc(const DebugLoc &dl)
{
  // shouldn't happen but who knows
  if (dl.isUnknown()) return;
  // create a stream
  raw_svector_ostream O(pbuf);
  // print
  O << "  .loc 1 " << dl.getLine() << ' ' << dl.getCol() << '\n';
  // emit
  EMIT;
}


// if there are only global varibles and no functions, no section is printed :(
void CodasipAsmPrinter::EmitStartOfAsmFile(Module &M)
{
  OutStreamer.EmitRawText(StringRef("  .data"));
}


// print vararg metainfo
void CodasipAsmPrinter::EmitFunctionBodyStart()
{
  const Function *func = MF->getFunction();
  if (func->isVarArg())
  {
    const CodasipFunctionInfo *finfo = MF->getInfo<CodasipFunctionInfo>();
    raw_svector_ostream O(pbuf);
    O << "  .set _Count_gpregs__, " << finfo->ArgsInfo.Count_gpregs << '\n';
    EMIT;
    O << "  .set _FO_gpregs__, " << finfo->ArgsInfo.FO_gpregs << '\n';
    EMIT;
    O << "  .set _StackOff__, " << finfo->ArgsInfo.StackOff << '\n';
    EMIT;
  }
}

