1//===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains a printer that converts from our internal
11/// representation of machine-dependent LLVM code to the WebAssembly assembly
12/// language.
13///
14//===----------------------------------------------------------------------===//
15
16#include "WebAssemblyAsmPrinter.h"
17#include "MCTargetDesc/WebAssemblyInstPrinter.h"
18#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
20#include "TargetInfo/WebAssemblyTargetInfo.h"
21#include "WebAssembly.h"
22#include "WebAssemblyMCInstLower.h"
23#include "WebAssemblyMachineFunctionInfo.h"
24#include "WebAssemblyRegisterInfo.h"
25#include "WebAssemblyTargetMachine.h"
26#include "llvm/ADT/SmallSet.h"
27#include "llvm/ADT/StringExtras.h"
28#include "llvm/BinaryFormat/Wasm.h"
29#include "llvm/CodeGen/Analysis.h"
30#include "llvm/CodeGen/AsmPrinter.h"
31#include "llvm/CodeGen/MachineConstantPool.h"
32#include "llvm/CodeGen/MachineInstr.h"
33#include "llvm/CodeGen/MachineModuleInfoImpls.h"
34#include "llvm/IR/DataLayout.h"
35#include "llvm/IR/DebugInfoMetadata.h"
36#include "llvm/IR/GlobalVariable.h"
37#include "llvm/IR/Metadata.h"
38#include "llvm/MC/MCContext.h"
39#include "llvm/MC/MCSectionWasm.h"
40#include "llvm/MC/MCStreamer.h"
41#include "llvm/MC/MCSymbol.h"
42#include "llvm/MC/MCSymbolWasm.h"
43#include "llvm/Support/Debug.h"
44#include "llvm/Support/TargetRegistry.h"
45#include "llvm/Support/raw_ostream.h"
46
47using namespace llvm;
48
49#define DEBUG_TYPE "asm-printer"
50
51extern cl::opt<bool> WasmKeepRegisters;
52
53//===----------------------------------------------------------------------===//
54// Helpers.
55//===----------------------------------------------------------------------===//
56
57MVT WebAssemblyAsmPrinter::getRegType(unsigned RegNo) const {
58  const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo();
59  const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
60  for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64, MVT::v16i8, MVT::v8i16,
61                MVT::v4i32, MVT::v2i64, MVT::v4f32, MVT::v2f64})
62    if (TRI->isTypeLegalForClass(*TRC, T))
63      return T;
64  LLVM_DEBUG(errs() << "Unknown type for register number: " << RegNo);
65  llvm_unreachable("Unknown register type");
66  return MVT::Other;
67}
68
69std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) {
70  Register RegNo = MO.getReg();
71  assert(Register::isVirtualRegister(RegNo) &&
72         "Unlowered physical register encountered during assembly printing");
73  assert(!MFI->isVRegStackified(RegNo));
74  unsigned WAReg = MFI->getWAReg(RegNo);
75  assert(WAReg != WebAssemblyFunctionInfo::UnusedReg);
76  return '$' + utostr(WAReg);
77}
78
79WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
80  MCTargetStreamer *TS = OutStreamer->getTargetStreamer();
81  return static_cast<WebAssemblyTargetStreamer *>(TS);
82}
83
84//===----------------------------------------------------------------------===//
85// WebAssemblyAsmPrinter Implementation.
86//===----------------------------------------------------------------------===//
87
88void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
89  for (auto &It : OutContext.getSymbols()) {
90    // Emit a .globaltype and .eventtype declaration.
91    auto Sym = cast<MCSymbolWasm>(It.getValue());
92    if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_GLOBAL)
93      getTargetStreamer()->emitGlobalType(Sym);
94    else if (Sym->getType() == wasm::WASM_SYMBOL_TYPE_EVENT)
95      getTargetStreamer()->emitEventType(Sym);
96  }
97
98  for (const auto &F : M) {
99    if (F.isIntrinsic())
100      continue;
101
102    // Emit function type info for all undefined functions
103    if (F.isDeclarationForLinker()) {
104      SmallVector<MVT, 4> Results;
105      SmallVector<MVT, 4> Params;
106      computeSignatureVTs(F.getFunctionType(), F, TM, Params, Results);
107      auto *Sym = cast<MCSymbolWasm>(getSymbol(&F));
108      Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
109      if (!Sym->getSignature()) {
110        auto Signature = signatureFromMVTs(Results, Params);
111        Sym->setSignature(Signature.get());
112        addSignature(std::move(Signature));
113      }
114      // FIXME: this was originally intended for post-linking and was only used
115      // for imports that were only called indirectly (i.e. s2wasm could not
116      // infer the type from a call). With object files it applies to all
117      // imports. so fix the names and the tests, or rethink how import
118      // delcarations work in asm files.
119      getTargetStreamer()->emitFunctionType(Sym);
120
121      if (TM.getTargetTriple().isOSBinFormatWasm() &&
122          F.hasFnAttribute("wasm-import-module")) {
123        StringRef Name =
124            F.getFnAttribute("wasm-import-module").getValueAsString();
125        Sym->setImportModule(Name);
126        getTargetStreamer()->emitImportModule(Sym, Name);
127      }
128      if (TM.getTargetTriple().isOSBinFormatWasm() &&
129          F.hasFnAttribute("wasm-import-name")) {
130        StringRef Name =
131            F.getFnAttribute("wasm-import-name").getValueAsString();
132        Sym->setImportName(Name);
133        getTargetStreamer()->emitImportName(Sym, Name);
134      }
135    }
136
137    if (F.hasFnAttribute("wasm-export-name")) {
138      auto *Sym = cast<MCSymbolWasm>(getSymbol(&F));
139      StringRef Name = F.getFnAttribute("wasm-export-name").getValueAsString();
140      Sym->setExportName(Name);
141      getTargetStreamer()->emitExportName(Sym, Name);
142    }
143  }
144
145  for (const auto &G : M.globals()) {
146    if (!G.hasInitializer() && G.hasExternalLinkage()) {
147      if (G.getValueType()->isSized()) {
148        uint16_t Size = M.getDataLayout().getTypeAllocSize(G.getValueType());
149        OutStreamer->emitELFSize(getSymbol(&G),
150                                 MCConstantExpr::create(Size, OutContext));
151      }
152    }
153  }
154
155  if (const NamedMDNode *Named = M.getNamedMetadata("wasm.custom_sections")) {
156    for (const Metadata *MD : Named->operands()) {
157      const auto *Tuple = dyn_cast<MDTuple>(MD);
158      if (!Tuple || Tuple->getNumOperands() != 2)
159        continue;
160      const MDString *Name = dyn_cast<MDString>(Tuple->getOperand(0));
161      const MDString *Contents = dyn_cast<MDString>(Tuple->getOperand(1));
162      if (!Name || !Contents)
163        continue;
164
165      OutStreamer->PushSection();
166      std::string SectionName = (".custom_section." + Name->getString()).str();
167      MCSectionWasm *MySection =
168          OutContext.getWasmSection(SectionName, SectionKind::getMetadata());
169      OutStreamer->SwitchSection(MySection);
170      OutStreamer->EmitBytes(Contents->getString());
171      OutStreamer->PopSection();
172    }
173  }
174
175  EmitProducerInfo(M);
176  EmitTargetFeatures(M);
177}
178
179void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
180  llvm::SmallVector<std::pair<std::string, std::string>, 4> Languages;
181  if (const NamedMDNode *Debug = M.getNamedMetadata("llvm.dbg.cu")) {
182    llvm::SmallSet<StringRef, 4> SeenLanguages;
183    for (size_t I = 0, E = Debug->getNumOperands(); I < E; ++I) {
184      const auto *CU = cast<DICompileUnit>(Debug->getOperand(I));
185      StringRef Language = dwarf::LanguageString(CU->getSourceLanguage());
186      Language.consume_front("DW_LANG_");
187      if (SeenLanguages.insert(Language).second)
188        Languages.emplace_back(Language.str(), "");
189    }
190  }
191
192  llvm::SmallVector<std::pair<std::string, std::string>, 4> Tools;
193  if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) {
194    llvm::SmallSet<StringRef, 4> SeenTools;
195    for (size_t I = 0, E = Ident->getNumOperands(); I < E; ++I) {
196      const auto *S = cast<MDString>(Ident->getOperand(I)->getOperand(0));
197      std::pair<StringRef, StringRef> Field = S->getString().split("version");
198      StringRef Name = Field.first.trim();
199      StringRef Version = Field.second.trim();
200      if (SeenTools.insert(Name).second)
201        Tools.emplace_back(Name.str(), Version.str());
202    }
203  }
204
205  int FieldCount = int(!Languages.empty()) + int(!Tools.empty());
206  if (FieldCount != 0) {
207    MCSectionWasm *Producers = OutContext.getWasmSection(
208        ".custom_section.producers", SectionKind::getMetadata());
209    OutStreamer->PushSection();
210    OutStreamer->SwitchSection(Producers);
211    OutStreamer->EmitULEB128IntValue(FieldCount);
212    for (auto &Producers : {std::make_pair("language", &Languages),
213            std::make_pair("processed-by", &Tools)}) {
214      if (Producers.second->empty())
215        continue;
216      OutStreamer->EmitULEB128IntValue(strlen(Producers.first));
217      OutStreamer->EmitBytes(Producers.first);
218      OutStreamer->EmitULEB128IntValue(Producers.second->size());
219      for (auto &Producer : *Producers.second) {
220        OutStreamer->EmitULEB128IntValue(Producer.first.size());
221        OutStreamer->EmitBytes(Producer.first);
222        OutStreamer->EmitULEB128IntValue(Producer.second.size());
223        OutStreamer->EmitBytes(Producer.second);
224      }
225    }
226    OutStreamer->PopSection();
227  }
228}
229
230void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) {
231  struct FeatureEntry {
232    uint8_t Prefix;
233    StringRef Name;
234  };
235
236  // Read target features and linkage policies from module metadata
237  SmallVector<FeatureEntry, 4> EmittedFeatures;
238  for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
239    std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
240    Metadata *Policy = M.getModuleFlag(MDKey);
241    if (Policy == nullptr)
242      continue;
243
244    FeatureEntry Entry;
245    Entry.Prefix = 0;
246    Entry.Name = KV.Key;
247
248    if (auto *MD = cast<ConstantAsMetadata>(Policy))
249      if (auto *I = cast<ConstantInt>(MD->getValue()))
250        Entry.Prefix = I->getZExtValue();
251
252    // Silently ignore invalid metadata
253    if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED &&
254        Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED &&
255        Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED)
256      continue;
257
258    EmittedFeatures.push_back(Entry);
259  }
260
261  if (EmittedFeatures.size() == 0)
262    return;
263
264  // Emit features and linkage policies into the "target_features" section
265  MCSectionWasm *FeaturesSection = OutContext.getWasmSection(
266      ".custom_section.target_features", SectionKind::getMetadata());
267  OutStreamer->PushSection();
268  OutStreamer->SwitchSection(FeaturesSection);
269
270  OutStreamer->EmitULEB128IntValue(EmittedFeatures.size());
271  for (auto &F : EmittedFeatures) {
272    OutStreamer->EmitIntValue(F.Prefix, 1);
273    OutStreamer->EmitULEB128IntValue(F.Name.size());
274    OutStreamer->EmitBytes(F.Name);
275  }
276
277  OutStreamer->PopSection();
278}
279
280void WebAssemblyAsmPrinter::EmitConstantPool() {
281  assert(MF->getConstantPool()->getConstants().empty() &&
282         "WebAssembly disables constant pools");
283}
284
285void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
286  // Nothing to do; jump tables are incorporated into the instruction stream.
287}
288
289void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
290  const Function &F = MF->getFunction();
291  SmallVector<MVT, 1> ResultVTs;
292  SmallVector<MVT, 4> ParamVTs;
293  computeSignatureVTs(F.getFunctionType(), F, TM, ParamVTs, ResultVTs);
294  auto Signature = signatureFromMVTs(ResultVTs, ParamVTs);
295  auto *WasmSym = cast<MCSymbolWasm>(CurrentFnSym);
296  WasmSym->setSignature(Signature.get());
297  addSignature(std::move(Signature));
298  WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
299
300  // FIXME: clean up how params and results are emitted (use signatures)
301  getTargetStreamer()->emitFunctionType(WasmSym);
302
303  // Emit the function index.
304  if (MDNode *Idx = F.getMetadata("wasm.index")) {
305    assert(Idx->getNumOperands() == 1);
306
307    getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(
308        cast<ConstantAsMetadata>(Idx->getOperand(0))->getValue()));
309  }
310
311  SmallVector<wasm::ValType, 16> Locals;
312  valTypesFromMVTs(MFI->getLocals(), Locals);
313  getTargetStreamer()->emitLocal(Locals);
314
315  AsmPrinter::EmitFunctionBodyStart();
316}
317
318void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
319  LLVM_DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n');
320
321  switch (MI->getOpcode()) {
322  case WebAssembly::ARGUMENT_i32:
323  case WebAssembly::ARGUMENT_i32_S:
324  case WebAssembly::ARGUMENT_i64:
325  case WebAssembly::ARGUMENT_i64_S:
326  case WebAssembly::ARGUMENT_f32:
327  case WebAssembly::ARGUMENT_f32_S:
328  case WebAssembly::ARGUMENT_f64:
329  case WebAssembly::ARGUMENT_f64_S:
330  case WebAssembly::ARGUMENT_v16i8:
331  case WebAssembly::ARGUMENT_v16i8_S:
332  case WebAssembly::ARGUMENT_v8i16:
333  case WebAssembly::ARGUMENT_v8i16_S:
334  case WebAssembly::ARGUMENT_v4i32:
335  case WebAssembly::ARGUMENT_v4i32_S:
336  case WebAssembly::ARGUMENT_v2i64:
337  case WebAssembly::ARGUMENT_v2i64_S:
338  case WebAssembly::ARGUMENT_v4f32:
339  case WebAssembly::ARGUMENT_v4f32_S:
340  case WebAssembly::ARGUMENT_v2f64:
341  case WebAssembly::ARGUMENT_v2f64_S:
342    // These represent values which are live into the function entry, so there's
343    // no instruction to emit.
344    break;
345  case WebAssembly::FALLTHROUGH_RETURN: {
346    // These instructions represent the implicit return at the end of a
347    // function body.
348    if (isVerbose()) {
349      OutStreamer->AddComment("fallthrough-return");
350      OutStreamer->AddBlankLine();
351    }
352    break;
353  }
354  case WebAssembly::COMPILER_FENCE:
355    // This is a compiler barrier that prevents instruction reordering during
356    // backend compilation, and should not be emitted.
357    break;
358  case WebAssembly::EXTRACT_EXCEPTION_I32:
359  case WebAssembly::EXTRACT_EXCEPTION_I32_S:
360    // These are pseudo instructions that simulates popping values from stack.
361    // We print these only when we have -wasm-keep-registers on for assembly
362    // readability.
363    if (!WasmKeepRegisters)
364      break;
365    LLVM_FALLTHROUGH;
366  default: {
367    WebAssemblyMCInstLower MCInstLowering(OutContext, *this);
368    MCInst TmpInst;
369    MCInstLowering.lower(MI, TmpInst);
370    EmitToStreamer(*OutStreamer, TmpInst);
371    break;
372  }
373  }
374}
375
376bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI,
377                                            unsigned OpNo,
378                                            const char *ExtraCode,
379                                            raw_ostream &OS) {
380  // First try the generic code, which knows about modifiers like 'c' and 'n'.
381  if (!AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS))
382    return false;
383
384  if (!ExtraCode) {
385    const MachineOperand &MO = MI->getOperand(OpNo);
386    switch (MO.getType()) {
387    case MachineOperand::MO_Immediate:
388      OS << MO.getImm();
389      return false;
390    case MachineOperand::MO_Register:
391      // FIXME: only opcode that still contains registers, as required by
392      // MachineInstr::getDebugVariable().
393      assert(MI->getOpcode() == WebAssembly::INLINEASM);
394      OS << regToString(MO);
395      return false;
396    case MachineOperand::MO_GlobalAddress:
397      PrintSymbolOperand(MO, OS);
398      return false;
399    case MachineOperand::MO_ExternalSymbol:
400      GetExternalSymbolSymbol(MO.getSymbolName())->print(OS, MAI);
401      printOffset(MO.getOffset(), OS);
402      return false;
403    case MachineOperand::MO_MachineBasicBlock:
404      MO.getMBB()->getSymbol()->print(OS, MAI);
405      return false;
406    default:
407      break;
408    }
409  }
410
411  return true;
412}
413
414bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
415                                                  unsigned OpNo,
416                                                  const char *ExtraCode,
417                                                  raw_ostream &OS) {
418  // The current approach to inline asm is that "r" constraints are expressed
419  // as local indices, rather than values on the operand stack. This simplifies
420  // using "r" as it eliminates the need to push and pop the values in a
421  // particular order, however it also makes it impossible to have an "m"
422  // constraint. So we don't support it.
423
424  return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, ExtraCode, OS);
425}
426
427// Force static initialization.
428extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyAsmPrinter() {
429  RegisterAsmPrinter<WebAssemblyAsmPrinter> X(getTheWebAssemblyTarget32());
430  RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(getTheWebAssemblyTarget64());
431}
432