WebAssemblyAsmPrinter.cpp revision 327952
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/MC/MCSymbolWasm.h"
37#include "llvm/MC/MCSymbolELF.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  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      getTargetStreamer()->emitIndirectFunctionType(getSymbol(&F), Params,
88                                                    Results);
89    }
90  }
91  for (const auto &G : M.globals()) {
92    if (!G.hasInitializer() && G.hasExternalLinkage()) {
93      if (G.getValueType()->isSized()) {
94        uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType());
95        if (TM.getTargetTriple().isOSBinFormatELF())
96          getTargetStreamer()->emitGlobalImport(G.getGlobalIdentifier());
97        OutStreamer->emitELFSize(getSymbol(&G),
98                                 MCConstantExpr::create(Size, OutContext));
99      }
100    }
101  }
102}
103
104void WebAssemblyAsmPrinter::EmitConstantPool() {
105  assert(MF->getConstantPool()->getConstants().empty() &&
106         "WebAssembly disables constant pools");
107}
108
109void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
110  // Nothing to do; jump tables are incorporated into the instruction stream.
111}
112
113void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
114  getTargetStreamer()->emitParam(CurrentFnSym, MFI->getParams());
115
116  SmallVector<MVT, 4> ResultVTs;
117  const Function &F = MF->getFunction();
118
119  // Emit the function index.
120  if (MDNode *Idx = F.getMetadata("wasm.index")) {
121    assert(Idx->getNumOperands() == 1);
122
123    getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
124        cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
125  }
126
127  ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs);
128
129  // If the return type needs to be legalized it will get converted into
130  // passing a pointer.
131  if (ResultVTs.size() == 1)
132    getTargetStreamer()->emitResult(CurrentFnSym, ResultVTs);
133  else
134    getTargetStreamer()->emitResult(CurrentFnSym, ArrayRef<MVT>());
135
136  if (TM.getTargetTriple().isOSBinFormatELF()) {
137    assert(MFI->getLocals().empty());
138    for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
139      unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
140      unsigned WAReg = MFI->getWAReg(VReg);
141      // Don't declare unused registers.
142      if (WAReg == WebAssemblyFunctionInfo::UnusedReg)
143        continue;
144      // Don't redeclare parameters.
145      if (WAReg < MFI->getParams().size())
146        continue;
147      // Don't declare stackified registers.
148      if (int(WAReg) < 0)
149        continue;
150      MFI->addLocal(getRegType(VReg));
151    }
152  }
153
154  getTargetStreamer()->emitLocal(MFI->getLocals());
155
156  AsmPrinter::EmitFunctionBodyStart();
157}
158
159void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() {
160  if (TM.getTargetTriple().isOSBinFormatELF())
161    getTargetStreamer()->emitEndFunc();
162}
163
164void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
165  DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
166
167  switch (MI->getOpcode()) {
168  case WebAssembly::ARGUMENT_I32:
169  case WebAssembly::ARGUMENT_I64:
170  case WebAssembly::ARGUMENT_F32:
171  case WebAssembly::ARGUMENT_F64:
172  case WebAssembly::ARGUMENT_v16i8:
173  case WebAssembly::ARGUMENT_v8i16:
174  case WebAssembly::ARGUMENT_v4i32:
175  case WebAssembly::ARGUMENT_v4f32:
176    // These represent values which are live into the function entry, so there's
177    // no instruction to emit.
178    break;
179  case WebAssembly::FALLTHROUGH_RETURN_I32:
180  case WebAssembly::FALLTHROUGH_RETURN_I64:
181  case WebAssembly::FALLTHROUGH_RETURN_F32:
182  case WebAssembly::FALLTHROUGH_RETURN_F64:
183  case WebAssembly::FALLTHROUGH_RETURN_v16i8:
184  case WebAssembly::FALLTHROUGH_RETURN_v8i16:
185  case WebAssembly::FALLTHROUGH_RETURN_v4i32:
186  case WebAssembly::FALLTHROUGH_RETURN_v4f32: {
187    // These instructions represent the implicit return at the end of a
188    // function body. The operand is always a pop.
189    assert(MFI->isVRegStackified(MI->getOperand(0).getReg()));
190
191    if (isVerbose()) {
192      OutStreamer->AddComment("fallthrough-return: $pop" +
193                              Twine(MFI->getWARegStackId(
194                                  MFI->getWAReg(MI->getOperand(0).getReg()))));
195      OutStreamer->AddBlankLine();
196    }
197    break;
198  }
199  case WebAssembly::FALLTHROUGH_RETURN_VOID:
200    // This instruction represents the implicit return at the end of a
201    // function body with no return value.
202    if (isVerbose()) {
203      OutStreamer->AddComment("fallthrough-return");
204      OutStreamer->AddBlankLine();
205    }
206    break;
207  default: {
208    WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
209    MCInst TmpInst;
210    MCInstLowering.Lower(MI, TmpInst);
211    EmitToStreamer(*OutStreamer, TmpInst);
212    break;
213  }
214  }
215}
216
217const MCExpr *WebAssemblyAsmPrinter::lowerConstant(const Constant *CV) {
218  if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV))
219    if (GV->getValueType()->isFunctionTy()) {
220      return MCSymbolRefExpr::create(
221          getSymbol(GV), MCSymbolRefExpr::VK_WebAssembly_FUNCTION, OutContext);
222    }
223  return AsmPrinter::lowerConstant(CV);
224}
225
226bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
227                                            unsigned OpNo, unsigned AsmVariant,
228                                            const char *ExtraCode,
229                                            raw_ostream &OS) {
230  if (AsmVariant != 0)
231    report_fatal_error("There are no defined alternate asm variants");
232
233  // First try the generic code, which knows about modifiers like 'c' and 'n'.
234  if (!AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS))
235    return false;
236
237  if (!ExtraCode) {
238    const MachineOperand &MO = MI->getOperand(OpNo);
239    switch (MO.getType()) {
240    case MachineOperand::MO_Immediate:
241      OS << MO.getImm();
242      return false;
243    case MachineOperand::MO_Register:
244      OS << regToString(MO);
245      return false;
246    case MachineOperand::MO_GlobalAddress:
247      getSymbol(MO.getGlobal())->print(OS, MAI);
248      printOffset(MO.getOffset(), OS);
249      return false;
250    case MachineOperand::MO_ExternalSymbol:
251      GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI);
252      printOffset(MO.getOffset(), OS);
253      return false;
254    case MachineOperand::MO_MachineBasicBlock:
255      MO.getMBB()->getSymbol()->print(OS, MAI);
256      return false;
257    default:
258      break;
259    }
260  }
261
262  return true;
263}
264
265bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
266                                                  unsigned OpNo,
267                                                  unsigned AsmVariant,
268                                                  const char *ExtraCode,
269                                                  raw_ostream &OS) {
270  if (AsmVariant != 0)
271    report_fatal_error("There are no defined alternate asm variants");
272
273  // The current approach to inline asm is that "r" constraints are expressed
274  // as local indices, rather than values on the operand stack. This simplifies
275  // using "r" as it eliminates the need to push and pop the values in a
276  // particular order, however it also makes it impossible to have an "m"
277  // constraint. So we don't support it.
278
279  return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS);
280}
281
282// Force static initialization.
283extern "C" void LLVMInitializeWebAssemblyAsmPrinter() {
284  RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32());
285  RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64());
286}
287