1984dc4b9SReid Kleckner //===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===// 2984dc4b9SReid Kleckner // 3984dc4b9SReid Kleckner // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4984dc4b9SReid Kleckner // See https://llvm.org/LICENSE.txt for license information. 5984dc4b9SReid Kleckner // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6984dc4b9SReid Kleckner // 7984dc4b9SReid Kleckner //===----------------------------------------------------------------------===// 8984dc4b9SReid Kleckner /// 9984dc4b9SReid Kleckner /// \file 10984dc4b9SReid Kleckner /// This file implements several utility functions for WebAssembly. 11984dc4b9SReid Kleckner /// 12984dc4b9SReid Kleckner //===----------------------------------------------------------------------===// 13984dc4b9SReid Kleckner 14984dc4b9SReid Kleckner #include "WebAssemblyUtilities.h" 15984dc4b9SReid Kleckner #include "WebAssemblyMachineFunctionInfo.h" 16c921ac72SHeejin Ahn #include "WebAssemblyTargetMachine.h" 17984dc4b9SReid Kleckner #include "llvm/CodeGen/MachineInstr.h" 18984dc4b9SReid Kleckner #include "llvm/IR/Function.h" 19984dc4b9SReid Kleckner #include "llvm/MC/MCContext.h" 20984dc4b9SReid Kleckner using namespace llvm; 21984dc4b9SReid Kleckner 22984dc4b9SReid Kleckner // Function names in libc++abi and libunwind 23984dc4b9SReid Kleckner const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch"; 24984dc4b9SReid Kleckner const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow"; 25984dc4b9SReid Kleckner const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev"; 26984dc4b9SReid Kleckner const char *const WebAssembly::PersonalityWrapperFn = 27984dc4b9SReid Kleckner "_Unwind_Wasm_CallPersonality"; 28984dc4b9SReid Kleckner 29984dc4b9SReid Kleckner /// Test whether MI is a child of some other node in an expression tree. 30984dc4b9SReid Kleckner bool WebAssembly::isChild(const MachineInstr &MI, 31984dc4b9SReid Kleckner const WebAssemblyFunctionInfo &MFI) { 32984dc4b9SReid Kleckner if (MI.getNumOperands() == 0) 33984dc4b9SReid Kleckner return false; 34984dc4b9SReid Kleckner const MachineOperand &MO = MI.getOperand(0); 35984dc4b9SReid Kleckner if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) 36984dc4b9SReid Kleckner return false; 37984dc4b9SReid Kleckner Register Reg = MO.getReg(); 38984dc4b9SReid Kleckner return Reg.isVirtual() && MFI.isVRegStackified(Reg); 39984dc4b9SReid Kleckner } 40984dc4b9SReid Kleckner 41984dc4b9SReid Kleckner bool WebAssembly::mayThrow(const MachineInstr &MI) { 42984dc4b9SReid Kleckner switch (MI.getOpcode()) { 43984dc4b9SReid Kleckner case WebAssembly::THROW: 44984dc4b9SReid Kleckner case WebAssembly::THROW_S: 456bbf7f06SHeejin Ahn case WebAssembly::THROW_REF: 466bbf7f06SHeejin Ahn case WebAssembly::THROW_REF_S: 47984dc4b9SReid Kleckner case WebAssembly::RETHROW: 48984dc4b9SReid Kleckner case WebAssembly::RETHROW_S: 49984dc4b9SReid Kleckner return true; 50984dc4b9SReid Kleckner } 51984dc4b9SReid Kleckner if (isCallIndirect(MI.getOpcode())) 52984dc4b9SReid Kleckner return true; 53984dc4b9SReid Kleckner if (!MI.isCall()) 54984dc4b9SReid Kleckner return false; 55984dc4b9SReid Kleckner 56984dc4b9SReid Kleckner const MachineOperand &MO = getCalleeOp(MI); 57984dc4b9SReid Kleckner assert(MO.isGlobal() || MO.isSymbol()); 58984dc4b9SReid Kleckner 59984dc4b9SReid Kleckner if (MO.isSymbol()) { 60984dc4b9SReid Kleckner // Some intrinsics are lowered to calls to external symbols, which are then 61984dc4b9SReid Kleckner // lowered to calls to library functions. Most of libcalls don't throw, but 62984dc4b9SReid Kleckner // we only list some of them here now. 63984dc4b9SReid Kleckner // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo 64984dc4b9SReid Kleckner // instead for more accurate info. 65984dc4b9SReid Kleckner const char *Name = MO.getSymbolName(); 66984dc4b9SReid Kleckner if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 || 67984dc4b9SReid Kleckner strcmp(Name, "memset") == 0) 68984dc4b9SReid Kleckner return false; 69984dc4b9SReid Kleckner return true; 70984dc4b9SReid Kleckner } 71984dc4b9SReid Kleckner 72984dc4b9SReid Kleckner const auto *F = dyn_cast<Function>(MO.getGlobal()); 73984dc4b9SReid Kleckner if (!F) 74984dc4b9SReid Kleckner return true; 75984dc4b9SReid Kleckner if (F->doesNotThrow()) 76984dc4b9SReid Kleckner return false; 77984dc4b9SReid Kleckner // These functions never throw 78984dc4b9SReid Kleckner if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn || 79984dc4b9SReid Kleckner F->getName() == StdTerminateFn) 80984dc4b9SReid Kleckner return false; 81984dc4b9SReid Kleckner 82984dc4b9SReid Kleckner // TODO Can we exclude call instructions that are marked as 'nounwind' in the 83984dc4b9SReid Kleckner // original LLVm IR? (Even when the callee may throw) 84984dc4b9SReid Kleckner return true; 85984dc4b9SReid Kleckner } 86984dc4b9SReid Kleckner 87984dc4b9SReid Kleckner const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) { 88984dc4b9SReid Kleckner switch (MI.getOpcode()) { 89984dc4b9SReid Kleckner case WebAssembly::CALL: 90984dc4b9SReid Kleckner case WebAssembly::CALL_S: 91984dc4b9SReid Kleckner case WebAssembly::RET_CALL: 92984dc4b9SReid Kleckner case WebAssembly::RET_CALL_S: 93984dc4b9SReid Kleckner return MI.getOperand(MI.getNumExplicitDefs()); 94984dc4b9SReid Kleckner case WebAssembly::CALL_INDIRECT: 95984dc4b9SReid Kleckner case WebAssembly::CALL_INDIRECT_S: 96984dc4b9SReid Kleckner case WebAssembly::RET_CALL_INDIRECT: 97984dc4b9SReid Kleckner case WebAssembly::RET_CALL_INDIRECT_S: 98984dc4b9SReid Kleckner return MI.getOperand(MI.getNumExplicitOperands() - 1); 99984dc4b9SReid Kleckner default: 100984dc4b9SReid Kleckner llvm_unreachable("Not a call instruction"); 101984dc4b9SReid Kleckner } 102984dc4b9SReid Kleckner } 103984dc4b9SReid Kleckner 104984dc4b9SReid Kleckner MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol( 105984dc4b9SReid Kleckner MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 106984dc4b9SReid Kleckner StringRef Name = "__indirect_function_table"; 107984dc4b9SReid Kleckner MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 108984dc4b9SReid Kleckner if (Sym) { 109984dc4b9SReid Kleckner if (!Sym->isFunctionTable()) 110984dc4b9SReid Kleckner Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 111984dc4b9SReid Kleckner } else { 112c2244f82SSam Clegg bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit(); 113984dc4b9SReid Kleckner Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 114c2244f82SSam Clegg Sym->setFunctionTable(is64); 115984dc4b9SReid Kleckner // The default function table is synthesized by the linker. 116984dc4b9SReid Kleckner Sym->setUndefined(); 117984dc4b9SReid Kleckner } 118984dc4b9SReid Kleckner // MVP object files can't have symtab entries for tables. 119*c3536b26SDan Gohman if (!(Subtarget && Subtarget->hasCallIndirectOverlong())) 120984dc4b9SReid Kleckner Sym->setOmitFromLinkingSection(); 121984dc4b9SReid Kleckner return Sym; 122984dc4b9SReid Kleckner } 123984dc4b9SReid Kleckner 124984dc4b9SReid Kleckner MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol( 125984dc4b9SReid Kleckner MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 126984dc4b9SReid Kleckner StringRef Name = "__funcref_call_table"; 127984dc4b9SReid Kleckner MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 128984dc4b9SReid Kleckner if (Sym) { 129984dc4b9SReid Kleckner if (!Sym->isFunctionTable()) 130984dc4b9SReid Kleckner Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 131984dc4b9SReid Kleckner } else { 132984dc4b9SReid Kleckner Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 133984dc4b9SReid Kleckner 134984dc4b9SReid Kleckner // Setting Weak ensure only one table is left after linking when multiple 135984dc4b9SReid Kleckner // modules define the table. 136984dc4b9SReid Kleckner Sym->setWeak(true); 137984dc4b9SReid Kleckner 138984dc4b9SReid Kleckner wasm::WasmLimits Limits = {0, 1, 1}; 139103fa325SDerek Schuff wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits}; 140984dc4b9SReid Kleckner Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); 141984dc4b9SReid Kleckner Sym->setTableType(TableType); 142984dc4b9SReid Kleckner } 143984dc4b9SReid Kleckner // MVP object files can't have symtab entries for tables. 144*c3536b26SDan Gohman if (!(Subtarget && Subtarget->hasCallIndirectOverlong())) 145984dc4b9SReid Kleckner Sym->setOmitFromLinkingSection(); 146984dc4b9SReid Kleckner return Sym; 147984dc4b9SReid Kleckner } 148984dc4b9SReid Kleckner 149984dc4b9SReid Kleckner // Find a catch instruction from an EH pad. 150984dc4b9SReid Kleckner MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) { 151984dc4b9SReid Kleckner assert(EHPad->isEHPad()); 152984dc4b9SReid Kleckner auto Pos = EHPad->begin(); 153984dc4b9SReid Kleckner // Skip any label or debug instructions. Also skip 'end' marker instructions 154984dc4b9SReid Kleckner // that may exist after marker placement in CFGStackify. 155984dc4b9SReid Kleckner while (Pos != EHPad->end() && 156984dc4b9SReid Kleckner (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode()))) 157984dc4b9SReid Kleckner Pos++; 158984dc4b9SReid Kleckner if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode())) 159984dc4b9SReid Kleckner return &*Pos; 160984dc4b9SReid Kleckner return nullptr; 161984dc4b9SReid Kleckner } 162984dc4b9SReid Kleckner 163984dc4b9SReid Kleckner unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) { 164984dc4b9SReid Kleckner assert(RC != nullptr); 165984dc4b9SReid Kleckner switch (RC->getID()) { 166984dc4b9SReid Kleckner case WebAssembly::I32RegClassID: 167984dc4b9SReid Kleckner return WebAssembly::COPY_I32; 168984dc4b9SReid Kleckner case WebAssembly::I64RegClassID: 169984dc4b9SReid Kleckner return WebAssembly::COPY_I64; 170984dc4b9SReid Kleckner case WebAssembly::F32RegClassID: 171984dc4b9SReid Kleckner return WebAssembly::COPY_F32; 172984dc4b9SReid Kleckner case WebAssembly::F64RegClassID: 173984dc4b9SReid Kleckner return WebAssembly::COPY_F64; 174984dc4b9SReid Kleckner case WebAssembly::V128RegClassID: 175984dc4b9SReid Kleckner return WebAssembly::COPY_V128; 176984dc4b9SReid Kleckner case WebAssembly::FUNCREFRegClassID: 177984dc4b9SReid Kleckner return WebAssembly::COPY_FUNCREF; 178984dc4b9SReid Kleckner case WebAssembly::EXTERNREFRegClassID: 179984dc4b9SReid Kleckner return WebAssembly::COPY_EXTERNREF; 180c179d50fSHeejin Ahn case WebAssembly::EXNREFRegClassID: 181c179d50fSHeejin Ahn return WebAssembly::COPY_EXNREF; 182984dc4b9SReid Kleckner default: 183984dc4b9SReid Kleckner llvm_unreachable("Unexpected register class"); 184984dc4b9SReid Kleckner } 185984dc4b9SReid Kleckner } 186c921ac72SHeejin Ahn 187c921ac72SHeejin Ahn bool WebAssembly::canLowerMultivalueReturn( 188c921ac72SHeejin Ahn const WebAssemblySubtarget *Subtarget) { 189c921ac72SHeejin Ahn const auto &TM = static_cast<const WebAssemblyTargetMachine &>( 190c921ac72SHeejin Ahn Subtarget->getTargetLowering()->getTargetMachine()); 191c921ac72SHeejin Ahn return Subtarget->hasMultivalue() && TM.usesMultivalueABI(); 192c921ac72SHeejin Ahn } 193c921ac72SHeejin Ahn 194c921ac72SHeejin Ahn bool WebAssembly::canLowerReturn(size_t ResultSize, 195c921ac72SHeejin Ahn const WebAssemblySubtarget *Subtarget) { 196c921ac72SHeejin Ahn return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget); 197c921ac72SHeejin Ahn } 198