//===-- HexagonAsmPrinter.h - Print machine code to an Hexagon .s file ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Hexagon Assembly printer class.
//
//===----------------------------------------------------------------------===//

#ifndef HEXAGONASMPRINTER_H
#define HEXAGONASMPRINTER_H

#include "Hexagon.h"
#include "HexagonTargetMachine.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"

namespace llvm {
  class HexagonAsmPrinter : public AsmPrinter {
    const HexagonSubtarget *Subtarget;

  public:
    explicit HexagonAsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
      : AsmPrinter(TM, Streamer) {
      Subtarget = &TM.getSubtarget<HexagonSubtarget>();
    }

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

    bool isBlockOnlyReachableByFallthrough(const MachineBasicBlock *MBB) const;

    virtual void EmitInstruction(const MachineInstr *MI);
    virtual void EmitAlignment(unsigned NumBits,
                               const GlobalValue *GV = 0) const;

    void printOperand(const MachineInstr *MI, unsigned OpNo, raw_ostream &O);
    bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
                         unsigned AsmVariant, const char *ExtraCode,
                         raw_ostream &OS);
    bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
                               unsigned AsmVariant, const char *ExtraCode,
                               raw_ostream &OS);

    /// printInstruction - This method is automatically generated by tablegen
    /// from the instruction set description.  This method returns true if the
    /// machine instruction was sufficiently described to print it, otherwise it
    /// returns false.
    void printInstruction(const MachineInstr *MI, raw_ostream &O);

    //    void printMachineInstruction(const MachineInstr *MI);
    void printOp(const MachineOperand &MO, raw_ostream &O);

    /// printRegister - Print register according to target requirements.
    ///
    void printRegister(const MachineOperand &MO, bool R0AsZero,
                       raw_ostream &O) {
      unsigned RegNo = MO.getReg();
      assert(TargetRegisterInfo::isPhysicalRegister(RegNo) && "Not physreg??");
      O << getRegisterName(RegNo);
    }

    void printImmOperand(const MachineInstr *MI, unsigned OpNo,
                                raw_ostream &O) {
      int value = MI->getOperand(OpNo).getImm();
      O << value;
    }

    void printNegImmOperand(const MachineInstr *MI, unsigned OpNo,
                                   raw_ostream &O) {
      int value = MI->getOperand(OpNo).getImm();
      O << -value;
    }

    void printMEMriOperand(const MachineInstr *MI, unsigned OpNo,
                                  raw_ostream &O) {
      const MachineOperand &MO1 = MI->getOperand(OpNo);
      const MachineOperand &MO2 = MI->getOperand(OpNo+1);

      O << getRegisterName(MO1.getReg())
        << " + #"
        << (int) MO2.getImm();
    }

    void printFrameIndexOperand(const MachineInstr *MI, unsigned OpNo,
                                       raw_ostream &O) {
      const MachineOperand &MO1 = MI->getOperand(OpNo);
      const MachineOperand &MO2 = MI->getOperand(OpNo+1);

      O << getRegisterName(MO1.getReg())
        << ", #"
        << MO2.getImm();
    }

    void printBranchOperand(const MachineInstr *MI, unsigned OpNo,
                            raw_ostream &O) {
      // Branches can take an immediate operand.  This is used by the branch
      // selection pass to print $+8, an eight byte displacement from the PC.
      if (MI->getOperand(OpNo).isImm()) {
        O << "$+" << MI->getOperand(OpNo).getImm()*4;
      } else {
        printOp(MI->getOperand(OpNo), O);
      }
    }

    void printCallOperand(const MachineInstr *MI, unsigned OpNo,
                          raw_ostream &O) {
    }

    void printAbsAddrOperand(const MachineInstr *MI, unsigned OpNo,
                             raw_ostream &O) {
    }

    void printSymbolHi(const MachineInstr *MI, unsigned OpNo, raw_ostream &O) {
      O << "#HI(";
      if (MI->getOperand(OpNo).isImm()) {
        printImmOperand(MI, OpNo, O);
      }
      else {
        printOp(MI->getOperand(OpNo), O);
      }
      O << ")";
    }

    void printSymbolLo(const MachineInstr *MI, unsigned OpNo, raw_ostream &O) {
      O << "#HI(";
      if (MI->getOperand(OpNo).isImm()) {
        printImmOperand(MI, OpNo, O);
      }
      else {
        printOp(MI->getOperand(OpNo), O);
      }
      O << ")";
    }

    void printPredicateOperand(const MachineInstr *MI, unsigned OpNo,
                               raw_ostream &O);

#if 0
    void printModuleLevelGV(const GlobalVariable* GVar, raw_ostream &O);
#endif

    void printAddrModeBasePlusOffset(const MachineInstr *MI, int OpNo,
                                     raw_ostream &O);

    void printGlobalOperand(const MachineInstr *MI, int OpNo, raw_ostream &O);
    void printJumpTable(const MachineInstr *MI, int OpNo, raw_ostream &O);
    void printConstantPool(const MachineInstr *MI, int OpNo, raw_ostream &O);

    static const char *getRegisterName(unsigned RegNo);

#if 0
    void EmitStartOfAsmFile(Module &M);
#endif
  };

} // end of llvm namespace

#endif
