[WebAssembly] Remove special cases for things that are no longer special. NFC.
[oota-llvm.git] / lib / Target / WebAssembly / WebAssemblyAsmPrinter.cpp
index aa7734118a60cfd3d6fa7781841cb9597345336b..6eb9ae6c5268950bf35f0676ee1c072e89b793ee 100644 (file)
@@ -22,6 +22,7 @@
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
 
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/CodeGen/AsmPrinter.h"
 #include "llvm/CodeGen/MachineConstantPool.h"
 #include "llvm/CodeGen/MachineInstr.h"
@@ -41,10 +42,12 @@ namespace {
 
 class WebAssemblyAsmPrinter final : public AsmPrinter {
   const WebAssemblyInstrInfo *TII;
+  const MachineRegisterInfo *MRI;
+  unsigned NumArgs;
 
 public:
   WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
-      : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {}
+      : AsmPrinter(TM, std::move(Streamer)), TII(nullptr), MRI(nullptr) {}
 
 private:
   const char *getPassName() const override {
@@ -60,7 +63,10 @@ private:
   }
 
   bool runOnMachineFunction(MachineFunction &MF) override {
-    TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
+    const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
+    TII = Subtarget.getInstrInfo();
+    MRI = &MF.getRegInfo();
+    NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments();
     return AsmPrinter::runOnMachineFunction(MF);
   }
 
@@ -68,41 +74,130 @@ private:
   // AsmPrinter Implementation.
   //===------------------------------------------------------------------===//
 
+  void EmitJumpTableInfo() override;
   void EmitConstantPool() override;
-  void EmitFunctionEntryLabel() override;
   void EmitFunctionBodyStart() override;
-  void EmitFunctionBodyEnd() override;
-
   void EmitInstruction(const MachineInstr *MI) override;
+  void EmitEndOfAsmFile(Module &M) override;
+
+  std::string getRegTypeName(unsigned RegNo) const;
+  static std::string toString(const APFloat &APF);
+  const char *toString(Type *Ty) const;
+  std::string regToString(const MachineOperand &MO);
+  std::string argToString(const MachineOperand &MO);
 };
 
 } // end anonymous namespace
 
+//===----------------------------------------------------------------------===//
+// Helpers.
 //===----------------------------------------------------------------------===//
 
