//===-- MipsAsmPrinter.cpp - Mips LLVM assembly writer --------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Bruno Cardoso Lopes and is distributed under the 
// University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a printer that converts from our internal representation
// of machine-dependent LLVM code to GAS-format MIPS assembly language.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "mips-asm-printer"

#include "Mips.h"
#include "MipsInstrInfo.h"
#include "MipsTargetMachine.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/Target/TargetAsmInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Support/Mangler.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MathExtras.h"
#include <cctype>

using namespace llvm;

STATISTIC(EmittedInsts, "Number of machine instrs printed");

namespace {
  struct VISIBILITY_HIDDEN MipsAsmPrinter : public AsmPrinter {
    MipsAsmPrinter(std::ostream &O, MipsTargetMachine &TM, 
                   const TargetAsmInfo *T): 
                   AsmPrinter(O, TM, T) {}

    virtual const char *getPassName() const {
      return "Mips Assembly Printer";
    }

    void printOperand(const MachineInstr *MI, int opNum);
    void printMemOperand(const MachineInstr *MI, int opNum, 
                         const char *Modifier = 0);

    bool printInstruction(const MachineInstr *MI);  // autogenerated.
    bool runOnMachineFunction(MachineFunction &F);
    bool doInitialization(Module &M);
    bool doFinalization(Module &M);
  };
} // end of anonymous namespace

#include "MipsGenAsmWriter.inc"

/// createMipsCodePrinterPass - Returns a pass that prints the MIPS
/// assembly code for a MachineFunction to the given output stream,
/// using the given target machine description.  This should work
/// regardless of whether the function is in SSA form.
FunctionPass *llvm::createMipsCodePrinterPass(std::ostream &o,
                                              MipsTargetMachine &tm) 
{
  return new MipsAsmPrinter(o, tm, tm.getTargetAsmInfo());
}

/// runOnMachineFunction - This uses the printMachineInstruction()
/// method to print assembly for each instruction.
bool MipsAsmPrinter::
runOnMachineFunction(MachineFunction &MF) 
{
  SetupMachineFunction(MF);

  // Print out constants referenced by the function
  EmitConstantPool(MF.getConstantPool());

  O << "\n\n";

  // What's my mangled name?
  CurrentFnName = Mang->getValueName(MF.getFunction());

  // Print out the label for the function.
  const Function *F = MF.getFunction();
  SwitchToTextSection(getSectionForFunction(*F).c_str(), F);

  // On Mips GAS if .align #n is present, #n means the number of bits 
  // to be cleared to align. So, if we want 4 byte alignment, we must 
  // have .align 2
  // TODO:
  //  add gas ".mask" and ".fmask"
  EmitAlignment(1, F);
  O << "\t.globl\t" << CurrentFnName << "\n";
  O << "\t.ent\t" << CurrentFnName << "\n";
  O << "\t.type\t"  << CurrentFnName << ", @function\n";
  O << CurrentFnName << ":\n";

  // Print out code for the function.
  for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
       I != E; ++I) {

    // Print a label for the basic block.
    if (I != MF.begin()) {
      printBasicBlockLabel(I, true);
      O << '\n';
    }

    for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
         II != E; ++II) {
      // Print the assembly for the instruction.
      O << "\t";
      printInstruction(II);
      ++EmittedInsts;
    }
  }

  // close function with asm directive
  O << "\t.end\t" << CurrentFnName << "\n";

  // We didn't modify anything.
  return false;
}

