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 const char *const WebAssembly::ClangCallTerminateFn = "__clang_call_terminate";
22 const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";
23 const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";
24 const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";
25 const char *const WebAssembly::PersonalityWrapperFn =
26     "_Unwind_Wasm_CallPersonality";
27 
28 /// Test whether MI is a child of some other node in an expression tree.
29 bool WebAssembly::isChild(const MachineInstr &MI,
30                           const WebAssemblyFunctionInfo &MFI) {
31   if (MI.getNumOperands() == 0)
32     return false;
33   const MachineOperand &MO = MI.getOperand(0);
34   if (!MO.isReg() || MO.isImplicit() || !MO.isDef())
35     return false;
36   Register Reg = MO.getReg();
37   return Register::isVirtualRegister(Reg) && MFI.isVRegStackified(Reg);
38 }
39 
40 bool WebAssembly::mayThrow(const MachineInstr &MI) {
41   switch (MI.getOpcode()) {
42   case WebAssembly::THROW:
43   case WebAssembly::THROW_S:
44   case WebAssembly::RETHROW:
45   case WebAssembly::RETHROW_S:
46     return true;
47   }
48   if (isCallIndirect(MI.getOpcode()))
49     return true;
50   if (!MI.isCall())
51     return false;
52 
53   const MachineOperand &MO = getCalleeOp(MI);
54   assert(MO.isGlobal() || MO.isSymbol());
55 
56   if (MO.isSymbol()) {
57     // Some intrinsics are lowered to calls to external symbols, which are then
58     // lowered to calls to library functions. Most of libcalls don't throw, but
59     // we only list some of them here now.
60     // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo
61     // instead for more accurate info.
62     const char *Name = MO.getSymbolName();
63     if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||
64         strcmp(Name, "memset") == 0)
65       return false;
66     return true;
67   }
68 
69   const auto *F = dyn_cast<Function>(MO.getGlobal());
70   if (!F)
71     return true;
72   if (F->doesNotThrow())
73     return false;
74   // These functions never throw
75   if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||
76       F->getName() == ClangCallTerminateFn || F->getName() == StdTerminateFn)
77     return false;
78 
79   // TODO Can we exclude call instructions that are marked as 'nounwind' in the
80   // original LLVm IR? (Even when the callee may throw)
81   return true;
82 }
83 
84 const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) {
85   switch (MI.getOpcode()) {
86   case WebAssembly::CALL:
87   case WebAssembly::CALL_S:
88   case WebAssembly::RET_CALL:
89   case WebAssembly::RET_CALL_S:
90     return MI.getOperand(MI.getNumExplicitDefs());
91   case WebAssembly::CALL_INDIRECT:
92   case WebAssembly::CALL_INDIRECT_S:
93   case WebAssembly::RET_CALL_INDIRECT:
94   case WebAssembly::RET_CALL_INDIRECT_S:
95     return MI.getOperand(MI.getNumOperands() - 1);
96   default:
97     llvm_unreachable("Not a call instruction");
98   }
99 }
100 
101 MCSymbolWasm *
102 WebAssembly::getOrCreateFunctionTableSymbol(MCContext &Ctx,
103                                             const StringRef &Name) {
104   // FIXME: Duplicates functionality from
105   // MC/WasmObjectWriter::recordRelocation.
106   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
107   if (Sym) {
108     if (!Sym->isFunctionTable())
109       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
110   } else {
111     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
112     Sym->setFunctionTable();
113     // The default function table is synthesized by the linker.
114     Sym->setUndefined();
115   }
116   return Sym;
117 }
118 
119 // Find a catch instruction from an EH pad.
120 MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {
121   assert(EHPad->isEHPad());
122   auto Pos = EHPad->begin();
123   // Skip any label or debug instructions. Also skip 'end' marker instructions
124   // that may exist after marker placement in CFGStackify.
125   while (Pos != EHPad->end() &&
126          (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode())))
127     Pos++;
128   if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode()))
129     return &*Pos;
130   return nullptr;
131 }
132