[WebAssembly] Switch to a more traditional assembly syntax
[oota-llvm.git] / lib / Target / WebAssembly / WebAssemblyAsmPrinter.cpp
index 94ea1085ff440a05b760682cc5c6bf1c8ff1d56a..f62cec9f59437b12ea1162a08cdfa8e754e17f12 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"
@@ -40,8 +41,8 @@ using namespace llvm;
 namespace {
 
 class WebAssemblyAsmPrinter final : public AsmPrinter {
-  bool hasAddr64;
   const WebAssemblyInstrInfo *TII;
+  unsigned NumArgs;
 
 public:
   WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
@@ -62,8 +63,8 @@ private:
 
   bool runOnMachineFunction(MachineFunction &MF) override {
     const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
-    hasAddr64 = Subtarget.hasAddr64();
     TII = Subtarget.getInstrInfo();
+    NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments();
     return AsmPrinter::runOnMachineFunction(MF);
   }
 
@@ -71,15 +72,16 @@ private:
   // AsmPrinter Implementation.
   //===------------------------------------------------------------------===//
 
-  void EmitGlobalVariable(const GlobalVariable *GV) override;
-
   void EmitJumpTableInfo() override;
   void EmitConstantPool() override;
-  void EmitFunctionEntryLabel() override;
   void EmitFunctionBodyStart() override;
-  void EmitFunctionBodyEnd() override;
 
   void EmitInstruction(const MachineInstr *MI) override;
+
+  static std::string toString(const APFloat &APF);
+  const char *toString(Type *Ty) const;
+  std::string regToString(unsigned RegNo);
+  std::string argToString(unsigned ArgNo);
 };
 
 } // end anonymous namespace
@@ -101,7 +103,7 @@ static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII,
 
 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
 
