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"
20using namespace llvm;
21
22// Function names in libc++abi and libunwind
23const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
24const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
25const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
26const char *const WebAssembly::PersonalityWrapperFn =
27 "_Unwind_Wasm_CallPersonality";
28
29/// Test whether MI is a child of some other node in an expression tree.
30bool WebAssembly::isChild(const MachineInstr &MI,
31 const WebAssemblyFunctionInfo &MFI) {
32 if (MI.getNumOperands() == 0)
33 return false;
34 const MachineOperand &MO = MI.getOperand(i: 0);
35 if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
36 return false;
37 Register Reg = MO.getReg();
38 return Reg.isVirtual() && MFI.isVRegStackified(VReg: Reg);
39}
40
41bool 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(Opc: 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(s1: Name, s2: "memcpy") == 0 || strcmp(s1: Name, s2: "memmove") == 0 ||
67 strcmp(s1: Name, s2: "memset") == 0)
68 return false;
69 return true;
70 }
71
72 const auto *F = dyn_cast<Function>(Val: 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
87const 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(i: 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(i: MI.getNumExplicitOperands() - 1);
99 default:
100 llvm_unreachable("Not a call instruction");
101 }
102}
103
104MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
105 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
106 StringRef Name = "__indirect_function_table";
107 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Val: Ctx.lookupSymbol(Name));
108 if (Sym) {
109 if (!Sym->isFunctionTable())
110 Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table");
111 } else {
112 bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit();
113 Sym = cast<MCSymbolWasm>(Val: 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
124MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
125 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
126 StringRef Name = "__funcref_call_table";
127 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Val: Ctx.lookupSymbol(Name));
128 if (Sym) {
129 if (!Sym->isFunctionTable())
130 Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table");
131 } else {
132 Sym = cast<MCSymbolWasm>(Val: 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 = {.Flags: 0, .Minimum: 1, .Maximum: 1, .PageSize: 0};
139 wasm::WasmTableType TableType = {.ElemType: wasm::ValType::FUNCREF, .Limits: 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.
150MachineInstr *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(Opc: Pos->getOpcode())))
157 Pos++;
158 if (Pos != EHPad->end() && WebAssembly::isCatch(Opc: Pos->getOpcode()))
159 return &*Pos;
160 return nullptr;
161}
162
163unsigned 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
187bool 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
194bool WebAssembly::canLowerReturn(size_t ResultSize,
195 const WebAssemblySubtarget *Subtarget) {
196 return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
197}
198