WebAssemblyAsmPrinter.cpp revision 341825
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/// 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/MCSectionWasm.h"
35#include "llvm/MC/MCStreamer.h"
36#include "llvm/MC/MCSymbol.h"
37#include "llvm/MC/MCSymbolWasm.h"
38#include "llvm/Support/Debug.h"
39#include "llvm/Support/TargetRegistry.h"
40#include "llvm/Support/raw_ostream.h"
41using namespace llvm;
42
43#define DEBUG_TYPE "asm-printer"
44
45//===----------------------------------------------------------------------===//
46// Helpers.
47//===----------------------------------------------------------------------===//
48
49MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const {
50  const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo();
51  const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
52  for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16,
53                MVT::v4i32, MVT::v4f32})
54    if (TRI->isTypeLegalForClass(*TRC, T))
55      return T;
56  LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo);
57  llvm_unreachable("Unknown register type");
58  return MVT::Other;
59}
60
61std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
62  unsigned RegNo = MO.getReg();
63  assert(TargetRegisterInfo::isVirtualRegister(RegNo) &&
64         "Unlowered physical register encountered during assembly printing");
65  assert(!MFI->isVRegStackified(RegNo));
66  unsigned WAReg = MFI->getWAReg(RegNo);
67  assert(WAReg != WebAssemblyFunctionInfo::UnusedReg);
68  return '$' + utostr(WAReg);
69}
70
71WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
72  MCTargetStreamer *TS = OutStreamer->getTargetStreamer();
73  return static_cast<WebAssemblyTargetStreamer *>(TS);
74}
75
76//===----------------------------------------------------------------------===//
77// WebAssemblyAsmPrinter Implementation.
78//===----------------------------------------------------------------------===//
79
80void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
81  for (const auto &F : M) {
82    // Emit function type info for all undefined functions
83    if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
84      SmallVector<MVT, 4> Results;
85      SmallVector<MVT, 4> Params;
86      ComputeSignatureVTs(F, TM, Params, Results);
87      MCSymbol *Sym = getSymbol(&F);
88      getTargetStreamer()->emitIndirectFunctionType(Sym, Params, Results);
89
90      if (TM.getTargetTriple().isOSBinFormatWasm() &&
91          F.hasFnAttribute("wasm-import-module")) {
92        MCSymbolWasm *WasmSym = cast<MCSymbolWasm>(Sym);
93        StringRef Name = F.getFnAttribute("wasm-import-module")
94                             .getValueAsString();
95        getTargetStreamer()->emitImportModule(WasmSym, Name);
96      }
97    }
98  }
99  for (const auto &G : M.globals()) {
100    if (!G.hasInitializer() && G.hasExternalLinkage()) {
101      if (G.getValueType()->isSized()) {
102        uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType());
103        OutStreamer->emitELFSize(getSymbol(&G),
104                                 MCConstantExpr::create(Size, OutContext));
105      }
106    }
107  }
108
109  if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) {
110    for (const Metadata *MD : Named->operands()) {
111      const MDTuple *Tuple = dyn_cast<MDTuple>(MD);
112      if (!Tuple || Tuple->getNumOperands() != 2)
113        continue;
114      const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0));
115      const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1));
116      if (!Name || !Contents)
117        continue;
118
119      OutStreamer->PushSection();
120      std::string SectionName = (".custom_section." + Name->getString()).str();
121      MCSectionWasm *mySection =
122          OutContext.getWasmSection(SectionName, SectionKind::getMetadata());
123      OutStreamer->SwitchSection(mySection);
124      OutStreamer->EmitBytes(Contents->getString());
125      OutStreamer->PopSection();
126    }
127  }
128}
129
130void WebAssemblyAsmPrinter::EmitConstantPool() {
131  assert(MF->getConstantPool()->getConstants().empty() &&
132         "WebAssembly disables constant pools");
133}
134
135void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
136  // Nothing to do; jump tables are incorporated into the instruction stream.
137}
138
139void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
140  getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams());
141
142  SmallVector<MVT, 4> ResultVTs;
143  const Function &F = MF->getFunction();
144
145  // Emit the function index.
146  if (MDNode *Idx = F.getMetadata("wasm.index")) {
147    assert(Idx->getNumOperands() == 1);
148
149    getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
150        cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
151  }
152
153  ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
154
155  // If the return type needs to be legalized it will get converted into
156  // passing a pointer.
157  if (ResultVTs.size() == 1)
158    getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs);
159  else
160    getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>());
161
162  getTargetStreamer()->emitLocal(MFI->getLocals());
163
164  AsmPrinter::EmitFunctionBodyStart();
165}
166
167void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
168  LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
169
170  switch (MI->getOpcode()) {
171  case WebAssembly::ARGUMENT_I32:
172  case WebAssembly::ARGUMENT_I64:
173  case WebAssembly::ARGUMENT_F32:
174  case WebAssembly::ARGUMENT_F64:
175  case WebAssembly::ARGUMENT_v16i8:
176  case WebAssembly::ARGUMENT_v8i16:
177  case WebAssembly::ARGUMENT_v4i32:
178  case WebAssembly::ARGUMENT_v4f32:
179    // These represent values which are live into the function entry, so there's
180    // no instruction to emit.
181    break;
182  case WebAssembly::FALLTHROUGH_RETURN_I32:
183  case WebAssembly::FALLTHROUGH_RETURN_I64:
184  case WebAssembly::FALLTHROUGH_RETURN_F32:
185  case WebAssembly::FALLTHROUGH_RETURN_F64:
186  case WebAssembly::FALLTHROUGH_RETURN_v16i8:
187  case WebAssembly::FALLTHROUGH_RETURN_v8i16:
188  case WebAssembly::FALLTHROUGH_RETURN_v4i32:
189  case WebAssembly::FALLTHROUGH_RETURN_v4f32: {
190    // These instructions represent the implicit return at the end of a
191    // function body. The operand is always a pop.
192    assert(MFI->isVRegStackified(MI->getOperand(0).getReg()));
193
194    if (isVerbose()) {
195      OutStreamer->AddComment("fallthrough-return: $pop" +
196                              Twine(MFI->getWARegStackId(
197                                  MFI->getWAReg(MI->getOperand(0).getReg()))));
198      OutStreamer->AddBlankLine();
199    }
200    break;
201  }
202  case WebAssembly::FALLTHROUGH_RETURN_VOID:
203    // This instruction represents the implicit return at the end of a
204    // function body with no return value.
205    if (isVerbose()) {
206      OutStreamer->AddComment("fallthrough-return");
207      OutStreamer->AddBlankLine();
208    }
209    break;
210  default: {
211    WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
212    MCInst TmpInst;
213    MCInstLowering.Lower(MI, TmpInst);
214    EmitToStreamer(*OutStreamer, TmpInst);
215    break;
216  }
217  }
218}
219
220const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) {
221  if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV))
222    if (GV->getValueType()->isFunctionTy()) {
223      return MCSymbolRefExpr::create(
224          getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext);
225    }
226  return AsmPrinter::lowerConstant(CV);
227}
228
229bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
230                                            unsigned OpNo, unsigned AsmVariant,
231                                            const char *ExtraCode,
232                                            raw_ostream &OS) {
233  if (AsmVariant != 0)
234    report_fatal_error("There are no defined alternate asm variants");
235
236  // First try the generic code, which knows about modifiers like 'c' and 'n'.
237  if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS))
238    return false;
239
240  if (!ExtraCode) {
241    const MachineOperand &MO = MI->getOperand(OpNo);
242    switch (MO.getType()) {
243    case MachineOperand::MO_Immediate:
244      OS << MO.getImm();
245      return false;
246    case MachineOperand::MO_Register:
247      OS << regToString(MO);
248      return false;
249    case MachineOperand::MO_GlobalAddress:
250      getSymbol(MO.getGlobal())->print(OS, MAI);
251      printOffset(MO.getOffset(), OS);
252      return false;
253    case MachineOperand::MO_ExternalSymbol:
254      GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI);
255      printOffset(MO.getOffset(), OS);
256      return false;
257    case MachineOperand::MO_MachineBasicBlock:
258      MO.getMBB()->getSymbol()->print(OS, MAI);
259      return false;
260    default:
261      break;
262    }
263  }
264
265  return true;
266}
267
268bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
269                                                  unsigned OpNo,
270                                                  unsigned AsmVariant,
271                                                  const char *ExtraCode,
272                                                  raw_ostream &OS) {
273  if (AsmVariant != 0)
274    report_fatal_error("There are no defined alternate asm variants");
275
276  // The current approach to inline asm is that "r" constraints are expressed
277  // as local indices, rather than values on the operand stack. This simplifies
278  // using "r" as it eliminates the need to push and pop the values in a
279  // particular order, however it also makes it impossible to have an "m"
280  // constraint. So we don't support it.
281
282  return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS);
283}
284
285// Force static initialization.
286extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
287  RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32());
288  RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64());
289}
290