-static std::string toString(const APFloat &FP) {
+std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
   static const size_t BufBytes = 128;
   char buf[BufBytes];
   if (FP.isNaN())
@@ -118,20 +120,52 @@ static std::string toString(const APFloat &FP) {
   return buf;
 }
 
-static const char *toString(const Type *Ty, bool hasAddr64) {
+std::string WebAssemblyAsmPrinter::regToString(unsigned RegNo) {
+  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(unsigned ArgNo) {
+  // 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;
+  default:
+    break;
   // Treat all pointers as the underlying integer into linear memory.
-  case Type::PointerTyID: return hasAddr64 ? "i64" : "i32";
-  case Type::FloatTyID:  return "f32";
-  case Type::DoubleTyID: return "f64";
+  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 8: return "i8";
-    case 16: return "i16";
-    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');
@@ -139,76 +173,10 @@ static const char *toString(const Type *Ty, bool hasAddr64) {
   return "<invalid>";
 }
 
-
 //===----------------------------------------------------------------------===//
 // WebAssemblyAsmPrinter Implementation.
 //===----------------------------------------------------------------------===//
 
-void WebAssemblyAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
-  SmallString<128> Str;
-  raw_svector_ostream OS(Str);
-  StringRef Name = GV->getName();
-  DEBUG(dbgs() << "Global " << Name << '\n');
-
-  if (!GV->hasInitializer()) {
-    DEBUG(dbgs() << "  Skipping declaration.\n");
-    return;
-  }
-
-  // Check to see if this is a special global used by LLVM.
-  static const char *Ignored[] = {"llvm.used", "llvm.metadata"};
-  for (const char *I : Ignored)
-    if (Name == I)
-      return;
-  // FIXME: Handle the following globals.
-  static const char *Unhandled[] = {"llvm.global_ctors", "llvm.global_dtors"};
-  for (const char *U : Unhandled)
-    if (Name == U)
-      report_fatal_error("Unhandled global");
-  if (Name.startswith("llvm."))
-    report_fatal_error("Unknown LLVM-internal global");
-
-  if (GV->isThreadLocal())
-    report_fatal_error("TLS isn't yet supported by WebAssembly");
-
-  const DataLayout &DL = getDataLayout();
-  const Constant *Init = GV->getInitializer();
-  if (isa<UndefValue>(Init))
-    Init = Constant::getNullValue(Init->getType());
-  unsigned Align = DL.getPrefTypeAlignment(Init->getType());
-
-  switch (GV->getLinkage()) {
-  case GlobalValue::InternalLinkage:
-  case GlobalValue::PrivateLinkage:
-    break;
-  case GlobalValue::AppendingLinkage:
-  case GlobalValue::LinkOnceAnyLinkage:
-  case GlobalValue::LinkOnceODRLinkage:
-  case GlobalValue::WeakAnyLinkage:
-  case GlobalValue::WeakODRLinkage:
-  case GlobalValue::ExternalLinkage:
-  case GlobalValue::CommonLinkage:
-    report_fatal_error("Linkage types other than internal and private aren't "
-                       "supported by WebAssembly yet");
-  default:
-    llvm_unreachable("Unknown linkage type");
-    return;
-  }
-
-  OS << "(global " << toSymbol(Name) << ' '
-     << toString(Init->getType(), hasAddr64) << ' ';
-  if (const auto *C = dyn_cast<ConstantInt>(Init)) {
-    assert(C->getBitWidth() <= 64 && "Printing wider types unimplemented");
-    OS << C->getZExtValue();
-  } else if (const auto *C = dyn_cast<ConstantFP>(Init)) {
-    OS << toString(C->getValueAPF());
-  } else {
-    assert(false && "Only integer and floating-point constants are supported");
-  }
-  OS << ") ;; align " << Align;
-  OutStreamer->EmitRawText(OS.str());
-}
-
 void WebAssemblyAsmPrinter::EmitConstantPool() {
   assert(MF->getConstantPool()->getConstants().empty() &&
          "WebAssembly disables constant pools");
@@ -218,44 +186,30 @@ void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
   // Nothing to do; jump tables are incorporated into the instruction stream.
 }
 
-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::EmitFunctionBodyStart() {
-  SmallString<128> Str;
-  raw_svector_ostream OS(Str);
   const Function *F = MF->getFunction();
-  const Type *Rt = F->getReturnType();
+  Type *Rt = F->getReturnType();
+
   if (!Rt->isVoidTy() || !F->arg_empty()) {
-    for (const Argument &A : F->args())
-      OS << " (param " << toString(A.getType(), hasAddr64) << ')';
-    if (!Rt->isVoidTy())
-      OS << " (result " << toString(Rt, hasAddr64) << ')';
+    SmallString<128> Str;
+    raw_svector_ostream OS(Str);
+    bool First = true;
+    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;
+    }
     OutStreamer->EmitRawText(OS.str());
   }
-}
 
-void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() {
-  SmallString<128> Str;
-  raw_svector_ostream OS(Str);
-  OS << ") ;; end func " << toSymbol(CurrentFnSym->getName());
-  OutStreamer->EmitRawText(OS.str());
+  AsmPrinter::EmitFunctionBodyStart();
 }
 
 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
@@ -269,46 +223,76 @@ 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) << ' ';
-  }
-
-  if (MI->getOpcode() == WebAssembly::COPY) {
-    OS << '@' << TargetRegisterInfo::virtReg2Index(MI->getOperand(1).getReg());
-  } else {
-    OS << '(' << OpcodeName(TII, MI);
-    for (const MachineOperand &MO : MI->uses())
+  switch (MI->getOpcode()) {
+  case TargetOpcode::COPY:
+    OS << regToString(MI->getOperand(1).getReg());
+    break;
+  case WebAssembly::GLOBAL:
+    // TODO: wasm64
+    OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName());
+    break;
+  case WebAssembly::ARGUMENT_I32:
+  case WebAssembly::ARGUMENT_I64:
+  case WebAssembly::ARGUMENT_F32:
+  case WebAssembly::ARGUMENT_F64:
+    OS << argToString(MI->getOperand(1).getImm());
+    break;
+  case WebAssembly::Immediate_I32:
+    OS << "i32.const " << MI->getOperand(1).getImm();
+    break;
+  case WebAssembly::Immediate_I64:
+    OS << "i64.const " << MI->getOperand(1).getImm();
+    break;
+  case WebAssembly::Immediate_F32:
+    OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
+    break;
+  case WebAssembly::Immediate_F64:
+    OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
+    break;
+  default: {
+    OS << OpcodeName(TII, MI);
+    bool NeedComma = false;
+    for (const MachineOperand &MO : MI->uses()) {
+      if (MO.isReg() && MO.isImplicit())
+        continue;
+      if (NeedComma)
+        OS << ',';
+      NeedComma = true;
+      OS << ' ';
       switch (MO.getType()) {
       default:
         llvm_unreachable("unexpected machine operand type");
-      case MachineOperand::MO_Register: {
-        if (MO.isImplicit())
-          continue;
-        unsigned Reg = MO.getReg();
-        OS << " @" << TargetRegisterInfo::virtReg2Index(Reg);
-      } 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;
+      case MachineOperand::MO_Register:
+        OS << regToString(MO.getReg());
+        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;
       }
-    OS << ')';
+    }
+    break;
+  }
   }
-
-  if (NumDefs != 0)
-    OS << ')';
 
   OutStreamer->EmitRawText(OS.str());
+
+  if (NumDefs != 0) {
+    SmallString<128> Str;
+    raw_svector_ostream OS(Str);
+    OS << "\t" "set_local "
+       << regToString(MI->getOperand(0).getReg()) << ", "
+          "pop";
+    OutStreamer->EmitRawText(OS.str());
+  }
 }
 
 // Force static initialization.