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