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