[Nios2] final infrastructure to provide compilation of a return from a function

This patch includes all missing functionality needed to provide first
compilation of a simple program that just returns from a function.
I've added a test case that checks for "ret" instruction printed in assembly
output.

Patch by Andrei Grischenko (andrei.l.grischenko@intel.com)
Differential revision: https://reviews.llvm.org/D39688

llvm-svn: 320035
diff --git a/llvm/lib/Target/Nios2/Nios2AsmPrinter.cpp b/llvm/lib/Target/Nios2/Nios2AsmPrinter.cpp
new file mode 100644
index 0000000..1abf195
--- /dev/null
+++ b/llvm/lib/Target/Nios2/Nios2AsmPrinter.cpp
@@ -0,0 +1,153 @@
+//===-- Nios2AsmPrinter.cpp - Nios2 LLVM Assembly Printer -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file 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 NIOS2 assembly language.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InstPrinter/Nios2InstPrinter.h"
+#include "MCTargetDesc/Nios2BaseInfo.h"
+#include "Nios2.h"
+#include "Nios2TargetMachine.h"
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/TargetRegistry.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "nios2-asm-printer"
+
+namespace {
+
+class Nios2AsmPrinter : public AsmPrinter {
+
+public:
+  explicit Nios2AsmPrinter(TargetMachine &TM,
+                           std::unique_ptr<MCStreamer> Streamer)
+      : AsmPrinter(TM, std::move(Streamer)) {}
+
+  StringRef getPassName() const override { return "Nios2 Assembly Printer"; }
+
+  //- EmitInstruction() must exists or will have run time error.
+  void EmitInstruction(const MachineInstr *MI) override;
+  bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
+                       unsigned AsmVariant, const char *ExtraCode,
+                       raw_ostream &O) override;
+  bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
+                             unsigned AsmVariant, const char *ExtraCode,
+                             raw_ostream &O) override;
+  void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O);
+  void EmitFunctionEntryLabel() override;
+};
+} // namespace
+
+//- EmitInstruction() must exists or will have run time error.
+void Nios2AsmPrinter::EmitInstruction(const MachineInstr *MI) {
+
+  //  Print out both ordinary instruction and boudle instruction
+  MachineBasicBlock::const_instr_iterator I = MI->getIterator();
+  MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
+
+  do {
+
+    if (I->isPseudo()) {
+      llvm_unreachable("Pseudo opcode found in EmitInstruction()");
+    }
+
+    MCInst TmpInst0;
+    LowerNios2MachineInstToMCInst(&*I, TmpInst0, *this);
+    EmitToStreamer(*OutStreamer, TmpInst0);
+  } while ((++I != E) && I->isInsideBundle()); // Delay slot check
+}
+
+//		.type	main,@function
+//->		.ent	main                    # @main
+//	main:
+void Nios2AsmPrinter::EmitFunctionEntryLabel() {
+  OutStreamer->EmitLabel(CurrentFnSym);
+}
+
+// Print out an operand for an inline asm expression.
+bool Nios2AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
+                                      unsigned AsmVariant,
+                                      const char *ExtraCode, raw_ostream &O) {
+  printOperand(MI, OpNum, O);
+  return false;
+}
+
+bool Nios2AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
+                                            unsigned OpNum, unsigned AsmVariant,
+                                            const char *ExtraCode,
+                                            raw_ostream &O) {
+  if (ExtraCode && ExtraCode[0])
+    return true; // Unknown modifier
+
+  const MachineOperand &MO = MI->getOperand(OpNum);
+  assert(MO.isReg() && "unexpected inline asm memory operand");
+  O << "($" << Nios2InstPrinter::getRegisterName(MO.getReg()) << ")";
+
+  return false;
+}
+
+void Nios2AsmPrinter::printOperand(const MachineInstr *MI, int opNum,
+                                   raw_ostream &O) {
+  const MachineOperand &MO = MI->getOperand(opNum);
+  bool closeP = false;
+
+  if (MO.getTargetFlags())
+    closeP = true;
+
+  switch (MO.getTargetFlags()) {
+  case Nios2FG::MO_ABS_HI:
+    O << "%hiadj(";
+    break;
+  case Nios2FG::MO_ABS_LO:
+    O << "%lo(";
+    break;
+  }
+
+  switch (MO.getType()) {
+  case MachineOperand::MO_Register:
+    O << '$'
+      << StringRef(Nios2InstPrinter::getRegisterName(MO.getReg())).lower();
+    break;
+
+  case MachineOperand::MO_Immediate:
+    O << MO.getImm();
+    break;
+
+  case MachineOperand::MO_MachineBasicBlock:
+    MO.getMBB()->getSymbol()->print(O, MAI);
+    return;
+
+  case MachineOperand::MO_GlobalAddress:
+    getSymbol(MO.getGlobal())->print(O, MAI);
+    break;
+
+  case MachineOperand::MO_BlockAddress:
+    O << GetBlockAddressSymbol(MO.getBlockAddress())->getName();
+    break;
+
+  case MachineOperand::MO_ExternalSymbol:
+    O << MO.getSymbolName();
+    break;
+
+  default:
+    llvm_unreachable("<unknown operand type>");
+  }
+
+  if (closeP)
+    O << ")";
+}
+
+// Force static initialization.
+extern "C" void LLVMInitializeNios2AsmPrinter() {
+  RegisterAsmPrinter<Nios2AsmPrinter> X(getTheNios2Target());
+}