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