15f757f3fSDimitry Andric //===-- WebAssemblyUtilities.cpp - WebAssembly Utility Functions ----------===// 25f757f3fSDimitry Andric // 35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f757f3fSDimitry Andric // 75f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 85f757f3fSDimitry Andric /// 95f757f3fSDimitry Andric /// \file 105f757f3fSDimitry Andric /// This file implements several utility functions for WebAssembly. 115f757f3fSDimitry Andric /// 125f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 135f757f3fSDimitry Andric 145f757f3fSDimitry Andric #include "WebAssemblyUtilities.h" 155f757f3fSDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 16*0fca6ea1SDimitry Andric #include "WebAssemblyTargetMachine.h" 175f757f3fSDimitry Andric #include "llvm/CodeGen/MachineInstr.h" 185f757f3fSDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h" 195f757f3fSDimitry Andric #include "llvm/IR/Function.h" 205f757f3fSDimitry Andric #include "llvm/MC/MCContext.h" 215f757f3fSDimitry Andric using namespace llvm; 225f757f3fSDimitry Andric 235f757f3fSDimitry Andric // Function names in libc++abi and libunwind 245f757f3fSDimitry Andric const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch"; 255f757f3fSDimitry Andric const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow"; 265f757f3fSDimitry Andric const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev"; 275f757f3fSDimitry Andric const char *const WebAssembly::PersonalityWrapperFn = 285f757f3fSDimitry Andric "_Unwind_Wasm_CallPersonality"; 295f757f3fSDimitry Andric 305f757f3fSDimitry Andric /// Test whether MI is a child of some other node in an expression tree. 315f757f3fSDimitry Andric bool WebAssembly::isChild(const MachineInstr &MI, 325f757f3fSDimitry Andric const WebAssemblyFunctionInfo &MFI) { 335f757f3fSDimitry Andric if (MI.getNumOperands() == 0) 345f757f3fSDimitry Andric return false; 355f757f3fSDimitry Andric const MachineOperand &MO = MI.getOperand(0); 365f757f3fSDimitry Andric if (!MO.isReg() || MO.isImplicit() || !MO.isDef()) 375f757f3fSDimitry Andric return false; 385f757f3fSDimitry Andric Register Reg = MO.getReg(); 395f757f3fSDimitry Andric return Reg.isVirtual() && MFI.isVRegStackified(Reg); 405f757f3fSDimitry Andric } 415f757f3fSDimitry Andric 425f757f3fSDimitry Andric bool WebAssembly::mayThrow(const MachineInstr &MI) { 435f757f3fSDimitry Andric switch (MI.getOpcode()) { 445f757f3fSDimitry Andric case WebAssembly::THROW: 455f757f3fSDimitry Andric case WebAssembly::THROW_S: 465f757f3fSDimitry Andric case WebAssembly::RETHROW: 475f757f3fSDimitry Andric case WebAssembly::RETHROW_S: 485f757f3fSDimitry Andric return true; 495f757f3fSDimitry Andric } 505f757f3fSDimitry Andric if (isCallIndirect(MI.getOpcode())) 515f757f3fSDimitry Andric return true; 525f757f3fSDimitry Andric if (!MI.isCall()) 535f757f3fSDimitry Andric return false; 545f757f3fSDimitry Andric 555f757f3fSDimitry Andric const MachineOperand &MO = getCalleeOp(MI); 565f757f3fSDimitry Andric assert(MO.isGlobal() || MO.isSymbol()); 575f757f3fSDimitry Andric 585f757f3fSDimitry Andric if (MO.isSymbol()) { 595f757f3fSDimitry Andric // Some intrinsics are lowered to calls to external symbols, which are then 605f757f3fSDimitry Andric // lowered to calls to library functions. Most of libcalls don't throw, but 615f757f3fSDimitry Andric // we only list some of them here now. 625f757f3fSDimitry Andric // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo 635f757f3fSDimitry Andric // instead for more accurate info. 645f757f3fSDimitry Andric const char *Name = MO.getSymbolName(); 655f757f3fSDimitry Andric if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 || 665f757f3fSDimitry Andric strcmp(Name, "memset") == 0) 675f757f3fSDimitry Andric return false; 685f757f3fSDimitry Andric return true; 695f757f3fSDimitry Andric } 705f757f3fSDimitry Andric 715f757f3fSDimitry Andric const auto *F = dyn_cast<Function>(MO.getGlobal()); 725f757f3fSDimitry Andric if (!F) 735f757f3fSDimitry Andric return true; 745f757f3fSDimitry Andric if (F->doesNotThrow()) 755f757f3fSDimitry Andric return false; 765f757f3fSDimitry Andric // These functions never throw 775f757f3fSDimitry Andric if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn || 785f757f3fSDimitry Andric F->getName() == StdTerminateFn) 795f757f3fSDimitry Andric return false; 805f757f3fSDimitry Andric 815f757f3fSDimitry Andric // TODO Can we exclude call instructions that are marked as 'nounwind' in the 825f757f3fSDimitry Andric // original LLVm IR? (Even when the callee may throw) 835f757f3fSDimitry Andric return true; 845f757f3fSDimitry Andric } 855f757f3fSDimitry Andric 865f757f3fSDimitry Andric const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) { 875f757f3fSDimitry Andric switch (MI.getOpcode()) { 885f757f3fSDimitry Andric case WebAssembly::CALL: 895f757f3fSDimitry Andric case WebAssembly::CALL_S: 905f757f3fSDimitry Andric case WebAssembly::RET_CALL: 915f757f3fSDimitry Andric case WebAssembly::RET_CALL_S: 925f757f3fSDimitry Andric return MI.getOperand(MI.getNumExplicitDefs()); 935f757f3fSDimitry Andric case WebAssembly::CALL_INDIRECT: 945f757f3fSDimitry Andric case WebAssembly::CALL_INDIRECT_S: 955f757f3fSDimitry Andric case WebAssembly::RET_CALL_INDIRECT: 965f757f3fSDimitry Andric case WebAssembly::RET_CALL_INDIRECT_S: 975f757f3fSDimitry Andric return MI.getOperand(MI.getNumExplicitOperands() - 1); 985f757f3fSDimitry Andric default: 995f757f3fSDimitry Andric llvm_unreachable("Not a call instruction"); 1005f757f3fSDimitry Andric } 1015f757f3fSDimitry Andric } 1025f757f3fSDimitry Andric 1035f757f3fSDimitry Andric MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol( 1045f757f3fSDimitry Andric MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 1055f757f3fSDimitry Andric StringRef Name = "__indirect_function_table"; 1065f757f3fSDimitry Andric MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 1075f757f3fSDimitry Andric if (Sym) { 1085f757f3fSDimitry Andric if (!Sym->isFunctionTable()) 1095f757f3fSDimitry Andric Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 1105f757f3fSDimitry Andric } else { 111*0fca6ea1SDimitry Andric bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit(); 1125f757f3fSDimitry Andric Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 113*0fca6ea1SDimitry Andric Sym->setFunctionTable(is64); 1145f757f3fSDimitry Andric // The default function table is synthesized by the linker. 1155f757f3fSDimitry Andric Sym->setUndefined(); 1165f757f3fSDimitry Andric } 1175f757f3fSDimitry Andric // MVP object files can't have symtab entries for tables. 1185f757f3fSDimitry Andric if (!(Subtarget && Subtarget->hasReferenceTypes())) 1195f757f3fSDimitry Andric Sym->setOmitFromLinkingSection(); 1205f757f3fSDimitry Andric return Sym; 1215f757f3fSDimitry Andric } 1225f757f3fSDimitry Andric 1235f757f3fSDimitry Andric MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol( 1245f757f3fSDimitry Andric MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { 1255f757f3fSDimitry Andric StringRef Name = "__funcref_call_table"; 1265f757f3fSDimitry Andric MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name)); 1275f757f3fSDimitry Andric if (Sym) { 1285f757f3fSDimitry Andric if (!Sym->isFunctionTable()) 1295f757f3fSDimitry Andric Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table"); 1305f757f3fSDimitry Andric } else { 1315f757f3fSDimitry Andric Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name)); 1325f757f3fSDimitry Andric 1335f757f3fSDimitry Andric // Setting Weak ensure only one table is left after linking when multiple 1345f757f3fSDimitry Andric // modules define the table. 1355f757f3fSDimitry Andric Sym->setWeak(true); 1365f757f3fSDimitry Andric 1375f757f3fSDimitry Andric wasm::WasmLimits Limits = {0, 1, 1}; 1387a6dacacSDimitry Andric wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits}; 1395f757f3fSDimitry Andric Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); 1405f757f3fSDimitry Andric Sym->setTableType(TableType); 1415f757f3fSDimitry Andric } 1425f757f3fSDimitry Andric // MVP object files can't have symtab entries for tables. 1435f757f3fSDimitry Andric if (!(Subtarget && Subtarget->hasReferenceTypes())) 1445f757f3fSDimitry Andric Sym->setOmitFromLinkingSection(); 1455f757f3fSDimitry Andric return Sym; 1465f757f3fSDimitry Andric } 1475f757f3fSDimitry Andric 1485f757f3fSDimitry Andric // Find a catch instruction from an EH pad. 1495f757f3fSDimitry Andric MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) { 1505f757f3fSDimitry Andric assert(EHPad->isEHPad()); 1515f757f3fSDimitry Andric auto Pos = EHPad->begin(); 1525f757f3fSDimitry Andric // Skip any label or debug instructions. Also skip 'end' marker instructions 1535f757f3fSDimitry Andric // that may exist after marker placement in CFGStackify. 1545f757f3fSDimitry Andric while (Pos != EHPad->end() && 1555f757f3fSDimitry Andric (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode()))) 1565f757f3fSDimitry Andric Pos++; 1575f757f3fSDimitry Andric if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode())) 1585f757f3fSDimitry Andric return &*Pos; 1595f757f3fSDimitry Andric return nullptr; 1605f757f3fSDimitry Andric } 1615f757f3fSDimitry Andric 1625f757f3fSDimitry Andric unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) { 1635f757f3fSDimitry Andric assert(RC != nullptr); 1645f757f3fSDimitry Andric switch (RC->getID()) { 1655f757f3fSDimitry Andric case WebAssembly::I32RegClassID: 1665f757f3fSDimitry Andric return WebAssembly::COPY_I32; 1675f757f3fSDimitry Andric case WebAssembly::I64RegClassID: 1685f757f3fSDimitry Andric return WebAssembly::COPY_I64; 1695f757f3fSDimitry Andric case WebAssembly::F32RegClassID: 1705f757f3fSDimitry Andric return WebAssembly::COPY_F32; 1715f757f3fSDimitry Andric case WebAssembly::F64RegClassID: 1725f757f3fSDimitry Andric return WebAssembly::COPY_F64; 1735f757f3fSDimitry Andric case WebAssembly::V128RegClassID: 1745f757f3fSDimitry Andric return WebAssembly::COPY_V128; 1755f757f3fSDimitry Andric case WebAssembly::FUNCREFRegClassID: 1765f757f3fSDimitry Andric return WebAssembly::COPY_FUNCREF; 1775f757f3fSDimitry Andric case WebAssembly::EXTERNREFRegClassID: 1785f757f3fSDimitry Andric return WebAssembly::COPY_EXTERNREF; 179*0fca6ea1SDimitry Andric case WebAssembly::EXNREFRegClassID: 180*0fca6ea1SDimitry Andric return WebAssembly::COPY_EXNREF; 1815f757f3fSDimitry Andric default: 1825f757f3fSDimitry Andric llvm_unreachable("Unexpected register class"); 1835f757f3fSDimitry Andric } 1845f757f3fSDimitry Andric } 185*0fca6ea1SDimitry Andric 186*0fca6ea1SDimitry Andric bool WebAssembly::canLowerMultivalueReturn( 187*0fca6ea1SDimitry Andric const WebAssemblySubtarget *Subtarget) { 188*0fca6ea1SDimitry Andric const auto &TM = static_cast<const WebAssemblyTargetMachine &>( 189*0fca6ea1SDimitry Andric Subtarget->getTargetLowering()->getTargetMachine()); 190*0fca6ea1SDimitry Andric return Subtarget->hasMultivalue() && TM.usesMultivalueABI(); 191*0fca6ea1SDimitry Andric } 192*0fca6ea1SDimitry Andric 193*0fca6ea1SDimitry Andric bool WebAssembly::canLowerReturn(size_t ResultSize, 194*0fca6ea1SDimitry Andric const WebAssemblySubtarget *Subtarget) { 195*0fca6ea1SDimitry Andric return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget); 196*0fca6ea1SDimitry Andric } 197