1// WebAssemblyMCInstLower.cpp - Convert WebAssembly MachineInstr to an MCInst //
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 code to lower WebAssembly MachineInstrs to their
11/// corresponding MCInst records.
12///
13//===----------------------------------------------------------------------===//
14
15#include "WebAssemblyMCInstLower.h"
16#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
17#include "TargetInfo/WebAssemblyTargetInfo.h"
18#include "WebAssemblyAsmPrinter.h"
19#include "WebAssemblyMachineFunctionInfo.h"
20#include "WebAssemblyRuntimeLibcallSignatures.h"
21#include "llvm/CodeGen/AsmPrinter.h"
22#include "llvm/CodeGen/MachineFunction.h"
23#include "llvm/IR/Constants.h"
24#include "llvm/MC/MCAsmInfo.h"
25#include "llvm/MC/MCContext.h"
26#include "llvm/MC/MCExpr.h"
27#include "llvm/MC/MCInst.h"
28#include "llvm/MC/MCSymbolWasm.h"
29#include "llvm/Support/ErrorHandling.h"
30#include "llvm/Support/raw_ostream.h"
31using namespace llvm;
32
33// This disables the removal of registers when lowering into MC, as required
34// by some current tests.
35cl::opt<bool>
36    WasmKeepRegisters("wasm-keep-registers", cl::Hidden,
37                      cl::desc("WebAssembly: output stack registers in"
38                               " instruction output for test purposes only."),
39                      cl::init(false));
40
41static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
42
43MCSymbol *
44WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
45  const GlobalValue *Global = MO.getGlobal();
46  auto *WasmSym = cast<MCSymbolWasm>(Printer.getSymbol(Global));
47
48  if (const auto *FuncTy = dyn_cast<FunctionType>(Global->getValueType())) {
49    const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
50    const TargetMachine &TM = MF.getTarget();
51    const Function &CurrentFunc = MF.getFunction();
52
53    SmallVector<MVT, 1> ResultMVTs;
54    SmallVector<MVT, 4> ParamMVTs;
55    const auto *const F = dyn_cast<Function>(Global);
56    computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs);
57
58    auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs);
59    WasmSym->setSignature(Signature.get());
60    Printer.addSignature(std::move(Signature));
61    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
62  }
63
64  return WasmSym;
65}
66
67MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
68    const MachineOperand &MO) const {
69  const char *Name = MO.getSymbolName();
70  auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
71  const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
72
73  // Except for certain known symbols, all symbols used by CodeGen are
74  // functions. It's OK to hardcode knowledge of specific symbols here; this
75  // method is precisely there for fetching the signatures of known
76  // Clang-provided symbols.
77  if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
78      strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
79      strcmp(Name, "__tls_size") == 0 || strcmp(Name, "__tls_align") == 0) {
80    bool Mutable =
81        strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
82    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
83    WasmSym->setGlobalType(wasm::WasmGlobalType{
84        uint8_t(Subtarget.hasAddr64() && strcmp(Name, "__table_base") != 0
85                    ? wasm::WASM_TYPE_I64
86                    : wasm::WASM_TYPE_I32),
87        Mutable});
88    return WasmSym;
89  }
90
91  SmallVector<wasm::ValType, 4> Returns;
92  SmallVector<wasm::ValType, 4> Params;
93  if (strcmp(Name, "__cpp_exception") == 0) {
94    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_EVENT);
95    // We can't confirm its signature index for now because there can be
96    // imported exceptions. Set it to be 0 for now.
97    WasmSym->setEventType(
98        {wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
99    // We may have multiple C++ compilation units to be linked together, each of
100    // which defines the exception symbol. To resolve them, we declare them as
101    // weak.
102    WasmSym->setWeak(true);
103    WasmSym->setExternal(true);
104
105    // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
106    // (for wasm64) param type and void return type. The reaon is, all C++
107    // exception values are pointers, and to share the type section with
108    // functions, exceptions are assumed to have void return type.
109    Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
110                                           : wasm::ValType::I32);
111  } else { // Function symbols
112    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
113    getLibcallSignature(Subtarget, Name, Returns, Params);
114  }
115  auto Signature =
116      std::make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params));
117  WasmSym->setSignature(Signature.get());
118  Printer.addSignature(std::move(Signature));
119
120  return WasmSym;
121}
122
123MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
124                                                     MCSymbol *Sym) const {
125  MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None;
126  unsigned TargetFlags = MO.getTargetFlags();
127
128  switch (TargetFlags) {
129    case WebAssemblyII::MO_NO_FLAG:
130      break;
131    case WebAssemblyII::MO_GOT:
132      Kind = MCSymbolRefExpr::VK_GOT;
133      break;
134    case WebAssemblyII::MO_MEMORY_BASE_REL:
135      Kind = MCSymbolRefExpr::VK_WASM_MBREL;
136      break;
137    case WebAssemblyII::MO_TABLE_BASE_REL:
138      Kind = MCSymbolRefExpr::VK_WASM_TBREL;
139      break;
140    default:
141      llvm_unreachable("Unknown target flag on GV operand");
142  }
143
144  const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx);
145
146  if (MO.getOffset() != 0) {
147    const auto *WasmSym = cast<MCSymbolWasm>(Sym);
148    if (TargetFlags == WebAssemblyII::MO_GOT)
149      report_fatal_error("GOT symbol references do not support offsets");
150    if (WasmSym->isFunction())
151      report_fatal_error("Function addresses with offsets not supported");
152    if (WasmSym->isGlobal())
153      report_fatal_error("Global indexes with offsets not supported");
154    if (WasmSym->isEvent())
155      report_fatal_error("Event indexes with offsets not supported");
156
157    Expr = MCBinaryExpr::createAdd(
158        Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
159  }
160
161  return MCOperand::createExpr(Expr);
162}
163
164MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand(
165    SmallVector<wasm::ValType, 1> &&Returns,
166    SmallVector<wasm::ValType, 4> &&Params) const {
167  auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
168                                                         std::move(Params));
169  MCSymbol *Sym = Printer.createTempSymbol("typeindex");
170  auto *WasmSym = cast<MCSymbolWasm>(Sym);
171  WasmSym->setSignature(Signature.get());
172  Printer.addSignature(std::move(Signature));
173  WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
174  const MCExpr *Expr =
175      MCSymbolRefExpr::create(WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);
176  return MCOperand::createExpr(Expr);
177}
178
179// Return the WebAssembly type associated with the given register class.
180static wasm::ValType getType(const TargetRegisterClass *RC) {
181  if (RC == &WebAssembly::I32RegClass)
182    return wasm::ValType::I32;
183  if (RC == &WebAssembly::I64RegClass)
184    return wasm::ValType::I64;
185  if (RC == &WebAssembly::F32RegClass)
186    return wasm::ValType::F32;
187  if (RC == &WebAssembly::F64RegClass)
188    return wasm::ValType::F64;
189  if (RC == &WebAssembly::V128RegClass)
190    return wasm::ValType::V128;
191  llvm_unreachable("Unexpected register class");
192}
193
194static void getFunctionReturns(const MachineInstr *MI,
195                               SmallVectorImpl<wasm::ValType> &Returns) {
196  const Function &F = MI->getMF()->getFunction();
197  const TargetMachine &TM = MI->getMF()->getTarget();
198  Type *RetTy = F.getReturnType();
199  SmallVector<MVT, 4> CallerRetTys;
200  computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
201  valTypesFromMVTs(CallerRetTys, Returns);
202}
203
204void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
205                                   MCInst &OutMI) const {
206  OutMI.setOpcode(MI->getOpcode());
207
208  const MCInstrDesc &Desc = MI->getDesc();
209  unsigned NumVariadicDefs = MI->getNumExplicitDefs() - Desc.getNumDefs();
210  for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {
211    const MachineOperand &MO = MI->getOperand(I);
212
213    MCOperand MCOp;
214    switch (MO.getType()) {
215    default:
216      MI->print(errs());
217      llvm_unreachable("unknown operand type");
218    case MachineOperand::MO_MachineBasicBlock:
219      MI->print(errs());
220      llvm_unreachable("MachineBasicBlock operand should have been rewritten");
221    case MachineOperand::MO_Register: {
222      // Ignore all implicit register operands.
223      if (MO.isImplicit())
224        continue;
225      const WebAssemblyFunctionInfo &MFI =
226          *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();
227      unsigned WAReg = MFI.getWAReg(MO.getReg());
228      MCOp = MCOperand::createReg(WAReg);
229      break;
230    }
231    case MachineOperand::MO_Immediate: {
232      unsigned DescIndex = I - NumVariadicDefs;
233      if (DescIndex < Desc.NumOperands) {
234        const MCOperandInfo &Info = Desc.OpInfo[DescIndex];
235        if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
236          SmallVector<wasm::ValType, 4> Returns;
237          SmallVector<wasm::ValType, 4> Params;
238
239          const MachineRegisterInfo &MRI =
240              MI->getParent()->getParent()->getRegInfo();
241          for (const MachineOperand &MO : MI->defs())
242            Returns.push_back(getType(MRI.getRegClass(MO.getReg())));
243          for (const MachineOperand &MO : MI->explicit_uses())
244            if (MO.isReg())
245              Params.push_back(getType(MRI.getRegClass(MO.getReg())));
246
247          // call_indirect instructions have a callee operand at the end which
248          // doesn't count as a param.
249          if (WebAssembly::isCallIndirect(MI->getOpcode()))
250            Params.pop_back();
251
252          // return_call_indirect instructions have the return type of the
253          // caller
254          if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)
255            getFunctionReturns(MI, Returns);
256
257          MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));
258          break;
259        } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
260          auto BT = static_cast<WebAssembly::BlockType>(MO.getImm());
261          assert(BT != WebAssembly::BlockType::Invalid);
262          if (BT == WebAssembly::BlockType::Multivalue) {
263            SmallVector<wasm::ValType, 1> Returns;
264            getFunctionReturns(MI, Returns);
265            MCOp = lowerTypeIndexOperand(std::move(Returns),
266                                         SmallVector<wasm::ValType, 4>());
267            break;
268          }
269        }
270      }
271      MCOp = MCOperand::createImm(MO.getImm());
272      break;
273    }
274    case MachineOperand::MO_FPImmediate: {
275      // TODO: MC converts all floating point immediate operands to double.
276      // This is fine for numeric values, but may cause NaNs to change bits.
277      const ConstantFP *Imm = MO.getFPImm();
278      if (Imm->getType()->isFloatTy())
279        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat());
280      else if (Imm->getType()->isDoubleTy())
281        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble());
282      else
283        llvm_unreachable("unknown floating point immediate type");
284      break;
285    }
286    case MachineOperand::MO_GlobalAddress:
287      MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
288      break;
289    case MachineOperand::MO_ExternalSymbol:
290      // The target flag indicates whether this is a symbol for a
291      // variable or a function.
292      assert(MO.getTargetFlags() == 0 &&
293             "WebAssembly uses only symbol flags on ExternalSymbols");
294      MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));
295      break;
296    case MachineOperand::MO_MCSymbol:
297      // This is currently used only for LSDA symbols (GCC_except_table),
298      // because global addresses or other external symbols are handled above.
299      assert(MO.getTargetFlags() == 0 &&
300             "WebAssembly does not use target flags on MCSymbol");
301      MCOp = lowerSymbolOperand(MO, MO.getMCSymbol());
302      break;
303    }
304
305    OutMI.addOperand(MCOp);
306  }
307
308  if (!WasmKeepRegisters)
309    removeRegisterOperands(MI, OutMI);
310  else if (Desc.variadicOpsAreDefs())
311    OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs()));
312}
313
314static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
315  // Remove all uses of stackified registers to bring the instruction format
316  // into its final stack form used thruout MC, and transition opcodes to
317  // their _S variant.
318  // We do this separate from the above code that still may need these
319  // registers for e.g. call_indirect signatures.
320  // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for
321  // details.
322  // TODO: the code above creates new registers which are then removed here.
323  // That code could be slightly simplified by not doing that, though maybe
324  // it is simpler conceptually to keep the code above in "register mode"
325  // until this transition point.
326  // FIXME: we are not processing inline assembly, which contains register
327  // operands, because it is used by later target generic code.
328  if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())
329    return;
330
331  // Transform to _S instruction.
332  auto RegOpcode = OutMI.getOpcode();
333  auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);
334  assert(StackOpcode != -1 && "Failed to stackify instruction");
335  OutMI.setOpcode(StackOpcode);
336
337  // Remove register operands.
338  for (auto I = OutMI.getNumOperands(); I; --I) {
339    auto &MO = OutMI.getOperand(I - 1);
340    if (MO.isReg()) {
341      OutMI.erase(&MO);
342    }
343  }
344}
345