1//===- WebAssemblyInstructionSelector.cpp ------------------------*- C++ -*-==//
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-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file
9/// This file implements the targeting of the InstructionSelector class for
10/// WebAssembly.
11/// \todo This should be generated by TableGen.
12//===----------------------------------------------------------------------===//
13
14#include "GISel/WebAssemblyRegisterBankInfo.h"
15#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16#include "WebAssemblyRegisterInfo.h"
17#include "WebAssemblySubtarget.h"
18#include "WebAssemblyTargetMachine.h"
19#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
20#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
21#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
22#include "llvm/CodeGen/GlobalISel/Utils.h"
23#include "llvm/CodeGen/MachineOperand.h"
24#include "llvm/CodeGen/TargetLowering.h"
25#include "llvm/IR/IntrinsicsWebAssembly.h"
26
27#define DEBUG_TYPE "wasm-isel"
28
29using namespace llvm;
30
31namespace {
32
33#define GET_GLOBALISEL_PREDICATE_BITSET
34#include "WebAssemblyGenGlobalISel.inc"
35#undef GET_GLOBALISEL_PREDICATE_BITSET
36
37class WebAssemblyInstructionSelector : public InstructionSelector {
38public:
39 WebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM,
40 const WebAssemblySubtarget &STI,
41 const WebAssemblyRegisterBankInfo &RBI);
42
43 bool select(MachineInstr &I) override;
44
45 InstructionSelector::ComplexRendererFns
46 selectAddrOperands32(MachineOperand &Root) const;
47 InstructionSelector::ComplexRendererFns
48 selectAddrOperands64(MachineOperand &Root) const;
49
50 static const char *getName() { return DEBUG_TYPE; }
51
52private:
53 bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
54 bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
55
56 InstructionSelector::ComplexRendererFns
57 selectAddrOperands(LLT AddrType, unsigned int ConstOpc,
58 MachineOperand &Root) const;
59
60 const WebAssemblyTargetMachine &TM;
61 const WebAssemblySubtarget &STI;
62 const WebAssemblyInstrInfo &TII;
63 const WebAssemblyRegisterInfo &TRI;
64 const WebAssemblyRegisterBankInfo &RBI;
65
66#define GET_GLOBALISEL_PREDICATES_DECL
67#include "WebAssemblyGenGlobalISel.inc"
68#undef GET_GLOBALISEL_PREDICATES_DECL
69
70#define GET_GLOBALISEL_TEMPORARIES_DECL
71#include "WebAssemblyGenGlobalISel.inc"
72#undef GET_GLOBALISEL_TEMPORARIES_DECL
73};
74
75} // end anonymous namespace
76
77#define GET_GLOBALISEL_IMPL
78#include "WebAssemblyGenGlobalISel.inc"
79#undef GET_GLOBALISEL_IMPL
80
81WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
82 const WebAssemblyTargetMachine &TM, const WebAssemblySubtarget &STI,
83 const WebAssemblyRegisterBankInfo &RBI)
84 : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()),
85 RBI(RBI),
86
87#define GET_GLOBALISEL_PREDICATES_INIT
88#include "WebAssemblyGenGlobalISel.inc"
89#undef GET_GLOBALISEL_PREDICATES_INIT
90#define GET_GLOBALISEL_TEMPORARIES_INIT
91#include "WebAssemblyGenGlobalISel.inc"
92#undef GET_GLOBALISEL_TEMPORARIES_INIT
93{
94}
95
96InstructionSelector::ComplexRendererFns
97WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
98 unsigned int ConstOpc,
99 MachineOperand &Root) const {
100 return {{
101 [=](MachineInstrBuilder &MIB) { MIB.addImm(Val: 0); },
102 [=](MachineInstrBuilder &MIB) { MIB.addReg(RegNo: Root.getReg()); },
103 }};
104}
105
106InstructionSelector::ComplexRendererFns
107WebAssemblyInstructionSelector::selectAddrOperands32(
108 MachineOperand &Root) const {
109 return selectAddrOperands(AddrType: LLT::integer(SizeInBits: 32), ConstOpc: WebAssembly::CONST_I32, Root);
110}
111
112InstructionSelector::ComplexRendererFns
113WebAssemblyInstructionSelector::selectAddrOperands64(
114 MachineOperand &Root) const {
115 return selectAddrOperands(AddrType: LLT::integer(SizeInBits: 64), ConstOpc: WebAssembly::CONST_I64, Root);
116}
117
118bool WebAssemblyInstructionSelector::selectCopy(
119 MachineInstr &I, MachineRegisterInfo &MRI) const {
120 Register DstReg = I.getOperand(i: 0).getReg();
121 Register SrcReg = I.getOperand(i: 1).getReg();
122
123 const TargetRegisterClass *DstRC =
124 TRI.getConstrainedRegClassForReg(Reg: DstReg, MRI);
125 if (!DstRC)
126 return false;
127
128 const TargetRegisterClass *SrcRC =
129 TRI.getConstrainedRegClassForReg(Reg: SrcReg, MRI);
130 if (!SrcRC)
131 return false;
132
133 if (DstReg.isVirtual())
134 RBI.constrainGenericRegister(Reg: DstReg, RC: *DstRC, MRI);
135 if (SrcReg.isVirtual())
136 RBI.constrainGenericRegister(Reg: SrcReg, RC: *SrcRC, MRI);
137
138 if (DstRC != SrcRC) {
139 if (DstReg.isPhysical() || SrcReg.isPhysical())
140 llvm_unreachable("COPY to/from SP[32/64] or FP[32/64] with mismatching "
141 "classes not currently supported");
142
143 if (DstRC == &WebAssembly::I32RegClass &&
144 SrcRC == &WebAssembly::F32RegClass) {
145 I.setDesc(TII.get(Opcode: WebAssembly::I32_REINTERPRET_F32));
146 return true;
147 }
148 if (DstRC == &WebAssembly::F32RegClass &&
149 SrcRC == &WebAssembly::I32RegClass) {
150 I.setDesc(TII.get(Opcode: WebAssembly::F32_REINTERPRET_I32));
151 return true;
152 }
153 if (DstRC == &WebAssembly::I64RegClass &&
154 SrcRC == &WebAssembly::F64RegClass) {
155 I.setDesc(TII.get(Opcode: WebAssembly::I64_REINTERPRET_F64));
156 return true;
157 }
158 if (DstRC == &WebAssembly::F64RegClass &&
159 SrcRC == &WebAssembly::I64RegClass) {
160 I.setDesc(TII.get(Opcode: WebAssembly::F64_REINTERPRET_I64));
161 return true;
162 }
163
164 llvm_unreachable("COPY between unsupported reg classes.");
165 }
166
167 return true;
168}
169
170bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
171 MachineBasicBlock &MBB = *I.getParent();
172 MachineFunction &MF = *MBB.getParent();
173 MachineRegisterInfo &MRI = MF.getRegInfo();
174 const TargetLowering &TLI = *STI.getTargetLowering();
175
176 if (!I.isPreISelOpcode()) {
177 if (I.isCopy())
178 return selectCopy(I, MRI);
179 return true;
180 }
181
182 if (selectImpl(I, CoverageInfo&: *CoverageInfo))
183 return true;
184
185 using namespace TargetOpcode;
186
187 switch (I.getOpcode()) {
188 case G_IMPLICIT_DEF: {
189 const Register DefReg = I.getOperand(i: 0).getReg();
190
191 const TargetRegisterClass *DefRC =
192 TRI.getConstrainedRegClassForReg(Reg: DefReg, MRI);
193
194 if (!DefRC)
195 return false;
196
197 I.setDesc(TII.get(Opcode: TargetOpcode::IMPLICIT_DEF));
198 return RBI.constrainGenericRegister(Reg: DefReg, RC: *DefRC, MRI) != nullptr;
199 }
200 case G_PTRTOINT: {
201 bool PtrIsI64 = MRI.getType(Reg: I.getOperand(i: 1).getReg()).getSizeInBits() == 64;
202
203 I.setDesc(
204 TII.get(Opcode: PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
205 constrainSelectedInstRegOperands(I, TII, TRI, RBI);
206 return true;
207 }
208 case G_INTTOPTR: {
209 bool PtrIsI64 = MRI.getType(Reg: I.getOperand(i: 0).getReg()).getSizeInBits() == 64;
210
211 I.setDesc(
212 TII.get(Opcode: PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
213 constrainSelectedInstRegOperands(I, TII, TRI, RBI);
214 return true;
215 }
216 case G_PTRMASK: {
217 bool PtrIsI64 = MRI.getType(Reg: I.getOperand(i: 0).getReg()).getSizeInBits() == 64;
218
219 I.setDesc(TII.get(Opcode: PtrIsI64 ? WebAssembly::AND_I64 : WebAssembly::AND_I32));
220 constrainSelectedInstRegOperands(I, TII, TRI, RBI);
221 return true;
222 }
223 case G_GLOBAL_VALUE: {
224 assert(I.getOperand(1).getTargetFlags() == 0 &&
225 "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
226
227 unsigned OperandFlags = 0;
228 const llvm::GlobalValue *GV = I.getOperand(i: 1).getGlobal();
229 LLT PtrTy = MRI.getType(Reg: I.getOperand(i: 0).getReg());
230 bool PtrIsI64 = PtrTy.getSizeInBits() == 64;
231
232 if (TLI.isPositionIndependent()) {
233 if (TM.shouldAssumeDSOLocal(GV)) {
234 const char *BaseName;
235 if (GV->getValueType()->isFunctionTy()) {
236 BaseName = MF.createExternalSymbolName(Name: "__table_base");
237 OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL;
238 } else {
239 BaseName = MF.createExternalSymbolName(Name: "__memory_base");
240 OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL;
241 }
242 MachineIRBuilder B(I);
243
244 Register MemBase = MRI.createVirtualRegister(
245 RegClass: PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
246
247 Register Offset = MRI.createVirtualRegister(
248 RegClass: PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
249
250 B.buildInstr(Opcode: PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
251 : WebAssembly::GLOBAL_GET_I32)
252 .addDef(RegNo: MemBase)
253 .addExternalSymbol(FnName: BaseName);
254
255 B.buildInstr(Opcode: PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32)
256 .addDef(RegNo: Offset)
257 .addGlobalAddress(GV, Offset: I.getOperand(i: 1).getOffset(), TargetFlags: OperandFlags);
258
259 auto MIB =
260 B.buildInstr(Opcode: PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
261 .addDef(RegNo: I.getOperand(i: 0).getReg())
262 .addReg(RegNo: MemBase)
263 .addReg(RegNo: Offset);
264 constrainSelectedInstRegOperands(I&: *MIB, TII, TRI, RBI);
265
266 I.eraseFromParent();
267 return true;
268 }
269 OperandFlags = WebAssemblyII::MO_GOT;
270 }
271
272 unsigned NewOpc =
273 PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
274
275 if (OperandFlags & WebAssemblyII::MO_GOT) {
276 NewOpc =
277 PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 : WebAssembly::GLOBAL_GET_I32;
278 }
279
280 I.setDesc(TII.get(Opcode: NewOpc));
281 I.getOperand(i: 1).setTargetFlags(OperandFlags);
282 constrainSelectedInstRegOperands(I, TII, TRI, RBI);
283
284 return true;
285 }
286 default:
287 break;
288 }
289
290 return false;
291}
292
293namespace llvm {
294InstructionSelector *
295createWebAssemblyInstructionSelector(const WebAssemblyTargetMachine &TM,
296 const WebAssemblySubtarget &Subtarget,
297 const WebAssemblyRegisterBankInfo &RBI) {
298 return new WebAssemblyInstructionSelector(TM, Subtarget, RBI);
299}
300} // namespace llvm
301