//===-- SystemZMCInstLower.cpp - Lower MachineInstr to MCInst -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "SystemZMCInstLower.h"
#include "SystemZAsmPrinter.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Target/Mangler.h"

using namespace llvm;

// If Opcode is an interprocedural reference that can be shortened,
// return the short form, otherwise return 0.
static unsigned getShortenedInstr(unsigned Opcode) {
  switch (Opcode) {
  case SystemZ::BRASL: return SystemZ::BRAS;
  }
  return Opcode;
}

// Return the VK_* enumeration for MachineOperand target flags Flags.
static MCSymbolRefExpr::VariantKind getVariantKind(unsigned Flags) {
  switch (Flags & SystemZII::MO_SYMBOL_MODIFIER) {
    case 0:
      return MCSymbolRefExpr::VK_None;
    case SystemZII::MO_GOT:
      return MCSymbolRefExpr::VK_GOT;
  }
  llvm_unreachable("Unrecognised MO_ACCESS_MODEL");
}

SystemZMCInstLower::SystemZMCInstLower(Mangler *mang, MCContext &ctx,
                                       SystemZAsmPrinter &asmprinter)
  : Mang(mang), Ctx(ctx), AsmPrinter(asmprinter) {}

MCOperand SystemZMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
                                                 const MCSymbol *Symbol,
                                                 int64_t Offset) const {
  MCSymbolRefExpr::VariantKind Kind = getVariantKind(MO.getTargetFlags());
  const MCExpr *Expr = MCSymbolRefExpr::Create(Symbol, Kind, Ctx);
  if (Offset) {
    const MCExpr *OffsetExpr = MCConstantExpr::Create(Offset, Ctx);
    Expr = MCBinaryExpr::CreateAdd(Expr, OffsetExpr, Ctx);
  }
  return MCOperand::CreateExpr(Expr);
}

MCOperand SystemZMCInstLower::lowerOperand(const MachineOperand &MO) const {
  switch (MO.getType()) {
  default:
    llvm_unreachable("unknown operand type");

  case MachineOperand::MO_Register:
    return MCOperand::CreateReg(MO.getReg());

  case MachineOperand::MO_Immediate:
    return MCOperand::CreateImm(MO.getImm());

  case MachineOperand::MO_MachineBasicBlock:
    return lowerSymbolOperand(MO, MO.getMBB()->getSymbol(),
                              /* MO has no offset field */0);

  case MachineOperand::MO_GlobalAddress:
    return lowerSymbolOperand(MO, Mang->getSymbol(MO.getGlobal()),
                              MO.getOffset());

  case MachineOperand::MO_ExternalSymbol: {
    StringRef Name = MO.getSymbolName();
    return lowerSymbolOperand(MO, AsmPrinter.GetExternalSymbolSymbol(Name),
                              MO.getOffset());
  }

  case MachineOperand::MO_JumpTableIndex:
    return lowerSymbolOperand(MO, AsmPrinter.GetJTISymbol(MO.getIndex()),
                              /* MO has no offset field */0);

  case MachineOperand::MO_ConstantPoolIndex:
    return lowerSymbolOperand(MO, AsmPrinter.GetCPISymbol(MO.getIndex()),
                              MO.getOffset());

  case MachineOperand::MO_BlockAddress: {
    const BlockAddress *BA = MO.getBlockAddress();
    return lowerSymbolOperand(MO, AsmPrinter.GetBlockAddressSymbol(BA),
                              MO.getOffset());
  }
  }
}

void SystemZMCInstLower::lower(const MachineInstr *MI, MCInst &OutMI) const {
  unsigned Opcode = MI->getOpcode();
  // When emitting binary code, start with the shortest form of an instruction
  // and then relax it where necessary.
  if (!AsmPrinter.OutStreamer.hasRawTextSupport())
    Opcode = getShortenedInstr(Opcode);
  OutMI.setOpcode(Opcode);
  for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {
    const MachineOperand &MO = MI->getOperand(I);
    // Ignore all implicit register operands.
    if (!MO.isReg() || !MO.isImplicit())
      OutMI.addOperand(lowerOperand(MO));
  }
}
