xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
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"
165f757f3fSDimitry Andric #include "WebAssemblySubtarget.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 {
1115f757f3fSDimitry Andric     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
1125f757f3fSDimitry Andric     Sym->setFunctionTable();
1135f757f3fSDimitry Andric     // The default function table is synthesized by the linker.
1145f757f3fSDimitry Andric     Sym->setUndefined();
1155f757f3fSDimitry Andric   }
1165f757f3fSDimitry Andric   // MVP object files can't have symtab entries for tables.
1175f757f3fSDimitry Andric   if (!(Subtarget && Subtarget->hasReferenceTypes()))
1185f757f3fSDimitry Andric     Sym->setOmitFromLinkingSection();
1195f757f3fSDimitry Andric   return Sym;
1205f757f3fSDimitry Andric }
1215f757f3fSDimitry Andric 
1225f757f3fSDimitry Andric MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(
1235f757f3fSDimitry Andric     MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {
1245f757f3fSDimitry Andric   StringRef Name = "__funcref_call_table";
1255f757f3fSDimitry Andric   MCSymbolWasm *Sym = cast_or_null<MCSymbolWasm>(Ctx.lookupSymbol(Name));
1265f757f3fSDimitry Andric   if (Sym) {
1275f757f3fSDimitry Andric     if (!Sym->isFunctionTable())
1285f757f3fSDimitry Andric       Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");
1295f757f3fSDimitry Andric   } else {
1305f757f3fSDimitry Andric     Sym = cast<MCSymbolWasm>(Ctx.getOrCreateSymbol(Name));
1315f757f3fSDimitry Andric 
1325f757f3fSDimitry Andric     // Setting Weak ensure only one table is left after linking when multiple
1335f757f3fSDimitry Andric     // modules define the table.
1345f757f3fSDimitry Andric     Sym->setWeak(true);
1355f757f3fSDimitry Andric 
1365f757f3fSDimitry Andric     wasm::WasmLimits Limits = {0, 1, 1};
137*7a6dacacSDimitry Andric     wasm::WasmTableType TableType = {wasm::ValType::FUNCREF, Limits};
1385f757f3fSDimitry Andric     Sym->setType(wasm::WASM_SYMBOL_TYPE_TABLE);
1395f757f3fSDimitry Andric     Sym->setTableType(TableType);
1405f757f3fSDimitry Andric   }
1415f757f3fSDimitry Andric   // MVP object files can't have symtab entries for tables.
1425f757f3fSDimitry Andric   if (!(Subtarget && Subtarget->hasReferenceTypes()))
1435f757f3fSDimitry Andric     Sym->setOmitFromLinkingSection();
1445f757f3fSDimitry Andric   return Sym;
1455f757f3fSDimitry Andric }
1465f757f3fSDimitry Andric 
1475f757f3fSDimitry Andric // Find a catch instruction from an EH pad.
1485f757f3fSDimitry Andric MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {
1495f757f3fSDimitry Andric   assert(EHPad->isEHPad());
1505f757f3fSDimitry Andric   auto Pos = EHPad->begin();
1515f757f3fSDimitry Andric   // Skip any label or debug instructions. Also skip 'end' marker instructions
1525f757f3fSDimitry Andric   // that may exist after marker placement in CFGStackify.
1535f757f3fSDimitry Andric   while (Pos != EHPad->end() &&
1545f757f3fSDimitry Andric          (Pos->isLabel() || Pos->isDebugInstr() || isMarker(Pos->getOpcode())))
1555f757f3fSDimitry Andric     Pos++;
1565f757f3fSDimitry Andric   if (Pos != EHPad->end() && WebAssembly::isCatch(Pos->getOpcode()))
1575f757f3fSDimitry Andric     return &*Pos;
1585f757f3fSDimitry Andric   return nullptr;
1595f757f3fSDimitry Andric }
1605f757f3fSDimitry Andric 
1615f757f3fSDimitry Andric unsigned WebAssembly::getCopyOpcodeForRegClass(const TargetRegisterClass *RC) {
1625f757f3fSDimitry Andric   assert(RC != nullptr);
1635f757f3fSDimitry Andric   switch (RC->getID()) {
1645f757f3fSDimitry Andric   case WebAssembly::I32RegClassID:
1655f757f3fSDimitry Andric     return WebAssembly::COPY_I32;
1665f757f3fSDimitry Andric   case WebAssembly::I64RegClassID:
1675f757f3fSDimitry Andric     return WebAssembly::COPY_I64;
1685f757f3fSDimitry Andric   case WebAssembly::F32RegClassID:
1695f757f3fSDimitry Andric     return WebAssembly::COPY_F32;
1705f757f3fSDimitry Andric   case WebAssembly::F64RegClassID:
1715f757f3fSDimitry Andric     return WebAssembly::COPY_F64;
1725f757f3fSDimitry Andric   case WebAssembly::V128RegClassID:
1735f757f3fSDimitry Andric     return WebAssembly::COPY_V128;
1745f757f3fSDimitry Andric   case WebAssembly::FUNCREFRegClassID:
1755f757f3fSDimitry Andric     return WebAssembly::COPY_FUNCREF;
1765f757f3fSDimitry Andric   case WebAssembly::EXTERNREFRegClassID:
1775f757f3fSDimitry Andric     return WebAssembly::COPY_EXTERNREF;
1785f757f3fSDimitry Andric   default:
1795f757f3fSDimitry Andric     llvm_unreachable("Unexpected register class");
1805f757f3fSDimitry Andric   }
1815f757f3fSDimitry Andric }
182