1292915Sdim// WebAssemblyMCInstLower.cpp - Convert WebAssembly MachineInstr to an MCInst //
2292915Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6292915Sdim//
7292915Sdim//===----------------------------------------------------------------------===//
8292915Sdim///
9292915Sdim/// \file
10341825Sdim/// This file contains code to lower WebAssembly MachineInstrs to their
11292915Sdim/// corresponding MCInst records.
12292915Sdim///
13292915Sdim//===----------------------------------------------------------------------===//
14292915Sdim
15292915Sdim#include "WebAssemblyMCInstLower.h"
16321369Sdim#include "WebAssemblyAsmPrinter.h"
17292915Sdim#include "WebAssemblyMachineFunctionInfo.h"
18321369Sdim#include "WebAssemblyRuntimeLibcallSignatures.h"
19353358Sdim#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
20292915Sdim#include "llvm/CodeGen/AsmPrinter.h"
21292915Sdim#include "llvm/CodeGen/MachineFunction.h"
22292915Sdim#include "llvm/IR/Constants.h"
23292915Sdim#include "llvm/MC/MCAsmInfo.h"
24292915Sdim#include "llvm/MC/MCContext.h"
25292915Sdim#include "llvm/MC/MCExpr.h"
26292915Sdim#include "llvm/MC/MCInst.h"
27321369Sdim#include "llvm/MC/MCSymbolWasm.h"
28292915Sdim#include "llvm/Support/ErrorHandling.h"
29292915Sdim#include "llvm/Support/raw_ostream.h"
30292915Sdimusing namespace llvm;
31292915Sdim
32344779Sdim// Defines llvm::WebAssembly::getStackOpcode to convert register instructions to
33344779Sdim// stack instructions
34344779Sdim#define GET_INSTRMAP_INFO 1
35344779Sdim#include "WebAssemblyGenInstrInfo.inc"
36344779Sdim
37344779Sdim// This disables the removal of registers when lowering into MC, as required
38344779Sdim// by some current tests.
39353358Sdimcl::opt<bool>
40344779Sdim    WasmKeepRegisters("wasm-keep-registers", cl::Hidden,
41344779Sdim                      cl::desc("WebAssembly: output stack registers in"
42344779Sdim                               " instruction output for test purposes only."),
43344779Sdim                      cl::init(false));
44344779Sdim
45344779Sdimstatic void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);
46344779Sdim
47292915SdimMCSymbol *
48292915SdimWebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
49321369Sdim  const GlobalValue *Global = MO.getGlobal();
50353358Sdim  auto *WasmSym = cast<MCSymbolWasm>(Printer.getSymbol(Global));
51321369Sdim
52321369Sdim  if (const auto *FuncTy = dyn_cast<FunctionType>(Global->getValueType())) {
53321369Sdim    const MachineFunction &MF = *MO.getParent()->getParent()->getParent();
54321369Sdim    const TargetMachine &TM = MF.getTarget();
55327952Sdim    const Function &CurrentFunc = MF.getFunction();
56321369Sdim
57344779Sdim    SmallVector<MVT, 1> ResultMVTs;
58344779Sdim    SmallVector<MVT, 4> ParamMVTs;
59353358Sdim    computeSignatureVTs(FuncTy, CurrentFunc, TM, ParamMVTs, ResultMVTs);
60321369Sdim
61353358Sdim    auto Signature = signatureFromMVTs(ResultMVTs, ParamMVTs);
62344779Sdim    WasmSym->setSignature(Signature.get());
63344779Sdim    Printer.addSignature(std::move(Signature));
64341825Sdim    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
65321369Sdim  }
66321369Sdim
67321369Sdim  return WasmSym;
68292915Sdim}
69292915Sdim
70292915SdimMCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
71292915Sdim    const MachineOperand &MO) const {
72321369Sdim  const char *Name = MO.getSymbolName();
73353358Sdim  auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
74321369Sdim  const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
75321369Sdim
76353358Sdim  // Except for certain known symbols, all symbols used by CodeGen are
77353358Sdim  // functions. It's OK to hardcode knowledge of specific symbols here; this
78353358Sdim  // method is precisely there for fetching the signatures of known
79353358Sdim  // Clang-provided symbols.
80353358Sdim  if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
81353358Sdim      strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
82360784Sdim      strcmp(Name, "__tls_size") == 0 || strcmp(Name, "__tls_align") == 0) {
83353358Sdim    bool Mutable =
84353358Sdim        strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
85341825Sdim    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
86341825Sdim    WasmSym->setGlobalType(wasm::WasmGlobalType{
87341825Sdim        uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
88341825Sdim                                      : wasm::WASM_TYPE_I32),
89353358Sdim        Mutable});
90321369Sdim    return WasmSym;
91341825Sdim  }
92321369Sdim
93321369Sdim  SmallVector<wasm::ValType, 4> Returns;
94321369Sdim  SmallVector<wasm::ValType, 4> Params;
95344779Sdim  if (strcmp(Name, "__cpp_exception") == 0) {
96344779Sdim    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_EVENT);
97344779Sdim    // We can't confirm its signature index for now because there can be
98344779Sdim    // imported exceptions. Set it to be 0 for now.
99344779Sdim    WasmSym->setEventType(
100344779Sdim        {wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
101344779Sdim    // We may have multiple C++ compilation units to be linked together, each of
102344779Sdim    // which defines the exception symbol. To resolve them, we declare them as
103344779Sdim    // weak.
104344779Sdim    WasmSym->setWeak(true);
105344779Sdim    WasmSym->setExternal(true);
106321369Sdim
107344779Sdim    // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
108344779Sdim    // (for wasm64) param type and void return type. The reaon is, all C++
109344779Sdim    // exception values are pointers, and to share the type section with
110344779Sdim    // functions, exceptions are assumed to have void return type.
111344779Sdim    Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
112344779Sdim                                           : wasm::ValType::I32);
113344779Sdim  } else { // Function symbols
114344779Sdim    WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
115353358Sdim    getLibcallSignature(Subtarget, Name, Returns, Params);
116344779Sdim  }
117344779Sdim  auto Signature =
118360784Sdim      std::make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params));
119344779Sdim  WasmSym->setSignature(Signature.get());
120344779Sdim  Printer.addSignature(std::move(Signature));
121321369Sdim
122321369Sdim  return WasmSym;
123292915Sdim}
124292915Sdim
125353358SdimMCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
126353358Sdim                                                     MCSymbol *Sym) const {
127353358Sdim  MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None;
128353358Sdim  unsigned TargetFlags = MO.getTargetFlags();
129321369Sdim
130353358Sdim  switch (TargetFlags) {
131353358Sdim    case WebAssemblyII::MO_NO_FLAG:
132353358Sdim      break;
133353358Sdim    case WebAssemblyII::MO_GOT:
134353358Sdim      Kind = MCSymbolRefExpr::VK_GOT;
135353358Sdim      break;
136353358Sdim    case WebAssemblyII::MO_MEMORY_BASE_REL:
137353358Sdim      Kind = MCSymbolRefExpr::VK_WASM_MBREL;
138353358Sdim      break;
139353358Sdim    case WebAssemblyII::MO_TABLE_BASE_REL:
140353358Sdim      Kind = MCSymbolRefExpr::VK_WASM_TBREL;
141353358Sdim      break;
142353358Sdim    default:
143353358Sdim      llvm_unreachable("Unknown target flag on GV operand");
144353358Sdim  }
145292915Sdim
146353358Sdim  const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Kind, Ctx);
147353358Sdim
148353358Sdim  if (MO.getOffset() != 0) {
149353358Sdim    const auto *WasmSym = cast<MCSymbolWasm>(Sym);
150353358Sdim    if (TargetFlags == WebAssemblyII::MO_GOT)
151353358Sdim      report_fatal_error("GOT symbol references do not support offsets");
152353358Sdim    if (WasmSym->isFunction())
153294024Sdim      report_fatal_error("Function addresses with offsets not supported");
154353358Sdim    if (WasmSym->isGlobal())
155344779Sdim      report_fatal_error("Global indexes with offsets not supported");
156353358Sdim    if (WasmSym->isEvent())
157344779Sdim      report_fatal_error("Event indexes with offsets not supported");
158353358Sdim
159353358Sdim    Expr = MCBinaryExpr::createAdd(
160353358Sdim        Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
161292915Sdim  }
162292915Sdim
163292915Sdim  return MCOperand::createExpr(Expr);
164292915Sdim}
165292915Sdim
166360784SdimMCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand(
167360784Sdim    SmallVector<wasm::ValType, 1> &&Returns,
168360784Sdim    SmallVector<wasm::ValType, 4> &&Params) const {
169360784Sdim  auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
170360784Sdim                                                         std::move(Params));
171360784Sdim  MCSymbol *Sym = Printer.createTempSymbol("typeindex");
172360784Sdim  auto *WasmSym = cast<MCSymbolWasm>(Sym);
173360784Sdim  WasmSym->setSignature(Signature.get());
174360784Sdim  Printer.addSignature(std::move(Signature));
175360784Sdim  WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
176360784Sdim  const MCExpr *Expr =
177360784Sdim      MCSymbolRefExpr::create(WasmSym, MCSymbolRefExpr::VK_WASM_TYPEINDEX, Ctx);
178360784Sdim  return MCOperand::createExpr(Expr);
179360784Sdim}
180360784Sdim
181321369Sdim// Return the WebAssembly type associated with the given register class.
182321369Sdimstatic wasm::ValType getType(const TargetRegisterClass *RC) {
183321369Sdim  if (RC == &WebAssembly::I32RegClass)
184321369Sdim    return wasm::ValType::I32;
185321369Sdim  if (RC == &WebAssembly::I64RegClass)
186321369Sdim    return wasm::ValType::I64;
187321369Sdim  if (RC == &WebAssembly::F32RegClass)
188321369Sdim    return wasm::ValType::F32;
189321369Sdim  if (RC == &WebAssembly::F64RegClass)
190321369Sdim    return wasm::ValType::F64;
191344779Sdim  if (RC == &WebAssembly::V128RegClass)
192344779Sdim    return wasm::ValType::V128;
193321369Sdim  llvm_unreachable("Unexpected register class");
194321369Sdim}
195321369Sdim
196360784Sdimstatic void getFunctionReturns(const MachineInstr *MI,
197360784Sdim                               SmallVectorImpl<wasm::ValType> &Returns) {
198360784Sdim  const Function &F = MI->getMF()->getFunction();
199360784Sdim  const TargetMachine &TM = MI->getMF()->getTarget();
200360784Sdim  Type *RetTy = F.getReturnType();
201360784Sdim  SmallVector<MVT, 4> CallerRetTys;
202360784Sdim  computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
203360784Sdim  valTypesFromMVTs(CallerRetTys, Returns);
204360784Sdim}
205360784Sdim
206353358Sdimvoid WebAssemblyMCInstLower::lower(const MachineInstr *MI,
207292915Sdim                                   MCInst &OutMI) const {
208292915Sdim  OutMI.setOpcode(MI->getOpcode());
209292915Sdim
210321369Sdim  const MCInstrDesc &Desc = MI->getDesc();
211353358Sdim  for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {
212353358Sdim    const MachineOperand &MO = MI->getOperand(I);
213292915Sdim
214292915Sdim    MCOperand MCOp;
215292915Sdim    switch (MO.getType()) {
216292915Sdim    default:
217321369Sdim      MI->print(errs());
218292915Sdim      llvm_unreachable("unknown operand type");
219294024Sdim    case MachineOperand::MO_MachineBasicBlock:
220321369Sdim      MI->print(errs());
221294024Sdim      llvm_unreachable("MachineBasicBlock operand should have been rewritten");
222292915Sdim    case MachineOperand::MO_Register: {
223292915Sdim      // Ignore all implicit register operands.
224292915Sdim      if (MO.isImplicit())
225292915Sdim        continue;
226292915Sdim      const WebAssemblyFunctionInfo &MFI =
227292915Sdim          *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();
228292915Sdim      unsigned WAReg = MFI.getWAReg(MO.getReg());
229292915Sdim      MCOp = MCOperand::createReg(WAReg);
230292915Sdim      break;
231292915Sdim    }
232292915Sdim    case MachineOperand::MO_Immediate:
233353358Sdim      if (I < Desc.NumOperands) {
234353358Sdim        const MCOperandInfo &Info = Desc.OpInfo[I];
235321369Sdim        if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {
236341825Sdim          SmallVector<wasm::ValType, 4> Returns;
237341825Sdim          SmallVector<wasm::ValType, 4> Params;
238321369Sdim
239341825Sdim          const MachineRegisterInfo &MRI =
240341825Sdim              MI->getParent()->getParent()->getRegInfo();
241341825Sdim          for (const MachineOperand &MO : MI->defs())
242341825Sdim            Returns.push_back(getType(MRI.getRegClass(MO.getReg())));
243341825Sdim          for (const MachineOperand &MO : MI->explicit_uses())
244341825Sdim            if (MO.isReg())
245341825Sdim              Params.push_back(getType(MRI.getRegClass(MO.getReg())));
246321369Sdim
247341825Sdim          // call_indirect instructions have a callee operand at the end which
248341825Sdim          // doesn't count as a param.
249353358Sdim          if (WebAssembly::isCallIndirect(MI->getOpcode()))
250341825Sdim            Params.pop_back();
251321369Sdim
252360784Sdim          // return_call_indirect instructions have the return type of the
253360784Sdim          // caller
254360784Sdim          if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)
255360784Sdim            getFunctionReturns(MI, Returns);
256341825Sdim
257360784Sdim          MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));
258341825Sdim          break;
259360784Sdim        } else if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {
260360784Sdim          auto BT = static_cast<WebAssembly::BlockType>(MO.getImm());
261360784Sdim          assert(BT != WebAssembly::BlockType::Invalid);
262360784Sdim          if (BT == WebAssembly::BlockType::Multivalue) {
263360784Sdim            SmallVector<wasm::ValType, 1> Returns;
264360784Sdim            getFunctionReturns(MI, Returns);
265360784Sdim            MCOp = lowerTypeIndexOperand(std::move(Returns),
266360784Sdim                                         SmallVector<wasm::ValType, 4>());
267360784Sdim            break;
268360784Sdim          }
269321369Sdim        }
270321369Sdim      }
271292915Sdim      MCOp = MCOperand::createImm(MO.getImm());
272292915Sdim      break;
273292915Sdim    case MachineOperand::MO_FPImmediate: {
274292915Sdim      // TODO: MC converts all floating point immediate operands to double.
275292915Sdim      // This is fine for numeric values, but may cause NaNs to change bits.
276292915Sdim      const ConstantFP *Imm = MO.getFPImm();
277292915Sdim      if (Imm->getType()->isFloatTy())
278292915Sdim        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToFloat());
279292915Sdim      else if (Imm->getType()->isDoubleTy())
280292915Sdim        MCOp = MCOperand::createFPImm(Imm->getValueAPF().convertToDouble());
281292915Sdim      else
282292915Sdim        llvm_unreachable("unknown floating point immediate type");
283292915Sdim      break;
284292915Sdim    }
285292915Sdim    case MachineOperand::MO_GlobalAddress:
286353358Sdim      MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
287292915Sdim      break;
288292915Sdim    case MachineOperand::MO_ExternalSymbol:
289294024Sdim      // The target flag indicates whether this is a symbol for a
290294024Sdim      // variable or a function.
291353358Sdim      assert(MO.getTargetFlags() == 0 &&
292344779Sdim             "WebAssembly uses only symbol flags on ExternalSymbols");
293353358Sdim      MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));
294292915Sdim      break;
295344779Sdim    case MachineOperand::MO_MCSymbol:
296344779Sdim      // This is currently used only for LSDA symbols (GCC_except_table),
297344779Sdim      // because global addresses or other external symbols are handled above.
298344779Sdim      assert(MO.getTargetFlags() == 0 &&
299344779Sdim             "WebAssembly does not use target flags on MCSymbol");
300353358Sdim      MCOp = lowerSymbolOperand(MO, MO.getMCSymbol());
301344779Sdim      break;
302292915Sdim    }
303292915Sdim
304292915Sdim    OutMI.addOperand(MCOp);
305292915Sdim  }
306344779Sdim
307344779Sdim  if (!WasmKeepRegisters)
308344779Sdim    removeRegisterOperands(MI, OutMI);
309292915Sdim}
310344779Sdim
311344779Sdimstatic void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
312344779Sdim  // Remove all uses of stackified registers to bring the instruction format
313344779Sdim  // into its final stack form used thruout MC, and transition opcodes to
314344779Sdim  // their _S variant.
315344779Sdim  // We do this seperate from the above code that still may need these
316344779Sdim  // registers for e.g. call_indirect signatures.
317344779Sdim  // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for
318344779Sdim  // details.
319344779Sdim  // TODO: the code above creates new registers which are then removed here.
320344779Sdim  // That code could be slightly simplified by not doing that, though maybe
321344779Sdim  // it is simpler conceptually to keep the code above in "register mode"
322344779Sdim  // until this transition point.
323344779Sdim  // FIXME: we are not processing inline assembly, which contains register
324344779Sdim  // operands, because it is used by later target generic code.
325344779Sdim  if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())
326344779Sdim    return;
327344779Sdim
328344779Sdim  // Transform to _S instruction.
329344779Sdim  auto RegOpcode = OutMI.getOpcode();
330344779Sdim  auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);
331344779Sdim  assert(StackOpcode != -1 && "Failed to stackify instruction");
332344779Sdim  OutMI.setOpcode(StackOpcode);
333344779Sdim
334344779Sdim  // Remove register operands.
335344779Sdim  for (auto I = OutMI.getNumOperands(); I; --I) {
336344779Sdim    auto &MO = OutMI.getOperand(I - 1);
337344779Sdim    if (MO.isReg()) {
338344779Sdim      OutMI.erase(&MO);
339344779Sdim    }
340344779Sdim  }
341344779Sdim}
342