void MipsAsmPrinter::
printOperand(const MachineInstr *MI, int opNum) 
{
  const MachineOperand &MO = MI->getOperand(opNum);
  const MRegisterInfo  &RI = *TM.getRegisterInfo();
  bool  closeP=false;

  // %hi and %lo used on mips gas to break large constants
  if (MI->getOpcode() == Mips::LUi && !MO.isRegister() 
      && !MO.isImmediate()) {
    O << "%hi(";
    closeP = true;
  } else if ((MI->getOpcode() == Mips::ADDiu) && !MO.isRegister() 
             && !MO.isImmediate()) {
    O << "%lo(";
    closeP = true;
  }
 
  switch (MO.getType()) 
  {
    case MachineOperand::MO_Register:
      if (MRegisterInfo::isPhysicalRegister(MO.getReg()))
        O << "$" << LowercaseString (RI.get(MO.getReg()).Name);
      else
        O << "$" << MO.getReg();
      break;

    case MachineOperand::MO_Immediate:
      if ((MI->getOpcode() == Mips::SLTiu) || (MI->getOpcode() == Mips::ORi) || 
          (MI->getOpcode() == Mips::LUi) || (MI->getOpcode() == Mips::ANDi))
        O << (unsigned int)MO.getImmedValue();
      else
        O << (int)MO.getImmedValue();
      break;

    case MachineOperand::MO_MachineBasicBlock:
      printBasicBlockLabel(MO.getMachineBasicBlock());
      return;

    case MachineOperand::MO_GlobalAddress:
      O << Mang->getValueName(MO.getGlobal());
      break;

    case MachineOperand::MO_ExternalSymbol:
      O << MO.getSymbolName();
      break;

    case MachineOperand::MO_ConstantPoolIndex:
      O << TAI->getPrivateGlobalPrefix() << "CPI"
        << getFunctionNumber() << "_" << MO.getConstantPoolIndex();
      break;
  
    default:
      O << "<unknown operand type>"; abort (); break;
  }

  if (closeP) O << ")";
}

void MipsAsmPrinter::
printMemOperand(const MachineInstr *MI, int opNum, const char *Modifier) 
{
  // lw/sw $reg, MemOperand
  // will turn into :
  // lw/sw $reg, imm($reg)
  printOperand(MI, opNum);
  O << "(";
  printOperand(MI, opNum+1);
  O << ")";
}

bool MipsAsmPrinter::
doInitialization(Module &M) 
{
  Mang = new Mangler(M);
  return false; // success
}

bool MipsAsmPrinter::
doFinalization(Module &M) 
{
  const TargetData *TD = TM.getTargetData();

  // Print out module-level global variables here.
  for (Module::const_global_iterator I = M.global_begin(),
         E = M.global_end(); I != E; ++I)

    // External global require no code
    if (I->hasInitializer()) {

      // Check to see if this is a special global 
      // used by LLVM, if so, emit it.
      if (EmitSpecialLLVMGlobal(I))
        continue;
      
      O << "\n\n";
      std::string name = Mang->getValueName(I);
      Constant *C      = I->getInitializer();
      unsigned Size    = TD->getTypeSize(C->getType());
      unsigned Align   = TD->getPrefTypeAlignment(C->getType());

      if (C->isNullValue() && (I->hasLinkOnceLinkage() || 
        I->hasInternalLinkage() || I->hasWeakLinkage() 
        /* FIXME: Verify correct */)) {

        SwitchToDataSection(".data", I);
        if (I->hasInternalLinkage())
          O << "\t.local " << name << "\n";

        O << "\t.comm " << name << "," 
          << TD->getTypeSize(C->getType()) 
          << "," << Align << "\n";

      } else {

        switch (I->getLinkage()) 
        {
          case GlobalValue::LinkOnceLinkage:
          case GlobalValue::WeakLinkage:   
            // FIXME: Verify correct for weak.
            // Nonnull linkonce -> weak
            O << "\t.weak " << name << "\n";
            SwitchToDataSection("", I);
            O << "\t.section\t\".llvm.linkonce.d." << name
                          << "\",\"aw\",@progbits\n";
            break;
          case GlobalValue::AppendingLinkage:
            // FIXME: appending linkage variables 
            // should go into a section of  their name or 
            // something.  For now, just emit them as external.
          case GlobalValue::ExternalLinkage:
            // If external or appending, declare as a global symbol
            O << "\t.globl " << name << "\n";
          case GlobalValue::InternalLinkage:
            if (C->isNullValue())
              SwitchToDataSection(".bss", I);
            else
              SwitchToDataSection(".data", I);
            break;
          case GlobalValue::GhostLinkage:
            cerr << "Should not have any" 
                 << "unmaterialized functions!\n";
            abort();
          case GlobalValue::DLLImportLinkage:
            cerr << "DLLImport linkage is" 
                 << "not supported by this target!\n";
            abort();
          case GlobalValue::DLLExportLinkage:
            cerr << "DLLExport linkage is" 
                 << "not supported by this target!\n";
            abort();
          default:
            assert(0 && "Unknown linkage type!");          
        }
        O << "\t.align " << Align << "\n";
        O << "\t.type " << name << ",@object\n";
        O << "\t.size " << name << "," << Size << "\n";
        O << name << ":\n";
        EmitGlobalConstant(C);
    }
  }

  AsmPrinter::doFinalization(M);
  return false; // success
}
