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 // Exception handling & setjmp-longjmp handling related options. These are 22 // defined here to be shared between WebAssembly and its subdirectories. 23 24 // Emscripten's asm.js-style exception handling 25 cl::opt<bool> WebAssembly::WasmEnableEmEH( 26 "enable-emscripten-cxx-exceptions", 27 cl::desc("WebAssembly Emscripten-style exception handling"), 28 cl::init(false)); 29 // Emscripten's asm.js-style setjmp/longjmp handling 30 cl::opt<bool> WebAssembly::WasmEnableEmSjLj( 31 "enable-emscripten-sjlj", 32 cl::desc("WebAssembly Emscripten-style setjmp/longjmp handling"), 33 cl::init(false)); 34 // Exception handling using wasm EH instructions 35 cl::opt<bool> 36 WebAssembly::WasmEnableEH("wasm-enable-eh", 37 cl::desc("WebAssembly exception handling"), 38 cl::init(false)); 39 // setjmp/longjmp handling using wasm EH instrutions 40 cl::opt<bool> 41 WebAssembly::WasmEnableSjLj("wasm-enable-sjlj", 42 cl::desc("WebAssembly setjmp/longjmp handling"), 43 cl::init(false)); 44 45 // Function names in libc++abi and libunwind 46 const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch"; 47 const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow"; 48 const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev"; 49 const char *const WebAssembly::PersonalityWrapperFn = 50 "_Unwind_Wasm_CallPersonality"; 51 52 /// Test whether MI is a child of some other node in an expression tree. 53 bool WebAssembly::isChild(const MachineInstr &MI, 54 const WebAssemblyFunctionInfo &MFI) { 55 if (MI.getNumOperands() == 0) 56 return false; 57 const MachineOperand &MO = MI.getOperand(0); 58 if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) 59 return false; 60 Register Reg = MO.getReg(); 61 return Register::isVirtualRegister(Reg) && MFI.isVRegStackified(Reg); 62 } 63 64 bool WebAssembly::mayThrow(const MachineInstr &MI) { 65 switch (MI.getOpcode()) { 66 case WebAssembly::THROW: 67 case WebAssembly::THROW_S: 68 case WebAssembly::RETHROW: 69 case WebAssembly::RETHROW_S: 70 return true; 71 } 72 if (isCallIndirect(MI.getOpcode())) 73 return true; 74 if (!MI.isCall()) 75 return false; 76 77 const MachineOperand &MO = getCalleeOp(MI); 78 assert(MO.isGlobal() || MO.isSymbol()); 79 80 if (MO.isSymbol()) { 81 // Some intrinsics are lowered to calls to external symbols, which are then 82 // lowered to calls to library functions. Most of libcalls don't throw, but 83 // we only list some of them here now. 84 // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo 85 // instead for more accurate info. 86 const char *Name = MO.getSymbolName(); 87 if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 || 88 strcmp(Name, "memset") == 0) 89 return false; 90 return true; 91 } 92 93 const auto *F = dyn_cast<Function>(MO.getGlobal()); 94 if (!F) 95 return true; 96 if (F->doesNotThrow()) 97 return false; 98 // These functions never throw 99 if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn || 100 F->getName() == StdTerminateFn) 101 return false; 102 103 // TODO Can we exclude call instructions that are marked as 'nounwind' in the 104 // original LLVm IR? (Even when the callee may throw) 105 return true; 106 } 107 108 const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) { 109 switch (MI.getOpcode()) { 110 case WebAssembly::CALL: 111 case WebAssembly::CALL_S: 112 case WebAssembly::RET_CALL: 113 case WebAssembly::RET_CALL_S: 114 return MI.getOperand(MI.getNumExplicitDefs()); 115 case WebAssembly::CALL_INDIRECT: 116 case WebAssembly::CALL_INDIRECT_S: 117 case WebAssembly::RET_CALL_INDIRECT: 118 case WebAssembly::RET_CALL_INDIRECT_S: 119 return MI.getOperand(MI.getNumExplicitOperands() - 1); 120 default: 121 llvm_unreachable("Not a call instruction"); 122 } 123 } 124 125 MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol( 126 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 127 StringRef Name = "__indirect_function_table"; 128 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 129 if (Sym) { 130 if (!Sym->isFunctionTable()) 131 Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 132 } else { 133 Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 134 Sym->setFunctionTable(); 135 // The default function table is synthesized by the linker. 136 Sym->setUndefined(); 137 } 138 // MVP object files can't have symtab entries for tables. 139 if (!(Subtarget && Subtarget->hasReferenceTypes())) 140 Sym->setOmitFromLinkingSection(); 141 return Sym; 142 } 143 144 MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol( 145 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 146 StringRef Name = "__funcref_call_table"; 147 MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 148 if (Sym) { 149 if (!Sym->isFunctionTable()) 150 Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 151 } else { 152 Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 153 154 // Setting Weak ensure only one table is left after linking when multiple 155 // modules define the table. 156 Sym->setWeak(true); 157 158 wasm::WasmLimits Limits = {0, 1, 1}; 159 wasm::WasmTableType TableType = {wasm::WASM_TYPE_FUNCREF, Limits}; 160 Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); 161 Sym->setTableType(TableType); 162 } 163 // MVP object files can't have symtab entries for tables. 164 if (!(Subtarget && Subtarget->hasReferenceTypes())) 165 Sym->setOmitFromLinkingSection(); 166 return Sym; 167 } 168 169 // Find a catch instruction from an EH pad. 170 MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) { 171 assert(EHPad->isEHPad()); 172 auto Pos = EHPad->begin(); 173 // Skip any label or debug instructions. Also skip 'end' marker instructions 174 // that may exist after marker placement in CFGStackify. 175 while (Pos != EHPad->end() && 176 (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode()))) 177 Pos++; 178 if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode())) 179 return &*Pos; 180 return nullptr; 181 } 182 183 unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) { 184 assert(RC != nullptr); 185 switch (RC->getID()) { 186 case WebAssembly::I32RegClassID: 187 return WebAssembly::COPY_I32; 188 case WebAssembly::I64RegClassID: 189 return WebAssembly::COPY_I64; 190 case WebAssembly::F32RegClassID: 191 return WebAssembly::COPY_F32; 192 case WebAssembly::F64RegClassID: 193 return WebAssembly::COPY_F64; 194 case WebAssembly::V128RegClassID: 195 return WebAssembly::COPY_V128; 196 case WebAssembly::FUNCREFRegClassID: 197 return WebAssembly::COPY_FUNCREF; 198 case WebAssembly::EXTERNREFRegClassID: 199 return WebAssembly::COPY_EXTERNREF; 200 default: 201 llvm_unreachable("Unexpected register class"); 202 } 203 } 204