xref: /llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp (revision c3536b263f253a69fb336fb0617ee33a01a5c5dd)
1984dc4b9SReid Kleckner //===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===//
2984dc4b9SReid Kleckner //
3984dc4b9SReid Kleckner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4984dc4b9SReid Kleckner // See https://llvm.org/LICENSE.txt for license information.
5984dc4b9SReid Kleckner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6984dc4b9SReid Kleckner //
7984dc4b9SReid Kleckner //===----------------------------------------------------------------------===//
8984dc4b9SReid Kleckner ///
9984dc4b9SReid Kleckner /// \file
10984dc4b9SReid Kleckner /// This file implements several utility functions for WebAssembly.
11984dc4b9SReid Kleckner ///
12984dc4b9SReid Kleckner //===----------------------------------------------------------------------===//
13984dc4b9SReid Kleckner 
14984dc4b9SReid Kleckner #include "WebAssemblyUtilities.h"
15984dc4b9SReid Kleckner #include "WebAssemblyMachineFunctionInfo.h"
16c921ac72SHeejin Ahn #include "WebAssemblyTargetMachine.h"
17984dc4b9SReid Kleckner #include "llvm/CodeGen/MachineInstr.h"
18984dc4b9SReid Kleckner #include "llvm/IR/Function.h"
19984dc4b9SReid Kleckner #include "llvm/MC/MCContext.h"
20984dc4b9SReid Kleckner using namespace llvm;
21984dc4b9SReid Kleckner 
22984dc4b9SReid Kleckner // Function names in libc++abi and libunwind
23984dc4b9SReid Kleckner const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
24984dc4b9SReid Kleckner const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
25984dc4b9SReid Kleckner const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
26984dc4b9SReid Kleckner const char *const WebAssembly::PersonalityWrapperFn =
27984dc4b9SReid Kleckner     "_Unwind_Wasm_CallPersonality";
28984dc4b9SReid Kleckner 
29984dc4b9SReid Kleckner /// Test whether MI is a child of some other node in an expression tree.
30984dc4b9SReid Kleckner bool WebAssembly::isChild(const MachineInstr &MI,
31984dc4b9SReid Kleckner                           const WebAssemblyFunctionInfo &MFI) {
32984dc4b9SReid Kleckner   if (MI.getNumOperands() == 0)
33984dc4b9SReid Kleckner     return false;
34984dc4b9SReid Kleckner   const MachineOperand &MO = MI.getOperand(0);
35984dc4b9SReid Kleckner   if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
36984dc4b9SReid Kleckner     return false;
37984dc4b9SReid Kleckner   Register Reg = MO.getReg();
38984dc4b9SReid Kleckner   return Reg.isVirtual() && MFI.isVRegStackified(Reg);
39984dc4b9SReid Kleckner }
40984dc4b9SReid Kleckner 
41984dc4b9SReid Kleckner bool WebAssembly::mayThrow(const MachineInstr &MI) {
42984dc4b9SReid Kleckner   switch (MI.getOpcode()) {
43984dc4b9SReid Kleckner   case WebAssembly::THROW:
44984dc4b9SReid Kleckner   case WebAssembly::THROW_S:
456bbf7f06SHeejin Ahn   case WebAssembly::THROW_REF:
466bbf7f06SHeejin Ahn   case WebAssembly::THROW_REF_S:
47984dc4b9SReid Kleckner   case WebAssembly::RETHROW:
48984dc4b9SReid Kleckner   case WebAssembly::RETHROW_S:
49984dc4b9SReid Kleckner     return true;
50984dc4b9SReid Kleckner   }
51984dc4b9SReid Kleckner   if (isCallIndirect(MI.getOpcode()))
52984dc4b9SReid Kleckner     return true;
53984dc4b9SReid Kleckner   if (!MI.isCall())
54984dc4b9SReid Kleckner     return false;
55984dc4b9SReid Kleckner 
56984dc4b9SReid Kleckner   const MachineOperand &MO = getCalleeOp(MI);
57984dc4b9SReid Kleckner   assert(MO.isGlobal() || MO.isSymbol());
58984dc4b9SReid Kleckner 
59984dc4b9SReid Kleckner   if (MO.isSymbol()) {
60984dc4b9SReid Kleckner     // Some intrinsics are lowered to calls to external symbols, which are then
61984dc4b9SReid Kleckner     // lowered to calls to library functions. Most of libcalls don't throw, but
62984dc4b9SReid Kleckner     // we only list some of them here now.
63984dc4b9SReid Kleckner     // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo
64984dc4b9SReid Kleckner     // instead for more accurate info.
65984dc4b9SReid Kleckner     const char *Name = MO.getSymbolName();
66984dc4b9SReid Kleckner     if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||
67984dc4b9SReid Kleckner         strcmp(Name, "memset") == 0)
68984dc4b9SReid Kleckner       return false;
69984dc4b9SReid Kleckner     return true;
70984dc4b9SReid Kleckner   }
71984dc4b9SReid Kleckner 
72984dc4b9SReid Kleckner   const auto *F = dyn_cast<Function>(MO.getGlobal());
73984dc4b9SReid Kleckner   if (!F)
74984dc4b9SReid Kleckner     return true;
75984dc4b9SReid Kleckner   if (F->doesNotThrow())
76984dc4b9SReid Kleckner     return false;
77984dc4b9SReid Kleckner   // These functions never throw
78984dc4b9SReid Kleckner   if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||
79984dc4b9SReid Kleckner       F->getName() == StdTerminateFn)
80984dc4b9SReid Kleckner     return false;
81984dc4b9SReid Kleckner 
82984dc4b9SReid Kleckner   // TODO Can we exclude call instructions that are marked as 'nounwind' in the
83984dc4b9SReid Kleckner   // original LLVm IR? (Even when the callee may throw)
84984dc4b9SReid Kleckner   return true;
85984dc4b9SReid Kleckner }
86984dc4b9SReid Kleckner 
87984dc4b9SReid Kleckner const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) {
88984dc4b9SReid Kleckner   switch (MI.getOpcode()) {
89984dc4b9SReid Kleckner   case WebAssembly::CALL:
90984dc4b9SReid Kleckner   case WebAssembly::CALL_S:
91984dc4b9SReid Kleckner   case WebAssembly::RET_CALL:
92984dc4b9SReid Kleckner   case WebAssembly::RET_CALL_S:
93984dc4b9SReid Kleckner     return MI.getOperand(MI.getNumExplicitDefs());
94984dc4b9SReid Kleckner   case WebAssembly::CALL_INDIRECT:
95984dc4b9SReid Kleckner   case WebAssembly::CALL_INDIRECT_S:
96984dc4b9SReid Kleckner   case WebAssembly::RET_CALL_INDIRECT:
97984dc4b9SReid Kleckner   case WebAssembly::RET_CALL_INDIRECT_S:
98984dc4b9SReid Kleckner     return MI.getOperand(MI.getNumExplicitOperands() - 1);
99984dc4b9SReid Kleckner   default:
100984dc4b9SReid Kleckner     llvm_unreachable("Not a call instruction");
101984dc4b9SReid Kleckner   }
102984dc4b9SReid Kleckner }
103984dc4b9SReid Kleckner 
104984dc4b9SReid Kleckner MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
105984dc4b9SReid Kleckner     MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
106984dc4b9SReid Kleckner   StringRef Name = "__indirect_function_table";
107984dc4b9SReid Kleckner   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
108984dc4b9SReid Kleckner   if (Sym) {
109984dc4b9SReid Kleckner     if (!Sym->isFunctionTable())
110984dc4b9SReid Kleckner       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
111984dc4b9SReid Kleckner   } else {
112c2244f82SSam Clegg     bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit();
113984dc4b9SReid Kleckner     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
114c2244f82SSam Clegg     Sym->setFunctionTable(is64);
115984dc4b9SReid Kleckner     // The default function table is synthesized by the linker.
116984dc4b9SReid Kleckner     Sym->setUndefined();
117984dc4b9SReid Kleckner   }
118984dc4b9SReid Kleckner   // MVP object files can't have symtab entries for tables.
119*c3536b26SDan Gohman   if (!(Subtarget && Subtarget->hasCallIndirectOverlong()))
120984dc4b9SReid Kleckner     Sym->setOmitFromLinkingSection();
121984dc4b9SReid Kleckner   return Sym;
122984dc4b9SReid Kleckner }
123984dc4b9SReid Kleckner 
124984dc4b9SReid Kleckner MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
125984dc4b9SReid Kleckner     MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
126984dc4b9SReid Kleckner   StringRef Name = "__funcref_call_table";
127984dc4b9SReid Kleckner   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
128984dc4b9SReid Kleckner   if (Sym) {
129984dc4b9SReid Kleckner     if (!Sym->isFunctionTable())
130984dc4b9SReid Kleckner       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
131984dc4b9SReid Kleckner   } else {
132984dc4b9SReid Kleckner     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
133984dc4b9SReid Kleckner 
134984dc4b9SReid Kleckner     // Setting Weak ensure only one table is left after linking when multiple
135984dc4b9SReid Kleckner     // modules define the table.
136984dc4b9SReid Kleckner     Sym->setWeak(true);
137984dc4b9SReid Kleckner 
138984dc4b9SReid Kleckner     wasm::WasmLimits Limits = {0, 1, 1};
139103fa325SDerek Schuff     wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits};
140984dc4b9SReid Kleckner     Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
141984dc4b9SReid Kleckner     Sym->setTableType(TableType);
142984dc4b9SReid Kleckner   }
143984dc4b9SReid Kleckner   // MVP object files can't have symtab entries for tables.
144*c3536b26SDan Gohman   if (!(Subtarget && Subtarget->hasCallIndirectOverlong()))
145984dc4b9SReid Kleckner     Sym->setOmitFromLinkingSection();
146984dc4b9SReid Kleckner   return Sym;
147984dc4b9SReid Kleckner }
148984dc4b9SReid Kleckner 
149984dc4b9SReid Kleckner // Find a catch instruction from an EH pad.
150984dc4b9SReid Kleckner MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {
151984dc4b9SReid Kleckner   assert(EHPad->isEHPad());
152984dc4b9SReid Kleckner   auto Pos = EHPad->begin();
153984dc4b9SReid Kleckner   // Skip any label or debug instructions. Also skip 'end' marker instructions
154984dc4b9SReid Kleckner   // that may exist after marker placement in CFGStackify.
155984dc4b9SReid Kleckner   while (Pos != EHPad->end() &&
156984dc4b9SReid Kleckner          (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode())))
157984dc4b9SReid Kleckner     Pos++;
158984dc4b9SReid Kleckner   if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode()))
159984dc4b9SReid Kleckner     return &*Pos;
160984dc4b9SReid Kleckner   return nullptr;
161984dc4b9SReid Kleckner }
162984dc4b9SReid Kleckner 
163984dc4b9SReid Kleckner unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
164984dc4b9SReid Kleckner   assert(RC != nullptr);
165984dc4b9SReid Kleckner   switch (RC->getID()) {
166984dc4b9SReid Kleckner   case WebAssembly::I32RegClassID:
167984dc4b9SReid Kleckner     return WebAssembly::COPY_I32;
168984dc4b9SReid Kleckner   case WebAssembly::I64RegClassID:
169984dc4b9SReid Kleckner     return WebAssembly::COPY_I64;
170984dc4b9SReid Kleckner   case WebAssembly::F32RegClassID:
171984dc4b9SReid Kleckner     return WebAssembly::COPY_F32;
172984dc4b9SReid Kleckner   case WebAssembly::F64RegClassID:
173984dc4b9SReid Kleckner     return WebAssembly::COPY_F64;
174984dc4b9SReid Kleckner   case WebAssembly::V128RegClassID:
175984dc4b9SReid Kleckner     return WebAssembly::COPY_V128;
176984dc4b9SReid Kleckner   case WebAssembly::FUNCREFRegClassID:
177984dc4b9SReid Kleckner     return WebAssembly::COPY_FUNCREF;
178984dc4b9SReid Kleckner   case WebAssembly::EXTERNREFRegClassID:
179984dc4b9SReid Kleckner     return WebAssembly::COPY_EXTERNREF;
180c179d50fSHeejin Ahn   case WebAssembly::EXNREFRegClassID:
181c179d50fSHeejin Ahn     return WebAssembly::COPY_EXNREF;
182984dc4b9SReid Kleckner   default:
183984dc4b9SReid Kleckner     llvm_unreachable("Unexpected register class");
184984dc4b9SReid Kleckner   }
185984dc4b9SReid Kleckner }
186c921ac72SHeejin Ahn 
187c921ac72SHeejin Ahn bool WebAssembly::canLowerMultivalueReturn(
188c921ac72SHeejin Ahn     const WebAssemblySubtarget *Subtarget) {
189c921ac72SHeejin Ahn   const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
190c921ac72SHeejin Ahn       Subtarget->getTargetLowering()->getTargetMachine());
191c921ac72SHeejin Ahn   return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
192c921ac72SHeejin Ahn }
193c921ac72SHeejin Ahn 
194c921ac72SHeejin Ahn bool WebAssembly::canLowerReturn(size_t ResultSize,
195c921ac72SHeejin Ahn                                  const WebAssemblySubtarget *Subtarget) {
196c921ac72SHeejin Ahn   return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
197c921ac72SHeejin Ahn }
198