1//===-- WebAssemblyFastISel.cpp - WebAssembly FastISel implementation -----===//
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///
9/// \file
10/// This file defines the WebAssembly-specific support for the FastISel
11/// class. Some of the target-specific code is generated by tablegen in the file
12/// WebAssemblyGenFastISel.inc, which is #included here.
13///
14/// TODO: kill flags
15///
16//===----------------------------------------------------------------------===//
17
18#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19#include "Utils/WasmAddressSpaces.h"
20#include "WebAssemblyMachineFunctionInfo.h"
21#include "WebAssemblySubtarget.h"
22#include "WebAssemblyUtilities.h"
23#include "llvm/Analysis/BranchProbabilityInfo.h"
24#include "llvm/CodeGen/FastISel.h"
25#include "llvm/CodeGen/FunctionLoweringInfo.h"
26#include "llvm/CodeGen/MachineConstantPool.h"
27#include "llvm/CodeGen/MachineFrameInfo.h"
28#include "llvm/CodeGen/MachineInstrBuilder.h"
29#include "llvm/CodeGen/MachineModuleInfo.h"
30#include "llvm/CodeGen/MachineRegisterInfo.h"
31#include "llvm/IR/DataLayout.h"
32#include "llvm/IR/DerivedTypes.h"
33#include "llvm/IR/Function.h"
34#include "llvm/IR/GetElementPtrTypeIterator.h"
35#include "llvm/IR/GlobalVariable.h"
36#include "llvm/IR/Instructions.h"
37#include "llvm/IR/IntrinsicsWebAssembly.h"
38#include "llvm/IR/Operator.h"
39
40using namespace llvm;
41
42#define DEBUG_TYPE "wasm-fastisel"
43
44namespace {
45
46class WebAssemblyFastISel final : public FastISel {
47 // All possible address modes.
48 class Address {
49 public:
50 enum BaseKind { RegBase, FrameIndexBase };
51
52 private:
53 BaseKind Kind = RegBase;
54 union {
55 unsigned Reg;
56 int FI;
57 } Base;
58
59 // Whether the base has been determined yet
60 bool IsBaseSet = false;
61
62 int64_t Offset = 0;
63
64 const GlobalValue *GV = nullptr;
65
66 public:
67 // Innocuous defaults for our address.
68 Address() { Base.Reg = 0; }
69 void setKind(BaseKind K) {
70 assert(!isSet() && "Can't change kind with non-zero base");
71 Kind = K;
72 }
73 BaseKind getKind() const { return Kind; }
74 bool isRegBase() const { return Kind == RegBase; }
75 bool isFIBase() const { return Kind == FrameIndexBase; }
76 void setReg(unsigned Reg) {
77 assert(isRegBase() && "Invalid base register access!");
78 assert(!IsBaseSet && "Base cannot be reset");
79 Base.Reg = Reg;
80 IsBaseSet = true;
81 }
82 unsigned getReg() const {
83 assert(isRegBase() && "Invalid base register access!");
84 return Base.Reg;
85 }
86 void setFI(unsigned FI) {
87 assert(isFIBase() && "Invalid base frame index access!");
88 assert(!IsBaseSet && "Base cannot be reset");
89 Base.FI = FI;
90 IsBaseSet = true;
91 }
92 unsigned getFI() const {
93 assert(isFIBase() && "Invalid base frame index access!");
94 return Base.FI;
95 }
96
97 void setOffset(int64_t NewOffset) {
98 assert(NewOffset >= 0 && "Offsets must be non-negative");
99 Offset = NewOffset;
100 }
101 int64_t getOffset() const { return Offset; }
102 void setGlobalValue(const GlobalValue *G) { GV = G; }
103 const GlobalValue *getGlobalValue() const { return GV; }
104 bool isSet() const { return IsBaseSet; }
105 };
106
107 /// Keep a pointer to the WebAssemblySubtarget around so that we can make the
108 /// right decision when generating code for different targets.
109 const WebAssemblySubtarget *Subtarget;
110 LLVMContext *Context;
111
112private:
113 // Utility helper routines
114 MVT::SimpleValueType getSimpleType(Type *Ty) {
115 EVT VT = TLI.getValueType(DL, Ty, /*AllowUnknown=*/true);
116 return VT.isSimple() ? VT.getSimpleVT().SimpleTy
117 : MVT::INVALID_SIMPLE_VALUE_TYPE;
118 }
119 MVT::SimpleValueType getLegalType(MVT::SimpleValueType VT) {
120 switch (VT) {
121 case MVT::i1:
122 case MVT::i8:
123 case MVT::i16:
124 return MVT::i32;
125 case MVT::i32:
126 case MVT::i64:
127 case MVT::f32:
128 case MVT::f64:
129 return VT;
130 case MVT::funcref:
131 case MVT::externref:
132 if (Subtarget->hasReferenceTypes())
133 return VT;
134 break;
135 case MVT::exnref:
136 if (Subtarget->hasReferenceTypes() && Subtarget->hasExceptionHandling())
137 return VT;
138 break;
139 case MVT::f16:
140 return MVT::f32;
141 case MVT::v16i8:
142 case MVT::v8i16:
143 case MVT::v4i32:
144 case MVT::v4f32:
145 case MVT::v2i64:
146 case MVT::v2f64:
147 if (Subtarget->hasSIMD128())
148 return VT;
149 break;
150 default:
151 break;
152 }
153 return MVT::INVALID_SIMPLE_VALUE_TYPE;
154 }
155 bool computeAddress(const Value *Obj, Address &Addr);
156 void materializeLoadStoreOperands(Address &Addr);
157 void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB,
158 MachineMemOperand *MMO);
159 bool emitLoad(Register ResultReg, unsigned Opc, const LoadInst *LoadInst);
160 unsigned maskI1Value(unsigned Reg, const Value *V);
161 unsigned getRegForI1Value(const Value *V, const BasicBlock *BB, bool &Not);
162 unsigned zeroExtendToI32(unsigned Reg, const Value *V,
163 MVT::SimpleValueType From);
164 unsigned signExtendToI32(unsigned Reg, const Value *V,
165 MVT::SimpleValueType From);
166 unsigned zeroExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From,
167 MVT::SimpleValueType To);
168 unsigned signExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From,
169 MVT::SimpleValueType To);
170 unsigned getRegForUnsignedValue(const Value *V);
171 unsigned getRegForSignedValue(const Value *V);
172 unsigned getRegForPromotedValue(const Value *V, bool IsSigned);
173 unsigned notValue(unsigned Reg);
174 unsigned copyValue(unsigned Reg);
175
176 // Backend specific FastISel code.
177 Register fastMaterializeAlloca(const AllocaInst *AI) override;
178 Register fastMaterializeConstant(const Constant *C) override;
179 bool fastLowerArguments() override;
180
181 // Selection routines.
182 bool selectCall(const Instruction *I);
183 bool selectSelect(const Instruction *I);
184 bool selectTrunc(const Instruction *I);
185 bool selectZExt(const Instruction *I);
186 bool selectSExt(const Instruction *I);
187 bool selectICmp(const Instruction *I);
188 bool selectFCmp(const Instruction *I);
189 bool selectBitCast(const Instruction *I);
190 bool selectLoad(const Instruction *I);
191 bool selectStore(const Instruction *I);
192 bool selectCondBr(const Instruction *I);
193 bool selectRet(const Instruction *I);
194 bool selectUnreachable(const Instruction *I);
195
196public:
197 // Backend specific FastISel code.
198 WebAssemblyFastISel(FunctionLoweringInfo &FuncInfo,
199 const TargetLibraryInfo *LibInfo,
200 const LibcallLoweringInfo *LibcallLowering)
201 : FastISel(FuncInfo, LibInfo, LibcallLowering,
202 /*SkipTargetIndependentISel=*/true) {
203 Subtarget = &FuncInfo.MF->getSubtarget<WebAssemblySubtarget>();
204 Context = &FuncInfo.Fn->getContext();
205 }
206
207 bool fastSelectInstruction(const Instruction *I) override;
208 bool tryToFoldLoadIntoMI(MachineInstr *MI, unsigned OpNo,
209 const LoadInst *LI) override;
210
211#include "WebAssemblyGenFastISel.inc"
212};
213
214} // end anonymous namespace
215
216bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) {
217 const User *U = nullptr;
218 unsigned Opcode = Instruction::UserOp1;
219 if (const auto *I = dyn_cast<Instruction>(Val: Obj)) {
220 // Don't walk into other basic blocks unless the object is an alloca from
221 // another block, otherwise it may not have a virtual register assigned.
222 if (FuncInfo.StaticAllocaMap.count(Val: static_cast<const AllocaInst *>(Obj)) ||
223 FuncInfo.getMBB(BB: I->getParent()) == FuncInfo.MBB) {
224 Opcode = I->getOpcode();
225 U = I;
226 }
227 } else if (const auto *C = dyn_cast<ConstantExpr>(Val: Obj)) {
228 Opcode = C->getOpcode();
229 U = C;
230 }
231
232 if (auto *Ty = dyn_cast<PointerType>(Val: Obj->getType()))
233 if (Ty->getAddressSpace() > 255)
234 // Fast instruction selection doesn't support the special
235 // address spaces.
236 return false;
237
238 if (const auto *GV = dyn_cast<GlobalValue>(Val: Obj)) {
239 if (TLI.isPositionIndependent())
240 return false;
241 if (Addr.getGlobalValue())
242 return false;
243 if (GV->isThreadLocal())
244 return false;
245 Addr.setGlobalValue(GV);
246 return true;
247 }
248
249 switch (Opcode) {
250 default:
251 break;
252 case Instruction::BitCast: {
253 // Look through bitcasts.
254 return computeAddress(Obj: U->getOperand(i: 0), Addr);
255 }
256 case Instruction::IntToPtr: {
257 // Look past no-op inttoptrs.
258 if (TLI.getValueType(DL, Ty: U->getOperand(i: 0)->getType()) ==
259 TLI.getPointerTy(DL))
260 return computeAddress(Obj: U->getOperand(i: 0), Addr);
261 break;
262 }
263 case Instruction::PtrToInt: {
264 // Look past no-op ptrtoints.
265 if (TLI.getValueType(DL, Ty: U->getType()) == TLI.getPointerTy(DL))
266 return computeAddress(Obj: U->getOperand(i: 0), Addr);
267 break;
268 }
269 case Instruction::GetElementPtr: {
270 Address SavedAddr = Addr;
271 uint64_t TmpOffset = Addr.getOffset();
272 // Non-inbounds geps can wrap; wasm's offsets can't.
273 if (!cast<GEPOperator>(Val: U)->isInBounds())
274 goto unsupported_gep;
275 // Iterate through the GEP folding the constants into offsets where
276 // we can.
277 for (gep_type_iterator GTI = gep_type_begin(GEP: U), E = gep_type_end(GEP: U);
278 GTI != E; ++GTI) {
279 const Value *Op = GTI.getOperand();
280 if (StructType *STy = GTI.getStructTypeOrNull()) {
281 const StructLayout *SL = DL.getStructLayout(Ty: STy);
282 unsigned Idx = cast<ConstantInt>(Val: Op)->getZExtValue();
283 TmpOffset += SL->getElementOffset(Idx);
284 } else {
285 uint64_t S = GTI.getSequentialElementStride(DL);
286 for (;;) {
287 if (const auto *CI = dyn_cast<ConstantInt>(Val: Op)) {
288 // Constant-offset addressing.
289 TmpOffset += CI->getSExtValue() * S;
290 break;
291 }
292 if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) {
293 // An unscaled add of a register. Set it as the new base.
294 Register Reg = getRegForValue(V: Op);
295 if (Reg == 0)
296 return false;
297 Addr.setReg(Reg);
298 break;
299 }
300 if (canFoldAddIntoGEP(GEP: U, Add: Op)) {
301 // A compatible add with a constant operand. Fold the constant.
302 auto *CI = cast<ConstantInt>(Val: cast<AddOperator>(Val: Op)->getOperand(i_nocapture: 1));
303 TmpOffset += CI->getSExtValue() * S;
304 // Iterate on the other operand.
305 Op = cast<AddOperator>(Val: Op)->getOperand(i_nocapture: 0);
306 continue;
307 }
308 // Unsupported
309 goto unsupported_gep;
310 }
311 }
312 }
313 // Don't fold in negative offsets.
314 if (int64_t(TmpOffset) >= 0) {
315 // Try to grab the base operand now.
316 Addr.setOffset(TmpOffset);
317 if (computeAddress(Obj: U->getOperand(i: 0), Addr))
318 return true;
319 }
320 // We failed, restore everything and try the other options.
321 Addr = SavedAddr;
322 unsupported_gep:
323 break;
324 }
325 case Instruction::Alloca: {
326 const auto *AI = cast<AllocaInst>(Val: Obj);
327 auto SI = FuncInfo.StaticAllocaMap.find(Val: AI);
328 if (SI != FuncInfo.StaticAllocaMap.end()) {
329 if (Addr.isSet()) {
330 return false;
331 }
332 Addr.setKind(Address::FrameIndexBase);
333 Addr.setFI(SI->second);
334 return true;
335 }
336 break;
337 }
338 case Instruction::Add: {
339 // We should not fold operands into an offset when 'nuw' (no unsigned wrap)
340 // is not present, because the address calculation does not wrap.
341 if (auto *OFBinOp = dyn_cast<OverflowingBinaryOperator>(Val: U))
342 if (!OFBinOp->hasNoUnsignedWrap())
343 break;
344
345 // Adds of constants are common and easy enough.
346 const Value *LHS = U->getOperand(i: 0);
347 const Value *RHS = U->getOperand(i: 1);
348
349 if (isa<ConstantInt>(Val: LHS))
350 std::swap(a&: LHS, b&: RHS);
351
352 if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) {
353 uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue();
354 if (int64_t(TmpOffset) >= 0) {
355 Addr.setOffset(TmpOffset);
356 return computeAddress(Obj: LHS, Addr);
357 }
358 }
359
360 Address Backup = Addr;
361 if (computeAddress(Obj: LHS, Addr) && computeAddress(Obj: RHS, Addr))
362 return true;
363 Addr = Backup;
364
365 break;
366 }
367 case Instruction::Sub: {
368 // We should not fold operands into an offset when 'nuw' (no unsigned wrap)
369 // is not present, because the address calculation does not wrap.
370 if (auto *OFBinOp = dyn_cast<OverflowingBinaryOperator>(Val: U))
371 if (!OFBinOp->hasNoUnsignedWrap())
372 break;
373
374 // Subs of constants are common and easy enough.
375 const Value *LHS = U->getOperand(i: 0);
376 const Value *RHS = U->getOperand(i: 1);
377
378 if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) {
379 int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue();
380 if (TmpOffset >= 0) {
381 Addr.setOffset(TmpOffset);
382 return computeAddress(Obj: LHS, Addr);
383 }
384 }
385 break;
386 }
387 }
388 if (Addr.isSet()) {
389 return false;
390 }
391 Register Reg = getRegForValue(V: Obj);
392 if (Reg == 0)
393 return false;
394 Addr.setReg(Reg);
395 return Addr.getReg() != 0;
396}
397
398void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) {
399 if (Addr.isRegBase()) {
400 unsigned Reg = Addr.getReg();
401 if (Reg == 0) {
402 Reg = createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
403 : &WebAssembly::I32RegClass);
404 unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
405 : WebAssembly::CONST_I32;
406 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: Reg)
407 .addImm(Val: 0);
408 Addr.setReg(Reg);
409 }
410 }
411}
412
413void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr,
414 const MachineInstrBuilder &MIB,
415 MachineMemOperand *MMO) {
416 // Set the alignment operand (this is rewritten in SetP2AlignOperands).
417 // TODO: Disable SetP2AlignOperands for FastISel and just do it here.
418 MIB.addImm(Val: 0);
419
420 if (const GlobalValue *GV = Addr.getGlobalValue())
421 MIB.addGlobalAddress(GV, Offset: Addr.getOffset());
422 else
423 MIB.addImm(Val: Addr.getOffset());
424
425 if (Addr.isRegBase())
426 MIB.addReg(RegNo: Addr.getReg());
427 else
428 MIB.addFrameIndex(Idx: Addr.getFI());
429
430 MIB.addMemOperand(MMO);
431}
432
433bool WebAssemblyFastISel::emitLoad(Register ResultReg, unsigned Opc,
434 const LoadInst *Load) {
435 Address Addr;
436 if (!computeAddress(Obj: Load->getPointerOperand(), Addr))
437 return false;
438
439 materializeLoadStoreOperands(Addr);
440 auto MIB =
441 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg);
442 addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Load));
443
444 return true;
445}
446
447unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) {
448 return zeroExtendToI32(Reg, V, From: MVT::i1);
449}
450
451unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V,
452 const BasicBlock *BB,
453 bool &Not) {
454 if (const auto *ICmp = dyn_cast<ICmpInst>(Val: V))
455 if (const ConstantInt *C = dyn_cast<ConstantInt>(Val: ICmp->getOperand(i_nocapture: 1)))
456 if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(BitWidth: 32) &&
457 ICmp->getParent() == BB) {
458 Not = ICmp->isTrueWhenEqual();
459 return getRegForValue(V: ICmp->getOperand(i_nocapture: 0));
460 }
461
462 Not = false;
463 Register Reg = getRegForValue(V);
464 if (Reg == 0)
465 return 0;
466 return maskI1Value(Reg, V);
467}
468
469unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V,
470 MVT::SimpleValueType From) {
471 if (Reg == 0)
472 return 0;
473
474 switch (From) {
475 case MVT::i1:
476 // If the value is naturally an i1, we don't need to mask it. We only know
477 // if a value is naturally an i1 if it is definitely lowered by FastISel,
478 // not a DAG ISel fallback.
479 if (V != nullptr && isa<Argument>(Val: V) && cast<Argument>(Val: V)->hasZExtAttr())
480 return copyValue(Reg);
481 break;
482 case MVT::i8:
483 case MVT::i16:
484 break;
485 case MVT::i32:
486 return copyValue(Reg);
487 default:
488 return 0;
489 }
490
491 Register Imm = createResultReg(RC: &WebAssembly::I32RegClass);
492 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
493 MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: Imm)
494 .addImm(Val: ~(~uint64_t(0) << MVT(From).getSizeInBits()));
495
496 Register Result = createResultReg(RC: &WebAssembly::I32RegClass);
497 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::AND_I32),
498 DestReg: Result)
499 .addReg(RegNo: Reg)
500 .addReg(RegNo: Imm);
501
502 return Result;
503}
504
505unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V,
506 MVT::SimpleValueType From) {
507 if (Reg == 0)
508 return 0;
509
510 switch (From) {
511 case MVT::i1:
512 case MVT::i8:
513 case MVT::i16:
514 break;
515 case MVT::i32:
516 return copyValue(Reg);
517 default:
518 return 0;
519 }
520
521 if (Subtarget->hasSignExt()) {
522 if (From == MVT::i8 || From == MVT::i16) {
523 Register Result = createResultReg(RC: &WebAssembly::I32RegClass);
524 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
525 MCID: TII.get(Opcode: From == MVT::i16 ? WebAssembly::I32_EXTEND16_S_I32
526 : WebAssembly::I32_EXTEND8_S_I32),
527 DestReg: Result)
528 .addReg(RegNo: Reg);
529 return Result;
530 }
531 }
532
533 Register Imm = createResultReg(RC: &WebAssembly::I32RegClass);
534 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
535 MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: Imm)
536 .addImm(Val: 32 - MVT(From).getSizeInBits());
537
538 Register Left = createResultReg(RC: &WebAssembly::I32RegClass);
539 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::SHL_I32),
540 DestReg: Left)
541 .addReg(RegNo: Reg)
542 .addReg(RegNo: Imm);
543
544 Register Right = createResultReg(RC: &WebAssembly::I32RegClass);
545 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
546 MCID: TII.get(Opcode: WebAssembly::SHR_S_I32), DestReg: Right)
547 .addReg(RegNo: Left)
548 .addReg(RegNo: Imm);
549
550 return Right;
551}
552
553unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V,
554 MVT::SimpleValueType From,
555 MVT::SimpleValueType To) {
556 if (To == MVT::i64) {
557 if (From == MVT::i64)
558 return copyValue(Reg);
559
560 Reg = zeroExtendToI32(Reg, V, From);
561
562 Register Result = createResultReg(RC: &WebAssembly::I64RegClass);
563 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
564 MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_U_I32), DestReg: Result)
565 .addReg(RegNo: Reg);
566 return Result;
567 }
568
569 if (To == MVT::i32)
570 return zeroExtendToI32(Reg, V, From);
571
572 return 0;
573}
574
575unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V,
576 MVT::SimpleValueType From,
577 MVT::SimpleValueType To) {
578 if (To == MVT::i64) {
579 if (From == MVT::i64)
580 return copyValue(Reg);
581
582 Register Result = createResultReg(RC: &WebAssembly::I64RegClass);
583
584 if (Subtarget->hasSignExt()) {
585 switch (From) {
586 case MVT::i8:
587 case MVT::i16: {
588 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
589 MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_U_I32), DestReg: Result)
590 .addReg(RegNo: Reg);
591
592 Reg = Result;
593 Result = createResultReg(RC: &WebAssembly::I64RegClass);
594
595 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
596 MCID: TII.get(Opcode: From == MVT::i8 ? WebAssembly::I64_EXTEND8_S_I64
597 : WebAssembly::I64_EXTEND16_S_I64),
598 DestReg: Result)
599 .addReg(RegNo: Reg);
600 return Result;
601 }
602 case MVT::i32:
603 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
604 MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_S_I32), DestReg: Result)
605 .addReg(RegNo: Reg);
606 return Result;
607 default:
608 break;
609 }
610 }
611
612 Reg = signExtendToI32(Reg, V, From);
613 if (Reg == 0)
614 return 0;
615
616 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
617 MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_S_I32), DestReg: Result)
618 .addReg(RegNo: Reg);
619 return Result;
620 }
621
622 if (To == MVT::i32)
623 return signExtendToI32(Reg, V, From);
624
625 return 0;
626}
627
628unsigned WebAssemblyFastISel::getRegForUnsignedValue(const Value *V) {
629 MVT::SimpleValueType From = getSimpleType(Ty: V->getType());
630 MVT::SimpleValueType To = getLegalType(VT: From);
631 Register VReg = getRegForValue(V);
632 if (VReg == 0)
633 return 0;
634 if (From == To)
635 return VReg;
636 return zeroExtend(Reg: VReg, V, From, To);
637}
638
639unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) {
640 MVT::SimpleValueType From = getSimpleType(Ty: V->getType());
641 MVT::SimpleValueType To = getLegalType(VT: From);
642 Register VReg = getRegForValue(V);
643 if (VReg == 0)
644 return 0;
645 if (From == To)
646 return VReg;
647 return signExtend(Reg: VReg, V, From, To);
648}
649
650unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V,
651 bool IsSigned) {
652 return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V);
653}
654
655unsigned WebAssemblyFastISel::notValue(unsigned Reg) {
656 assert(MRI.getRegClass(Reg) == &WebAssembly::I32RegClass);
657
658 Register NotReg = createResultReg(RC: &WebAssembly::I32RegClass);
659 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::EQZ_I32),
660 DestReg: NotReg)
661 .addReg(RegNo: Reg);
662 return NotReg;
663}
664
665unsigned WebAssemblyFastISel::copyValue(unsigned Reg) {
666 Register ResultReg = createResultReg(RC: MRI.getRegClass(Reg));
667 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::COPY),
668 DestReg: ResultReg)
669 .addReg(RegNo: Reg);
670 return ResultReg;
671}
672
673Register WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) {
674 auto SI = FuncInfo.StaticAllocaMap.find(Val: AI);
675
676 if (SI != FuncInfo.StaticAllocaMap.end()) {
677 Register ResultReg =
678 createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
679 : &WebAssembly::I32RegClass);
680 unsigned Opc =
681 Subtarget->hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32;
682 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
683 .addFrameIndex(Idx: SI->second);
684 return ResultReg;
685 }
686
687 return Register();
688}
689
690Register WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) {
691 if (const GlobalValue *GV = dyn_cast<GlobalValue>(Val: C)) {
692 if (TLI.isPositionIndependent())
693 return Register();
694 if (GV->isThreadLocal())
695 return Register();
696 Register ResultReg =
697 createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass
698 : &WebAssembly::I32RegClass);
699 unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64
700 : WebAssembly::CONST_I32;
701 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
702 .addGlobalAddress(GV);
703 return ResultReg;
704 }
705
706 // Let target-independent code handle it.
707 return Register();
708}
709
710bool WebAssemblyFastISel::fastLowerArguments() {
711 if (!FuncInfo.CanLowerReturn)
712 return false;
713
714 const Function *F = FuncInfo.Fn;
715 if (F->isVarArg())
716 return false;
717
718 if (FuncInfo.Fn->getCallingConv() == CallingConv::Swift)
719 return false;
720
721 unsigned I = 0;
722 for (auto const &Arg : F->args()) {
723 const AttributeList &Attrs = F->getAttributes();
724 if (Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::ByVal) ||
725 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftSelf) ||
726 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftError) ||
727 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::InAlloca) ||
728 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::Nest))
729 return false;
730
731 Type *ArgTy = Arg.getType();
732 if (ArgTy->isStructTy() || ArgTy->isArrayTy())
733 return false;
734 if (!Subtarget->hasSIMD128() && ArgTy->isVectorTy())
735 return false;
736
737 unsigned Opc;
738 const TargetRegisterClass *RC;
739 switch (getSimpleType(Ty: ArgTy)) {
740 case MVT::i1:
741 case MVT::i8:
742 case MVT::i16:
743 case MVT::i32:
744 Opc = WebAssembly::ARGUMENT_i32;
745 RC = &WebAssembly::I32RegClass;
746 break;
747 case MVT::i64:
748 Opc = WebAssembly::ARGUMENT_i64;
749 RC = &WebAssembly::I64RegClass;
750 break;
751 case MVT::f32:
752 Opc = WebAssembly::ARGUMENT_f32;
753 RC = &WebAssembly::F32RegClass;
754 break;
755 case MVT::f64:
756 Opc = WebAssembly::ARGUMENT_f64;
757 RC = &WebAssembly::F64RegClass;
758 break;
759 case MVT::v16i8:
760 Opc = WebAssembly::ARGUMENT_v16i8;
761 RC = &WebAssembly::V128RegClass;
762 break;
763 case MVT::v8i16:
764 Opc = WebAssembly::ARGUMENT_v8i16;
765 RC = &WebAssembly::V128RegClass;
766 break;
767 case MVT::v4i32:
768 Opc = WebAssembly::ARGUMENT_v4i32;
769 RC = &WebAssembly::V128RegClass;
770 break;
771 case MVT::v2i64:
772 Opc = WebAssembly::ARGUMENT_v2i64;
773 RC = &WebAssembly::V128RegClass;
774 break;
775 case MVT::v4f32:
776 Opc = WebAssembly::ARGUMENT_v4f32;
777 RC = &WebAssembly::V128RegClass;
778 break;
779 case MVT::v2f64:
780 Opc = WebAssembly::ARGUMENT_v2f64;
781 RC = &WebAssembly::V128RegClass;
782 break;
783 case MVT::funcref:
784 Opc = WebAssembly::ARGUMENT_funcref;
785 RC = &WebAssembly::FUNCREFRegClass;
786 break;
787 case MVT::externref:
788 Opc = WebAssembly::ARGUMENT_externref;
789 RC = &WebAssembly::EXTERNREFRegClass;
790 break;
791 case MVT::exnref:
792 Opc = WebAssembly::ARGUMENT_exnref;
793 RC = &WebAssembly::EXNREFRegClass;
794 break;
795 default:
796 return false;
797 }
798 Register ResultReg = createResultReg(RC);
799 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
800 .addImm(Val: I);
801 updateValueMap(I: &Arg, Reg: ResultReg);
802
803 ++I;
804 }
805
806 MRI.addLiveIn(Reg: WebAssembly::ARGUMENTS);
807
808 auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>();
809 for (auto const &Arg : F->args()) {
810 MVT::SimpleValueType ArgTy = getLegalType(VT: getSimpleType(Ty: Arg.getType()));
811 if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) {
812 MFI->clearParamsAndResults();
813 return false;
814 }
815 MFI->addParam(VT: ArgTy);
816 }
817
818 if (!F->getReturnType()->isVoidTy()) {
819 MVT::SimpleValueType RetTy =
820 getLegalType(VT: getSimpleType(Ty: F->getReturnType()));
821 if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) {
822 MFI->clearParamsAndResults();
823 return false;
824 }
825 MFI->addResult(VT: RetTy);
826 }
827
828 return true;
829}
830
831bool WebAssemblyFastISel::selectCall(const Instruction *I) {
832 const auto *Call = cast<CallInst>(Val: I);
833
834 // TODO: Support tail calls in FastISel
835 if (Call->isMustTailCall() || Call->isInlineAsm() ||
836 Call->getFunctionType()->isVarArg())
837 return false;
838
839 Function *Func = Call->getCalledFunction();
840 if (Func && Func->isIntrinsic())
841 return false;
842
843 if (Call->getCallingConv() == CallingConv::Swift)
844 return false;
845
846 bool IsDirect = Func != nullptr;
847 if (!IsDirect && isa<ConstantExpr>(Val: Call->getCalledOperand()))
848 return false;
849
850 FunctionType *FuncTy = Call->getFunctionType();
851 unsigned Opc = IsDirect ? WebAssembly::CALL : WebAssembly::CALL_INDIRECT;
852 bool IsVoid = FuncTy->getReturnType()->isVoidTy();
853 unsigned ResultReg;
854 if (!IsVoid) {
855 if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy())
856 return false;
857
858 MVT::SimpleValueType RetTy = getSimpleType(Ty: Call->getType());
859 switch (RetTy) {
860 case MVT::i1:
861 case MVT::i8:
862 case MVT::i16:
863 case MVT::i32:
864 ResultReg = createResultReg(RC: &WebAssembly::I32RegClass);
865 break;
866 case MVT::i64:
867 ResultReg = createResultReg(RC: &WebAssembly::I64RegClass);
868 break;
869 case MVT::f32:
870 ResultReg = createResultReg(RC: &WebAssembly::F32RegClass);
871 break;
872 case MVT::f64:
873 ResultReg = createResultReg(RC: &WebAssembly::F64RegClass);
874 break;
875 case MVT::v16i8:
876 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
877 break;
878 case MVT::v8i16:
879 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
880 break;
881 case MVT::v4i32:
882 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
883 break;
884 case MVT::v2i64:
885 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
886 break;
887 case MVT::v4f32:
888 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
889 break;
890 case MVT::v2f64:
891 ResultReg = createResultReg(RC: &WebAssembly::V128RegClass);
892 break;
893 case MVT::funcref:
894 ResultReg = createResultReg(RC: &WebAssembly::FUNCREFRegClass);
895 break;
896 case MVT::externref:
897 ResultReg = createResultReg(RC: &WebAssembly::EXTERNREFRegClass);
898 break;
899 case MVT::exnref:
900 ResultReg = createResultReg(RC: &WebAssembly::EXNREFRegClass);
901 break;
902 default:
903 return false;
904 }
905 }
906
907 SmallVector<unsigned, 8> Args;
908 for (unsigned I = 0, E = Call->arg_size(); I < E; ++I) {
909 Value *V = Call->getArgOperand(i: I);
910 MVT::SimpleValueType ArgTy = getSimpleType(Ty: V->getType());
911 if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE)
912 return false;
913
914 const AttributeList &Attrs = Call->getAttributes();
915 if (Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::ByVal) ||
916 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftSelf) ||
917 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftError) ||
918 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::InAlloca) ||
919 Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::Nest))
920 return false;
921
922 unsigned Reg;
923
924 if (Call->paramHasAttr(ArgNo: I, Kind: Attribute::SExt))
925 Reg = getRegForSignedValue(V);
926 else if (Call->paramHasAttr(ArgNo: I, Kind: Attribute::ZExt))
927 Reg = getRegForUnsignedValue(V);
928 else
929 Reg = getRegForValue(V);
930
931 if (Reg == 0)
932 return false;
933
934 Args.push_back(Elt: Reg);
935 }
936
937 unsigned CalleeReg = 0;
938 // A call through a funcref is expressed as a call through the pointer
939 // produced by llvm.wasm.funcref.to_ptr. Recover the funcref operand, place it
940 // into __funcref_call_table, and call it.
941 //
942 // TODO: Use call_ref if wasm-gc feature is available, would lead to simpler
943 // code here.
944 const Value *FuncrefArg = nullptr;
945 if (const auto *Conv = dyn_cast<CallInst>(Val: Call->getCalledOperand()))
946 if (Conv->getIntrinsicID() == Intrinsic::wasm_funcref_to_ptr)
947 FuncrefArg = Conv->getArgOperand(i: 0);
948
949 const bool IsFuncrefCall = FuncrefArg != nullptr;
950 MCSymbolWasm *Table = nullptr;
951
952 if (!IsDirect) {
953 if (!IsFuncrefCall) {
954 // Table is ___indirect_function_table
955 Table = WebAssembly::getOrCreateFunctionTableSymbol(Ctx&: MF->getContext(),
956 Subtarget);
957 CalleeReg = getRegForValue(V: Call->getCalledOperand());
958 if (!CalleeReg)
959 return false;
960 } else {
961 // Table is __funcref_call_table
962 Table = WebAssembly::getOrCreateFuncrefCallTableSymbol(Ctx&: MF->getContext(),
963 Subtarget);
964 CalleeReg = getRegForValue(V: FuncrefArg);
965 // Put the funcref in slot 0 of __funcref_call_table
966 unsigned ZeroReg = createResultReg(RC: &WebAssembly::I32RegClass);
967 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
968 MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: ZeroReg)
969 .addImm(Val: 0);
970 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
971 MCID: TII.get(Opcode: WebAssembly::TABLE_SET_FUNCREF))
972 .addSym(Sym: Table)
973 .addReg(RegNo: ZeroReg)
974 .addReg(RegNo: CalleeReg);
975 // Set CalleeReg to an immediate 0
976 CalleeReg = createResultReg(RC: &WebAssembly::I32RegClass);
977 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
978 MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: CalleeReg)
979 .addImm(Val: 0);
980 }
981 }
982
983 auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc));
984
985 if (!IsVoid)
986 MIB.addReg(RegNo: ResultReg, Flags: RegState::Define);
987
988 if (IsDirect) {
989 MIB.addGlobalAddress(GV: Func);
990 } else {
991 // Placeholder for the type index.
992 MIB.addImm(Val: 0);
993 if (Subtarget->hasCallIndirectOverlong()) {
994 MIB.addSym(Sym: Table);
995 } else {
996 // Otherwise for the MVP there is at most one table whose number is 0, but
997 // we can't write a table symbol or issue relocations. Instead we just
998 // ensure the table is live.
999 Table->setNoStrip();
1000 MIB.addImm(Val: 0);
1001 }
1002 }
1003
1004 for (unsigned ArgReg : Args)
1005 MIB.addReg(RegNo: ArgReg);
1006
1007 if (!IsDirect)
1008 MIB.addReg(RegNo: CalleeReg);
1009
1010 if (IsFuncrefCall) {
1011 // Clear slot 0 of the funcref call table after the call.
1012 unsigned ZeroReg = createResultReg(RC: &WebAssembly::I32RegClass);
1013 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1014 MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: ZeroReg)
1015 .addImm(Val: 0);
1016 unsigned NullReg = createResultReg(RC: &WebAssembly::FUNCREFRegClass);
1017 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1018 MCID: TII.get(Opcode: WebAssembly::REF_NULL_FUNCREF), DestReg: NullReg);
1019 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1020 MCID: TII.get(Opcode: WebAssembly::TABLE_SET_FUNCREF))
1021 .addSym(Sym: Table)
1022 .addReg(RegNo: ZeroReg)
1023 .addReg(RegNo: NullReg);
1024 }
1025
1026 if (!IsVoid)
1027 updateValueMap(I: Call, Reg: ResultReg);
1028
1029 diagnoseDontCall(CI: *Call);
1030 return true;
1031}
1032
1033bool WebAssemblyFastISel::selectSelect(const Instruction *I) {
1034 const auto *Select = cast<SelectInst>(Val: I);
1035
1036 bool Not;
1037 unsigned CondReg =
1038 getRegForI1Value(V: Select->getCondition(), BB: I->getParent(), Not);
1039 if (CondReg == 0)
1040 return false;
1041
1042 Register TrueReg = getRegForValue(V: Select->getTrueValue());
1043 if (TrueReg == 0)
1044 return false;
1045
1046 Register FalseReg = getRegForValue(V: Select->getFalseValue());
1047 if (FalseReg == 0)
1048 return false;
1049
1050 if (Not)
1051 std::swap(a&: TrueReg, b&: FalseReg);
1052
1053 unsigned Opc;
1054 const TargetRegisterClass *RC;
1055 switch (getSimpleType(Ty: Select->getType())) {
1056 case MVT::i1:
1057 case MVT::i8:
1058 case MVT::i16:
1059 case MVT::i32:
1060 Opc = WebAssembly::SELECT_I32;
1061 RC = &WebAssembly::I32RegClass;
1062 break;
1063 case MVT::i64:
1064 Opc = WebAssembly::SELECT_I64;
1065 RC = &WebAssembly::I64RegClass;
1066 break;
1067 case MVT::f32:
1068 Opc = WebAssembly::SELECT_F32;
1069 RC = &WebAssembly::F32RegClass;
1070 break;
1071 case MVT::f64:
1072 Opc = WebAssembly::SELECT_F64;
1073 RC = &WebAssembly::F64RegClass;
1074 break;
1075 case MVT::funcref:
1076 Opc = WebAssembly::SELECT_FUNCREF;
1077 RC = &WebAssembly::FUNCREFRegClass;
1078 break;
1079 case MVT::externref:
1080 Opc = WebAssembly::SELECT_EXTERNREF;
1081 RC = &WebAssembly::EXTERNREFRegClass;
1082 break;
1083 case MVT::exnref:
1084 Opc = WebAssembly::SELECT_EXNREF;
1085 RC = &WebAssembly::EXNREFRegClass;
1086 break;
1087 default:
1088 return false;
1089 }
1090
1091 Register ResultReg = createResultReg(RC);
1092 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
1093 .addReg(RegNo: TrueReg)
1094 .addReg(RegNo: FalseReg)
1095 .addReg(RegNo: CondReg);
1096
1097 updateValueMap(I: Select, Reg: ResultReg);
1098 return true;
1099}
1100
1101bool WebAssemblyFastISel::selectTrunc(const Instruction *I) {
1102 const auto *Trunc = cast<TruncInst>(Val: I);
1103
1104 const Value *Op = Trunc->getOperand(i_nocapture: 0);
1105 MVT::SimpleValueType From = getSimpleType(Ty: Op->getType());
1106 MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: Trunc->getType()));
1107 Register In = getRegForValue(V: Op);
1108 if (In == 0)
1109 return false;
1110
1111 auto Truncate = [&](Register Reg) -> unsigned {
1112 if (From == MVT::i64) {
1113 if (To == MVT::i64)
1114 return copyValue(Reg);
1115
1116 if (To == MVT::i1 || To == MVT::i8 || To == MVT::i16 || To == MVT::i32) {
1117 Register Result = createResultReg(RC: &WebAssembly::I32RegClass);
1118 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1119 MCID: TII.get(Opcode: WebAssembly::I32_WRAP_I64), DestReg: Result)
1120 .addReg(RegNo: Reg);
1121 return Result;
1122 }
1123 }
1124
1125 if (From == MVT::i32)
1126 return copyValue(Reg);
1127
1128 return 0;
1129 };
1130
1131 unsigned Reg = Truncate(In);
1132 if (Reg == 0)
1133 return false;
1134
1135 updateValueMap(I: Trunc, Reg);
1136 return true;
1137}
1138
1139bool WebAssemblyFastISel::selectZExt(const Instruction *I) {
1140 const auto *ZExt = cast<ZExtInst>(Val: I);
1141
1142 const Value *Op = ZExt->getOperand(i_nocapture: 0);
1143 MVT::SimpleValueType From = getSimpleType(Ty: Op->getType());
1144 MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: ZExt->getType()));
1145 Register In = getRegForValue(V: Op);
1146 if (In == 0)
1147 return false;
1148 unsigned Reg = zeroExtend(Reg: In, V: Op, From, To);
1149 if (Reg == 0)
1150 return false;
1151
1152 updateValueMap(I: ZExt, Reg);
1153 return true;
1154}
1155
1156bool WebAssemblyFastISel::selectSExt(const Instruction *I) {
1157 const auto *SExt = cast<SExtInst>(Val: I);
1158
1159 const Value *Op = SExt->getOperand(i_nocapture: 0);
1160 MVT::SimpleValueType From = getSimpleType(Ty: Op->getType());
1161 MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: SExt->getType()));
1162 Register In = getRegForValue(V: Op);
1163 if (In == 0)
1164 return false;
1165 unsigned Reg = signExtend(Reg: In, V: Op, From, To);
1166 if (Reg == 0)
1167 return false;
1168
1169 updateValueMap(I: SExt, Reg);
1170 return true;
1171}
1172
1173bool WebAssemblyFastISel::selectICmp(const Instruction *I) {
1174 const auto *ICmp = cast<ICmpInst>(Val: I);
1175
1176 // The I32 test below classifies every non-i64 type as i32, so a vector
1177 // compare would emit a scalar compare over v128 registers and produce
1178 // an invalid module. The SelectionDAG lowers vector compares to SIMD
1179 // compares.
1180 if (ICmp->getOperand(i_nocapture: 0)->getType()->isVectorTy())
1181 return false;
1182
1183 bool I32 = getSimpleType(Ty: ICmp->getOperand(i_nocapture: 0)->getType()) != MVT::i64;
1184 unsigned Opc;
1185 bool IsSigned = false;
1186 switch (ICmp->getPredicate()) {
1187 case ICmpInst::ICMP_EQ:
1188 Opc = I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64;
1189 break;
1190 case ICmpInst::ICMP_NE:
1191 Opc = I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64;
1192 break;
1193 case ICmpInst::ICMP_UGT:
1194 Opc = I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64;
1195 break;
1196 case ICmpInst::ICMP_UGE:
1197 Opc = I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64;
1198 break;
1199 case ICmpInst::ICMP_ULT:
1200 Opc = I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64;
1201 break;
1202 case ICmpInst::ICMP_ULE:
1203 Opc = I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64;
1204 break;
1205 case ICmpInst::ICMP_SGT:
1206 Opc = I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64;
1207 IsSigned = true;
1208 break;
1209 case ICmpInst::ICMP_SGE:
1210 Opc = I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64;
1211 IsSigned = true;
1212 break;
1213 case ICmpInst::ICMP_SLT:
1214 Opc = I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64;
1215 IsSigned = true;
1216 break;
1217 case ICmpInst::ICMP_SLE:
1218 Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64;
1219 IsSigned = true;
1220 break;
1221 default:
1222 return false;
1223 }
1224
1225 unsigned LHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 0), IsSigned);
1226 if (LHS == 0)
1227 return false;
1228
1229 unsigned RHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 1), IsSigned);
1230 if (RHS == 0)
1231 return false;
1232
1233 Register ResultReg = createResultReg(RC: &WebAssembly::I32RegClass);
1234 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
1235 .addReg(RegNo: LHS)
1236 .addReg(RegNo: RHS);
1237 updateValueMap(I: ICmp, Reg: ResultReg);
1238 return true;
1239}
1240
1241bool WebAssemblyFastISel::selectFCmp(const Instruction *I) {
1242 const auto *FCmp = cast<FCmpInst>(Val: I);
1243
1244 // The F32 test below classifies every non-f64 type as f32, so a vector
1245 // compare would emit a scalar compare over v128 registers and produce
1246 // an invalid module. The SelectionDAG lowers vector compares to SIMD
1247 // compares.
1248 if (FCmp->getOperand(i_nocapture: 0)->getType()->isVectorTy())
1249 return false;
1250
1251 Register LHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 0));
1252 if (LHS == 0)
1253 return false;
1254
1255 Register RHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 1));
1256 if (RHS == 0)
1257 return false;
1258
1259 bool F32 = getSimpleType(Ty: FCmp->getOperand(i_nocapture: 0)->getType()) != MVT::f64;
1260 unsigned Opc;
1261 bool Not = false;
1262 switch (FCmp->getPredicate()) {
1263 case FCmpInst::FCMP_OEQ:
1264 Opc = F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64;
1265 break;
1266 case FCmpInst::FCMP_UNE:
1267 Opc = F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64;
1268 break;
1269 case FCmpInst::FCMP_OGT:
1270 Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64;
1271 break;
1272 case FCmpInst::FCMP_OGE:
1273 Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64;
1274 break;
1275 case FCmpInst::FCMP_OLT:
1276 Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64;
1277 break;
1278 case FCmpInst::FCMP_OLE:
1279 Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64;
1280 break;
1281 case FCmpInst::FCMP_UGT:
1282 Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64;
1283 Not = true;
1284 break;
1285 case FCmpInst::FCMP_UGE:
1286 Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64;
1287 Not = true;
1288 break;
1289 case FCmpInst::FCMP_ULT:
1290 Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64;
1291 Not = true;
1292 break;
1293 case FCmpInst::FCMP_ULE:
1294 Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64;
1295 Not = true;
1296 break;
1297 default:
1298 return false;
1299 }
1300
1301 Register ResultReg = createResultReg(RC: &WebAssembly::I32RegClass);
1302 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg)
1303 .addReg(RegNo: LHS)
1304 .addReg(RegNo: RHS);
1305
1306 if (Not)
1307 ResultReg = notValue(Reg: ResultReg);
1308
1309 updateValueMap(I: FCmp, Reg: ResultReg);
1310 return true;
1311}
1312
1313bool WebAssemblyFastISel::selectBitCast(const Instruction *I) {
1314 // Target-independent code can handle this, except it doesn't set the dead
1315 // flag on the ARGUMENTS clobber, so we have to do that manually in order
1316 // to satisfy code that expects this of isBitcast() instructions.
1317 EVT VT = TLI.getValueType(DL, Ty: I->getOperand(i: 0)->getType());
1318 EVT RetVT = TLI.getValueType(DL, Ty: I->getType());
1319 if (!VT.isSimple() || !RetVT.isSimple())
1320 return false;
1321
1322 Register In = getRegForValue(V: I->getOperand(i: 0));
1323 if (In == 0)
1324 return false;
1325
1326 if (VT == RetVT) {
1327 // No-op bitcast.
1328 updateValueMap(I, Reg: In);
1329 return true;
1330 }
1331
1332 Register Reg =
1333 fastEmit_ISD_BITCAST_r(VT: VT.getSimpleVT(), RetVT: RetVT.getSimpleVT(), Op0: In);
1334 if (!Reg)
1335 return false;
1336 MachineBasicBlock::iterator Iter = FuncInfo.InsertPt;
1337 --Iter;
1338 assert(Iter->isBitcast());
1339 Iter->setImplicitPhysRegDefsDead();
1340 updateValueMap(I, Reg);
1341 return true;
1342}
1343
1344static unsigned getSExtLoadOpcode(unsigned LoadSize, bool I64Result, bool A64) {
1345 if (I64Result) {
1346 switch (LoadSize) {
1347 default:
1348 return WebAssembly::INSTRUCTION_LIST_END;
1349 case 8:
1350 return A64 ? WebAssembly::LOAD8_S_I64_A64 : WebAssembly::LOAD8_S_I64_A32;
1351 case 16:
1352 return A64 ? WebAssembly::LOAD16_S_I64_A64
1353 : WebAssembly::LOAD16_S_I64_A32;
1354 case 32:
1355 return A64 ? WebAssembly::LOAD32_S_I64_A64
1356 : WebAssembly::LOAD32_S_I64_A32;
1357 }
1358 }
1359
1360 switch (LoadSize) {
1361 default:
1362 return WebAssembly::INSTRUCTION_LIST_END;
1363 case 8:
1364 return A64 ? WebAssembly::LOAD8_S_I32_A64 : WebAssembly::LOAD8_S_I32_A32;
1365 case 16:
1366 return A64 ? WebAssembly::LOAD16_S_I32_A64 : WebAssembly::LOAD16_S_I32_A32;
1367 }
1368}
1369
1370static unsigned getZExtLoadOpcode(unsigned LoadSize, bool I64Result, bool A64) {
1371 if (I64Result) {
1372 switch (LoadSize) {
1373 default:
1374 return WebAssembly::INSTRUCTION_LIST_END;
1375 case 8:
1376 return A64 ? WebAssembly::LOAD8_U_I64_A64 : WebAssembly::LOAD8_U_I64_A32;
1377 case 16:
1378 return A64 ? WebAssembly::LOAD16_U_I64_A64
1379 : WebAssembly::LOAD16_U_I64_A32;
1380 case 32:
1381 return A64 ? WebAssembly::LOAD32_U_I64_A64
1382 : WebAssembly::LOAD32_U_I64_A32;
1383 }
1384 }
1385
1386 switch (LoadSize) {
1387 default:
1388 return WebAssembly::INSTRUCTION_LIST_END;
1389 case 8:
1390 return A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32;
1391 case 16:
1392 return A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32;
1393 }
1394}
1395
1396static bool isFoldableSExtOpcode(unsigned Opc) {
1397 switch (Opc) {
1398 default:
1399 return false;
1400 case WebAssembly::I32_EXTEND8_S_I32:
1401 case WebAssembly::I32_EXTEND16_S_I32:
1402 case WebAssembly::I64_EXTEND8_S_I64:
1403 case WebAssembly::I64_EXTEND16_S_I64:
1404 case WebAssembly::I64_EXTEND32_S_I64:
1405 case WebAssembly::I64_EXTEND_S_I32:
1406 return true;
1407 }
1408}
1409
1410static bool isI64SExtResult(unsigned Opc) {
1411 switch (Opc) {
1412 default:
1413 llvm_unreachable("unexpected opcode");
1414 case WebAssembly::I32_EXTEND8_S_I32:
1415 case WebAssembly::I32_EXTEND16_S_I32:
1416 return false;
1417 case WebAssembly::I64_EXTEND8_S_I64:
1418 case WebAssembly::I64_EXTEND16_S_I64:
1419 case WebAssembly::I64_EXTEND32_S_I64:
1420 case WebAssembly::I64_EXTEND_S_I32:
1421 return true;
1422 }
1423}
1424
1425static unsigned getFoldedLoadOpcode(MachineInstr *MI, MachineRegisterInfo &MRI,
1426 const LoadInst *LI, bool A64) {
1427 unsigned Opc = MI->getOpcode();
1428
1429 if (isFoldableSExtOpcode(Opc)) {
1430 unsigned LoadSize = LI->getType()->getPrimitiveSizeInBits();
1431 return getSExtLoadOpcode(LoadSize, I64Result: isI64SExtResult(Opc), A64);
1432 }
1433
1434 return WebAssembly::INSTRUCTION_LIST_END;
1435}
1436
1437static unsigned getFoldedI64LoadOpcode(Register DestReg, const LoadInst *LI,
1438 MachineRegisterInfo &MRI, bool A64,
1439 MachineInstr *&OuterUserMI,
1440 unsigned NarrowOpc) {
1441 if (!MRI.hasOneNonDBGUse(RegNo: DestReg))
1442 return NarrowOpc;
1443
1444 MachineInstr *UserMI = &*MRI.use_instr_nodbg_begin(RegNo: DestReg);
1445 unsigned LoadSize = LI->getType()->getPrimitiveSizeInBits();
1446 switch (UserMI->getOpcode()) {
1447 case WebAssembly::I64_EXTEND_U_I32:
1448 OuterUserMI = UserMI;
1449 return getZExtLoadOpcode(LoadSize, /*I64Result=*/true, A64);
1450 case WebAssembly::I64_EXTEND_S_I32:
1451 OuterUserMI = UserMI;
1452 return getSExtLoadOpcode(LoadSize, /*I64Result=*/true, A64);
1453 default:
1454 return NarrowOpc;
1455 }
1456}
1457
1458/// Matches a sign-extension pattern (shl + shr_s) to fold it into a signed
1459/// load. FastISel assumes that 'sext' from i8 or i16 will first be lowered to a
1460/// 32-bit zero-extending load (i32.load8_u / i32.load16_u) followed by 32-bit
1461/// shifts, even when extending to i64. Therefore, this function only matches
1462/// 32-bit shifts (SHL_I32 / SHR_S_I32) and specifically checks if both shift
1463/// amounts are identical, compile-time constants that match the exact extension
1464/// size (32 - LoadBitWidth).
1465static unsigned matchFoldableShift(MachineInstr *MI, const LoadInst *LI,
1466 MachineRegisterInfo &MRI, bool A64,
1467 MachineInstr *&UserMI,
1468 MachineInstr *&OuterUserMI) {
1469 unsigned Opc = MI->getOpcode();
1470 unsigned NewOpc = WebAssembly::INSTRUCTION_LIST_END;
1471 if (Opc != WebAssembly::SHL_I32)
1472 return NewOpc;
1473
1474 Register DestReg = MI->getOperand(i: 0).getReg();
1475 if (!MRI.hasOneNonDBGUse(RegNo: DestReg))
1476 return NewOpc;
1477
1478 UserMI = &*MRI.use_instr_nodbg_begin(RegNo: DestReg);
1479 unsigned UserOpc = UserMI->getOpcode();
1480 if (UserOpc != WebAssembly::SHR_S_I32)
1481 return NewOpc;
1482
1483 Type *LoadTy = LI->getType();
1484 if (!LoadTy->isIntegerTy(BitWidth: 8) && !LoadTy->isIntegerTy(BitWidth: 16))
1485 return NewOpc;
1486
1487 int64_t ExpectedShiftAmt = 32 - LoadTy->getIntegerBitWidth();
1488 Register ShlAmtReg = MI->getOperand(i: 2).getReg();
1489 Register ShrAmtReg = UserMI->getOperand(i: 2).getReg();
1490 MachineInstr *ShlAmtDef = MRI.getUniqueVRegDef(Reg: ShlAmtReg);
1491 MachineInstr *ShrAmtDef = MRI.getUniqueVRegDef(Reg: ShrAmtReg);
1492 auto IsExpectedConst = [ExpectedShiftAmt](MachineInstr *MI) {
1493 return MI && MI->getOpcode() == WebAssembly::CONST_I32 &&
1494 MI->getOperand(i: 1).getImm() == ExpectedShiftAmt;
1495 };
1496 if (!IsExpectedConst(ShlAmtDef) || !IsExpectedConst(ShrAmtDef))
1497 return NewOpc;
1498
1499 unsigned LoadSize = LoadTy->getIntegerBitWidth();
1500 unsigned NarrowOpc = getSExtLoadOpcode(LoadSize, /*I64Result=*/false, A64);
1501 if (NarrowOpc == WebAssembly::INSTRUCTION_LIST_END)
1502 return WebAssembly::INSTRUCTION_LIST_END;
1503
1504 return getFoldedI64LoadOpcode(DestReg: UserMI->getOperand(i: 0).getReg(), LI, MRI, A64,
1505 OuterUserMI, NarrowOpc);
1506}
1507
1508static unsigned matchFoldableSExtFromPromotedI32(MachineInstr *MI,
1509 const LoadInst *LI,
1510 MachineRegisterInfo &MRI,
1511 bool A64,
1512 MachineInstr *&UserMI) {
1513 if (MI->getOpcode() != WebAssembly::I64_EXTEND_U_I32)
1514 return WebAssembly::INSTRUCTION_LIST_END;
1515
1516 unsigned LoadSize = LI->getType()->getPrimitiveSizeInBits();
1517 Register DestReg = MI->getOperand(i: 0).getReg();
1518 if (!MRI.hasOneNonDBGUse(RegNo: DestReg))
1519 return WebAssembly::INSTRUCTION_LIST_END;
1520
1521 UserMI = &*MRI.use_instr_nodbg_begin(RegNo: DestReg);
1522 switch (UserMI->getOpcode()) {
1523 default:
1524 return WebAssembly::INSTRUCTION_LIST_END;
1525 case WebAssembly::I64_EXTEND8_S_I64:
1526 if (LoadSize != 8)
1527 return WebAssembly::INSTRUCTION_LIST_END;
1528 return getSExtLoadOpcode(LoadSize, I64Result: true, A64);
1529 case WebAssembly::I64_EXTEND16_S_I64:
1530 if (LoadSize != 16)
1531 return WebAssembly::INSTRUCTION_LIST_END;
1532 return getSExtLoadOpcode(LoadSize, I64Result: true, A64);
1533 }
1534}
1535
1536static unsigned matchFoldableCopyToI64Ext(MachineInstr *MI, const LoadInst *LI,
1537 MachineRegisterInfo &MRI, bool A64,
1538 MachineInstr *&OuterUserMI) {
1539 if (MI->getOpcode() != WebAssembly::COPY)
1540 return WebAssembly::INSTRUCTION_LIST_END;
1541
1542 unsigned LoadSize = LI->getType()->getPrimitiveSizeInBits();
1543 if (LoadSize != 32)
1544 return WebAssembly::INSTRUCTION_LIST_END;
1545
1546 Register CopyDst = MI->getOperand(i: 0).getReg();
1547 if (!MRI.hasOneNonDBGUse(RegNo: CopyDst))
1548 return WebAssembly::INSTRUCTION_LIST_END;
1549
1550 OuterUserMI = &*MRI.use_instr_nodbg_begin(RegNo: CopyDst);
1551 switch (OuterUserMI->getOpcode()) {
1552 default:
1553 return WebAssembly::INSTRUCTION_LIST_END;
1554 case WebAssembly::I64_EXTEND_U_I32:
1555 return getZExtLoadOpcode(LoadSize, I64Result: true, A64);
1556 case WebAssembly::I64_EXTEND_S_I32:
1557 return getSExtLoadOpcode(LoadSize, I64Result: true, A64);
1558 }
1559}
1560
1561static unsigned matchFoldableAnd(MachineInstr *MI, const LoadInst *LI,
1562 MachineRegisterInfo &MRI, bool A64,
1563 MachineInstr *&OuterUserMI) {
1564 if (MI->getOpcode() != WebAssembly::AND_I32 &&
1565 MI->getOpcode() != WebAssembly::AND_I64)
1566 return WebAssembly::INSTRUCTION_LIST_END;
1567
1568 uint64_t Mask = 0;
1569 bool IsConstant = false;
1570 for (unsigned I = 1; I <= 2; ++I) {
1571 Register Reg = MI->getOperand(i: I).getReg();
1572 MachineInstr *DefMI = MRI.getUniqueVRegDef(Reg);
1573 if (DefMI && (DefMI->getOpcode() == WebAssembly::CONST_I32 ||
1574 DefMI->getOpcode() == WebAssembly::CONST_I64)) {
1575 Mask = DefMI->getOperand(i: 1).getImm();
1576 IsConstant = true;
1577 break;
1578 }
1579 }
1580
1581 if (!IsConstant)
1582 return WebAssembly::INSTRUCTION_LIST_END;
1583
1584 unsigned LoadSize = LI->getType()->getPrimitiveSizeInBits();
1585 if (Mask != llvm::maskTrailingOnes<uint64_t>(N: LoadSize))
1586 return WebAssembly::INSTRUCTION_LIST_END;
1587
1588 if (MI->getOpcode() == WebAssembly::AND_I64)
1589 return getZExtLoadOpcode(LoadSize, /*I64Result=*/true, A64);
1590
1591 unsigned NarrowOpc = getZExtLoadOpcode(LoadSize, /*I64Result=*/false, A64);
1592 if (NarrowOpc == WebAssembly::INSTRUCTION_LIST_END)
1593 return WebAssembly::INSTRUCTION_LIST_END;
1594
1595 return getFoldedI64LoadOpcode(DestReg: MI->getOperand(i: 0).getReg(), LI, MRI, A64,
1596 OuterUserMI, NarrowOpc);
1597}
1598
1599bool WebAssemblyFastISel::tryToFoldLoadIntoMI(MachineInstr *MI, unsigned OpNo,
1600 const LoadInst *LI) {
1601 bool A64 = Subtarget->hasAddr64();
1602 MachineRegisterInfo &MRI = FuncInfo.MF->getRegInfo();
1603 Register ResultReg;
1604 MachineInstr *UserMI = nullptr;
1605 MachineInstr *OuterUserMI = nullptr;
1606 unsigned NewOpc = WebAssembly::INSTRUCTION_LIST_END;
1607 if ((NewOpc = matchFoldableSExtFromPromotedI32(MI, LI, MRI, A64, UserMI)) !=
1608 WebAssembly::INSTRUCTION_LIST_END) {
1609 ResultReg = UserMI->getOperand(i: 0).getReg();
1610 } else if ((NewOpc =
1611 matchFoldableCopyToI64Ext(MI, LI, MRI, A64, OuterUserMI)) !=
1612 WebAssembly::INSTRUCTION_LIST_END) {
1613 ResultReg = OuterUserMI->getOperand(i: 0).getReg();
1614 } else if ((NewOpc = matchFoldableAnd(MI, LI, MRI, A64, OuterUserMI)) !=
1615 WebAssembly::INSTRUCTION_LIST_END) {
1616 ResultReg = OuterUserMI ? OuterUserMI->getOperand(i: 0).getReg()
1617 : MI->getOperand(i: 0).getReg();
1618 } else if ((NewOpc = getFoldedLoadOpcode(MI, MRI, LI, A64)) !=
1619 WebAssembly::INSTRUCTION_LIST_END) {
1620 ResultReg = MI->getOperand(i: 0).getReg();
1621 } else if ((NewOpc =
1622 matchFoldableShift(MI, LI, MRI, A64, UserMI, OuterUserMI)) !=
1623 WebAssembly::INSTRUCTION_LIST_END) {
1624 ResultReg = OuterUserMI ? OuterUserMI->getOperand(i: 0).getReg()
1625 : UserMI->getOperand(i: 0).getReg();
1626 } else {
1627 return false;
1628 }
1629
1630 if (!emitLoad(ResultReg, Opc: NewOpc, Load: LI))
1631 return false;
1632
1633 if (OuterUserMI) {
1634 MachineBasicBlock::iterator OuterIter(OuterUserMI);
1635 removeDeadCode(I: OuterIter, E: std::next(x: OuterIter));
1636 }
1637
1638 if (UserMI) {
1639 MachineBasicBlock::iterator UserIter(UserMI);
1640 removeDeadCode(I: UserIter, E: std::next(x: UserIter));
1641 }
1642
1643 MachineBasicBlock::iterator Iter(MI);
1644 removeDeadCode(I: Iter, E: std::next(x: Iter));
1645 return true;
1646}
1647
1648bool WebAssemblyFastISel::selectLoad(const Instruction *I) {
1649 const auto *Load = cast<LoadInst>(Val: I);
1650 if (Load->isAtomic())
1651 return false;
1652 if (!WebAssembly::isDefaultAddressSpace(AS: Load->getPointerAddressSpace()))
1653 return false;
1654 if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy())
1655 return false;
1656
1657 // TODO: Fold a following sign-/zero-extend into the load instruction.
1658
1659 unsigned Opc;
1660 const TargetRegisterClass *RC;
1661 bool A64 = Subtarget->hasAddr64();
1662 switch (getSimpleType(Ty: Load->getType())) {
1663 case MVT::i1:
1664 case MVT::i8:
1665 Opc = A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32;
1666 RC = &WebAssembly::I32RegClass;
1667 break;
1668 case MVT::i16:
1669 Opc = A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32;
1670 RC = &WebAssembly::I32RegClass;
1671 break;
1672 case MVT::i32:
1673 Opc = A64 ? WebAssembly::LOAD_I32_A64 : WebAssembly::LOAD_I32_A32;
1674 RC = &WebAssembly::I32RegClass;
1675 break;
1676 case MVT::i64:
1677 Opc = A64 ? WebAssembly::LOAD_I64_A64 : WebAssembly::LOAD_I64_A32;
1678 RC = &WebAssembly::I64RegClass;
1679 break;
1680 case MVT::f32:
1681 Opc = A64 ? WebAssembly::LOAD_F32_A64 : WebAssembly::LOAD_F32_A32;
1682 RC = &WebAssembly::F32RegClass;
1683 break;
1684 case MVT::f64:
1685 Opc = A64 ? WebAssembly::LOAD_F64_A64 : WebAssembly::LOAD_F64_A32;
1686 RC = &WebAssembly::F64RegClass;
1687 break;
1688 default:
1689 return false;
1690 }
1691
1692 Register ResultReg = createResultReg(RC);
1693 if (!emitLoad(ResultReg, Opc, Load))
1694 return false;
1695
1696 updateValueMap(I: Load, Reg: ResultReg);
1697 return true;
1698}
1699
1700bool WebAssemblyFastISel::selectStore(const Instruction *I) {
1701 const auto *Store = cast<StoreInst>(Val: I);
1702 if (Store->isAtomic())
1703 return false;
1704 if (!WebAssembly::isDefaultAddressSpace(AS: Store->getPointerAddressSpace()))
1705 return false;
1706 if (!Subtarget->hasSIMD128() &&
1707 Store->getValueOperand()->getType()->isVectorTy())
1708 return false;
1709
1710 Address Addr;
1711 if (!computeAddress(Obj: Store->getPointerOperand(), Addr))
1712 return false;
1713
1714 unsigned Opc;
1715 bool VTIsi1 = false;
1716 bool A64 = Subtarget->hasAddr64();
1717 switch (getSimpleType(Ty: Store->getValueOperand()->getType())) {
1718 case MVT::i1:
1719 VTIsi1 = true;
1720 [[fallthrough]];
1721 case MVT::i8:
1722 Opc = A64 ? WebAssembly::STORE8_I32_A64 : WebAssembly::STORE8_I32_A32;
1723 break;
1724 case MVT::i16:
1725 Opc = A64 ? WebAssembly::STORE16_I32_A64 : WebAssembly::STORE16_I32_A32;
1726 break;
1727 case MVT::i32:
1728 Opc = A64 ? WebAssembly::STORE_I32_A64 : WebAssembly::STORE_I32_A32;
1729 break;
1730 case MVT::i64:
1731 Opc = A64 ? WebAssembly::STORE_I64_A64 : WebAssembly::STORE_I64_A32;
1732 break;
1733 case MVT::f32:
1734 Opc = A64 ? WebAssembly::STORE_F32_A64 : WebAssembly::STORE_F32_A32;
1735 break;
1736 case MVT::f64:
1737 Opc = A64 ? WebAssembly::STORE_F64_A64 : WebAssembly::STORE_F64_A32;
1738 break;
1739 default:
1740 return false;
1741 }
1742
1743 materializeLoadStoreOperands(Addr);
1744
1745 Register ValueReg = getRegForValue(V: Store->getValueOperand());
1746 if (ValueReg == 0)
1747 return false;
1748 if (VTIsi1)
1749 ValueReg = maskI1Value(Reg: ValueReg, V: Store->getValueOperand());
1750
1751 auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc));
1752
1753 addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Store));
1754
1755 MIB.addReg(RegNo: ValueReg);
1756 return true;
1757}
1758
1759bool WebAssemblyFastISel::selectCondBr(const Instruction *I) {
1760 const auto *Br = cast<CondBrInst>(Val: I);
1761
1762 MachineBasicBlock *TBB = FuncInfo.getMBB(BB: Br->getSuccessor(i: 0));
1763 MachineBasicBlock *FBB = FuncInfo.getMBB(BB: Br->getSuccessor(i: 1));
1764
1765 bool Not;
1766 unsigned CondReg = getRegForI1Value(V: Br->getCondition(), BB: Br->getParent(), Not);
1767 if (CondReg == 0)
1768 return false;
1769
1770 unsigned Opc = WebAssembly::BR_IF;
1771 if (Not)
1772 Opc = WebAssembly::BR_UNLESS;
1773
1774 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc))
1775 .addMBB(MBB: TBB)
1776 .addReg(RegNo: CondReg);
1777
1778 finishCondBranch(BranchBB: Br->getParent(), TrueMBB: TBB, FalseMBB: FBB);
1779 return true;
1780}
1781
1782bool WebAssemblyFastISel::selectRet(const Instruction *I) {
1783 if (!FuncInfo.CanLowerReturn)
1784 return false;
1785
1786 const auto *Ret = cast<ReturnInst>(Val: I);
1787
1788 if (Ret->getNumOperands() == 0) {
1789 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1790 MCID: TII.get(Opcode: WebAssembly::RETURN));
1791 return true;
1792 }
1793
1794 // TODO: support multiple return in FastISel
1795 if (Ret->getNumOperands() > 1)
1796 return false;
1797
1798 Value *RV = Ret->getOperand(i_nocapture: 0);
1799 if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy())
1800 return false;
1801
1802 switch (getSimpleType(Ty: RV->getType())) {
1803 case MVT::i1:
1804 case MVT::i8:
1805 case MVT::i16:
1806 case MVT::i32:
1807 case MVT::i64:
1808 case MVT::f32:
1809 case MVT::f64:
1810 case MVT::v16i8:
1811 case MVT::v8i16:
1812 case MVT::v4i32:
1813 case MVT::v2i64:
1814 case MVT::v4f32:
1815 case MVT::v2f64:
1816 case MVT::funcref:
1817 case MVT::externref:
1818 case MVT::exnref:
1819 break;
1820 default:
1821 return false;
1822 }
1823
1824 unsigned Reg;
1825 if (FuncInfo.Fn->getAttributes().hasRetAttr(Kind: Attribute::SExt))
1826 Reg = getRegForSignedValue(V: RV);
1827 else if (FuncInfo.Fn->getAttributes().hasRetAttr(Kind: Attribute::ZExt))
1828 Reg = getRegForUnsignedValue(V: RV);
1829 else
1830 Reg = getRegForValue(V: RV);
1831
1832 if (Reg == 0)
1833 return false;
1834
1835 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::RETURN))
1836 .addReg(RegNo: Reg);
1837 return true;
1838}
1839
1840bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) {
1841 BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD,
1842 MCID: TII.get(Opcode: WebAssembly::UNREACHABLE));
1843 return true;
1844}
1845
1846bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) {
1847 switch (I->getOpcode()) {
1848 case Instruction::Call:
1849 if (selectCall(I))
1850 return true;
1851 break;
1852 case Instruction::Select:
1853 return selectSelect(I);
1854 case Instruction::Trunc:
1855 return selectTrunc(I);
1856 case Instruction::ZExt:
1857 return selectZExt(I);
1858 case Instruction::SExt:
1859 return selectSExt(I);
1860 case Instruction::ICmp:
1861 return selectICmp(I);
1862 case Instruction::FCmp:
1863 return selectFCmp(I);
1864 case Instruction::BitCast:
1865 return selectBitCast(I);
1866 case Instruction::Load:
1867 return selectLoad(I);
1868 case Instruction::Store:
1869 return selectStore(I);
1870 case Instruction::CondBr:
1871 return selectCondBr(I);
1872 case Instruction::Ret:
1873 return selectRet(I);
1874 case Instruction::Unreachable:
1875 return selectUnreachable(I);
1876 default:
1877 break;
1878 }
1879
1880 // Fall back to target-independent instruction selection.
1881 return selectOperator(I, Opcode: I->getOpcode());
1882}
1883
1884FastISel *
1885WebAssembly::createFastISel(FunctionLoweringInfo &FuncInfo,
1886 const TargetLibraryInfo *LibInfo,
1887 const LibcallLoweringInfo *LibcallLowering) {
1888 return new WebAssemblyFastISel(FuncInfo, LibInfo, LibcallLowering);
1889}
1890