WebAssembly: fix call/return 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   const MachineRegisterInfo *MRI;
46   unsigned NumArgs;
47
48 public:
49   WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
50       : AsmPrinter(TM, std::move(Streamer)), TII(nullptr), MRI(nullptr) {}
51
52 private:
53   const char *getPassName() const override {
54     return "WebAssembly Assembly Printer";
55   }
56
57   //===------------------------------------------------------------------===//
58   // MachineFunctionPass Implementation.
59   //===------------------------------------------------------------------===//
60
61   void getAnalysisUsage(AnalysisUsage &AU) const override {
62     AsmPrinter::getAnalysisUsage(AU);
63   }
64
65   bool runOnMachineFunction(MachineFunction &MF) override {
66     const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
67     TII = Subtarget.getInstrInfo();
68     MRI = &MF.getRegInfo();
69     NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments();
70     return AsmPrinter::runOnMachineFunction(MF);
71   }
72
73   //===------------------------------------------------------------------===//
74   // AsmPrinter Implementation.
75   //===------------------------------------------------------------------===//
76
77   void EmitJumpTableInfo() override;
78   void EmitConstantPool() override;
79   void EmitFunctionBodyStart() override;
80
81   void EmitInstruction(const MachineInstr *MI) override;
82
83   std::string getRegTypeName(unsigned RegNo) const;
84   static std::string toString(const APFloat &APF);
85   const char *toString(Type *Ty) const;
86   std::string regToString(const MachineOperand &MO);
87   std::string argToString(const MachineOperand &MO);
88 };
89
90 } // end anonymous namespace
91
92 //===----------------------------------------------------------------------===//
93 // Helpers.
94 //===----------------------------------------------------------------------===//
95
96 // Operand type (if any), followed by the lower-case version of the opcode's
97 // name matching the names WebAssembly opcodes are expected to have. The
98 // tablegen names are uppercase and suffixed with their type (after an
99 // underscore).
100 static std::string OpcodeName(const WebAssemblyInstrInfo *TII,
101                               const MachineInstr *MI) {
102   std::string N(StringRef(TII->getName(MI->getOpcode())).lower());
103   std::string::size_type Len = N.length();
104   std::string::size_type Under = N.rfind('_');
105   bool HasType = std::string::npos != Under;
106   std::string::size_type NameEnd = HasType ? Under : Len;
107   std::string Name(&N[0], &N[NameEnd]);
108   if (!HasType)
109     return Name;
110   for (const char *typelessOpcode : { "return", "call" })
111     if (Name == typelessOpcode)
112       return Name;
113   return std::string(&N[NameEnd + 1], &N[Len]) + '.' + Name;
114 }
115
116 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
117
118 std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const {
119   const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
120   for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
121     if (TRC->hasType(T))
122       return EVT(T).getEVTString();
123   DEBUG(errs() << "Unknown type for register number: " << RegNo);
124   llvm_unreachable("Unknown register type");
125   return "?";
126 }
127
128 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
129   static const size_t BufBytes = 128;
130   char buf[BufBytes];
131   if (FP.isNaN())
132     assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) ||
133             FP.bitwiseIsEqual(
134                 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) &&
135            "convertToHexString handles neither SNaN nor NaN payloads");
136   // Use C99's hexadecimal floating-point representation.
137   auto Written = FP.convertToHexString(
138       buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven);
139   (void)Written;
140   assert(Written != 0);
141   assert(Written < BufBytes);
142   return buf;
143 }
144
145 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
146   unsigned RegNo = MO.getReg();
147   if (TargetRegisterInfo::isPhysicalRegister(RegNo))
148     return WebAssemblyInstPrinter::getRegisterName(RegNo);
149
150   // WebAssembly arguments and local variables are in the same index space, and
151   // there are no explicit varargs, so we just add the number of arguments to
152   // the virtual register number to get the local variable number.
153   return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs);
154 }
155
156 std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) {
157   unsigned ArgNo = MO.getImm();
158   // Same as above, but we don't need to add NumArgs here.
159   return utostr(ArgNo);
160 }
161
162 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const {
163   switch (Ty->getTypeID()) {
164   default:
165     break;
166   // Treat all pointers as the underlying integer into linear memory.
167   case Type::PointerTyID:
168     switch (getPointerSize()) {
169     case 4:
170       return "i32";
171     case 8:
172       return "i64";
173     default:
174       llvm_unreachable("unsupported pointer size");
175     }
176     break;
177   case Type::FloatTyID:
178     return "f32";
179   case Type::DoubleTyID:
180     return "f64";
181   case Type::IntegerTyID:
182     switch (Ty->getIntegerBitWidth()) {
183     case 8:
184       return "i8";
185     case 16:
186       return "i16";
187     case 32:
188       return "i32";
189     case 64:
190       return "i64";
191     default:
192       break;
193     }
194   }
195   DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
196   llvm_unreachable("invalid type");
197   return "<invalid>";
198 }
199
200 //===----------------------------------------------------------------------===//
201 // WebAssemblyAsmPrinter Implementation.
202 //===----------------------------------------------------------------------===//
203
204 void WebAssemblyAsmPrinter::EmitConstantPool() {
205   assert(MF->getConstantPool()->getConstants().empty() &&
206          "WebAssembly disables constant pools");
207 }
208
209 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
210   // Nothing to do; jump tables are incorporated into the instruction stream.
211 }
212
213 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
214   const Function *F = MF->getFunction();
215   Type *Rt = F->getReturnType();
216   SmallString<128> Str;
217   raw_svector_ostream OS(Str);
218   bool First = true;
219
220   if (!Rt->isVoidTy() || !F->arg_empty()) {
221     for (const Argument &A : F->args()) {
222       OS << (First ? "" : "\n") << "\t.param " << toString(A.getType());
223       First = false;
224     }
225     if (!Rt->isVoidTy()) {
226       OS << (First ? "" : "\n") << "\t.result " << toString(Rt);
227       First = false;
228     }
229   }
230
231   bool FirstVReg = true;
232   for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
233     unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
234     // FIXME: Don't skip dead virtual registers for now: that would require
235     //        remapping all locals' numbers.
236     //if (!MRI->use_empty(VReg)) {
237       if (FirstVReg) {
238         OS << (First ? "" : "\n") << "\t.local ";
239         First = false;
240       }
241       OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg);
242       FirstVReg = false;
243     //}
244   }
245
246   if (!First)
247     OutStreamer->EmitRawText(OS.str());
248   AsmPrinter::EmitFunctionBodyStart();
249 }
250
251 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
252   DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
253   SmallString<128> Str;
254   raw_svector_ostream OS(Str);
255
256   unsigned NumDefs = MI->getDesc().getNumDefs();
257   assert(NumDefs <= 1 &&
258          "Instructions with multiple result values not implemented");
259
260   OS << '\t';
261
262   switch (MI->getOpcode()) {
263   case TargetOpcode::COPY:
264     OS << "get_local " << regToString(MI->getOperand(1));
265     break;
266   case WebAssembly::GLOBAL:
267     // TODO: wasm64
268     OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName());
269     break;
270   case WebAssembly::ARGUMENT_I32:
271   case WebAssembly::ARGUMENT_I64:
272   case WebAssembly::ARGUMENT_F32:
273   case WebAssembly::ARGUMENT_F64:
274     OS << "get_local " << argToString(MI->getOperand(1));
275     break;
276   case WebAssembly::Immediate_I32:
277     OS << "i32.const " << MI->getOperand(1).getImm();
278     break;
279   case WebAssembly::Immediate_I64:
280     OS << "i64.const " << MI->getOperand(1).getImm();
281     break;
282   case WebAssembly::Immediate_F32:
283     OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
284     break;
285   case WebAssembly::Immediate_F64:
286     OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
287     break;
288   default: {
289     OS << OpcodeName(TII, MI);
290     bool NeedComma = false;
291     for (const MachineOperand &MO : MI->uses()) {
292       if (MO.isReg() && MO.isImplicit())
293         continue;
294       if (NeedComma)
295         OS << ',';
296       NeedComma = true;
297       OS << ' ';
298       switch (MO.getType()) {
299       default:
300         llvm_unreachable("unexpected machine operand type");
301       case MachineOperand::MO_Register:
302         OS << "(get_local " << regToString(MO) << ')';
303         break;
304       case MachineOperand::MO_Immediate:
305         OS << MO.getImm();
306         break;
307       case MachineOperand::MO_FPImmediate:
308         OS << toString(MO.getFPImm()->getValueAPF());
309         break;
310       case MachineOperand::MO_GlobalAddress:
311         OS << toSymbol(MO.getGlobal()->getName());
312         break;
313       case MachineOperand::MO_MachineBasicBlock:
314         OS << toSymbol(MO.getMBB()->getSymbol()->getName());
315         break;
316       }
317     }
318     break;
319   }
320   }
321
322   OutStreamer->EmitRawText(OS.str());
323
324   if (NumDefs != 0) {
325     SmallString<128> Str;
326     raw_svector_ostream OS(Str);
327     const MachineOperand &Operand = MI->getOperand(0);
328     OS << "\tset_local " << regToString(Operand) << ", pop";
329     OutStreamer->EmitRawText(OS.str());
330   }
331 }
332
333 // Force static initialization.
334 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
335   RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32);
336   RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64);
337 }