blob: b5d0d68514e8e1568b3c1a40349f0cce0fc9ac63 [file]
//===- 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