-// Untyped, lower-case version of the opcode's name matching the names
-// WebAssembly opcodes are expected to have. The tablegen names are uppercase
-// and suffixed with their type (after an underscore).
-static SmallString<32> Name(const WebAssemblyInstrInfo *TII,
-                            const MachineInstr *MI) {
+// Operand type (if any), followed by the lower-case version of the opcode's
+// name matching the names WebAssembly opcodes are expected to have. The
+// tablegen names are uppercase and suffixed with their type (after an
+// underscore). Conversions are additionally prefixed with their input type
+// (before a double underscore).
+static std::string OpcodeName(const WebAssemblyInstrInfo *TII,
+                              const MachineInstr *MI) {
   std::string N(StringRef(TII->getName(MI->getOpcode())).lower());
-  std::string::size_type End = N.rfind('_');
-  End = std::string::npos == End ? N.length() : End;
-  return SmallString<32>(&N[0], &N[End]);
+  std::string::size_type Len = N.length();
+  std::string::size_type Under = N.rfind('_');
+  bool HasType = std::string::npos != Under;
+  std::string::size_type NameEnd = HasType ? Under : Len;
+  std::string Name(&N[0], &N[NameEnd]);
+  if (!HasType)
+    return Name;
+  for (const char *typelessOpcode : { "return", "call", "br_if" })
+    if (Name == typelessOpcode)
+      return Name;
+  std::string Type(&N[NameEnd + 1], &N[Len]);
+  std::string::size_type DoubleUnder = Name.find("__");
+  bool IsConv = std::string::npos != DoubleUnder;
+  if (!IsConv)
+    return Type + '.' + Name;
+  std::string InType(&Name[0], &Name[DoubleUnder]);
+  return Type + '.' + std::string(&Name[DoubleUnder + 2], &Name[NameEnd]) +
+      '/' + InType;
 }
 
 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
 
-static const char *toType(const Type *Ty) {
+std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const {
+  const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
+  for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
+    if (TRC->hasType(T))
+      return EVT(T).getEVTString();
+  DEBUG(errs() << "Unknown type for register number: " << RegNo);
+  llvm_unreachable("Unknown register type");
+  return "?";
+}
+
+std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
+  static const size_t BufBytes = 128;
+  char buf[BufBytes];
+  if (FP.isNaN())
+    assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) ||
+            FP.bitwiseIsEqual(
+                APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) &&
+           "convertToHexString handles neither SNaN nor NaN payloads");
+  // Use C99's hexadecimal floating-point representation.
+  auto Written = FP.convertToHexString(
+      buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven);
+  (void)Written;
+  assert(Written != 0);
+  assert(Written < BufBytes);
+  return buf;
+}
+
+std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
+  unsigned RegNo = MO.getReg();
+  if (TargetRegisterInfo::isPhysicalRegister(RegNo))
+    return WebAssemblyInstPrinter::getRegisterName(RegNo);
+
+  // WebAssembly arguments and local variables are in the same index space, and
+  // there are no explicit varargs, so we just add the number of arguments to
+  // the virtual register number to get the local variable number.
+  return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs);
+}
+
+std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) {
+  unsigned ArgNo = MO.getImm();
+  // Same as above, but we don't need to add NumArgs here.
+  return utostr(ArgNo);
+}
+
+const char *WebAssemblyAsmPrinter::toString(Type *Ty) const {
   switch (Ty->getTypeID()) {
-  default: break;
-  case Type::FloatTyID:  return "f32";
-  case Type::DoubleTyID: return "f64";
+  default:
+    break;
+  // Treat all pointers as the underlying integer into linear memory.
+  case Type::PointerTyID:
+    switch (getPointerSize()) {
+    case 4:
+      return "i32";
+    case 8:
+      return "i64";
+    default:
+      llvm_unreachable("unsupported pointer size");
+    }
+    break;
+  case Type::FloatTyID:
+    return "f32";
+  case Type::DoubleTyID:
+    return "f64";
   case Type::IntegerTyID:
     switch (Ty->getIntegerBitWidth()) {
-    case 32: return "i32";
-    case 64: return "i64";
-    default: break;
+    case 8:
+      return "i8";
+    case 16:
+      return "i16";
+    case 32:
+      return "i32";
+    case 64:
+      return "i64";
+    default:
+      break;
     }
   }
   DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
@@ -110,48 +205,55 @@ static const char *toType(const Type *Ty) {
   return "<invalid>";
 }
 
+//===----------------------------------------------------------------------===//
+// WebAssemblyAsmPrinter Implementation.
+//===----------------------------------------------------------------------===//
+
 void WebAssemblyAsmPrinter::EmitConstantPool() {
   assert(MF->getConstantPool()->getConstants().empty() &&
          "WebAssembly disables constant pools");
 }
 
-void WebAssemblyAsmPrinter::EmitFunctionEntryLabel() {
-  SmallString<128> Str;
-  raw_svector_ostream OS(Str);
-
-  CurrentFnSym->redefineIfPossible();
-
-  // The function label could have already been emitted if two symbols end up
-  // conflicting due to asm renaming.  Detect this and emit an error.
-  if (CurrentFnSym->isVariable())
-    report_fatal_error("'" + Twine(CurrentFnSym->getName()) +
-                       "' is a protected alias");
-  if (CurrentFnSym->isDefined())
-    report_fatal_error("'" + Twine(CurrentFnSym->getName()) +
-                       "' label emitted multiple times to assembly file");
-
-  OS << "(func " << toSymbol(CurrentFnSym->getName());
-  OutStreamer->EmitRawText(OS.str());
+void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
+  // Nothing to do; jump tables are incorporated into the instruction stream.
 }
 
 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
-  SmallString<128> Str;
-  raw_svector_ostream OS(Str);
   const Function *F = MF->getFunction();
-  for (const Argument &A : F->args())
-    OS << " (param " << toType(A.getType()) << ')';
-  const Type *Rt = F->getReturnType();
-  if (!Rt->isVoidTy())
-    OS << " (result " << toType(Rt) << ')';
-  OS << '\n';
-  OutStreamer->EmitRawText(OS.str());
-}
-
-void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() {
+  Type *Rt = F->getReturnType();
   SmallString<128> Str;
   raw_svector_ostream OS(Str);
-  OS << ") ;; end func " << toSymbol(CurrentFnSym->getName()) << '\n';
-  OutStreamer->EmitRawText(OS.str());
+  bool First = true;
+
+  if (!Rt->isVoidTy() || !F->arg_empty()) {
+    for (const Argument &A : F->args()) {
+      OS << (First ? "" : "\n") << "\t.param " << toString(A.getType());
+      First = false;
+    }
+    if (!Rt->isVoidTy()) {
+      OS << (First ? "" : "\n") << "\t.result " << toString(Rt);
+      First = false;
+    }
+  }
+
+  bool FirstVReg = true;
+  for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
+    unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
+    // FIXME: Don't skip dead virtual registers for now: that would require
+    //        remapping all locals' numbers.
+    //if (!MRI->use_empty(VReg)) {
+      if (FirstVReg) {
+        OS << (First ? "" : "\n") << "\t.local ";
+        First = false;
+      }
+      OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg);
+      FirstVReg = false;
+    //}
+  }
+
+  if (!First)
+    OutStreamer->EmitRawText(OS.str());
+  AsmPrinter::EmitFunctionBodyStart();
 }
 
 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
@@ -165,55 +267,93 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
 
   OS << '\t';
 
