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