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