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 "WebAssemblyAsmPrinter.h"
18 #include "InstPrinter/WebAssemblyInstPrinter.h"
19 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
20 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
21 #include "WebAssembly.h"
22 #include "WebAssemblyMCInstLower.h"
23 #include "WebAssemblyMachineFunctionInfo.h"
24 #include "WebAssemblyRegisterInfo.h"
25 #include "llvm/ADT/StringExtras.h"
26 #include "llvm/CodeGen/Analysis.h"
27 #include "llvm/CodeGen/AsmPrinter.h"
28 #include "llvm/CodeGen/MachineConstantPool.h"
29 #include "llvm/CodeGen/MachineInstr.h"
30 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
31 #include "llvm/IR/DataLayout.h"
32 #include "llvm/IR/GlobalVariable.h"
33 #include "llvm/MC/MCContext.h"
34 #include "llvm/MC/MCStreamer.h"
35 #include "llvm/MC/MCSymbol.h"
36 #include "llvm/Support/Debug.h"
37 #include "llvm/Support/TargetRegistry.h"
38 #include "llvm/Support/raw_ostream.h"
39 using namespace llvm;
40 
41 #define DEBUG_TYPE "asm-printer"
42 
43 //===----------------------------------------------------------------------===//
44 // Helpers.
45 //===----------------------------------------------------------------------===//
46 
47 MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const {
48   const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
49   for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16,
50                 MVT::v4i32, MVT::v4f32})
51     if (TRC->hasType(T))
52       return T;
53   DEBUG(errs() << "Unknown type for register number: " << RegNo);
54   llvm_unreachable("Unknown register type");
55   return MVT::Other;
56 }
57 
58 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
59   unsigned RegNo = MO.getReg();
60   assert(TargetRegisterInfo::isVirtualRegister(RegNo) &&
61          "Unlowered physical register encountered during assembly printing");
62   assert(!MFI->isVRegStackified(RegNo));
63   unsigned WAReg = MFI->getWAReg(RegNo);
64   assert(WAReg != WebAssemblyFunctionInfo::UnusedReg);
65   return '$' + utostr(WAReg);
66 }
67 
68 WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
69   MCTargetStreamer *TS = OutStreamer->getTargetStreamer();
70   return static_cast<WebAssemblyTargetStreamer *>(TS);
71 }
72 
73 //===----------------------------------------------------------------------===//
74 // WebAssemblyAsmPrinter Implementation.
75 //===----------------------------------------------------------------------===//
76 
77 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
78   for (const auto &F : M) {
79     // Emit function type info for all undefined functions
80     if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
81       SmallVector<MVT, 4> Results;
82       SmallVector<MVT, 4> Params;
83       ComputeSignatureVTs(F, TM, Params, Results);
84       getTargetStreamer()->emitIndirectFunctionType(F.getName(), Params,
85                                                     Results);
86     }
87   }
88   for (const auto &G : M.globals()) {
89     if (!G.hasInitializer() && G.hasExternalLinkage()) {
90       uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType());
91       getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier());
92       OutStreamer->emitELFSize(getSymbol(&G),
93                                MCConstantExpr::create(Size, OutContext));
94     }
95   }
96 
97   if (!TM.getTargetTriple().isOSBinFormatELF()) {
98     MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>();
99     getTargetStreamer()->emitGlobal(MMIW.getGlobals());
100   }
101 }
102 
103 void WebAssemblyAsmPrinter::EmitConstantPool() {
104   assert(MF->getConstantPool()->getConstants().empty() &&
105          "WebAssembly disables constant pools");
106 }
107 
108 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
109   // Nothing to do; jump tables are incorporated into the instruction stream.
110 }
111 
112 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
113   getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams());
114 
115   SmallVector<MVT, 4> ResultVTs;
116   const Function &F(*MF->getFunction());
117 
118   // Emit the function index.
119   if (MDNode *Idx = F.getMetadata("wasm.index")) {
120     assert(Idx->getNumOperands() == 1);
121 
122     getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
123         cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
124   }
125 
126   ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
127 
128   // If the return type needs to be legalized it will get converted into
129   // passing a pointer.
130   if (ResultVTs.size() == 1)
131     getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs);
132   else
133     getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>());
134 
135   if (TM.getTargetTriple().isOSBinFormatELF()) {
136     assert(MFI->getLocals().empty());
137     for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
138       unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
139       unsigned WAReg = MFI->getWAReg(VReg);
140       // Don't declare unused registers.
141       if (WAReg == WebAssemblyFunctionInfo::UnusedReg)
142         continue;
143       // Don't redeclare parameters.
144       if (WAReg < MFI->getParams().size())
145         continue;
146       // Don't declare stackified registers.
147       if (int(WAReg) < 0)
148         continue;
149       MFI->addLocal(getRegType(VReg));
150     }
151   }
152 
153   getTargetStreamer()->emitLocal(MFI->getLocals());
154 
155   AsmPrinter::EmitFunctionBodyStart();
156 }
157 
158 void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() {
159   if (TM.getTargetTriple().isOSBinFormatELF())
160     getTargetStreamer()->emitEndFunc();
161 }
162 
163 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
164   DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
165 
166   switch (MI->getOpcode()) {
167   case WebAssembly::ARGUMENT_I32:
168   case WebAssembly::ARGUMENT_I64:
169   case WebAssembly::ARGUMENT_F32:
170   case WebAssembly::ARGUMENT_F64:
171   case WebAssembly::ARGUMENT_v16i8:
172   case WebAssembly::ARGUMENT_v8i16:
173   case WebAssembly::ARGUMENT_v4i32:
174   case WebAssembly::ARGUMENT_v4f32:
175     // These represent values which are live into the function entry, so there's
176     // no instruction to emit.
177     break;
178   case WebAssembly::FALLTHROUGH_RETURN_I32:
179   case WebAssembly::FALLTHROUGH_RETURN_I64:
180   case WebAssembly::FALLTHROUGH_RETURN_F32:
181   case WebAssembly::FALLTHROUGH_RETURN_F64:
182   case WebAssembly::FALLTHROUGH_RETURN_v16i8:
183   case WebAssembly::FALLTHROUGH_RETURN_v8i16:
184   case WebAssembly::FALLTHROUGH_RETURN_v4i32:
185   case WebAssembly::FALLTHROUGH_RETURN_v4f32: {
186     // These instructions represent the implicit return at the end of a
187     // function body. The operand is always a pop.
188     assert(MFI->isVRegStackified(MI->getOperand(0).getReg()));
189 
190     if (isVerbose()) {
191       OutStreamer->AddComment("fallthrough-return: $pop" +
192                               utostr(MFI->getWARegStackId(
193                                   MFI->getWAReg(MI->getOperand(0).getReg()))));
194       OutStreamer->AddBlankLine();
195     }
196     break;
197   }
198   case WebAssembly::FALLTHROUGH_RETURN_VOID:
199     // This instruction represents the implicit return at the end of a
200     // function body with no return value.
201     if (isVerbose()) {
202       OutStreamer->AddComment("fallthrough-return");
203       OutStreamer->AddBlankLine();
204     }
205     break;
206   default: {
207     WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
208     MCInst TmpInst;
209     MCInstLowering.Lower(MI, TmpInst);
210     EmitToStreamer(*OutStreamer, TmpInst);
211     break;
212   }
213   }
214 }
215 
216 const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) {
217   if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV))
218     if (GV->getValueType()->isFunctionTy())
219       return MCSymbolRefExpr::create(
220           getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext);
221   return AsmPrinter::lowerConstant(CV);
222 }
223 
224 bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
225                                             unsigned OpNo, unsigned AsmVariant,
226                                             const char *ExtraCode,
227                                             raw_ostream &OS) {
228   if (AsmVariant != 0)
229     report_fatal_error("There are no defined alternate asm variants");
230 
231   // First try the generic code, which knows about modifiers like 'c' and 'n'.
232   if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS))
233     return false;
234 
235   if (!ExtraCode) {
236     const MachineOperand &MO = MI->getOperand(OpNo);
237     switch (MO.getType()) {
238     case MachineOperand::MO_Immediate:
239       OS << MO.getImm();
240       return false;
241     case MachineOperand::MO_Register:
242       OS << regToString(MO);
243       return false;
244     case MachineOperand::MO_GlobalAddress:
245       getSymbol(MO.getGlobal())->print(OS, MAI);
246       printOffset(MO.getOffset(), OS);
247       return false;
248     case MachineOperand::MO_ExternalSymbol:
249       GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI);
250       printOffset(MO.getOffset(), OS);
251       return false;
252     case MachineOperand::MO_MachineBasicBlock:
253       MO.getMBB()->getSymbol()->print(OS, MAI);
254       return false;
255     default:
256       break;
257     }
258   }
259 
260   return true;
261 }
262 
263 bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
264                                                   unsigned OpNo,
265                                                   unsigned AsmVariant,
266                                                   const char *ExtraCode,
267                                                   raw_ostream &OS) {
268   if (AsmVariant != 0)
269     report_fatal_error("There are no defined alternate asm variants");
270 
271   if (!ExtraCode) {
272     // TODO: For now, we just hard-code 0 as the constant offset; teach
273     // SelectInlineAsmMemoryOperand how to do address mode matching.
274     OS << "0(" + regToString(MI->getOperand(OpNo)) + ')';
275     return false;
276   }
277 
278   return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS);
279 }
280 
281 // Force static initialization.
282 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
283   RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32());
284   RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64());
285 }
286