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"
21using namespace llvm;
22
23// Function names in libc++abi and libunwind
24const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
25const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
26const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
27const char *const WebAssembly::PersonalityWrapperFn =
28 "_Unwind_Wasm_CallPersonality";
29
30/// Test whether MI is a child of some other node in an expression tree.
31bool WebAssembly::isChild(const MachineInstr &MI,
32 const WebAssemblyFunctionInfo &MFI) {
33 if (MI.getNumOperands() == 0)
34 return false;
35 const MachineOperand &MO = MI.getOperand(i: 0);
36 if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
37 return false;
38 Register Reg = MO.getReg();
39 return Reg.isVirtual() && MFI.isVRegStackified(VReg: Reg);
40}
41
42bool 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(Opc: 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(s1: Name, s2: "memcpy") == 0 || strcmp(s1: Name, s2: "memmove") == 0 ||
66 strcmp(s1: Name, s2: "memset") == 0)
67 return false;
68 return true;
69 }
70
71 const auto *F = dyn_cast<Function>(Val: 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
86const 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
103MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(
104 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
105 StringRef Name = "__indirect_function_table";
106 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Val: Ctx.lookupSymbol(Name));
107 if (Sym) {
108 if (!Sym->isFunctionTable())
109 Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table");
110 } else {
111 Sym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name));
112 Sym->setFunctionTable();
113 // The default function table is synthesized by the linker.
114 Sym->setUndefined();
115 }
116 // MVP object files can't have symtab entries for tables.
117 if (!(Subtarget && Subtarget->hasReferenceTypes()))
118 Sym->setOmitFromLinkingSection();
119 return Sym;
120}
121
122MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
123 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
124 StringRef Name = "__funcref_call_table";
125 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Val: Ctx.lookupSymbol(Name));
126 if (Sym) {
127 if (!Sym->isFunctionTable())
128 Ctx.reportError(L: SMLoc(), Msg: "symbol is not a wasm funcref table");
129 } else {
130 Sym = cast<MCSymbolWasm>(Val: Ctx.getOrCreateSymbol(Name));
131
132 // Setting Weak ensure only one table is left after linking when multiple
133 // modules define the table.
134 Sym->setWeak(true);
135
136 wasm::WasmLimits Limits = {.Flags: 0, .Minimum: 1, .Maximum: 1};
137 wasm::WasmTableType TableType = {.ElemType: wasm::ValType::FUNCREF, .Limits: Limits};
138 Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
139 Sym->setTableType(TableType);
140 }
141 // MVP object files can't have symtab entries for tables.
142 if (!(Subtarget && Subtarget->hasReferenceTypes()))
143 Sym->setOmitFromLinkingSection();
144 return Sym;
145}
146
147// Find a catch instruction from an EH pad.
148MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {
149 assert(EHPad->isEHPad());
150 auto Pos = EHPad->begin();
151 // Skip any label or debug instructions. Also skip 'end' marker instructions
152 // that may exist after marker placement in CFGStackify.
153 while (Pos != EHPad->end() &&
154 (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Opc: Pos->getOpcode())))
155 Pos++;
156 if (Pos != EHPad->end() && WebAssembly::isCatch(Opc: Pos->getOpcode()))
157 return &*Pos;
158 return nullptr;
159}
160
161unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
162 assert(RC != nullptr);
163 switch (RC->getID()) {
164 case WebAssembly::I32RegClassID:
165 return WebAssembly::COPY_I32;
166 case WebAssembly::I64RegClassID:
167 return WebAssembly::COPY_I64;
168 case WebAssembly::F32RegClassID:
169 return WebAssembly::COPY_F32;
170 case WebAssembly::F64RegClassID:
171 return WebAssembly::COPY_F64;
172 case WebAssembly::V128RegClassID:
173 return WebAssembly::COPY_V128;
174 case WebAssembly::FUNCREFRegClassID:
175 return WebAssembly::COPY_FUNCREF;
176 case WebAssembly::EXTERNREFRegClassID:
177 return WebAssembly::COPY_EXTERNREF;
178 default:
179 llvm_unreachable("Unexpected register class");
180 }
181}
182
183bool WebAssembly::canLowerMultivalueReturn(
184 const WebAssemblySubtarget *Subtarget) {
185 const auto &TM = static_cast<const WebAssemblyTargetMachine &>(
186 Subtarget->getTargetLowering()->getTargetMachine());
187 return Subtarget->hasMultivalue() && TM.usesMultivalueABI();
188}
189
190bool WebAssembly::canLowerReturn(size_t ResultSize,
191 const WebAssemblySubtarget *Subtarget) {
192 return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);
193}
194

source code of llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp