WebAssemblyMCInstLower.cpp revision 353358
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) {
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      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
166// Return the WebAssembly type associated with the given register class.
167static wasm::ValType getType(const TargetRegisterClass *RC) {
168  if (RC == &WebAssembly::I32RegClass)
169    return wasm::ValType::I32;
170  if (RC == &WebAssembly::I64RegClass)
171    return wasm::ValType::I64;
172  if (RC == &WebAssembly::F32RegClass)
173    return wasm::ValType::F32;
174  if (RC == &WebAssembly::F64RegClass)
175    return wasm::ValType::F64;
176  if (RC == &WebAssembly::V128RegClass)
177    return wasm::ValType::V128;
178  llvm_unreachable("Unexpected register class");
179}
180
181void WebAssemblyMCInstLower::lower(const MachineInstr *MI,
182                                   MCInst &OutMI) const {
183  OutMI.setOpcode(MI->getOpcode());
184
185  const MCInstrDesc &Desc = MI->getDesc();
186  for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {
187    const MachineOperand &MO = MI->getOperand(I);
188
189    MCOperand MCOp;
190    switch (MO.getType()) {
191    default:
192      MI->print(errs());
193      llvm_unreachable("unknown operand type");
194    case MachineOperand::MO_MachineBasicBlock:
195      MI->print(errs());
196      llvm_unreachable("MachineBasicBlock operand should have been rewritten");
197    case MachineOperand::MO_Register: {
198      // Ignore all implicit register operands.
199      if (MO.isImplicit())
200        continue;
201      const WebAssemblyFunctionInfo &MFI =
202          *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();
203      unsigned WAReg = MFI.getWAReg(MO.getReg());
204      MCOp = MCOperand::createReg(WAReg);
205      break;
206    }
207    case MachineOperand::MO_Immediate:
208      if (I < Desc.NumOperands) {
209        const MCOperandInfo &Info = Desc.OpInfo[I];
210        if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
211          MCSymbol *Sym = Printer.createTempSymbol("typeindex");
212
213          SmallVector<wasm::ValType, 4> Returns;
214          SmallVector<wasm::ValType, 4> Params;
215
216          const MachineRegisterInfo &MRI =
217              MI->getParent()->getParent()->getRegInfo();
218          for (const MachineOperand &MO : MI->defs())
219            Returns.push_back(getType(MRI.getRegClass(MO.getReg())));
220          for (const MachineOperand &MO : MI->explicit_uses())
221            if (MO.isReg())
222              Params.push_back(getType(MRI.getRegClass(MO.getReg())));
223
224          // call_indirect instructions have a callee operand at the end which
225          // doesn't count as a param.
226          if (WebAssembly::isCallIndirect(MI->getOpcode()))
227            Params.pop_back();
228
229          auto *WasmSym = cast<MCSymbolWasm>(Sym);
230          auto Signature = make_unique<wasm::WasmSignature>(std::move(Returns),
231                                                            std::move(Params));
232          WasmSym->setSignature(Signature.get());
233          Printer.addSignature(std::move(Signature));
234          WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
235
236          const MCExpr *Expr = MCSymbolRefExpr::create(
237              WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);
238          MCOp = MCOperand::createExpr(Expr);
239          break;
240        }
241      }
242      MCOp = MCOperand::createImm(MO.getImm());
243      break;
244    case MachineOperand::MO_FPImmediate: {
245      // TODO: MC converts all floating point immediate operands to double.
246      // This is fine for numeric values, but may cause NaNs to change bits.
247      const ConstantFP *Imm = MO.getFPImm();
248      if (Imm->getType()->isFloatTy())
249        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat());
250      else if (Imm->getType()->isDoubleTy())
251        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble());
252      else
253        llvm_unreachable("unknown floating point immediate type");
254      break;
255    }
256    case MachineOperand::MO_GlobalAddress:
257      MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
258      break;
259    case MachineOperand::MO_ExternalSymbol:
260      // The target flag indicates whether this is a symbol for a
261      // variable or a function.
262      assert(MO.getTargetFlags() == 0 &&
263             "WebAssembly uses only symbol flags on ExternalSymbols");
264      MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));
265      break;
266    case MachineOperand::MO_MCSymbol:
267      // This is currently used only for LSDA symbols (GCC_except_table),
268      // because global addresses or other external symbols are handled above.
269      assert(MO.getTargetFlags() == 0 &&
270             "WebAssembly does not use target flags on MCSymbol");
271      MCOp = lowerSymbolOperand(MO, MO.getMCSymbol());
272      break;
273    }
274
275    OutMI.addOperand(MCOp);
276  }
277
278  if (!WasmKeepRegisters)
279    removeRegisterOperands(MI, OutMI);
280}
281
282static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
283  // Remove all uses of stackified registers to bring the instruction format
284  // into its final stack form used thruout MC, and transition opcodes to
285  // their _S variant.
286  // We do this seperate from the above code that still may need these
287  // registers for e.g. call_indirect signatures.
288  // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for
289  // details.
290  // TODO: the code above creates new registers which are then removed here.
291  // That code could be slightly simplified by not doing that, though maybe
292  // it is simpler conceptually to keep the code above in "register mode"
293  // until this transition point.
294  // FIXME: we are not processing inline assembly, which contains register
295  // operands, because it is used by later target generic code.
296  if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())
297    return;
298
299  // Transform to _S instruction.
300  auto RegOpcode = OutMI.getOpcode();
301  auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);
302  assert(StackOpcode != -1 && "Failed to stackify instruction");
303  OutMI.setOpcode(StackOpcode);
304
305  // Remove register operands.
306  for (auto I = OutMI.getNumOperands(); I; --I) {
307    auto &MO = OutMI.getOperand(I - 1);
308    if (MO.isReg()) {
309      OutMI.erase(&MO);
310    }
311  }
312}
313