-  if (NumDefs != 0) {
-    const MachineOperand &MO = MI->getOperand(0);
-    unsigned Reg = MO.getReg();
-    OS << "(setlocal @" << TargetRegisterInfo::virtReg2Index(Reg) << ' ';
-  }
-
-  OS << '(' << Name(TII, MI);
-  for (const MachineOperand &MO : MI->uses())
-    switch (MO.getType()) {
-    default:
-      llvm_unreachable("unexpected machine operand type");
-    case MachineOperand::MO_Register: {
-      if (MO.isImplicit())
+  switch (MI->getOpcode()) {
+  case TargetOpcode::COPY:
+    OS << "get_local push, " << regToString(MI->getOperand(1));
+    break;
+  case WebAssembly::ARGUMENT_I32:
+  case WebAssembly::ARGUMENT_I64:
+  case WebAssembly::ARGUMENT_F32:
+  case WebAssembly::ARGUMENT_F64:
+    OS << "get_local push, " << argToString(MI->getOperand(1));
+    break;
+  default: {
+    OS << OpcodeName(TII, MI);
+    bool NeedComma = false;
+    bool DefsPushed = false;
+    if (NumDefs != 0 && !MI->isCall()) {
+      OS << " push";
+      NeedComma = true;
+      DefsPushed = true;
+    }
+    for (const MachineOperand &MO : MI->uses()) {
+      if (MO.isReg() && MO.isImplicit())
         continue;
-      unsigned Reg = MO.getReg();
-      OS << " @" << TargetRegisterInfo::virtReg2Index(Reg);
-    } break;
-    case MachineOperand::MO_Immediate: {
-      OS << ' ' << MO.getImm();
-    } break;
-    case MachineOperand::MO_FPImmediate: {
-      static const size_t BufBytes = 128;
-      char buf[BufBytes];
-      APFloat FP = MO.getFPImm()->getValueAPF();
-      if (FP.isNaN())
-        assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) ||
-                FP.bitwiseIsEqual(
-                    APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) &&
-               "convertToHexString handles neither SNaN nor NaN payloads");
-      // Use C99's hexadecimal floating-point representation.
-      auto Written =
-          FP.convertToHexString(buf, /*hexDigits=*/0, /*upperCase=*/false,
-                                APFloat::rmNearestTiesToEven);
-      (void)Written;
-      assert(Written != 0);
-      assert(Written < BufBytes);
-      OS << ' ' << buf;
-    } break;
-    case MachineOperand::MO_GlobalAddress: {
-      OS << ' ' << toSymbol(MO.getGlobal()->getName());
-    } break;
+      if (NeedComma)
+        OS << ',';
+      NeedComma = true;
+      OS << ' ';
+      switch (MO.getType()) {
+      default:
+        llvm_unreachable("unexpected machine operand type");
+      case MachineOperand::MO_Register:
+        OS << "(get_local " << regToString(MO) << ')';
+        break;
+      case MachineOperand::MO_Immediate:
+        OS << MO.getImm();
+        break;
+      case MachineOperand::MO_FPImmediate:
+        OS << toString(MO.getFPImm()->getValueAPF());
+        break;
+      case MachineOperand::MO_GlobalAddress:
+        OS << toSymbol(MO.getGlobal()->getName());
+        break;
+      case MachineOperand::MO_MachineBasicBlock:
+        OS << toSymbol(MO.getMBB()->getSymbol()->getName());
+        break;
+      }
+      if (NumDefs != 0 && !DefsPushed) {
+        // Special-case for calls; print the push after the callee.
+        assert(MI->isCall());
+        OS << ", push";
+        DefsPushed = true;
+      }
     }
-  OS << ')';
+    break;
+  }
+  }
 
-  if (NumDefs != 0)
-    OS << ')';
+  OutStreamer->EmitRawText(OS.str());
 
-  OS << '\n';
+  if (NumDefs != 0) {
+    SmallString<128> Str;
+    raw_svector_ostream OS(Str);
+    const MachineOperand &Operand = MI->getOperand(0);
+    OS << "\tset_local " << regToString(Operand) << ", pop";
+    OutStreamer->EmitRawText(OS.str());
+  }
+}
 
+void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
+  SmallString<128> Str;
+  raw_svector_ostream OS(Str);
+  for (const Function &F : M)
+    if (F.isDeclarationForLinker()) {
+      assert(F.hasName() && "imported functions must have a name");
+      if (F.getName().startswith("llvm."))
+        continue;
+      if (Str.empty())
+        OS << "\t.imports\n";
+      Type *Rt = F.getReturnType();
+      OS << "\t.import " << toSymbol(F.getName()) << " \"\" \"" << F.getName()
+         << "\" (param";
+      for (const Argument &A : F.args())
+        OS << ' ' << toString(A.getType());
+      OS << ')';
+      if (!Rt->isVoidTy())
+        OS << " (result " << toString(Rt) << ')';
+      OS << '\n';
+    }
   OutStreamer->EmitRawText(OS.str());
 }