[WebAssembly] Switch to a more traditional assembly syntax
[oota-llvm.git] / lib / Target / WebAssembly / WebAssemblyAsmPrinter.cpp
1 //===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file contains a printer that converts from our internal
12 /// representation of machine-dependent LLVM code to the WebAssembly assembly
13 /// language.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #include "WebAssembly.h"
18 #include "WebAssemblyMachineFunctionInfo.h"
19 #include "WebAssemblyRegisterInfo.h"
20 #include "WebAssemblySubtarget.h"
21 #include "InstPrinter/WebAssemblyInstPrinter.h"
22 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
23
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/CodeGen/AsmPrinter.h"
27 #include "llvm/CodeGen/MachineConstantPool.h"
28 #include "llvm/CodeGen/MachineInstr.h"
29 #include "llvm/IR/DataLayout.h"
30 #include "llvm/IR/DebugInfo.h"
31 #include "llvm/MC/MCStreamer.h"
32 #include "llvm/MC/MCSymbol.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/TargetRegistry.h"
35 #include "llvm/Support/raw_ostream.h"
36
37 using namespace llvm;
38
39 #define DEBUG_TYPE "asm-printer"
40
41 namespace {
42
43 class WebAssemblyAsmPrinter final : public AsmPrinter {
44   const WebAssemblyInstrInfo *TII;
45   unsigned NumArgs;
46
47 public:
48   WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
49       : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {}
50
51 private:
52   const char *getPassName() const override {
53     return "WebAssembly Assembly Printer";
54   }
55
56   //===------------------------------------------------------------------===//
57   // MachineFunctionPass Implementation.
58   //===------------------------------------------------------------------===//
59
60   void getAnalysisUsage(AnalysisUsage &AU) const override {
61     AsmPrinter::getAnalysisUsage(AU);
62   }
63
64   bool runOnMachineFunction(MachineFunction &MF) override {
65     const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
66     TII = Subtarget.getInstrInfo();
67     NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments();
68     return AsmPrinter::runOnMachineFunction(MF);
69   }
70
71   //===------------------------------------------------------------------===//
72   // AsmPrinter Implementation.
73   //===------------------------------------------------------------------===//
74
75   void EmitJumpTableInfo() override;
76   void EmitConstantPool() override;
77   void EmitFunctionBodyStart() override;
78
79   void EmitInstruction(const MachineInstr *MI) override;
80
81   static std::string toString(const APFloat &APF);
82   const char *toString(Type *Ty) const;
83   std::string regToString(unsigned RegNo);
84   std::string argToString(unsigned ArgNo);
85 };
86
87 } // end anonymous namespace
88
89 //===----------------------------------------------------------------------===//
90 // Helpers.
91 //===----------------------------------------------------------------------===//
92
93 // Untyped, lower-case version of the opcode's name matching the names
94 // WebAssembly opcodes are expected to have. The tablegen names are uppercase
95 // and suffixed with their type (after an underscore).
96 static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII,
97                                   const MachineInstr *MI) {
98   std::string N(StringRef(TII->getName(MI->getOpcode())).lower());
99   std::string::size_type End = N.rfind('_');
100   End = std::string::npos == End ? N.length() : End;
101   return SmallString<32>(&N[0], &N[End]);
102 }
103
104 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
105
106 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
107   static const size_t BufBytes = 128;
108   char buf[BufBytes];
109   if (FP.isNaN())
110     assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) ||
111             FP.bitwiseIsEqual(
112                 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) &&
113            "convertToHexString handles neither SNaN nor NaN payloads");
114   // Use C99's hexadecimal floating-point representation.
115   auto Written = FP.convertToHexString(
116       buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven);
117   (void)Written;
118   assert(Written != 0);
119   assert(Written < BufBytes);
120   return buf;
121 }
122
123 std::string WebAssemblyAsmPrinter::regToString(unsigned RegNo) {
124   if (TargetRegisterInfo::isPhysicalRegister(RegNo))
125     return WebAssemblyInstPrinter::getRegisterName(RegNo);
126
127   // WebAssembly arguments and local variables are in the same index space, and
128   // there are no explicit varargs, so we just add the number of arguments to
129   // the virtual register number to get the local variable number.
130   return '@' + utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs);
131 }
132
133 std::string WebAssemblyAsmPrinter::argToString(unsigned ArgNo) {
134   // Same as above, but we don't need to add NumArgs here.
135   return '@' + utostr(ArgNo);
136 }
137
138 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const {
139   switch (Ty->getTypeID()) {
140   default:
141     break;
142   // Treat all pointers as the underlying integer into linear memory.
143   case Type::PointerTyID:
144     switch (getPointerSize()) {
145     case 4:
146       return "i32";
147     case 8:
148       return "i64";
149     default:
150       llvm_unreachable("unsupported pointer size");
151     }
152     break;
153   case Type::FloatTyID:
154     return "f32";
155   case Type::DoubleTyID:
156     return "f64";
157   case Type::IntegerTyID:
158     switch (Ty->getIntegerBitWidth()) {
159     case 8:
160       return "i8";
161     case 16:
162       return "i16";
163     case 32:
164       return "i32";
165     case 64:
166       return "i64";
167     default:
168       break;
169     }
170   }
171   DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
172   llvm_unreachable("invalid type");
173   return "<invalid>";
174 }
175
176 //===----------------------------------------------------------------------===//
177 // WebAssemblyAsmPrinter Implementation.
178 //===----------------------------------------------------------------------===//
179
180 void WebAssemblyAsmPrinter::EmitConstantPool() {
181   assert(MF->getConstantPool()->getConstants().empty() &&
182          "WebAssembly disables constant pools");
183 }
184
185 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
186   // Nothing to do; jump tables are incorporated into the instruction stream.
187 }
188
189 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
190   const Function *F = MF->getFunction();
191   Type *Rt = F->getReturnType();
192
193   if (!Rt->isVoidTy() || !F->arg_empty()) {
194     SmallString<128> Str;
195     raw_svector_ostream OS(Str);
196     bool First = true;
197     for (const Argument &A : F->args()) {
198       OS << (First ? "" : "\n") << "\t"
199                                    ".param "
200          << toString(A.getType());
201       First = false;
202     }
203     if (!Rt->isVoidTy()) {
204       OS << (First ? "" : "\n") << "\t"
205                                    ".result "
206          << toString(Rt);
207       First = false;
208     }
209     OutStreamer->EmitRawText(OS.str());
210   }
211
212   AsmPrinter::EmitFunctionBodyStart();
213 }
214
215 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
216   DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
217   SmallString<128> Str;
218   raw_svector_ostream OS(Str);
219
220   unsigned NumDefs = MI->getDesc().getNumDefs();
221   assert(NumDefs <= 1 &&
222          "Instructions with multiple result values not implemented");
223
224   OS << '\t';
225
226   switch (MI->getOpcode()) {
227   case TargetOpcode::COPY:
228     OS << regToString(MI->getOperand(1).getReg());
229     break;
230   case WebAssembly::GLOBAL:
231     // TODO: wasm64
232     OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName());
233     break;
234   case WebAssembly::ARGUMENT_I32:
235   case WebAssembly::ARGUMENT_I64:
236   case WebAssembly::ARGUMENT_F32:
237   case WebAssembly::ARGUMENT_F64:
238     OS << argToString(MI->getOperand(1).getImm());
239     break;
240   case WebAssembly::Immediate_I32:
241     OS << "i32.const " << MI->getOperand(1).getImm();
242     break;
243   case WebAssembly::Immediate_I64:
244     OS << "i64.const " << MI->getOperand(1).getImm();
245     break;
246   case WebAssembly::Immediate_F32:
247     OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
248     break;
249   case WebAssembly::Immediate_F64:
250     OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
251     break;
252   default: {
253     OS << OpcodeName(TII, MI);
254     bool NeedComma = false;
255     for (const MachineOperand &MO : MI->uses()) {
256       if (MO.isReg() && MO.isImplicit())
257         continue;
258       if (NeedComma)
259         OS << ',';
260       NeedComma = true;
261       OS << ' ';
262       switch (MO.getType()) {
263       default:
264         llvm_unreachable("unexpected machine operand type");
265       case MachineOperand::MO_Register:
266         OS << regToString(MO.getReg());
267         break;
268       case MachineOperand::MO_Immediate:
269         OS << MO.getImm();
270         break;
271       case MachineOperand::MO_FPImmediate:
272         OS << toString(MO.getFPImm()->getValueAPF());
273         break;
274       case MachineOperand::MO_GlobalAddress:
275         OS << toSymbol(MO.getGlobal()->getName());
276         break;
277       case MachineOperand::MO_MachineBasicBlock:
278         OS << toSymbol(MO.getMBB()->getSymbol()->getName());
279         break;
280       }
281     }
282     break;
283   }
284   }
285
286   OutStreamer->EmitRawText(OS.str());
287
288   if (NumDefs != 0) {
289     SmallString<128> Str;
290     raw_svector_ostream OS(Str);
291     OS << "\t" "set_local "
292        << regToString(MI->getOperand(0).getReg()) << ", "
293           "pop";
294     OutStreamer->EmitRawText(OS.str());
295   }
296 }
297
298 // Force static initialization.
299 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
300   RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32);
301   RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64);
302 }