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