| //===- WebAssemblyInstructionSelector.cpp ------------------------*- C++ -*-==// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// This file implements the targeting of the InstructionSelector class for |
| /// WebAssembly. |
| /// \todo This should be generated by TableGen. |
| //===----------------------------------------------------------------------===// |
| |
| #include "GISel/WebAssemblyRegisterBankInfo.h" |
| #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
| #include "Utils/WasmAddressSpaces.h" |
| #include "Utils/WebAssemblyTypeUtilities.h" |
| #include "WebAssemblyRegisterInfo.h" |
| #include "WebAssemblySubtarget.h" |
| #include "WebAssemblyTargetMachine.h" |
| #include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h" |
| #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" |
| #include "llvm/CodeGen/GlobalISel/Utils.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include "llvm/CodeGen/TargetLowering.h" |
| #include "llvm/IR/IntrinsicsWebAssembly.h" |
| |
| #define DEBUG_TYPE "wasm-isel" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| #define GET_GLOBALISEL_PREDICATE_BITSET |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATE_BITSET |
| |
| class WebAssemblyInstructionSelector : public InstructionSelector { |
| public: |
| WebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM, |
| const WebAssemblySubtarget &STI, |
| const WebAssemblyRegisterBankInfo &RBI); |
| |
| bool select(MachineInstr &I) override; |
| |
| InstructionSelector::ComplexRendererFns |
| selectAddrOperands32(MachineOperand &Root) const; |
| InstructionSelector::ComplexRendererFns |
| selectAddrOperands64(MachineOperand &Root) const; |
| |
| static const char *getName() { return DEBUG_TYPE; } |
| |
| private: |
| bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; |
| bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const; |
| |
| InstructionSelector::ComplexRendererFns |
| selectAddrOperands(LLT AddrType, unsigned int ConstOpc, |
| MachineOperand &Root) const; |
| |
| const WebAssemblyTargetMachine &TM; |
| const WebAssemblySubtarget &STI; |
| const WebAssemblyInstrInfo &TII; |
| const WebAssemblyRegisterInfo &TRI; |
| const WebAssemblyRegisterBankInfo &RBI; |
| |
| #define GET_GLOBALISEL_PREDICATES_DECL |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATES_DECL |
| |
| #define GET_GLOBALISEL_TEMPORARIES_DECL |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_TEMPORARIES_DECL |
| }; |
| |
| } // end anonymous namespace |
| |
| #define GET_GLOBALISEL_IMPL |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_IMPL |
| |
| WebAssemblyInstructionSelector::WebAssemblyInstructionSelector( |
| const WebAssemblyTargetMachine &TM, const WebAssemblySubtarget &STI, |
| const WebAssemblyRegisterBankInfo &RBI) |
| : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()), |
| RBI(RBI), |
| |
| #define GET_GLOBALISEL_PREDICATES_INIT |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_PREDICATES_INIT |
| #define GET_GLOBALISEL_TEMPORARIES_INIT |
| #include "WebAssemblyGenGlobalISel.inc" |
| #undef GET_GLOBALISEL_TEMPORARIES_INIT |
| { |
| } |
| |
| InstructionSelector::ComplexRendererFns |
| WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType, |
| unsigned int ConstOpc, |
| MachineOperand &Root) const { |
| return {{ |
| [=](MachineInstrBuilder &MIB) { MIB.addImm(0); }, |
| [=](MachineInstrBuilder &MIB) { MIB.addReg(Root.getReg()); }, |
| }}; |
| } |
| |
| InstructionSelector::ComplexRendererFns |
| WebAssemblyInstructionSelector::selectAddrOperands32( |
| MachineOperand &Root) const { |
| return selectAddrOperands(LLT::integer(32), WebAssembly::CONST_I32, Root); |
| } |
| |
| InstructionSelector::ComplexRendererFns |
| WebAssemblyInstructionSelector::selectAddrOperands64( |
| MachineOperand &Root) const { |
| return selectAddrOperands(LLT::integer(64), WebAssembly::CONST_I64, Root); |
| } |
| |
| bool WebAssemblyInstructionSelector::selectCopy( |
| MachineInstr &I, MachineRegisterInfo &MRI) const { |
| const TargetRegisterClass *DstRC = |
| TRI.getConstrainedRegClassForOperand(I.getOperand(0), MRI); |
| if (!DstRC) |
| return false; |
| |
| const TargetRegisterClass *SrcRC = |
| TRI.getConstrainedRegClassForOperand(I.getOperand(1), MRI); |
| if (!SrcRC) |
| return false; |
| |
| Register DstReg = I.getOperand(0).getReg(); |
| Register SrcReg = I.getOperand(1).getReg(); |
| |
| if (DstReg.isVirtual()) |
| RBI.constrainGenericRegister(DstReg, *DstRC, MRI); |
| if (SrcReg.isVirtual()) |
| RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI); |
| |
| if (DstRC != SrcRC) { |
| if (DstReg.isPhysical() || SrcReg.isPhysical()) |
| llvm_unreachable("COPY to/from SP[32/64] or FP[32/64] with mismatching " |
| "classes not currently supported"); |
| |
| if (DstRC == &WebAssembly::I32RegClass && |
| SrcRC == &WebAssembly::F32RegClass) { |
| I.setDesc(TII.get(WebAssembly::I32_REINTERPRET_F32)); |
| return true; |
| } |
| if (DstRC == &WebAssembly::F32RegClass && |
| SrcRC == &WebAssembly::I32RegClass) { |
| I.setDesc(TII.get(WebAssembly::F32_REINTERPRET_I32)); |
| return true; |
| } |
| if (DstRC == &WebAssembly::I64RegClass && |
| SrcRC == &WebAssembly::F64RegClass) { |
| I.setDesc(TII.get(WebAssembly::I64_REINTERPRET_F64)); |
| return true; |
| } |
| if (DstRC == &WebAssembly::F64RegClass && |
| SrcRC == &WebAssembly::I64RegClass) { |
| I.setDesc(TII.get(WebAssembly::F64_REINTERPRET_I64)); |
| return true; |
| } |
| |
| llvm_unreachable("COPY between unsupported reg classes."); |
| } |
| |
| return true; |
| } |
| |
| bool WebAssemblyInstructionSelector::select(MachineInstr &I) { |
| MachineBasicBlock &MBB = *I.getParent(); |
| MachineFunction &MF = *MBB.getParent(); |
| MachineRegisterInfo &MRI = MF.getRegInfo(); |
| const TargetLowering &TLI = *STI.getTargetLowering(); |
| |
| if (!I.isPreISelOpcode()) { |
| if (I.isCopy()) |
| return selectCopy(I, MRI); |
| return true; |
| } |
| |
| if (selectImpl(I, *CoverageInfo)) |
| return true; |
| |
| using namespace TargetOpcode; |
| |
| switch (I.getOpcode()) { |
| case G_IMPLICIT_DEF: { |
| const Register DefReg = I.getOperand(0).getReg(); |
| |
| const TargetRegisterClass *DefRC = |
| TRI.getConstrainedRegClassForOperand(I.getOperand(0), MRI); |
| |
| if (!DefRC) |
| return false; |
| |
| I.setDesc(TII.get(TargetOpcode::IMPLICIT_DEF)); |
| return RBI.constrainGenericRegister(DefReg, *DefRC, MRI) != nullptr; |
| } |
| case G_PTRTOINT: { |
| bool PtrIsI64 = MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == 64; |
| |
| I.setDesc( |
| TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32)); |
| constrainSelectedInstRegOperands(I, TII, TRI, RBI); |
| return true; |
| } |
| case G_INTTOPTR: { |
| bool PtrIsI64 = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() == 64; |
| |
| I.setDesc( |
| TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32)); |
| constrainSelectedInstRegOperands(I, TII, TRI, RBI); |
| return true; |
| } |
| case G_PTRMASK: { |
| bool PtrIsI64 = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() == 64; |
| |
| I.setDesc(TII.get(PtrIsI64 ? WebAssembly::AND_I64 : WebAssembly::AND_I32)); |
| constrainSelectedInstRegOperands(I, TII, TRI, RBI); |
| return true; |
| } |
| case G_GLOBAL_VALUE: { |
| assert(I.getOperand(1).getTargetFlags() == 0 && |
| "Unexpected target flags on generic G_GLOBAL_VALUE instruction"); |
| |
| unsigned OperandFlags = 0; |
| const llvm::GlobalValue *GV = I.getOperand(1).getGlobal(); |
| LLT PtrTy = MRI.getType(I.getOperand(0).getReg()); |
| bool PtrIsI64 = PtrTy.getSizeInBits() == 64; |
| |
| if (TLI.isPositionIndependent()) { |
| if (TM.shouldAssumeDSOLocal(GV)) { |
| const char *BaseName; |
| if (GV->getValueType()->isFunctionTy()) { |
| BaseName = MF.createExternalSymbolName("__table_base"); |
| OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL; |
| } else { |
| BaseName = MF.createExternalSymbolName("__memory_base"); |
| OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL; |
| } |
| MachineIRBuilder B(I); |
| |
| Register MemBase = MRI.createVirtualRegister( |
| PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass); |
| |
| Register Offset = MRI.createVirtualRegister( |
| PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass); |
| |
| B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 |
| : WebAssembly::GLOBAL_GET_I32) |
| .addDef(MemBase) |
| .addExternalSymbol(BaseName); |
| |
| B.buildInstr(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32) |
| .addDef(Offset) |
| .addGlobalAddress(GV, I.getOperand(1).getOffset(), OperandFlags); |
| |
| auto MIB = |
| B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32) |
| .addDef(I.getOperand(0).getReg()) |
| .addReg(MemBase) |
| .addReg(Offset); |
| constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI); |
| |
| I.eraseFromParent(); |
| return true; |
| } |
| OperandFlags = WebAssemblyII::MO_GOT; |
| } |
| |
| unsigned NewOpc = |
| PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; |
| |
| if (OperandFlags & WebAssemblyII::MO_GOT) { |
| NewOpc = |
| PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 : WebAssembly::GLOBAL_GET_I32; |
| } |
| |
| I.setDesc(TII.get(NewOpc)); |
| I.getOperand(1).setTargetFlags(OperandFlags); |
| constrainSelectedInstRegOperands(I, TII, TRI, RBI); |
| |
| return true; |
| } |
| default: |
| break; |
| } |
| |
| return false; |
| } |
| |
| namespace llvm { |
| InstructionSelector * |
| createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM, |
| const WebAssemblySubtarget &Subtarget, |
| const WebAssemblyRegisterBankInfo &RBI) { |
| return new WebAssemblyInstructionSelector(TM, Subtarget, RBI); |
| } |
| } // namespace llvm |