197 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// 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/IR/Function.h"19#include "llvm/MC/MCContext.h"20using namespace llvm;21 22// Function names in libc++abi and libunwind23const char *const WebAssembly::CxaBeginCatchFn = "__cxa_begin_catch";24const char *const WebAssembly::CxaRethrowFn = "__cxa_rethrow";25const char *const WebAssembly::StdTerminateFn = "_ZSt9terminatev";26const char *const WebAssembly::PersonalityWrapperFn =27 "_Unwind_Wasm_CallPersonality";28 29/// Test whether MI is a child of some other node in an expression tree.30bool WebAssembly::isChild(const MachineInstr &MI,31 const WebAssemblyFunctionInfo &MFI) {32 if (MI.getNumOperands() == 0)33 return false;34 const MachineOperand &MO = MI.getOperand(0);35 if (!MO.isReg() || MO.isImplicit() || !MO.isDef())36 return false;37 Register Reg = MO.getReg();38 return Reg.isVirtual() && MFI.isVRegStackified(Reg);39}40 41bool WebAssembly::mayThrow(const MachineInstr &MI) {42 switch (MI.getOpcode()) {43 case WebAssembly::THROW:44 case WebAssembly::THROW_S:45 case WebAssembly::THROW_REF:46 case WebAssembly::THROW_REF_S:47 case WebAssembly::RETHROW:48 case WebAssembly::RETHROW_S:49 return true;50 }51 if (isCallIndirect(MI.getOpcode()))52 return true;53 if (!MI.isCall())54 return false;55 56 const MachineOperand &MO = getCalleeOp(MI);57 assert(MO.isGlobal() || MO.isSymbol());58 59 if (MO.isSymbol()) {60 // Some intrinsics are lowered to calls to external symbols, which are then61 // lowered to calls to library functions. Most of libcalls don't throw, but62 // we only list some of them here now.63 // TODO Consider adding 'nounwind' info in TargetLowering::CallLoweringInfo64 // instead for more accurate info.65 const char *Name = MO.getSymbolName();66 if (strcmp(Name, "memcpy") == 0 || strcmp(Name, "memmove") == 0 ||67 strcmp(Name, "memset") == 0)68 return false;69 return true;70 }71 72 const auto *F = dyn_cast<Function>(MO.getGlobal());73 if (!F)74 return true;75 if (F->doesNotThrow())76 return false;77 // These functions never throw78 if (F->getName() == CxaBeginCatchFn || F->getName() == PersonalityWrapperFn ||79 F->getName() == StdTerminateFn)80 return false;81 82 // TODO Can we exclude call instructions that are marked as 'nounwind' in the83 // original LLVm IR? (Even when the callee may throw)84 return true;85}86 87const MachineOperand &WebAssembly::getCalleeOp(const MachineInstr &MI) {88 switch (MI.getOpcode()) {89 case WebAssembly::CALL:90 case WebAssembly::CALL_S:91 case WebAssembly::RET_CALL:92 case WebAssembly::RET_CALL_S:93 return MI.getOperand(MI.getNumExplicitDefs());94 case WebAssembly::CALL_INDIRECT:95 case WebAssembly::CALL_INDIRECT_S:96 case WebAssembly::RET_CALL_INDIRECT:97 case WebAssembly::RET_CALL_INDIRECT_S:98 return MI.getOperand(MI.getNumExplicitOperands() - 1);99 default:100 llvm_unreachable("Not a call instruction");101 }102}103 104MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol(105 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {106 StringRef Name = "__indirect_function_table";107 auto *Sym = static_cast<MCSymbolWasm *>(Ctx.lookupSymbol(Name));108 if (Sym) {109 if (!Sym->isFunctionTable())110 Ctx.reportError(SMLoc(), "symbol is not a wasm funcref table");111 } else {112 bool is64 = Subtarget && Subtarget->getTargetTriple().isArch64Bit();113 Sym = static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name));114 Sym->setFunctionTable(is64);115 // The default function table is synthesized by the linker.116 }117 // MVP object files can't have symtab entries for tables.118 if (!(Subtarget && Subtarget->hasCallIndirectOverlong()))119 Sym->setOmitFromLinkingSection();120 return Sym;121}122 123MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol(124 MCContext &Ctx, const WebAssemblySubtarget *Subtarget) {125 StringRef Name = "__funcref_call_table";126 auto *Sym = static_cast<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 = static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(Name));132 133 // Setting Weak ensure only one table is left after linking when multiple134 // modules define the table.135 Sym->setWeak(true);136 137 wasm::WasmLimits Limits = {0, 1, 1, 0};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->hasCallIndirectOverlong()))144 Sym->setOmitFromLinkingSection();145 return Sym;146}147 148// Find a catch instruction from an EH pad.149MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) {150 assert(EHPad->isEHPad());151 auto Pos = EHPad->begin();152 // Skip any label or debug instructions. Also skip 'end' marker instructions153 // 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 162unsigned 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 186bool 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 193bool WebAssembly::canLowerReturn(size_t ResultSize,194 const WebAssemblySubtarget *Subtarget) {195 return ResultSize <= 1 || canLowerMultivalueReturn(Subtarget);196}197