Path: blob/main/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
35269 views
//- WebAssemblyISelDAGToDAG.cpp - A dag to dag inst selector for WebAssembly -//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file defines an instruction selector for the WebAssembly target.10///11//===----------------------------------------------------------------------===//1213#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"14#include "WebAssembly.h"15#include "WebAssemblyISelLowering.h"16#include "WebAssemblyTargetMachine.h"17#include "llvm/CodeGen/MachineFrameInfo.h"18#include "llvm/CodeGen/SelectionDAGISel.h"19#include "llvm/CodeGen/WasmEHFuncInfo.h"20#include "llvm/IR/DiagnosticInfo.h"21#include "llvm/IR/Function.h" // To access function attributes.22#include "llvm/IR/IntrinsicsWebAssembly.h"23#include "llvm/Support/Debug.h"24#include "llvm/Support/KnownBits.h"25#include "llvm/Support/MathExtras.h"26#include "llvm/Support/raw_ostream.h"2728using namespace llvm;2930#define DEBUG_TYPE "wasm-isel"31#define PASS_NAME "WebAssembly Instruction Selection"3233//===--------------------------------------------------------------------===//34/// WebAssembly-specific code to select WebAssembly machine instructions for35/// SelectionDAG operations.36///37namespace {38class WebAssemblyDAGToDAGISel final : public SelectionDAGISel {39/// Keep a pointer to the WebAssemblySubtarget around so that we can make the40/// right decision when generating code for different targets.41const WebAssemblySubtarget *Subtarget;4243public:44WebAssemblyDAGToDAGISel() = delete;4546WebAssemblyDAGToDAGISel(WebAssemblyTargetMachine &TM,47CodeGenOptLevel OptLevel)48: SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {}4950bool runOnMachineFunction(MachineFunction &MF) override {51LLVM_DEBUG(dbgs() << "********** ISelDAGToDAG **********\n"52"********** Function: "53<< MF.getName() << '\n');5455Subtarget = &MF.getSubtarget<WebAssemblySubtarget>();5657return SelectionDAGISel::runOnMachineFunction(MF);58}5960void PreprocessISelDAG() override;6162void Select(SDNode *Node) override;6364bool SelectInlineAsmMemoryOperand(const SDValue &Op,65InlineAsm::ConstraintCode ConstraintID,66std::vector<SDValue> &OutOps) override;6768bool SelectAddrOperands32(SDValue Op, SDValue &Offset, SDValue &Addr);69bool SelectAddrOperands64(SDValue Op, SDValue &Offset, SDValue &Addr);7071// Include the pieces autogenerated from the target description.72#include "WebAssemblyGenDAGISel.inc"7374private:75// add select functions here...7677bool SelectAddrOperands(MVT AddrType, unsigned ConstOpc, SDValue Op,78SDValue &Offset, SDValue &Addr);79bool SelectAddrAddOperands(MVT OffsetType, SDValue N, SDValue &Offset,80SDValue &Addr);81};8283class WebAssemblyDAGToDAGISelLegacy : public SelectionDAGISelLegacy {84public:85static char ID;86explicit WebAssemblyDAGToDAGISelLegacy(WebAssemblyTargetMachine &TM,87CodeGenOptLevel OptLevel)88: SelectionDAGISelLegacy(89ID, std::make_unique<WebAssemblyDAGToDAGISel>(TM, OptLevel)) {}90};91} // end anonymous namespace9293char WebAssemblyDAGToDAGISelLegacy::ID;9495INITIALIZE_PASS(WebAssemblyDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false,96false)9798void WebAssemblyDAGToDAGISel::PreprocessISelDAG() {99// Stack objects that should be allocated to locals are hoisted to WebAssembly100// locals when they are first used. However for those without uses, we hoist101// them here. It would be nice if there were some hook to do this when they102// are added to the MachineFrameInfo, but that's not the case right now.103MachineFrameInfo &FrameInfo = MF->getFrameInfo();104for (int Idx = 0; Idx < FrameInfo.getObjectIndexEnd(); Idx++)105WebAssemblyFrameLowering::getLocalForStackObject(*MF, Idx);106107SelectionDAGISel::PreprocessISelDAG();108}109110static SDValue getTagSymNode(int Tag, SelectionDAG *DAG) {111assert(Tag == WebAssembly::CPP_EXCEPTION || WebAssembly::C_LONGJMP);112auto &MF = DAG->getMachineFunction();113const auto &TLI = DAG->getTargetLoweringInfo();114MVT PtrVT = TLI.getPointerTy(DAG->getDataLayout());115const char *SymName = Tag == WebAssembly::CPP_EXCEPTION116? MF.createExternalSymbolName("__cpp_exception")117: MF.createExternalSymbolName("__c_longjmp");118return DAG->getTargetExternalSymbol(SymName, PtrVT);119}120121void WebAssemblyDAGToDAGISel::Select(SDNode *Node) {122// If we have a custom node, we already have selected!123if (Node->isMachineOpcode()) {124LLVM_DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n");125Node->setNodeId(-1);126return;127}128129MVT PtrVT = TLI->getPointerTy(CurDAG->getDataLayout());130auto GlobalGetIns = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64131: WebAssembly::GLOBAL_GET_I32;132133// Few custom selection stuff.134SDLoc DL(Node);135MachineFunction &MF = CurDAG->getMachineFunction();136switch (Node->getOpcode()) {137case ISD::ATOMIC_FENCE: {138if (!MF.getSubtarget<WebAssemblySubtarget>().hasAtomics())139break;140141uint64_t SyncScopeID = Node->getConstantOperandVal(2);142MachineSDNode *Fence = nullptr;143switch (SyncScopeID) {144case SyncScope::SingleThread:145// We lower a single-thread fence to a pseudo compiler barrier instruction146// preventing instruction reordering. This will not be emitted in final147// binary.148Fence = CurDAG->getMachineNode(WebAssembly::COMPILER_FENCE,149DL, // debug loc150MVT::Other, // outchain type151Node->getOperand(0) // inchain152);153break;154case SyncScope::System:155// Currently wasm only supports sequentially consistent atomics, so we156// always set the order to 0 (sequentially consistent).157Fence = CurDAG->getMachineNode(158WebAssembly::ATOMIC_FENCE,159DL, // debug loc160MVT::Other, // outchain type161CurDAG->getTargetConstant(0, DL, MVT::i32), // order162Node->getOperand(0) // inchain163);164break;165default:166llvm_unreachable("Unknown scope!");167}168169ReplaceNode(Node, Fence);170CurDAG->RemoveDeadNode(Node);171return;172}173174case ISD::INTRINSIC_WO_CHAIN: {175unsigned IntNo = Node->getConstantOperandVal(0);176switch (IntNo) {177case Intrinsic::wasm_tls_size: {178MachineSDNode *TLSSize = CurDAG->getMachineNode(179GlobalGetIns, DL, PtrVT,180CurDAG->getTargetExternalSymbol("__tls_size", PtrVT));181ReplaceNode(Node, TLSSize);182return;183}184185case Intrinsic::wasm_tls_align: {186MachineSDNode *TLSAlign = CurDAG->getMachineNode(187GlobalGetIns, DL, PtrVT,188CurDAG->getTargetExternalSymbol("__tls_align", PtrVT));189ReplaceNode(Node, TLSAlign);190return;191}192}193break;194}195196case ISD::INTRINSIC_W_CHAIN: {197unsigned IntNo = Node->getConstantOperandVal(1);198const auto &TLI = CurDAG->getTargetLoweringInfo();199MVT PtrVT = TLI.getPointerTy(CurDAG->getDataLayout());200switch (IntNo) {201case Intrinsic::wasm_tls_base: {202MachineSDNode *TLSBase = CurDAG->getMachineNode(203GlobalGetIns, DL, PtrVT, MVT::Other,204CurDAG->getTargetExternalSymbol("__tls_base", PtrVT),205Node->getOperand(0));206ReplaceNode(Node, TLSBase);207return;208}209210case Intrinsic::wasm_catch: {211int Tag = Node->getConstantOperandVal(2);212SDValue SymNode = getTagSymNode(Tag, CurDAG);213MachineSDNode *Catch =214CurDAG->getMachineNode(WebAssembly::CATCH, DL,215{216PtrVT, // exception pointer217MVT::Other // outchain type218},219{220SymNode, // exception symbol221Node->getOperand(0) // inchain222});223ReplaceNode(Node, Catch);224return;225}226}227break;228}229230case ISD::INTRINSIC_VOID: {231unsigned IntNo = Node->getConstantOperandVal(1);232switch (IntNo) {233case Intrinsic::wasm_throw: {234int Tag = Node->getConstantOperandVal(2);235SDValue SymNode = getTagSymNode(Tag, CurDAG);236MachineSDNode *Throw =237CurDAG->getMachineNode(WebAssembly::THROW, DL,238MVT::Other, // outchain type239{240SymNode, // exception symbol241Node->getOperand(3), // thrown value242Node->getOperand(0) // inchain243});244ReplaceNode(Node, Throw);245return;246}247case Intrinsic::wasm_rethrow: {248// RETHROW's BB argument will be populated in LateEHPrepare. Just use a249// '0' as a placeholder for now.250MachineSDNode *Rethrow = CurDAG->getMachineNode(251WebAssembly::RETHROW, DL,252MVT::Other, // outchain type253{254CurDAG->getConstant(0, DL, MVT::i32), // placeholder255Node->getOperand(0) // inchain256});257ReplaceNode(Node, Rethrow);258return;259}260}261break;262}263264case WebAssemblyISD::CALL:265case WebAssemblyISD::RET_CALL: {266// CALL has both variable operands and variable results, but ISel only267// supports one or the other. Split calls into two nodes glued together, one268// for the operands and one for the results. These two nodes will be269// recombined in a custom inserter hook into a single MachineInstr.270SmallVector<SDValue, 16> Ops;271for (size_t i = 1; i < Node->getNumOperands(); ++i) {272SDValue Op = Node->getOperand(i);273// Remove the wrapper when the call target is a function, an external274// symbol (which will be lowered to a library function), or an alias of275// a function. If the target is not a function/external symbol, we276// shouldn't remove the wrapper, because we cannot call it directly and277// instead we want it to be loaded with a CONST instruction and called278// with a call_indirect later.279if (i == 1 && Op->getOpcode() == WebAssemblyISD::Wrapper) {280SDValue NewOp = Op->getOperand(0);281if (auto *GlobalOp = dyn_cast<GlobalAddressSDNode>(NewOp.getNode())) {282if (isa<Function>(283GlobalOp->getGlobal()->stripPointerCastsAndAliases()))284Op = NewOp;285} else if (isa<ExternalSymbolSDNode>(NewOp.getNode())) {286Op = NewOp;287}288}289Ops.push_back(Op);290}291292// Add the chain last293Ops.push_back(Node->getOperand(0));294MachineSDNode *CallParams =295CurDAG->getMachineNode(WebAssembly::CALL_PARAMS, DL, MVT::Glue, Ops);296297unsigned Results = Node->getOpcode() == WebAssemblyISD::CALL298? WebAssembly::CALL_RESULTS299: WebAssembly::RET_CALL_RESULTS;300301SDValue Link(CallParams, 0);302MachineSDNode *CallResults =303CurDAG->getMachineNode(Results, DL, Node->getVTList(), Link);304ReplaceNode(Node, CallResults);305return;306}307308default:309break;310}311312// Select the default instruction.313SelectCode(Node);314}315316bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand(317const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,318std::vector<SDValue> &OutOps) {319switch (ConstraintID) {320case InlineAsm::ConstraintCode::m:321// We just support simple memory operands that just have a single address322// operand and need no special handling.323OutOps.push_back(Op);324return false;325default:326break;327}328329return true;330}331332bool WebAssemblyDAGToDAGISel::SelectAddrAddOperands(MVT OffsetType, SDValue N,333SDValue &Offset,334SDValue &Addr) {335assert(N.getNumOperands() == 2 && "Attempting to fold in a non-binary op");336337// WebAssembly constant offsets are performed as unsigned with infinite338// precision, so we need to check for NoUnsignedWrap so that we don't fold an339// offset for an add that needs wrapping.340if (N.getOpcode() == ISD::ADD && !N.getNode()->getFlags().hasNoUnsignedWrap())341return false;342343// Folds constants in an add into the offset.344for (size_t i = 0; i < 2; ++i) {345SDValue Op = N.getOperand(i);346SDValue OtherOp = N.getOperand(i == 0 ? 1 : 0);347348if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op)) {349Offset =350CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(N), OffsetType);351Addr = OtherOp;352return true;353}354}355return false;356}357358bool WebAssemblyDAGToDAGISel::SelectAddrOperands(MVT AddrType,359unsigned ConstOpc, SDValue N,360SDValue &Offset,361SDValue &Addr) {362SDLoc DL(N);363364// Fold target global addresses into the offset.365if (!TM.isPositionIndependent()) {366SDValue Op(N);367if (Op.getOpcode() == WebAssemblyISD::Wrapper)368Op = Op.getOperand(0);369370if (Op.getOpcode() == ISD::TargetGlobalAddress) {371Offset = Op;372Addr = SDValue(373CurDAG->getMachineNode(ConstOpc, DL, AddrType,374CurDAG->getTargetConstant(0, DL, AddrType)),3750);376return true;377}378}379380// Fold anything inside an add into the offset.381if (N.getOpcode() == ISD::ADD &&382SelectAddrAddOperands(AddrType, N, Offset, Addr))383return true;384385// Likewise, treat an 'or' node as an 'add' if the or'ed bits are known to be386// zero and fold them into the offset too.387if (N.getOpcode() == ISD::OR) {388bool OrIsAdd;389if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N.getOperand(1))) {390OrIsAdd =391CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue());392} else {393KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0);394KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0);395OrIsAdd = (~Known0.Zero & ~Known1.Zero) == 0;396}397398if (OrIsAdd && SelectAddrAddOperands(AddrType, N, Offset, Addr))399return true;400}401402// Fold constant addresses into the offset.403if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(N)) {404Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, AddrType);405Addr = SDValue(406CurDAG->getMachineNode(ConstOpc, DL, AddrType,407CurDAG->getTargetConstant(0, DL, AddrType)),4080);409return true;410}411412// Else it's a plain old load/store with no offset.413Offset = CurDAG->getTargetConstant(0, DL, AddrType);414Addr = N;415return true;416}417418bool WebAssemblyDAGToDAGISel::SelectAddrOperands32(SDValue Op, SDValue &Offset,419SDValue &Addr) {420return SelectAddrOperands(MVT::i32, WebAssembly::CONST_I32, Op, Offset, Addr);421}422423bool WebAssemblyDAGToDAGISel::SelectAddrOperands64(SDValue Op, SDValue &Offset,424SDValue &Addr) {425return SelectAddrOperands(MVT::i64, WebAssembly::CONST_I64, Op, Offset, Addr);426}427428/// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready429/// for instruction scheduling.430FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM,431CodeGenOptLevel OptLevel) {432return new WebAssemblyDAGToDAGISelLegacy(TM, OptLevel);433}434435436