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