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/WebAssemblyTypeUtilities.h" |
20 | #include "WebAssembly.h" |
21 | #include "WebAssemblyMachineFunctionInfo.h" |
22 | #include "WebAssemblySubtarget.h" |
23 | #include "WebAssemblyTargetMachine.h" |
24 | #include "WebAssemblyUtilities.h" |
25 | #include "llvm/Analysis/BranchProbabilityInfo.h" |
26 | #include "llvm/CodeGen/FastISel.h" |
27 | #include "llvm/CodeGen/FunctionLoweringInfo.h" |
28 | #include "llvm/CodeGen/MachineConstantPool.h" |
29 | #include "llvm/CodeGen/MachineFrameInfo.h" |
30 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
31 | #include "llvm/CodeGen/MachineModuleInfo.h" |
32 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
33 | #include "llvm/IR/DataLayout.h" |
34 | #include "llvm/IR/DerivedTypes.h" |
35 | #include "llvm/IR/Function.h" |
36 | #include "llvm/IR/GetElementPtrTypeIterator.h" |
37 | #include "llvm/IR/GlobalAlias.h" |
38 | #include "llvm/IR/GlobalVariable.h" |
39 | #include "llvm/IR/Instructions.h" |
40 | #include "llvm/IR/IntrinsicInst.h" |
41 | #include "llvm/IR/Operator.h" |
42 | #include "llvm/IR/PatternMatch.h" |
43 | |
44 | using namespace llvm; |
45 | using namespace PatternMatch; |
46 | |
47 | #define DEBUG_TYPE "wasm-fastisel" |
48 | |
49 | namespace { |
50 | |
51 | class WebAssemblyFastISel final : public FastISel { |
52 | // All possible address modes. |
53 | class Address { |
54 | public: |
55 | using BaseKind = enum { RegBase, FrameIndexBase }; |
56 | |
57 | private: |
58 | BaseKind Kind = RegBase; |
59 | union { |
60 | unsigned Reg; |
61 | int FI; |
62 | } Base; |
63 | |
64 | // Whether the base has been determined yet |
65 | bool IsBaseSet = false; |
66 | |
67 | int64_t Offset = 0; |
68 | |
69 | const GlobalValue *GV = nullptr; |
70 | |
71 | public: |
72 | // Innocuous defaults for our address. |
73 | Address() { Base.Reg = 0; } |
74 | void setKind(BaseKind K) { |
75 | assert(!isSet() && "Can't change kind with non-zero base" ); |
76 | Kind = K; |
77 | } |
78 | BaseKind getKind() const { return Kind; } |
79 | bool isRegBase() const { return Kind == RegBase; } |
80 | bool isFIBase() const { return Kind == FrameIndexBase; } |
81 | void setReg(unsigned Reg) { |
82 | assert(isRegBase() && "Invalid base register access!" ); |
83 | assert(!IsBaseSet && "Base cannot be reset" ); |
84 | Base.Reg = Reg; |
85 | IsBaseSet = true; |
86 | } |
87 | unsigned getReg() const { |
88 | assert(isRegBase() && "Invalid base register access!" ); |
89 | return Base.Reg; |
90 | } |
91 | void setFI(unsigned FI) { |
92 | assert(isFIBase() && "Invalid base frame index access!" ); |
93 | assert(!IsBaseSet && "Base cannot be reset" ); |
94 | Base.FI = FI; |
95 | IsBaseSet = true; |
96 | } |
97 | unsigned getFI() const { |
98 | assert(isFIBase() && "Invalid base frame index access!" ); |
99 | return Base.FI; |
100 | } |
101 | |
102 | void setOffset(int64_t NewOffset) { |
103 | assert(NewOffset >= 0 && "Offsets must be non-negative" ); |
104 | Offset = NewOffset; |
105 | } |
106 | int64_t getOffset() const { return Offset; } |
107 | void setGlobalValue(const GlobalValue *G) { GV = G; } |
108 | const GlobalValue *getGlobalValue() const { return GV; } |
109 | bool isSet() const { return IsBaseSet; } |
110 | }; |
111 | |
112 | /// Keep a pointer to the WebAssemblySubtarget around so that we can make the |
113 | /// right decision when generating code for different targets. |
114 | const WebAssemblySubtarget *Subtarget; |
115 | LLVMContext *Context; |
116 | |
117 | private: |
118 | // Utility helper routines |
119 | MVT::SimpleValueType getSimpleType(Type *Ty) { |
120 | EVT VT = TLI.getValueType(DL, Ty, /*AllowUnknown=*/true); |
121 | return VT.isSimple() ? VT.getSimpleVT().SimpleTy |
122 | : MVT::INVALID_SIMPLE_VALUE_TYPE; |
123 | } |
124 | MVT::SimpleValueType getLegalType(MVT::SimpleValueType VT) { |
125 | switch (VT) { |
126 | case MVT::i1: |
127 | case MVT::i8: |
128 | case MVT::i16: |
129 | return MVT::i32; |
130 | case MVT::i32: |
131 | case MVT::i64: |
132 | case MVT::f32: |
133 | case MVT::f64: |
134 | return VT; |
135 | case MVT::funcref: |
136 | case MVT::externref: |
137 | if (Subtarget->hasReferenceTypes()) |
138 | return VT; |
139 | break; |
140 | case MVT::exnref: |
141 | if (Subtarget->hasReferenceTypes() && Subtarget->hasExceptionHandling()) |
142 | return VT; |
143 | break; |
144 | case MVT::f16: |
145 | return MVT::f32; |
146 | case MVT::v16i8: |
147 | case MVT::v8i16: |
148 | case MVT::v4i32: |
149 | case MVT::v4f32: |
150 | case MVT::v2i64: |
151 | case MVT::v2f64: |
152 | if (Subtarget->hasSIMD128()) |
153 | return VT; |
154 | break; |
155 | default: |
156 | break; |
157 | } |
158 | return MVT::INVALID_SIMPLE_VALUE_TYPE; |
159 | } |
160 | bool computeAddress(const Value *Obj, Address &Addr); |
161 | void materializeLoadStoreOperands(Address &Addr); |
162 | void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, |
163 | MachineMemOperand *MMO); |
164 | unsigned maskI1Value(unsigned Reg, const Value *V); |
165 | unsigned getRegForI1Value(const Value *V, const BasicBlock *BB, bool &Not); |
166 | unsigned zeroExtendToI32(unsigned Reg, const Value *V, |
167 | MVT::SimpleValueType From); |
168 | unsigned signExtendToI32(unsigned Reg, const Value *V, |
169 | MVT::SimpleValueType From); |
170 | unsigned zeroExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From, |
171 | MVT::SimpleValueType To); |
172 | unsigned signExtend(unsigned Reg, const Value *V, MVT::SimpleValueType From, |
173 | MVT::SimpleValueType To); |
174 | unsigned getRegForUnsignedValue(const Value *V); |
175 | unsigned getRegForSignedValue(const Value *V); |
176 | unsigned getRegForPromotedValue(const Value *V, bool IsSigned); |
177 | unsigned notValue(unsigned Reg); |
178 | unsigned copyValue(unsigned Reg); |
179 | |
180 | // Backend specific FastISel code. |
181 | unsigned fastMaterializeAlloca(const AllocaInst *AI) override; |
182 | unsigned fastMaterializeConstant(const Constant *C) override; |
183 | bool fastLowerArguments() override; |
184 | |
185 | // Selection routines. |
186 | bool selectCall(const Instruction *I); |
187 | bool selectSelect(const Instruction *I); |
188 | bool selectTrunc(const Instruction *I); |
189 | bool selectZExt(const Instruction *I); |
190 | bool selectSExt(const Instruction *I); |
191 | bool selectICmp(const Instruction *I); |
192 | bool selectFCmp(const Instruction *I); |
193 | bool selectBitCast(const Instruction *I); |
194 | bool selectLoad(const Instruction *I); |
195 | bool selectStore(const Instruction *I); |
196 | bool selectBr(const Instruction *I); |
197 | bool selectRet(const Instruction *I); |
198 | bool selectUnreachable(const Instruction *I); |
199 | |
200 | public: |
201 | // Backend specific FastISel code. |
202 | WebAssemblyFastISel(FunctionLoweringInfo &FuncInfo, |
203 | const TargetLibraryInfo *LibInfo) |
204 | : FastISel(FuncInfo, LibInfo, /*SkipTargetIndependentISel=*/true) { |
205 | Subtarget = &FuncInfo.MF->getSubtarget<WebAssemblySubtarget>(); |
206 | Context = &FuncInfo.Fn->getContext(); |
207 | } |
208 | |
209 | bool fastSelectInstruction(const Instruction *I) override; |
210 | |
211 | #include "WebAssemblyGenFastISel.inc" |
212 | }; |
213 | |
214 | } // end anonymous namespace |
215 | |
216 | bool 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.MBBMap[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 | DenseMap<const AllocaInst *, int>::iterator SI = |
328 | FuncInfo.StaticAllocaMap.find(Val: AI); |
329 | if (SI != FuncInfo.StaticAllocaMap.end()) { |
330 | if (Addr.isSet()) { |
331 | return false; |
332 | } |
333 | Addr.setKind(Address::FrameIndexBase); |
334 | Addr.setFI(SI->second); |
335 | return true; |
336 | } |
337 | break; |
338 | } |
339 | case Instruction::Add: { |
340 | // Adds of constants are common and easy enough. |
341 | const Value *LHS = U->getOperand(i: 0); |
342 | const Value *RHS = U->getOperand(i: 1); |
343 | |
344 | if (isa<ConstantInt>(Val: LHS)) |
345 | std::swap(a&: LHS, b&: RHS); |
346 | |
347 | if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) { |
348 | uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue(); |
349 | if (int64_t(TmpOffset) >= 0) { |
350 | Addr.setOffset(TmpOffset); |
351 | return computeAddress(Obj: LHS, Addr); |
352 | } |
353 | } |
354 | |
355 | Address Backup = Addr; |
356 | if (computeAddress(Obj: LHS, Addr) && computeAddress(Obj: RHS, Addr)) |
357 | return true; |
358 | Addr = Backup; |
359 | |
360 | break; |
361 | } |
362 | case Instruction::Sub: { |
363 | // Subs of constants are common and easy enough. |
364 | const Value *LHS = U->getOperand(i: 0); |
365 | const Value *RHS = U->getOperand(i: 1); |
366 | |
367 | if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) { |
368 | int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue(); |
369 | if (TmpOffset >= 0) { |
370 | Addr.setOffset(TmpOffset); |
371 | return computeAddress(Obj: LHS, Addr); |
372 | } |
373 | } |
374 | break; |
375 | } |
376 | } |
377 | if (Addr.isSet()) { |
378 | return false; |
379 | } |
380 | Register Reg = getRegForValue(V: Obj); |
381 | if (Reg == 0) |
382 | return false; |
383 | Addr.setReg(Reg); |
384 | return Addr.getReg() != 0; |
385 | } |
386 | |
387 | void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) { |
388 | if (Addr.isRegBase()) { |
389 | unsigned Reg = Addr.getReg(); |
390 | if (Reg == 0) { |
391 | Reg = createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
392 | : &WebAssembly::I32RegClass); |
393 | unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
394 | : WebAssembly::CONST_I32; |
395 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: Reg) |
396 | .addImm(Val: 0); |
397 | Addr.setReg(Reg); |
398 | } |
399 | } |
400 | } |
401 | |
402 | void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr, |
403 | const MachineInstrBuilder &MIB, |
404 | MachineMemOperand *MMO) { |
405 | // Set the alignment operand (this is rewritten in SetP2AlignOperands). |
406 | // TODO: Disable SetP2AlignOperands for FastISel and just do it here. |
407 | MIB.addImm(Val: 0); |
408 | |
409 | if (const GlobalValue *GV = Addr.getGlobalValue()) |
410 | MIB.addGlobalAddress(GV, Offset: Addr.getOffset()); |
411 | else |
412 | MIB.addImm(Val: Addr.getOffset()); |
413 | |
414 | if (Addr.isRegBase()) |
415 | MIB.addReg(RegNo: Addr.getReg()); |
416 | else |
417 | MIB.addFrameIndex(Idx: Addr.getFI()); |
418 | |
419 | MIB.addMemOperand(MMO); |
420 | } |
421 | |
422 | unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) { |
423 | return zeroExtendToI32(Reg, V, From: MVT::i1); |
424 | } |
425 | |
426 | unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, |
427 | const BasicBlock *BB, |
428 | bool &Not) { |
429 | if (const auto *ICmp = dyn_cast<ICmpInst>(Val: V)) |
430 | if (const ConstantInt *C = dyn_cast<ConstantInt>(Val: ICmp->getOperand(i_nocapture: 1))) |
431 | if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(Bitwidth: 32) && |
432 | ICmp->getParent() == BB) { |
433 | Not = ICmp->isTrueWhenEqual(); |
434 | return getRegForValue(V: ICmp->getOperand(i_nocapture: 0)); |
435 | } |
436 | |
437 | Not = false; |
438 | Register Reg = getRegForValue(V); |
439 | if (Reg == 0) |
440 | return 0; |
441 | return maskI1Value(Reg, V); |
442 | } |
443 | |
444 | unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V, |
445 | MVT::SimpleValueType From) { |
446 | if (Reg == 0) |
447 | return 0; |
448 | |
449 | switch (From) { |
450 | case MVT::i1: |
451 | // If the value is naturally an i1, we don't need to mask it. We only know |
452 | // if a value is naturally an i1 if it is definitely lowered by FastISel, |
453 | // not a DAG ISel fallback. |
454 | if (V != nullptr && isa<Argument>(Val: V) && cast<Argument>(Val: V)->hasZExtAttr()) |
455 | return copyValue(Reg); |
456 | break; |
457 | case MVT::i8: |
458 | case MVT::i16: |
459 | break; |
460 | case MVT::i32: |
461 | return copyValue(Reg); |
462 | default: |
463 | return 0; |
464 | } |
465 | |
466 | Register Imm = createResultReg(RC: &WebAssembly::I32RegClass); |
467 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
468 | MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: Imm) |
469 | .addImm(Val: ~(~uint64_t(0) << MVT(From).getSizeInBits())); |
470 | |
471 | Register Result = createResultReg(RC: &WebAssembly::I32RegClass); |
472 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
473 | MCID: TII.get(Opcode: WebAssembly::AND_I32), DestReg: Result) |
474 | .addReg(RegNo: Reg) |
475 | .addReg(RegNo: Imm); |
476 | |
477 | return Result; |
478 | } |
479 | |
480 | unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V, |
481 | MVT::SimpleValueType From) { |
482 | if (Reg == 0) |
483 | return 0; |
484 | |
485 | switch (From) { |
486 | case MVT::i1: |
487 | case MVT::i8: |
488 | case MVT::i16: |
489 | break; |
490 | case MVT::i32: |
491 | return copyValue(Reg); |
492 | default: |
493 | return 0; |
494 | } |
495 | |
496 | Register Imm = createResultReg(RC: &WebAssembly::I32RegClass); |
497 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
498 | MCID: TII.get(Opcode: WebAssembly::CONST_I32), DestReg: Imm) |
499 | .addImm(Val: 32 - MVT(From).getSizeInBits()); |
500 | |
501 | Register Left = createResultReg(RC: &WebAssembly::I32RegClass); |
502 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
503 | MCID: TII.get(Opcode: WebAssembly::SHL_I32), DestReg: Left) |
504 | .addReg(RegNo: Reg) |
505 | .addReg(RegNo: Imm); |
506 | |
507 | Register Right = createResultReg(RC: &WebAssembly::I32RegClass); |
508 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
509 | MCID: TII.get(Opcode: WebAssembly::SHR_S_I32), DestReg: Right) |
510 | .addReg(RegNo: Left) |
511 | .addReg(RegNo: Imm); |
512 | |
513 | return Right; |
514 | } |
515 | |
516 | unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, |
517 | MVT::SimpleValueType From, |
518 | MVT::SimpleValueType To) { |
519 | if (To == MVT::i64) { |
520 | if (From == MVT::i64) |
521 | return copyValue(Reg); |
522 | |
523 | Reg = zeroExtendToI32(Reg, V, From); |
524 | |
525 | Register Result = createResultReg(RC: &WebAssembly::I64RegClass); |
526 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
527 | MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_U_I32), DestReg: Result) |
528 | .addReg(RegNo: Reg); |
529 | return Result; |
530 | } |
531 | |
532 | if (To == MVT::i32) |
533 | return zeroExtendToI32(Reg, V, From); |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, |
539 | MVT::SimpleValueType From, |
540 | MVT::SimpleValueType To) { |
541 | if (To == MVT::i64) { |
542 | if (From == MVT::i64) |
543 | return copyValue(Reg); |
544 | |
545 | Reg = signExtendToI32(Reg, V, From); |
546 | |
547 | Register Result = createResultReg(RC: &WebAssembly::I64RegClass); |
548 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
549 | MCID: TII.get(Opcode: WebAssembly::I64_EXTEND_S_I32), DestReg: Result) |
550 | .addReg(RegNo: Reg); |
551 | return Result; |
552 | } |
553 | |
554 | if (To == MVT::i32) |
555 | return signExtendToI32(Reg, V, From); |
556 | |
557 | return 0; |
558 | } |
559 | |
560 | unsigned WebAssemblyFastISel::getRegForUnsignedValue(const Value *V) { |
561 | MVT::SimpleValueType From = getSimpleType(Ty: V->getType()); |
562 | MVT::SimpleValueType To = getLegalType(VT: From); |
563 | Register VReg = getRegForValue(V); |
564 | if (VReg == 0) |
565 | return 0; |
566 | if (From == To) |
567 | return VReg; |
568 | return zeroExtend(Reg: VReg, V, From, To); |
569 | } |
570 | |
571 | unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) { |
572 | MVT::SimpleValueType From = getSimpleType(Ty: V->getType()); |
573 | MVT::SimpleValueType To = getLegalType(VT: From); |
574 | Register VReg = getRegForValue(V); |
575 | if (VReg == 0) |
576 | return 0; |
577 | if (From == To) |
578 | return VReg; |
579 | return signExtend(Reg: VReg, V, From, To); |
580 | } |
581 | |
582 | unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V, |
583 | bool IsSigned) { |
584 | return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V); |
585 | } |
586 | |
587 | unsigned WebAssemblyFastISel::notValue(unsigned Reg) { |
588 | assert(MRI.getRegClass(Reg) == &WebAssembly::I32RegClass); |
589 | |
590 | Register NotReg = createResultReg(RC: &WebAssembly::I32RegClass); |
591 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
592 | MCID: TII.get(Opcode: WebAssembly::EQZ_I32), DestReg: NotReg) |
593 | .addReg(RegNo: Reg); |
594 | return NotReg; |
595 | } |
596 | |
597 | unsigned WebAssemblyFastISel::copyValue(unsigned Reg) { |
598 | Register ResultReg = createResultReg(RC: MRI.getRegClass(Reg)); |
599 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::COPY), |
600 | DestReg: ResultReg) |
601 | .addReg(RegNo: Reg); |
602 | return ResultReg; |
603 | } |
604 | |
605 | unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { |
606 | DenseMap<const AllocaInst *, int>::iterator SI = |
607 | FuncInfo.StaticAllocaMap.find(Val: AI); |
608 | |
609 | if (SI != FuncInfo.StaticAllocaMap.end()) { |
610 | Register ResultReg = |
611 | createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
612 | : &WebAssembly::I32RegClass); |
613 | unsigned Opc = |
614 | Subtarget->hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32; |
615 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
616 | .addFrameIndex(Idx: SI->second); |
617 | return ResultReg; |
618 | } |
619 | |
620 | return 0; |
621 | } |
622 | |
623 | unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { |
624 | if (const GlobalValue *GV = dyn_cast<GlobalValue>(Val: C)) { |
625 | if (TLI.isPositionIndependent()) |
626 | return 0; |
627 | if (GV->isThreadLocal()) |
628 | return 0; |
629 | Register ResultReg = |
630 | createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
631 | : &WebAssembly::I32RegClass); |
632 | unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
633 | : WebAssembly::CONST_I32; |
634 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
635 | .addGlobalAddress(GV); |
636 | return ResultReg; |
637 | } |
638 | |
639 | // Let target-independent code handle it. |
640 | return 0; |
641 | } |
642 | |
643 | bool WebAssemblyFastISel::fastLowerArguments() { |
644 | if (!FuncInfo.CanLowerReturn) |
645 | return false; |
646 | |
647 | const Function *F = FuncInfo.Fn; |
648 | if (F->isVarArg()) |
649 | return false; |
650 | |
651 | if (FuncInfo.Fn->getCallingConv() == CallingConv::Swift) |
652 | return false; |
653 | |
654 | unsigned I = 0; |
655 | for (auto const &Arg : F->args()) { |
656 | const AttributeList &Attrs = F->getAttributes(); |
657 | if (Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::ByVal) || |
658 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftSelf) || |
659 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftError) || |
660 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::InAlloca) || |
661 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::Nest)) |
662 | return false; |
663 | |
664 | Type *ArgTy = Arg.getType(); |
665 | if (ArgTy->isStructTy() || ArgTy->isArrayTy()) |
666 | return false; |
667 | if (!Subtarget->hasSIMD128() && ArgTy->isVectorTy()) |
668 | return false; |
669 | |
670 | unsigned Opc; |
671 | const TargetRegisterClass *RC; |
672 | switch (getSimpleType(Ty: ArgTy)) { |
673 | case MVT::i1: |
674 | case MVT::i8: |
675 | case MVT::i16: |
676 | case MVT::i32: |
677 | Opc = WebAssembly::ARGUMENT_i32; |
678 | RC = &WebAssembly::I32RegClass; |
679 | break; |
680 | case MVT::i64: |
681 | Opc = WebAssembly::ARGUMENT_i64; |
682 | RC = &WebAssembly::I64RegClass; |
683 | break; |
684 | case MVT::f32: |
685 | Opc = WebAssembly::ARGUMENT_f32; |
686 | RC = &WebAssembly::F32RegClass; |
687 | break; |
688 | case MVT::f64: |
689 | Opc = WebAssembly::ARGUMENT_f64; |
690 | RC = &WebAssembly::F64RegClass; |
691 | break; |
692 | case MVT::v16i8: |
693 | Opc = WebAssembly::ARGUMENT_v16i8; |
694 | RC = &WebAssembly::V128RegClass; |
695 | break; |
696 | case MVT::v8i16: |
697 | Opc = WebAssembly::ARGUMENT_v8i16; |
698 | RC = &WebAssembly::V128RegClass; |
699 | break; |
700 | case MVT::v4i32: |
701 | Opc = WebAssembly::ARGUMENT_v4i32; |
702 | RC = &WebAssembly::V128RegClass; |
703 | break; |
704 | case MVT::v2i64: |
705 | Opc = WebAssembly::ARGUMENT_v2i64; |
706 | RC = &WebAssembly::V128RegClass; |
707 | break; |
708 | case MVT::v4f32: |
709 | Opc = WebAssembly::ARGUMENT_v4f32; |
710 | RC = &WebAssembly::V128RegClass; |
711 | break; |
712 | case MVT::v2f64: |
713 | Opc = WebAssembly::ARGUMENT_v2f64; |
714 | RC = &WebAssembly::V128RegClass; |
715 | break; |
716 | case MVT::funcref: |
717 | Opc = WebAssembly::ARGUMENT_funcref; |
718 | RC = &WebAssembly::FUNCREFRegClass; |
719 | break; |
720 | case MVT::externref: |
721 | Opc = WebAssembly::ARGUMENT_externref; |
722 | RC = &WebAssembly::EXTERNREFRegClass; |
723 | break; |
724 | case MVT::exnref: |
725 | Opc = WebAssembly::ARGUMENT_exnref; |
726 | RC = &WebAssembly::EXNREFRegClass; |
727 | break; |
728 | default: |
729 | return false; |
730 | } |
731 | Register ResultReg = createResultReg(RC); |
732 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
733 | .addImm(Val: I); |
734 | updateValueMap(I: &Arg, Reg: ResultReg); |
735 | |
736 | ++I; |
737 | } |
738 | |
739 | MRI.addLiveIn(Reg: WebAssembly::ARGUMENTS); |
740 | |
741 | auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>(); |
742 | for (auto const &Arg : F->args()) { |
743 | MVT::SimpleValueType ArgTy = getLegalType(VT: getSimpleType(Ty: Arg.getType())); |
744 | if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
745 | MFI->clearParamsAndResults(); |
746 | return false; |
747 | } |
748 | MFI->addParam(VT: ArgTy); |
749 | } |
750 | |
751 | if (!F->getReturnType()->isVoidTy()) { |
752 | MVT::SimpleValueType RetTy = |
753 | getLegalType(VT: getSimpleType(Ty: F->getReturnType())); |
754 | if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
755 | MFI->clearParamsAndResults(); |
756 | return false; |
757 | } |
758 | MFI->addResult(VT: RetTy); |
759 | } |
760 | |
761 | return true; |
762 | } |
763 | |
764 | bool WebAssemblyFastISel::selectCall(const Instruction *I) { |
765 | const auto *Call = cast<CallInst>(Val: I); |
766 | |
767 | // TODO: Support tail calls in FastISel |
768 | if (Call->isMustTailCall() || Call->isInlineAsm() || |
769 | Call->getFunctionType()->isVarArg()) |
770 | return false; |
771 | |
772 | Function *Func = Call->getCalledFunction(); |
773 | if (Func && Func->isIntrinsic()) |
774 | return false; |
775 | |
776 | if (Call->getCallingConv() == CallingConv::Swift) |
777 | return false; |
778 | |
779 | bool IsDirect = Func != nullptr; |
780 | if (!IsDirect && isa<ConstantExpr>(Val: Call->getCalledOperand())) |
781 | return false; |
782 | |
783 | FunctionType *FuncTy = Call->getFunctionType(); |
784 | unsigned Opc = IsDirect ? WebAssembly::CALL : WebAssembly::CALL_INDIRECT; |
785 | bool IsVoid = FuncTy->getReturnType()->isVoidTy(); |
786 | unsigned ResultReg; |
787 | if (!IsVoid) { |
788 | if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy()) |
789 | return false; |
790 | |
791 | MVT::SimpleValueType RetTy = getSimpleType(Ty: Call->getType()); |
792 | switch (RetTy) { |
793 | case MVT::i1: |
794 | case MVT::i8: |
795 | case MVT::i16: |
796 | case MVT::i32: |
797 | ResultReg = createResultReg(RC: &WebAssembly::I32RegClass); |
798 | break; |
799 | case MVT::i64: |
800 | ResultReg = createResultReg(RC: &WebAssembly::I64RegClass); |
801 | break; |
802 | case MVT::f32: |
803 | ResultReg = createResultReg(RC: &WebAssembly::F32RegClass); |
804 | break; |
805 | case MVT::f64: |
806 | ResultReg = createResultReg(RC: &WebAssembly::F64RegClass); |
807 | break; |
808 | case MVT::v16i8: |
809 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
810 | break; |
811 | case MVT::v8i16: |
812 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
813 | break; |
814 | case MVT::v4i32: |
815 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
816 | break; |
817 | case MVT::v2i64: |
818 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
819 | break; |
820 | case MVT::v4f32: |
821 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
822 | break; |
823 | case MVT::v2f64: |
824 | ResultReg = createResultReg(RC: &WebAssembly::V128RegClass); |
825 | break; |
826 | case MVT::funcref: |
827 | ResultReg = createResultReg(RC: &WebAssembly::FUNCREFRegClass); |
828 | break; |
829 | case MVT::externref: |
830 | ResultReg = createResultReg(RC: &WebAssembly::EXTERNREFRegClass); |
831 | break; |
832 | case MVT::exnref: |
833 | ResultReg = createResultReg(RC: &WebAssembly::EXNREFRegClass); |
834 | break; |
835 | default: |
836 | return false; |
837 | } |
838 | } |
839 | |
840 | SmallVector<unsigned, 8> Args; |
841 | for (unsigned I = 0, E = Call->arg_size(); I < E; ++I) { |
842 | Value *V = Call->getArgOperand(i: I); |
843 | MVT::SimpleValueType ArgTy = getSimpleType(Ty: V->getType()); |
844 | if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) |
845 | return false; |
846 | |
847 | const AttributeList &Attrs = Call->getAttributes(); |
848 | if (Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::ByVal) || |
849 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftSelf) || |
850 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::SwiftError) || |
851 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::InAlloca) || |
852 | Attrs.hasParamAttr(ArgNo: I, Kind: Attribute::Nest)) |
853 | return false; |
854 | |
855 | unsigned Reg; |
856 | |
857 | if (Call->paramHasAttr(ArgNo: I, Kind: Attribute::SExt)) |
858 | Reg = getRegForSignedValue(V); |
859 | else if (Call->paramHasAttr(ArgNo: I, Kind: Attribute::ZExt)) |
860 | Reg = getRegForUnsignedValue(V); |
861 | else |
862 | Reg = getRegForValue(V); |
863 | |
864 | if (Reg == 0) |
865 | return false; |
866 | |
867 | Args.push_back(Elt: Reg); |
868 | } |
869 | |
870 | unsigned CalleeReg = 0; |
871 | if (!IsDirect) { |
872 | CalleeReg = getRegForValue(V: Call->getCalledOperand()); |
873 | if (!CalleeReg) |
874 | return false; |
875 | } |
876 | |
877 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)); |
878 | |
879 | if (!IsVoid) |
880 | MIB.addReg(RegNo: ResultReg, flags: RegState::Define); |
881 | |
882 | if (IsDirect) { |
883 | MIB.addGlobalAddress(GV: Func); |
884 | } else { |
885 | // Placeholder for the type index. |
886 | MIB.addImm(Val: 0); |
887 | // The table into which this call_indirect indexes. |
888 | MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol( |
889 | Ctx&: MF->getContext(), Subtarget); |
890 | if (Subtarget->hasReferenceTypes()) { |
891 | MIB.addSym(Sym: Table); |
892 | } else { |
893 | // Otherwise for the MVP there is at most one table whose number is 0, but |
894 | // we can't write a table symbol or issue relocations. Instead we just |
895 | // ensure the table is live. |
896 | Table->setNoStrip(); |
897 | MIB.addImm(Val: 0); |
898 | } |
899 | } |
900 | |
901 | for (unsigned ArgReg : Args) |
902 | MIB.addReg(RegNo: ArgReg); |
903 | |
904 | if (!IsDirect) |
905 | MIB.addReg(RegNo: CalleeReg); |
906 | |
907 | if (!IsVoid) |
908 | updateValueMap(I: Call, Reg: ResultReg); |
909 | return true; |
910 | } |
911 | |
912 | bool WebAssemblyFastISel::selectSelect(const Instruction *I) { |
913 | const auto *Select = cast<SelectInst>(Val: I); |
914 | |
915 | bool Not; |
916 | unsigned CondReg = |
917 | getRegForI1Value(V: Select->getCondition(), BB: I->getParent(), Not); |
918 | if (CondReg == 0) |
919 | return false; |
920 | |
921 | Register TrueReg = getRegForValue(V: Select->getTrueValue()); |
922 | if (TrueReg == 0) |
923 | return false; |
924 | |
925 | Register FalseReg = getRegForValue(V: Select->getFalseValue()); |
926 | if (FalseReg == 0) |
927 | return false; |
928 | |
929 | if (Not) |
930 | std::swap(a&: TrueReg, b&: FalseReg); |
931 | |
932 | unsigned Opc; |
933 | const TargetRegisterClass *RC; |
934 | switch (getSimpleType(Ty: Select->getType())) { |
935 | case MVT::i1: |
936 | case MVT::i8: |
937 | case MVT::i16: |
938 | case MVT::i32: |
939 | Opc = WebAssembly::SELECT_I32; |
940 | RC = &WebAssembly::I32RegClass; |
941 | break; |
942 | case MVT::i64: |
943 | Opc = WebAssembly::SELECT_I64; |
944 | RC = &WebAssembly::I64RegClass; |
945 | break; |
946 | case MVT::f32: |
947 | Opc = WebAssembly::SELECT_F32; |
948 | RC = &WebAssembly::F32RegClass; |
949 | break; |
950 | case MVT::f64: |
951 | Opc = WebAssembly::SELECT_F64; |
952 | RC = &WebAssembly::F64RegClass; |
953 | break; |
954 | case MVT::funcref: |
955 | Opc = WebAssembly::SELECT_FUNCREF; |
956 | RC = &WebAssembly::FUNCREFRegClass; |
957 | break; |
958 | case MVT::externref: |
959 | Opc = WebAssembly::SELECT_EXTERNREF; |
960 | RC = &WebAssembly::EXTERNREFRegClass; |
961 | break; |
962 | case MVT::exnref: |
963 | Opc = WebAssembly::SELECT_EXNREF; |
964 | RC = &WebAssembly::EXNREFRegClass; |
965 | break; |
966 | default: |
967 | return false; |
968 | } |
969 | |
970 | Register ResultReg = createResultReg(RC); |
971 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
972 | .addReg(RegNo: TrueReg) |
973 | .addReg(RegNo: FalseReg) |
974 | .addReg(RegNo: CondReg); |
975 | |
976 | updateValueMap(I: Select, Reg: ResultReg); |
977 | return true; |
978 | } |
979 | |
980 | bool WebAssemblyFastISel::selectTrunc(const Instruction *I) { |
981 | const auto *Trunc = cast<TruncInst>(Val: I); |
982 | |
983 | Register Reg = getRegForValue(V: Trunc->getOperand(i_nocapture: 0)); |
984 | if (Reg == 0) |
985 | return false; |
986 | |
987 | if (Trunc->getOperand(i_nocapture: 0)->getType()->isIntegerTy(Bitwidth: 64)) { |
988 | Register Result = createResultReg(RC: &WebAssembly::I32RegClass); |
989 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
990 | MCID: TII.get(Opcode: WebAssembly::I32_WRAP_I64), DestReg: Result) |
991 | .addReg(RegNo: Reg); |
992 | Reg = Result; |
993 | } |
994 | |
995 | updateValueMap(I: Trunc, Reg); |
996 | return true; |
997 | } |
998 | |
999 | bool WebAssemblyFastISel::selectZExt(const Instruction *I) { |
1000 | const auto *ZExt = cast<ZExtInst>(Val: I); |
1001 | |
1002 | const Value *Op = ZExt->getOperand(i_nocapture: 0); |
1003 | MVT::SimpleValueType From = getSimpleType(Ty: Op->getType()); |
1004 | MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: ZExt->getType())); |
1005 | Register In = getRegForValue(V: Op); |
1006 | if (In == 0) |
1007 | return false; |
1008 | unsigned Reg = zeroExtend(Reg: In, V: Op, From, To); |
1009 | if (Reg == 0) |
1010 | return false; |
1011 | |
1012 | updateValueMap(I: ZExt, Reg); |
1013 | return true; |
1014 | } |
1015 | |
1016 | bool WebAssemblyFastISel::selectSExt(const Instruction *I) { |
1017 | const auto *SExt = cast<SExtInst>(Val: I); |
1018 | |
1019 | const Value *Op = SExt->getOperand(i_nocapture: 0); |
1020 | MVT::SimpleValueType From = getSimpleType(Ty: Op->getType()); |
1021 | MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: SExt->getType())); |
1022 | Register In = getRegForValue(V: Op); |
1023 | if (In == 0) |
1024 | return false; |
1025 | unsigned Reg = signExtend(Reg: In, V: Op, From, To); |
1026 | if (Reg == 0) |
1027 | return false; |
1028 | |
1029 | updateValueMap(I: SExt, Reg); |
1030 | return true; |
1031 | } |
1032 | |
1033 | bool WebAssemblyFastISel::selectICmp(const Instruction *I) { |
1034 | const auto *ICmp = cast<ICmpInst>(Val: I); |
1035 | |
1036 | bool I32 = getSimpleType(Ty: ICmp->getOperand(i_nocapture: 0)->getType()) != MVT::i64; |
1037 | unsigned Opc; |
1038 | bool IsSigned = false; |
1039 | switch (ICmp->getPredicate()) { |
1040 | case ICmpInst::ICMP_EQ: |
1041 | Opc = I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64; |
1042 | break; |
1043 | case ICmpInst::ICMP_NE: |
1044 | Opc = I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64; |
1045 | break; |
1046 | case ICmpInst::ICMP_UGT: |
1047 | Opc = I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64; |
1048 | break; |
1049 | case ICmpInst::ICMP_UGE: |
1050 | Opc = I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64; |
1051 | break; |
1052 | case ICmpInst::ICMP_ULT: |
1053 | Opc = I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64; |
1054 | break; |
1055 | case ICmpInst::ICMP_ULE: |
1056 | Opc = I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64; |
1057 | break; |
1058 | case ICmpInst::ICMP_SGT: |
1059 | Opc = I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64; |
1060 | IsSigned = true; |
1061 | break; |
1062 | case ICmpInst::ICMP_SGE: |
1063 | Opc = I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64; |
1064 | IsSigned = true; |
1065 | break; |
1066 | case ICmpInst::ICMP_SLT: |
1067 | Opc = I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64; |
1068 | IsSigned = true; |
1069 | break; |
1070 | case ICmpInst::ICMP_SLE: |
1071 | Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64; |
1072 | IsSigned = true; |
1073 | break; |
1074 | default: |
1075 | return false; |
1076 | } |
1077 | |
1078 | unsigned LHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 0), IsSigned); |
1079 | if (LHS == 0) |
1080 | return false; |
1081 | |
1082 | unsigned RHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 1), IsSigned); |
1083 | if (RHS == 0) |
1084 | return false; |
1085 | |
1086 | Register ResultReg = createResultReg(RC: &WebAssembly::I32RegClass); |
1087 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
1088 | .addReg(RegNo: LHS) |
1089 | .addReg(RegNo: RHS); |
1090 | updateValueMap(I: ICmp, Reg: ResultReg); |
1091 | return true; |
1092 | } |
1093 | |
1094 | bool WebAssemblyFastISel::selectFCmp(const Instruction *I) { |
1095 | const auto *FCmp = cast<FCmpInst>(Val: I); |
1096 | |
1097 | Register LHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 0)); |
1098 | if (LHS == 0) |
1099 | return false; |
1100 | |
1101 | Register RHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 1)); |
1102 | if (RHS == 0) |
1103 | return false; |
1104 | |
1105 | bool F32 = getSimpleType(Ty: FCmp->getOperand(i_nocapture: 0)->getType()) != MVT::f64; |
1106 | unsigned Opc; |
1107 | bool Not = false; |
1108 | switch (FCmp->getPredicate()) { |
1109 | case FCmpInst::FCMP_OEQ: |
1110 | Opc = F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64; |
1111 | break; |
1112 | case FCmpInst::FCMP_UNE: |
1113 | Opc = F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64; |
1114 | break; |
1115 | case FCmpInst::FCMP_OGT: |
1116 | Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
1117 | break; |
1118 | case FCmpInst::FCMP_OGE: |
1119 | Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
1120 | break; |
1121 | case FCmpInst::FCMP_OLT: |
1122 | Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
1123 | break; |
1124 | case FCmpInst::FCMP_OLE: |
1125 | Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
1126 | break; |
1127 | case FCmpInst::FCMP_UGT: |
1128 | Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
1129 | Not = true; |
1130 | break; |
1131 | case FCmpInst::FCMP_UGE: |
1132 | Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
1133 | Not = true; |
1134 | break; |
1135 | case FCmpInst::FCMP_ULT: |
1136 | Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
1137 | Not = true; |
1138 | break; |
1139 | case FCmpInst::FCMP_ULE: |
1140 | Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
1141 | Not = true; |
1142 | break; |
1143 | default: |
1144 | return false; |
1145 | } |
1146 | |
1147 | Register ResultReg = createResultReg(RC: &WebAssembly::I32RegClass); |
1148 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
1149 | .addReg(RegNo: LHS) |
1150 | .addReg(RegNo: RHS); |
1151 | |
1152 | if (Not) |
1153 | ResultReg = notValue(Reg: ResultReg); |
1154 | |
1155 | updateValueMap(I: FCmp, Reg: ResultReg); |
1156 | return true; |
1157 | } |
1158 | |
1159 | bool WebAssemblyFastISel::selectBitCast(const Instruction *I) { |
1160 | // Target-independent code can handle this, except it doesn't set the dead |
1161 | // flag on the ARGUMENTS clobber, so we have to do that manually in order |
1162 | // to satisfy code that expects this of isBitcast() instructions. |
1163 | EVT VT = TLI.getValueType(DL, Ty: I->getOperand(i: 0)->getType()); |
1164 | EVT RetVT = TLI.getValueType(DL, Ty: I->getType()); |
1165 | if (!VT.isSimple() || !RetVT.isSimple()) |
1166 | return false; |
1167 | |
1168 | Register In = getRegForValue(V: I->getOperand(i: 0)); |
1169 | if (In == 0) |
1170 | return false; |
1171 | |
1172 | if (VT == RetVT) { |
1173 | // No-op bitcast. |
1174 | updateValueMap(I, Reg: In); |
1175 | return true; |
1176 | } |
1177 | |
1178 | Register Reg = fastEmit_ISD_BITCAST_r(VT: VT.getSimpleVT(), RetVT: RetVT.getSimpleVT(), |
1179 | Op0: In); |
1180 | if (!Reg) |
1181 | return false; |
1182 | MachineBasicBlock::iterator Iter = FuncInfo.InsertPt; |
1183 | --Iter; |
1184 | assert(Iter->isBitcast()); |
1185 | Iter->setPhysRegsDeadExcept(UsedRegs: ArrayRef<Register>(), TRI); |
1186 | updateValueMap(I, Reg); |
1187 | return true; |
1188 | } |
1189 | |
1190 | bool WebAssemblyFastISel::selectLoad(const Instruction *I) { |
1191 | const auto *Load = cast<LoadInst>(Val: I); |
1192 | if (Load->isAtomic()) |
1193 | return false; |
1194 | if (!WebAssembly::isDefaultAddressSpace(AS: Load->getPointerAddressSpace())) |
1195 | return false; |
1196 | if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy()) |
1197 | return false; |
1198 | |
1199 | Address Addr; |
1200 | if (!computeAddress(Obj: Load->getPointerOperand(), Addr)) |
1201 | return false; |
1202 | |
1203 | // TODO: Fold a following sign-/zero-extend into the load instruction. |
1204 | |
1205 | unsigned Opc; |
1206 | const TargetRegisterClass *RC; |
1207 | bool A64 = Subtarget->hasAddr64(); |
1208 | switch (getSimpleType(Ty: Load->getType())) { |
1209 | case MVT::i1: |
1210 | case MVT::i8: |
1211 | Opc = A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32; |
1212 | RC = &WebAssembly::I32RegClass; |
1213 | break; |
1214 | case MVT::i16: |
1215 | Opc = A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32; |
1216 | RC = &WebAssembly::I32RegClass; |
1217 | break; |
1218 | case MVT::i32: |
1219 | Opc = A64 ? WebAssembly::LOAD_I32_A64 : WebAssembly::LOAD_I32_A32; |
1220 | RC = &WebAssembly::I32RegClass; |
1221 | break; |
1222 | case MVT::i64: |
1223 | Opc = A64 ? WebAssembly::LOAD_I64_A64 : WebAssembly::LOAD_I64_A32; |
1224 | RC = &WebAssembly::I64RegClass; |
1225 | break; |
1226 | case MVT::f32: |
1227 | Opc = A64 ? WebAssembly::LOAD_F32_A64 : WebAssembly::LOAD_F32_A32; |
1228 | RC = &WebAssembly::F32RegClass; |
1229 | break; |
1230 | case MVT::f64: |
1231 | Opc = A64 ? WebAssembly::LOAD_F64_A64 : WebAssembly::LOAD_F64_A32; |
1232 | RC = &WebAssembly::F64RegClass; |
1233 | break; |
1234 | default: |
1235 | return false; |
1236 | } |
1237 | |
1238 | materializeLoadStoreOperands(Addr); |
1239 | |
1240 | Register ResultReg = createResultReg(RC); |
1241 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), |
1242 | DestReg: ResultReg); |
1243 | |
1244 | addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Load)); |
1245 | |
1246 | updateValueMap(I: Load, Reg: ResultReg); |
1247 | return true; |
1248 | } |
1249 | |
1250 | bool WebAssemblyFastISel::selectStore(const Instruction *I) { |
1251 | const auto *Store = cast<StoreInst>(Val: I); |
1252 | if (Store->isAtomic()) |
1253 | return false; |
1254 | if (!WebAssembly::isDefaultAddressSpace(AS: Store->getPointerAddressSpace())) |
1255 | return false; |
1256 | if (!Subtarget->hasSIMD128() && |
1257 | Store->getValueOperand()->getType()->isVectorTy()) |
1258 | return false; |
1259 | |
1260 | Address Addr; |
1261 | if (!computeAddress(Obj: Store->getPointerOperand(), Addr)) |
1262 | return false; |
1263 | |
1264 | unsigned Opc; |
1265 | bool VTIsi1 = false; |
1266 | bool A64 = Subtarget->hasAddr64(); |
1267 | switch (getSimpleType(Ty: Store->getValueOperand()->getType())) { |
1268 | case MVT::i1: |
1269 | VTIsi1 = true; |
1270 | [[fallthrough]]; |
1271 | case MVT::i8: |
1272 | Opc = A64 ? WebAssembly::STORE8_I32_A64 : WebAssembly::STORE8_I32_A32; |
1273 | break; |
1274 | case MVT::i16: |
1275 | Opc = A64 ? WebAssembly::STORE16_I32_A64 : WebAssembly::STORE16_I32_A32; |
1276 | break; |
1277 | case MVT::i32: |
1278 | Opc = A64 ? WebAssembly::STORE_I32_A64 : WebAssembly::STORE_I32_A32; |
1279 | break; |
1280 | case MVT::i64: |
1281 | Opc = A64 ? WebAssembly::STORE_I64_A64 : WebAssembly::STORE_I64_A32; |
1282 | break; |
1283 | case MVT::f32: |
1284 | Opc = A64 ? WebAssembly::STORE_F32_A64 : WebAssembly::STORE_F32_A32; |
1285 | break; |
1286 | case MVT::f64: |
1287 | Opc = A64 ? WebAssembly::STORE_F64_A64 : WebAssembly::STORE_F64_A32; |
1288 | break; |
1289 | default: |
1290 | return false; |
1291 | } |
1292 | |
1293 | materializeLoadStoreOperands(Addr); |
1294 | |
1295 | Register ValueReg = getRegForValue(V: Store->getValueOperand()); |
1296 | if (ValueReg == 0) |
1297 | return false; |
1298 | if (VTIsi1) |
1299 | ValueReg = maskI1Value(Reg: ValueReg, V: Store->getValueOperand()); |
1300 | |
1301 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)); |
1302 | |
1303 | addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Store)); |
1304 | |
1305 | MIB.addReg(RegNo: ValueReg); |
1306 | return true; |
1307 | } |
1308 | |
1309 | bool WebAssemblyFastISel::selectBr(const Instruction *I) { |
1310 | const auto *Br = cast<BranchInst>(Val: I); |
1311 | if (Br->isUnconditional()) { |
1312 | MachineBasicBlock *MSucc = FuncInfo.MBBMap[Br->getSuccessor(i: 0)]; |
1313 | fastEmitBranch(MSucc, DbgLoc: Br->getDebugLoc()); |
1314 | return true; |
1315 | } |
1316 | |
1317 | MachineBasicBlock *TBB = FuncInfo.MBBMap[Br->getSuccessor(i: 0)]; |
1318 | MachineBasicBlock *FBB = FuncInfo.MBBMap[Br->getSuccessor(i: 1)]; |
1319 | |
1320 | bool Not; |
1321 | unsigned CondReg = getRegForI1Value(V: Br->getCondition(), BB: Br->getParent(), Not); |
1322 | if (CondReg == 0) |
1323 | return false; |
1324 | |
1325 | unsigned Opc = WebAssembly::BR_IF; |
1326 | if (Not) |
1327 | Opc = WebAssembly::BR_UNLESS; |
1328 | |
1329 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)) |
1330 | .addMBB(MBB: TBB) |
1331 | .addReg(RegNo: CondReg); |
1332 | |
1333 | finishCondBranch(BranchBB: Br->getParent(), TrueMBB: TBB, FalseMBB: FBB); |
1334 | return true; |
1335 | } |
1336 | |
1337 | bool WebAssemblyFastISel::selectRet(const Instruction *I) { |
1338 | if (!FuncInfo.CanLowerReturn) |
1339 | return false; |
1340 | |
1341 | const auto *Ret = cast<ReturnInst>(Val: I); |
1342 | |
1343 | if (Ret->getNumOperands() == 0) { |
1344 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
1345 | MCID: TII.get(Opcode: WebAssembly::RETURN)); |
1346 | return true; |
1347 | } |
1348 | |
1349 | // TODO: support multiple return in FastISel |
1350 | if (Ret->getNumOperands() > 1) |
1351 | return false; |
1352 | |
1353 | Value *RV = Ret->getOperand(i_nocapture: 0); |
1354 | if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy()) |
1355 | return false; |
1356 | |
1357 | switch (getSimpleType(Ty: RV->getType())) { |
1358 | case MVT::i1: |
1359 | case MVT::i8: |
1360 | case MVT::i16: |
1361 | case MVT::i32: |
1362 | case MVT::i64: |
1363 | case MVT::f32: |
1364 | case MVT::f64: |
1365 | case MVT::v16i8: |
1366 | case MVT::v8i16: |
1367 | case MVT::v4i32: |
1368 | case MVT::v2i64: |
1369 | case MVT::v4f32: |
1370 | case MVT::v2f64: |
1371 | case MVT::funcref: |
1372 | case MVT::externref: |
1373 | case MVT::exnref: |
1374 | break; |
1375 | default: |
1376 | return false; |
1377 | } |
1378 | |
1379 | unsigned Reg; |
1380 | if (FuncInfo.Fn->getAttributes().hasRetAttr(Kind: Attribute::SExt)) |
1381 | Reg = getRegForSignedValue(V: RV); |
1382 | else if (FuncInfo.Fn->getAttributes().hasRetAttr(Kind: Attribute::ZExt)) |
1383 | Reg = getRegForUnsignedValue(V: RV); |
1384 | else |
1385 | Reg = getRegForValue(V: RV); |
1386 | |
1387 | if (Reg == 0) |
1388 | return false; |
1389 | |
1390 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
1391 | MCID: TII.get(Opcode: WebAssembly::RETURN)) |
1392 | .addReg(RegNo: Reg); |
1393 | return true; |
1394 | } |
1395 | |
1396 | bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) { |
1397 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
1398 | MCID: TII.get(Opcode: WebAssembly::UNREACHABLE)); |
1399 | return true; |
1400 | } |
1401 | |
1402 | bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) { |
1403 | switch (I->getOpcode()) { |
1404 | case Instruction::Call: |
1405 | if (selectCall(I)) |
1406 | return true; |
1407 | break; |
1408 | case Instruction::Select: |
1409 | return selectSelect(I); |
1410 | case Instruction::Trunc: |
1411 | return selectTrunc(I); |
1412 | case Instruction::ZExt: |
1413 | return selectZExt(I); |
1414 | case Instruction::SExt: |
1415 | return selectSExt(I); |
1416 | case Instruction::ICmp: |
1417 | return selectICmp(I); |
1418 | case Instruction::FCmp: |
1419 | return selectFCmp(I); |
1420 | case Instruction::BitCast: |
1421 | return selectBitCast(I); |
1422 | case Instruction::Load: |
1423 | return selectLoad(I); |
1424 | case Instruction::Store: |
1425 | return selectStore(I); |
1426 | case Instruction::Br: |
1427 | return selectBr(I); |
1428 | case Instruction::Ret: |
1429 | return selectRet(I); |
1430 | case Instruction::Unreachable: |
1431 | return selectUnreachable(I); |
1432 | default: |
1433 | break; |
1434 | } |
1435 | |
1436 | // Fall back to target-independent instruction selection. |
1437 | return selectOperator(I, Opcode: I->getOpcode()); |
1438 | } |
1439 | |
1440 | FastISel *WebAssembly::createFastISel(FunctionLoweringInfo &FuncInfo, |
1441 | const TargetLibraryInfo *LibInfo) { |
1442 | return new WebAssemblyFastISel(FuncInfo, LibInfo); |
1443 | } |
1444 | |