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.
isChild(const MachineInstr & MI,const WebAssemblyFunctionInfo & MFI)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
mayThrow(const MachineInstr & MI)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
getCalleeOp(const MachineInstr & MI)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
getOrCreateFunctionTableSymbol(MCContext & Ctx,const WebAssemblySubtarget * Subtarget)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
getOrCreateFuncrefCallTableSymbol(MCContext & Ctx,const WebAssemblySubtarget * Subtarget)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.
findCatch(MachineBasicBlock * EHPad)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
getCopyOpcodeForRegClass(const TargetRegisterClass * RC)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
canLowerMultivalueReturn(const WebAssemblySubtarget * Subtarget)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
canLowerReturn(size_t ResultSize,const WebAssemblySubtarget * Subtarget)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