xref: /llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp (revision c3536b263f253a69fb336fb0617ee33a01a5c5dd)
1 //===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===//
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 implements several utility functions for WebAssembly.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #include "WebAssemblyUtilities.h"
15 #include "WebAssemblyMachineFunctionInfo.h"
16 #include "WebAssemblyTargetMachine.h"
17 #include "llvm/CodeGen/MachineInstr.h"
18 #include "llvm/IR/Function.h"
19 #include "llvm/MC/MCContext.h"
20 using namespace llvm;
21 
22 // Function names in libc++abi and libunwind
23 const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
24 const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
25 const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
26 const char *const WebAssembly::PersonalityWrapperFn =
27     "_Unwind_Wasm_CallPersonality";
28 
29 /// Test whether MI is a child of some other node in an expression tree.
30 bool WebAssembly::isChild(const MachineInstr &MI,
31                           const WebAssemblyFunctionInfo &MFI) {
32   if (MI.getNumOperands() == 0)
33     return false;
34   const MachineOperand &MO = MI.getOperand(0);
35   if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
36     return false;
37   Register Reg = MO.getReg();
38   return Reg.isVirtual() && MFI.isVRegStackified(Reg);
39 }
40 
41 bool WebAssembly::mayThrow(const MachineInstr &MI) {
42   switch (MI.getOpcode()) {
43   case WebAssembly::THROW:
44   case WebAssembly::THROW_S:
45   case WebAssembly::THROW_REF:
46   case WebAssembly::THROW_REF_S:
47   case WebAssembly::RETHROW:
48   case WebAssembly::RETHROW_S:
49     return true;
50   }
51   if (isCallIndirect(MI.getOpcode()))
52     return true;
53   if (!MI.isCall())
54     return false;
55 
56   const MachineOperand &MO = getCalleeOp(MI);
57   assert(MO.isGlobal() || MO.isSymbol());
58 
59   if (MO.isSymbol()) {
60     // Some intrinsics are lowered to calls to external symbols, which are then
61     // lowered to calls to library functions. Most of libcalls don't throw, but
62     // we only list some of them here now.
63     // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo
64     // instead for more accurate info.
65     const char *Name = MO.getSymbolName();
66     if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||
67         strcmp(Name, "memset") == 0)
68       return false;
69     return true;
70   }
71 
72   const auto *F = dyn_cast<Function>(MO.getGlobal());
73   if (!F)
74     return true;
75   if (F->doesNotThrow())
76     return false;
77   // These functions never throw
78   if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||
79       F->getName() == StdTerminateFn)
80     return false;
81 
82   // TODO Can we exclude call instructions that are marked as 'nounwind' in the
83   // original LLVm IR? (Even when the callee may throw)
84   return true;
85 }
86 
87 const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) {
88   switch (MI.getOpcode()) {
89   case WebAssembly::CALL:
90   case WebAssembly::CALL_S:
91   case WebAssembly::RET_CALL:
92   case WebAssembly::RET_CALL_S:
93     return MI.getOperand(MI.getNumExplicitDefs());
94   case WebAssembly::CALL_INDIRECT:
95   case WebAssembly::CALL_INDIRECT_S:
96   case WebAssembly::RET_CALL_INDIRECT:
97   case WebAssembly::RET_CALL_INDIRECT_S:
98     return MI.getOperand(MI.getNumExplicitOperands() - 1);
99   default:
100     llvm_unreachable("Not a call instruction");
101   }
102 }
103 
104 MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
105     MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
106   StringRef Name = "__indirect_function_table";
107   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
108   if (Sym) {
109     if (!Sym->isFunctionTable())
110       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
111   } else {
112     bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit();
113     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
114     Sym->setFunctionTable(is64);
115     // The default function table is synthesized by the linker.
116     Sym->setUndefined();
117   }
118   // MVP object files can't have symtab entries for tables.
119   if (!(Subtarget && Subtarget->hasCallIndirectOverlong()))
120     Sym->setOmitFromLinkingSection();
121   return Sym;
122 }
123 
124 MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
125     MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
126   StringRef Name = "__funcref_call_table";
127   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
128   if (Sym) {
129     if (!Sym->isFunctionTable())
130       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
131   } else {
132     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
133 
134     // Setting Weak ensure only one table is left after linking when multiple
135     // modules define the table.
136     Sym->setWeak(true);
137 
138     wasm::WasmLimits Limits = {0, 1, 1};
139     wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits};
140     Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
141     Sym->setTableType(TableType);
142   }
143   // MVP object files can't have symtab entries for tables.
144   if (!(Subtarget && Subtarget->hasCallIndirectOverlong()))
145     Sym->setOmitFromLinkingSection();
146   return Sym;
147 }
148 
149 // Find a catch instruction from an EH pad.
150 MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {
151   assert(EHPad->isEHPad());
152   auto Pos = EHPad->begin();
153   // Skip any label or debug instructions. Also skip 'end' marker instructions
154   // that may exist after marker placement in CFGStackify.
155   while (Pos != EHPad->end() &&
156          (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode())))
157     Pos++;
158   if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode()))
159     return &*Pos;
160   return nullptr;
161 }
162 
163 unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
164   assert(RC != nullptr);
165   switch (RC->getID()) {
166   case WebAssembly::I32RegClassID:
167     return WebAssembly::COPY_I32;
168   case WebAssembly::I64RegClassID:
169     return WebAssembly::COPY_I64;
170   case WebAssembly::F32RegClassID:
171     return WebAssembly::COPY_F32;
172   case WebAssembly::F64RegClassID:
173     return WebAssembly::COPY_F64;
174   case WebAssembly::V128RegClassID:
175     return WebAssembly::COPY_V128;
176   case WebAssembly::FUNCREFRegClassID:
177     return WebAssembly::COPY_FUNCREF;
178   case WebAssembly::EXTERNREFRegClassID:
179     return WebAssembly::COPY_EXTERNREF;
180   case WebAssembly::EXNREFRegClassID:
181     return WebAssembly::COPY_EXNREF;
182   default:
183     llvm_unreachable("Unexpected register class");
184   }
185 }
186 
187 bool WebAssembly::canLowerMultivalueReturn(
188     const WebAssemblySubtarget *Subtarget) {
189   const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
190       Subtarget->getTargetLowering()->getTargetMachine());
191   return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
192 }
193 
194 bool WebAssembly::canLowerReturn(size_t ResultSize,
195                                  const WebAssemblySubtarget *Subtarget) {
196   return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
197 }
198