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