5a6f4354b229490744dffb479e71978d14e88032
[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   void EmitInstruction(const MachineInstr *MI) override;
81   void EmitEndOfAsmFile(Module &M) 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). Conversions are additionally prefixed with their input type
100 // (before a double underscore).
101 static std::string OpcodeName(const WebAssemblyInstrInfo *TII,
102                               const MachineInstr *MI) {
103   std::string N(StringRef(TII->getName(MI->getOpcode())).lower());
104   std::string::size_type Len = N.length();
105   std::string::size_type Under = N.rfind('_');
106   bool HasType = std::string::npos != Under;
107   std::string::size_type NameEnd = HasType ? Under : Len;
108   std::string Name(&N[0], &N[NameEnd]);
109   if (!HasType)
110     return Name;
111   for (const char *typelessOpcode : { "return", "call", "br_if" })
112     if (Name == typelessOpcode)
113       return Name;
114   std::string Type(&N[NameEnd + 1], &N[Len]);
115   std::string::size_type DoubleUnder = Name.find("__");
116   bool IsConv = std::string::npos != DoubleUnder;
117   if (!IsConv)
118     return Type + '.' + Name;
119   std::string InType(&Name[0], &Name[DoubleUnder]);
120   return Type + '.' + std::string(&Name[DoubleUnder + 2], &Name[NameEnd]) +
121       '/' + InType;
122 }
123
124 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
125
126 std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const {
127   const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
128   for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
129     if (TRC->hasType(T))
130       return EVT(T).getEVTString();
131   DEBUG(errs() << "Unknown type for register number: " << RegNo);
132   llvm_unreachable("Unknown register type");
133   return "?";
134 }
135
136 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
137   static const size_t BufBytes = 128;
138   char buf[BufBytes];
139   if (FP.isNaN())
140     assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) ||
141             FP.bitwiseIsEqual(
142                 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) &&
143            "convertToHexString handles neither SNaN nor NaN payloads");
144   // Use C99's hexadecimal floating-point representation.
145   auto Written = FP.convertToHexString(
146       buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven);
147   (void)Written;
148   assert(Written != 0);
149   assert(Written < BufBytes);
150   return buf;
151 }
152
153 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
154   unsigned RegNo = MO.getReg();
155   if (TargetRegisterInfo::isPhysicalRegister(RegNo))
156     return WebAssemblyInstPrinter::getRegisterName(RegNo);
157
158   // WebAssembly arguments and local variables are in the same index space, and
159   // there are no explicit varargs, so we just add the number of arguments to
160   // the virtual register number to get the local variable number.
161   return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs);
162 }
163
164 std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) {
165   unsigned ArgNo = MO.getImm();
166   // Same as above, but we don't need to add NumArgs here.
167   return utostr(ArgNo);
168 }
169
170 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const {
171   switch (Ty->getTypeID()) {
172   default:
173     break;
174   // Treat all pointers as the underlying integer into linear memory.
175   case Type::PointerTyID:
176     switch (getPointerSize()) {
177     case 4:
178       return "i32";
179     case 8:
180       return "i64";
181     default:
182       llvm_unreachable("unsupported pointer size");
183     }
184     break;
185   case Type::FloatTyID:
186     return "f32";
187   case Type::DoubleTyID:
188     return "f64";
189   case Type::IntegerTyID:
190     switch (Ty->getIntegerBitWidth()) {
191     case 8:
192       return "i8";
193     case 16:
194       return "i16";
195     case 32:
196       return "i32";
197     case 64:
198       return "i64";
199     default:
200       break;
201     }
202   }
203   DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
204   llvm_unreachable("invalid type");
205   return "<invalid>";
206 }
207
208 //===----------------------------------------------------------------------===//
209 // WebAssemblyAsmPrinter Implementation.
210 //===----------------------------------------------------------------------===//
211
212 void WebAssemblyAsmPrinter::EmitConstantPool() {
213   assert(MF->getConstantPool()->getConstants().empty() &&
214          "WebAssembly disables constant pools");
215 }
216
217 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
218   // Nothing to do; jump tables are incorporated into the instruction stream.
219 }
220
221 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
222   const Function *F = MF->getFunction();
223   Type *Rt = F->getReturnType();
224   SmallString<128> Str;
225   raw_svector_ostream OS(Str);
226   bool First = true;
227
228   if (!Rt->isVoidTy() || !F->arg_empty()) {
229     for (const Argument &A : F->args()) {
230       OS << (First ? "" : "\n") << "\t.param " << toString(A.getType());
231       First = false;
232     }
233     if (!Rt->isVoidTy()) {
234       OS << (First ? "" : "\n") << "\t.result " << toString(Rt);
235       First = false;
236     }
237   }
238
239   bool FirstVReg = true;
240   for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
241     unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
242     // FIXME: Don't skip dead virtual registers for now: that would require
243     //        remapping all locals' numbers.
244     //if (!MRI->use_empty(VReg)) {
245       if (FirstVReg) {
246         OS << (First ? "" : "\n") << "\t.local ";
247         First = false;
248       }
249       OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg);
250       FirstVReg = false;
251     //}
252   }
253
254   if (!First)
255     OutStreamer->EmitRawText(OS.str());
256   AsmPrinter::EmitFunctionBodyStart();
257 }
258
259 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
260   DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
261   SmallString<128> Str;
262   raw_svector_ostream OS(Str);
263
264   unsigned NumDefs = MI->getDesc().getNumDefs();
265   assert(NumDefs <= 1 &&
266          "Instructions with multiple result values not implemented");
267
268   OS << '\t';
269
270   switch (MI->getOpcode()) {
271   case TargetOpcode::COPY:
272     OS << "get_local " << regToString(MI->getOperand(1));
273     break;
274   case WebAssembly::GLOBAL:
275     // TODO: wasm64
276     OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName());
277     break;
278   case WebAssembly::ARGUMENT_I32:
279   case WebAssembly::ARGUMENT_I64:
280   case WebAssembly::ARGUMENT_F32:
281   case WebAssembly::ARGUMENT_F64:
282     OS << "get_local " << argToString(MI->getOperand(1));
283     break;
284   case WebAssembly::Const_I32:
285     OS << "i32.const " << MI->getOperand(1).getImm();
286     break;
287   case WebAssembly::Const_I64:
288     OS << "i64.const " << MI->getOperand(1).getImm();
289     break;
290   case WebAssembly::Const_F32:
291     OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
292     break;
293   case WebAssembly::Const_F64:
294     OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF());
295     break;
296   default: {
297     OS << OpcodeName(TII, MI);
298     bool NeedComma = false;
299     for (const MachineOperand &MO : MI->uses()) {
300       if (MO.isReg() && MO.isImplicit())
301         continue;
302       if (NeedComma)
303         OS << ',';
304       NeedComma = true;
305       OS << ' ';
306       switch (MO.getType()) {
307       default:
308         llvm_unreachable("unexpected machine operand type");
309       case MachineOperand::MO_Register:
310         OS << "(get_local " << regToString(MO) << ')';
311         break;
312       case MachineOperand::MO_Immediate:
313         OS << MO.getImm();
314         break;
315       case MachineOperand::MO_FPImmediate:
316         OS << toString(MO.getFPImm()->getValueAPF());
317         break;
318       case MachineOperand::MO_GlobalAddress:
319         OS << toSymbol(MO.getGlobal()->getName());
320         break;
321       case MachineOperand::MO_MachineBasicBlock:
322         OS << toSymbol(MO.getMBB()->getSymbol()->getName());
323         break;
324       }
325     }
326     break;
327   }
328   }
329
330   OutStreamer->EmitRawText(OS.str());
331
332   if (NumDefs != 0) {
333     SmallString<128> Str;
334     raw_svector_ostream OS(Str);
335     const MachineOperand &Operand = MI->getOperand(0);
336     OS << "\tset_local " << regToString(Operand) << ", pop";
337     OutStreamer->EmitRawText(OS.str());
338   }
339 }
340
341 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
342   SmallString<128> Str;
343   raw_svector_ostream OS(Str);
344   for (const Function &F : M)
345     if (F.isDeclarationForLinker()) {
346       assert(F.hasName() && "imported functions must have a name");
347       if (F.getName().startswith("llvm."))
348         continue;
349       if (Str.empty())
350         OS << "\t.imports\n";
351       Type *Rt = F.getReturnType();
352       OS << "\t.import " << toSymbol(F.getName()) << " \"\" \"" << F.getName()
353          << "\" (param";
354       for (const Argument &A : F.args())
355         OS << ' ' << toString(A.getType());
356       OS << ')';
357       if (!Rt->isVoidTy())
358         OS << " (result " << toString(Rt) << ')';
359       OS << '\n';
360     }
361   OutStreamer->EmitRawText(OS.str());
362 }
363
364 // Force static initialization.
365 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
366   RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32);
367   RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64);
368 }