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::f16: |
141 | return MVT::f32; |
142 | case MVT::v16i8: |
143 | case MVT::v8i16: |
144 | case MVT::v4i32: |
145 | case MVT::v4f32: |
146 | case MVT::v2i64: |
147 | case MVT::v2f64: |
148 | if (Subtarget->hasSIMD128()) |
149 | return VT; |
150 | break; |
151 | default: |
152 | break; |
153 | } |
154 | return MVT::INVALID_SIMPLE_VALUE_TYPE; |
155 | } |
156 | bool computeAddress(const Value *Obj, Address &Addr); |
157 | void materializeLoadStoreOperands(Address &Addr); |
158 | void addLoadStoreOperands(const Address &Addr, const MachineInstrBuilder &MIB, |
159 | MachineMemOperand *MMO); |
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 | unsigned fastMaterializeAlloca(const AllocaInst *AI) override; |
178 | unsigned 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 selectBr(const Instruction *I); |
193 | bool selectRet(const Instruction *I); |
194 | bool selectUnreachable(const Instruction *I); |
195 | |
196 | public: |
197 | // Backend specific FastISel code. |
198 | WebAssemblyFastISel(FunctionLoweringInfo &FuncInfo, |
199 | const TargetLibraryInfo *LibInfo) |
200 | : FastISel(FuncInfo, LibInfo, /*SkipTargetIndependentISel=*/true) { |
201 | Subtarget = &FuncInfo.MF->getSubtarget<WebAssemblySubtarget>(); |
202 | Context = &FuncInfo.Fn->getContext(); |
203 | } |
204 | |
205 | bool fastSelectInstruction(const Instruction *I) override; |
206 | |
207 | #include "WebAssemblyGenFastISel.inc" |
208 | }; |
209 | |
210 | } // end anonymous namespace |
211 | |
212 | bool WebAssemblyFastISel::computeAddress(const Value *Obj, Address &Addr) { |
213 | const User *U = nullptr; |
214 | unsigned Opcode = Instruction::UserOp1; |
215 | if (const auto *I = dyn_cast<Instruction>(Val: Obj)) { |
216 | // Don't walk into other basic blocks unless the object is an alloca from |
217 | // another block, otherwise it may not have a virtual register assigned. |
218 | if (FuncInfo.StaticAllocaMap.count(Val: static_cast<const AllocaInst *>(Obj)) || |
219 | FuncInfo.MBBMap[I->getParent()] == FuncInfo.MBB) { |
220 | Opcode = I->getOpcode(); |
221 | U = I; |
222 | } |
223 | } else if (const auto *C = dyn_cast<ConstantExpr>(Val: Obj)) { |
224 | Opcode = C->getOpcode(); |
225 | U = C; |
226 | } |
227 | |
228 | if (auto *Ty = dyn_cast<PointerType>(Val: Obj->getType())) |
229 | if (Ty->getAddressSpace() > 255) |
230 | // Fast instruction selection doesn't support the special |
231 | // address spaces. |
232 | return false; |
233 | |
234 | if (const auto *GV = dyn_cast<GlobalValue>(Val: Obj)) { |
235 | if (TLI.isPositionIndependent()) |
236 | return false; |
237 | if (Addr.getGlobalValue()) |
238 | return false; |
239 | if (GV->isThreadLocal()) |
240 | return false; |
241 | Addr.setGlobalValue(GV); |
242 | return true; |
243 | } |
244 | |
245 | switch (Opcode) { |
246 | default: |
247 | break; |
248 | case Instruction::BitCast: { |
249 | // Look through bitcasts. |
250 | return computeAddress(Obj: U->getOperand(i: 0), Addr); |
251 | } |
252 | case Instruction::IntToPtr: { |
253 | // Look past no-op inttoptrs. |
254 | if (TLI.getValueType(DL, Ty: U->getOperand(i: 0)->getType()) == |
255 | TLI.getPointerTy(DL)) |
256 | return computeAddress(Obj: U->getOperand(i: 0), Addr); |
257 | break; |
258 | } |
259 | case Instruction::PtrToInt: { |
260 | // Look past no-op ptrtoints. |
261 | if (TLI.getValueType(DL, Ty: U->getType()) == TLI.getPointerTy(DL)) |
262 | return computeAddress(Obj: U->getOperand(i: 0), Addr); |
263 | break; |
264 | } |
265 | case Instruction::GetElementPtr: { |
266 | Address SavedAddr = Addr; |
267 | uint64_t TmpOffset = Addr.getOffset(); |
268 | // Non-inbounds geps can wrap; wasm's offsets can't. |
269 | if (!cast<GEPOperator>(Val: U)->isInBounds()) |
270 | goto unsupported_gep; |
271 | // Iterate through the GEP folding the constants into offsets where |
272 | // we can. |
273 | for (gep_type_iterator GTI = gep_type_begin(GEP: U), E = gep_type_end(GEP: U); |
274 | GTI != E; ++GTI) { |
275 | const Value *Op = GTI.getOperand(); |
276 | if (StructType *STy = GTI.getStructTypeOrNull()) { |
277 | const StructLayout *SL = DL.getStructLayout(Ty: STy); |
278 | unsigned Idx = cast<ConstantInt>(Val: Op)->getZExtValue(); |
279 | TmpOffset += SL->getElementOffset(Idx); |
280 | } else { |
281 | uint64_t S = GTI.getSequentialElementStride(DL); |
282 | for (;;) { |
283 | if (const auto *CI = dyn_cast<ConstantInt>(Val: Op)) { |
284 | // Constant-offset addressing. |
285 | TmpOffset += CI->getSExtValue() * S; |
286 | break; |
287 | } |
288 | if (S == 1 && Addr.isRegBase() && Addr.getReg() == 0) { |
289 | // An unscaled add of a register. Set it as the new base. |
290 | Register Reg = getRegForValue(V: Op); |
291 | if (Reg == 0) |
292 | return false; |
293 | Addr.setReg(Reg); |
294 | break; |
295 | } |
296 | if (canFoldAddIntoGEP(GEP: U, Add: Op)) { |
297 | // A compatible add with a constant operand. Fold the constant. |
298 | auto *CI = cast<ConstantInt>(Val: cast<AddOperator>(Val: Op)->getOperand(i_nocapture: 1)); |
299 | TmpOffset += CI->getSExtValue() * S; |
300 | // Iterate on the other operand. |
301 | Op = cast<AddOperator>(Val: Op)->getOperand(i_nocapture: 0); |
302 | continue; |
303 | } |
304 | // Unsupported |
305 | goto unsupported_gep; |
306 | } |
307 | } |
308 | } |
309 | // Don't fold in negative offsets. |
310 | if (int64_t(TmpOffset) >= 0) { |
311 | // Try to grab the base operand now. |
312 | Addr.setOffset(TmpOffset); |
313 | if (computeAddress(Obj: U->getOperand(i: 0), Addr)) |
314 | return true; |
315 | } |
316 | // We failed, restore everything and try the other options. |
317 | Addr = SavedAddr; |
318 | unsupported_gep: |
319 | break; |
320 | } |
321 | case Instruction::Alloca: { |
322 | const auto *AI = cast<AllocaInst>(Val: Obj); |
323 | DenseMap<const AllocaInst *, int>::iterator SI = |
324 | FuncInfo.StaticAllocaMap.find(Val: AI); |
325 | if (SI != FuncInfo.StaticAllocaMap.end()) { |
326 | if (Addr.isSet()) { |
327 | return false; |
328 | } |
329 | Addr.setKind(Address::FrameIndexBase); |
330 | Addr.setFI(SI->second); |
331 | return true; |
332 | } |
333 | break; |
334 | } |
335 | case Instruction::Add: { |
336 | // Adds of constants are common and easy enough. |
337 | const Value *LHS = U->getOperand(i: 0); |
338 | const Value *RHS = U->getOperand(i: 1); |
339 | |
340 | if (isa<ConstantInt>(Val: LHS)) |
341 | std::swap(a&: LHS, b&: RHS); |
342 | |
343 | if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) { |
344 | uint64_t TmpOffset = Addr.getOffset() + CI->getSExtValue(); |
345 | if (int64_t(TmpOffset) >= 0) { |
346 | Addr.setOffset(TmpOffset); |
347 | return computeAddress(Obj: LHS, Addr); |
348 | } |
349 | } |
350 | |
351 | Address Backup = Addr; |
352 | if (computeAddress(Obj: LHS, Addr) && computeAddress(Obj: RHS, Addr)) |
353 | return true; |
354 | Addr = Backup; |
355 | |
356 | break; |
357 | } |
358 | case Instruction::Sub: { |
359 | // Subs of constants are common and easy enough. |
360 | const Value *LHS = U->getOperand(i: 0); |
361 | const Value *RHS = U->getOperand(i: 1); |
362 | |
363 | if (const auto *CI = dyn_cast<ConstantInt>(Val: RHS)) { |
364 | int64_t TmpOffset = Addr.getOffset() - CI->getSExtValue(); |
365 | if (TmpOffset >= 0) { |
366 | Addr.setOffset(TmpOffset); |
367 | return computeAddress(Obj: LHS, Addr); |
368 | } |
369 | } |
370 | break; |
371 | } |
372 | } |
373 | if (Addr.isSet()) { |
374 | return false; |
375 | } |
376 | Register Reg = getRegForValue(V: Obj); |
377 | if (Reg == 0) |
378 | return false; |
379 | Addr.setReg(Reg); |
380 | return Addr.getReg() != 0; |
381 | } |
382 | |
383 | void WebAssemblyFastISel::materializeLoadStoreOperands(Address &Addr) { |
384 | if (Addr.isRegBase()) { |
385 | unsigned Reg = Addr.getReg(); |
386 | if (Reg == 0) { |
387 | Reg = createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
388 | : &WebAssembly::I32RegClass); |
389 | unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
390 | : WebAssembly::CONST_I32; |
391 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: Reg) |
392 | .addImm(Val: 0); |
393 | Addr.setReg(Reg); |
394 | } |
395 | } |
396 | } |
397 | |
398 | void WebAssemblyFastISel::addLoadStoreOperands(const Address &Addr, |
399 | const MachineInstrBuilder &MIB, |
400 | MachineMemOperand *MMO) { |
401 | // Set the alignment operand (this is rewritten in SetP2AlignOperands). |
402 | // TODO: Disable SetP2AlignOperands for FastISel and just do it here. |
403 | MIB.addImm(Val: 0); |
404 | |
405 | if (const GlobalValue *GV = Addr.getGlobalValue()) |
406 | MIB.addGlobalAddress(GV, Offset: Addr.getOffset()); |
407 | else |
408 | MIB.addImm(Val: Addr.getOffset()); |
409 | |
410 | if (Addr.isRegBase()) |
411 | MIB.addReg(RegNo: Addr.getReg()); |
412 | else |
413 | MIB.addFrameIndex(Idx: Addr.getFI()); |
414 | |
415 | MIB.addMemOperand(MMO); |
416 | } |
417 | |
418 | unsigned WebAssemblyFastISel::maskI1Value(unsigned Reg, const Value *V) { |
419 | return zeroExtendToI32(Reg, V, MVT::From: i1); |
420 | } |
421 | |
422 | unsigned WebAssemblyFastISel::getRegForI1Value(const Value *V, |
423 | const BasicBlock *BB, |
424 | bool &Not) { |
425 | if (const auto *ICmp = dyn_cast<ICmpInst>(Val: V)) |
426 | if (const ConstantInt *C = dyn_cast<ConstantInt>(Val: ICmp->getOperand(i_nocapture: 1))) |
427 | if (ICmp->isEquality() && C->isZero() && C->getType()->isIntegerTy(Bitwidth: 32) && |
428 | ICmp->getParent() == BB) { |
429 | Not = ICmp->isTrueWhenEqual(); |
430 | return getRegForValue(V: ICmp->getOperand(i_nocapture: 0)); |
431 | } |
432 | |
433 | Not = false; |
434 | Register Reg = getRegForValue(V); |
435 | if (Reg == 0) |
436 | return 0; |
437 | return maskI1Value(Reg, V); |
438 | } |
439 | |
440 | unsigned WebAssemblyFastISel::zeroExtendToI32(unsigned Reg, const Value *V, |
441 | MVT::SimpleValueType From) { |
442 | if (Reg == 0) |
443 | return 0; |
444 | |
445 | switch (From) { |
446 | case MVT::i1: |
447 | // If the value is naturally an i1, we don't need to mask it. We only know |
448 | // if a value is naturally an i1 if it is definitely lowered by FastISel, |
449 | // not a DAG ISel fallback. |
450 | if (V != nullptr && isa<Argument>(Val: V) && cast<Argument>(Val: V)->hasZExtAttr()) |
451 | return copyValue(Reg); |
452 | break; |
453 | case MVT::i8: |
454 | case MVT::i16: |
455 | break; |
456 | case MVT::i32: |
457 | return copyValue(Reg); |
458 | default: |
459 | return 0; |
460 | } |
461 | |
462 | Register Imm = createResultReg(RC: &WebAssembly::I32RegClass); |
463 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
464 | TII.get(WebAssembly::Opcode: CONST_I32), Imm) |
465 | .addImm(~(~uint64_t(0) << MVT(From).getSizeInBits())); |
466 | |
467 | Register Result = createResultReg(RC: &WebAssembly::I32RegClass); |
468 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
469 | TII.get(WebAssembly::Opcode: AND_I32), Result) |
470 | .addReg(Reg) |
471 | .addReg(Imm); |
472 | |
473 | return Result; |
474 | } |
475 | |
476 | unsigned WebAssemblyFastISel::signExtendToI32(unsigned Reg, const Value *V, |
477 | MVT::SimpleValueType From) { |
478 | if (Reg == 0) |
479 | return 0; |
480 | |
481 | switch (From) { |
482 | case MVT::i1: |
483 | case MVT::i8: |
484 | case MVT::i16: |
485 | break; |
486 | case MVT::i32: |
487 | return copyValue(Reg); |
488 | default: |
489 | return 0; |
490 | } |
491 | |
492 | Register Imm = createResultReg(RC: &WebAssembly::I32RegClass); |
493 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
494 | TII.get(WebAssembly::Opcode: CONST_I32), Imm) |
495 | .addImm(32 - MVT(From).getSizeInBits()); |
496 | |
497 | Register Left = createResultReg(RC: &WebAssembly::I32RegClass); |
498 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, |
499 | MCID: TII.get(Opcode: WebAssembly::SHL_I32), DestReg: Left) |
500 | .addReg(RegNo: Reg) |
501 | .addReg(RegNo: Imm); |
502 | |
503 | Register Right = createResultReg(RC: &WebAssembly::I32RegClass); |
504 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
505 | TII.get(WebAssembly::Opcode: SHR_S_I32), Right) |
506 | .addReg(Left) |
507 | .addReg(Imm); |
508 | |
509 | return Right; |
510 | } |
511 | |
512 | unsigned WebAssemblyFastISel::zeroExtend(unsigned Reg, const Value *V, |
513 | MVT::SimpleValueType From, |
514 | MVT::SimpleValueType To) { |
515 | if (To == MVT::i64) { |
516 | if (From == MVT::i64) |
517 | return copyValue(Reg); |
518 | |
519 | Reg = zeroExtendToI32(Reg, V, From); |
520 | |
521 | Register Result = createResultReg(RC: &WebAssembly::I64RegClass); |
522 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
523 | TII.get(WebAssembly::Opcode: I64_EXTEND_U_I32), Result) |
524 | .addReg(Reg); |
525 | return Result; |
526 | } |
527 | |
528 | if (To == MVT::i32) |
529 | return zeroExtendToI32(Reg, V, From); |
530 | |
531 | return 0; |
532 | } |
533 | |
534 | unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V, |
535 | MVT::SimpleValueType From, |
536 | MVT::SimpleValueType To) { |
537 | if (To == MVT::i64) { |
538 | if (From == MVT::i64) |
539 | return copyValue(Reg); |
540 | |
541 | Reg = signExtendToI32(Reg, V, From); |
542 | |
543 | Register Result = createResultReg(RC: &WebAssembly::I64RegClass); |
544 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
545 | TII.get(WebAssembly::Opcode: I64_EXTEND_S_I32), Result) |
546 | .addReg(Reg); |
547 | return Result; |
548 | } |
549 | |
550 | if (To == MVT::i32) |
551 | return signExtendToI32(Reg, V, From); |
552 | |
553 | return 0; |
554 | } |
555 | |
556 | unsigned WebAssemblyFastISel::getRegForUnsignedValue(const Value *V) { |
557 | MVT::SimpleValueType From = getSimpleType(Ty: V->getType()); |
558 | MVT::SimpleValueType To = getLegalType(VT: From); |
559 | Register VReg = getRegForValue(V); |
560 | if (VReg == 0) |
561 | return 0; |
562 | if (From == To) |
563 | return VReg; |
564 | return zeroExtend(Reg: VReg, V, From, To); |
565 | } |
566 | |
567 | unsigned WebAssemblyFastISel::getRegForSignedValue(const Value *V) { |
568 | MVT::SimpleValueType From = getSimpleType(Ty: V->getType()); |
569 | MVT::SimpleValueType To = getLegalType(VT: From); |
570 | Register VReg = getRegForValue(V); |
571 | if (VReg == 0) |
572 | return 0; |
573 | if (From == To) |
574 | return VReg; |
575 | return signExtend(Reg: VReg, V, From, To); |
576 | } |
577 | |
578 | unsigned WebAssemblyFastISel::getRegForPromotedValue(const Value *V, |
579 | bool IsSigned) { |
580 | return IsSigned ? getRegForSignedValue(V) : getRegForUnsignedValue(V); |
581 | } |
582 | |
583 | unsigned WebAssemblyFastISel::notValue(unsigned Reg) { |
584 | assert(MRI.getRegClass(Reg) == &WebAssembly::I32RegClass); |
585 | |
586 | Register NotReg = createResultReg(RC: &WebAssembly::I32RegClass); |
587 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
588 | TII.get(WebAssembly::Opcode: EQZ_I32), NotReg) |
589 | .addReg(Reg); |
590 | return NotReg; |
591 | } |
592 | |
593 | unsigned WebAssemblyFastISel::copyValue(unsigned Reg) { |
594 | Register ResultReg = createResultReg(RC: MRI.getRegClass(Reg)); |
595 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: WebAssembly::COPY), |
596 | DestReg: ResultReg) |
597 | .addReg(RegNo: Reg); |
598 | return ResultReg; |
599 | } |
600 | |
601 | unsigned WebAssemblyFastISel::fastMaterializeAlloca(const AllocaInst *AI) { |
602 | DenseMap<const AllocaInst *, int>::iterator SI = |
603 | FuncInfo.StaticAllocaMap.find(Val: AI); |
604 | |
605 | if (SI != FuncInfo.StaticAllocaMap.end()) { |
606 | Register ResultReg = |
607 | createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
608 | : &WebAssembly::I32RegClass); |
609 | unsigned Opc = |
610 | Subtarget->hasAddr64() ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32; |
611 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
612 | .addFrameIndex(Idx: SI->second); |
613 | return ResultReg; |
614 | } |
615 | |
616 | return 0; |
617 | } |
618 | |
619 | unsigned WebAssemblyFastISel::fastMaterializeConstant(const Constant *C) { |
620 | if (const GlobalValue *GV = dyn_cast<GlobalValue>(Val: C)) { |
621 | if (TLI.isPositionIndependent()) |
622 | return 0; |
623 | if (GV->isThreadLocal()) |
624 | return 0; |
625 | Register ResultReg = |
626 | createResultReg(RC: Subtarget->hasAddr64() ? &WebAssembly::I64RegClass |
627 | : &WebAssembly::I32RegClass); |
628 | unsigned Opc = Subtarget->hasAddr64() ? WebAssembly::CONST_I64 |
629 | : WebAssembly::CONST_I32; |
630 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
631 | .addGlobalAddress(GV); |
632 | return ResultReg; |
633 | } |
634 | |
635 | // Let target-independent code handle it. |
636 | return 0; |
637 | } |
638 | |
639 | bool WebAssemblyFastISel::fastLowerArguments() { |
640 | if (!FuncInfo.CanLowerReturn) |
641 | return false; |
642 | |
643 | const Function *F = FuncInfo.Fn; |
644 | if (F->isVarArg()) |
645 | return false; |
646 | |
647 | if (FuncInfo.Fn->getCallingConv() == CallingConv::Swift) |
648 | return false; |
649 | |
650 | unsigned I = 0; |
651 | for (auto const &Arg : F->args()) { |
652 | const AttributeList &Attrs = F->getAttributes(); |
653 | if (Attrs.hasParamAttr(I, Attribute::ByVal) || |
654 | Attrs.hasParamAttr(I, Attribute::SwiftSelf) || |
655 | Attrs.hasParamAttr(I, Attribute::SwiftError) || |
656 | Attrs.hasParamAttr(I, Attribute::InAlloca) || |
657 | Attrs.hasParamAttr(I, Attribute::Nest)) |
658 | return false; |
659 | |
660 | Type *ArgTy = Arg.getType(); |
661 | if (ArgTy->isStructTy() || ArgTy->isArrayTy()) |
662 | return false; |
663 | if (!Subtarget->hasSIMD128() && ArgTy->isVectorTy()) |
664 | return false; |
665 | |
666 | unsigned Opc; |
667 | const TargetRegisterClass *RC; |
668 | switch (getSimpleType(Ty: ArgTy)) { |
669 | case MVT::i1: |
670 | case MVT::i8: |
671 | case MVT::i16: |
672 | case MVT::i32: |
673 | Opc = WebAssembly::ARGUMENT_i32; |
674 | RC = &WebAssembly::I32RegClass; |
675 | break; |
676 | case MVT::i64: |
677 | Opc = WebAssembly::ARGUMENT_i64; |
678 | RC = &WebAssembly::I64RegClass; |
679 | break; |
680 | case MVT::f32: |
681 | Opc = WebAssembly::ARGUMENT_f32; |
682 | RC = &WebAssembly::F32RegClass; |
683 | break; |
684 | case MVT::f64: |
685 | Opc = WebAssembly::ARGUMENT_f64; |
686 | RC = &WebAssembly::F64RegClass; |
687 | break; |
688 | case MVT::v16i8: |
689 | Opc = WebAssembly::ARGUMENT_v16i8; |
690 | RC = &WebAssembly::V128RegClass; |
691 | break; |
692 | case MVT::v8i16: |
693 | Opc = WebAssembly::ARGUMENT_v8i16; |
694 | RC = &WebAssembly::V128RegClass; |
695 | break; |
696 | case MVT::v4i32: |
697 | Opc = WebAssembly::ARGUMENT_v4i32; |
698 | RC = &WebAssembly::V128RegClass; |
699 | break; |
700 | case MVT::v2i64: |
701 | Opc = WebAssembly::ARGUMENT_v2i64; |
702 | RC = &WebAssembly::V128RegClass; |
703 | break; |
704 | case MVT::v4f32: |
705 | Opc = WebAssembly::ARGUMENT_v4f32; |
706 | RC = &WebAssembly::V128RegClass; |
707 | break; |
708 | case MVT::v2f64: |
709 | Opc = WebAssembly::ARGUMENT_v2f64; |
710 | RC = &WebAssembly::V128RegClass; |
711 | break; |
712 | case MVT::funcref: |
713 | Opc = WebAssembly::ARGUMENT_funcref; |
714 | RC = &WebAssembly::FUNCREFRegClass; |
715 | break; |
716 | case MVT::externref: |
717 | Opc = WebAssembly::ARGUMENT_externref; |
718 | RC = &WebAssembly::EXTERNREFRegClass; |
719 | break; |
720 | default: |
721 | return false; |
722 | } |
723 | Register ResultReg = createResultReg(RC); |
724 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
725 | .addImm(Val: I); |
726 | updateValueMap(I: &Arg, Reg: ResultReg); |
727 | |
728 | ++I; |
729 | } |
730 | |
731 | MRI.addLiveIn(WebAssembly::ARGUMENTS); |
732 | |
733 | auto *MFI = MF->getInfo<WebAssemblyFunctionInfo>(); |
734 | for (auto const &Arg : F->args()) { |
735 | MVT::SimpleValueType ArgTy = getLegalType(VT: getSimpleType(Ty: Arg.getType())); |
736 | if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
737 | MFI->clearParamsAndResults(); |
738 | return false; |
739 | } |
740 | MFI->addParam(VT: ArgTy); |
741 | } |
742 | |
743 | if (!F->getReturnType()->isVoidTy()) { |
744 | MVT::SimpleValueType RetTy = |
745 | getLegalType(VT: getSimpleType(Ty: F->getReturnType())); |
746 | if (RetTy == MVT::INVALID_SIMPLE_VALUE_TYPE) { |
747 | MFI->clearParamsAndResults(); |
748 | return false; |
749 | } |
750 | MFI->addResult(VT: RetTy); |
751 | } |
752 | |
753 | return true; |
754 | } |
755 | |
756 | bool WebAssemblyFastISel::selectCall(const Instruction *I) { |
757 | const auto *Call = cast<CallInst>(Val: I); |
758 | |
759 | // TODO: Support tail calls in FastISel |
760 | if (Call->isMustTailCall() || Call->isInlineAsm() || |
761 | Call->getFunctionType()->isVarArg()) |
762 | return false; |
763 | |
764 | Function *Func = Call->getCalledFunction(); |
765 | if (Func && Func->isIntrinsic()) |
766 | return false; |
767 | |
768 | if (Call->getCallingConv() == CallingConv::Swift) |
769 | return false; |
770 | |
771 | bool IsDirect = Func != nullptr; |
772 | if (!IsDirect && isa<ConstantExpr>(Val: Call->getCalledOperand())) |
773 | return false; |
774 | |
775 | FunctionType *FuncTy = Call->getFunctionType(); |
776 | unsigned Opc = IsDirect ? WebAssembly::CALL : WebAssembly::CALL_INDIRECT; |
777 | bool IsVoid = FuncTy->getReturnType()->isVoidTy(); |
778 | unsigned ResultReg; |
779 | if (!IsVoid) { |
780 | if (!Subtarget->hasSIMD128() && Call->getType()->isVectorTy()) |
781 | return false; |
782 | |
783 | MVT::SimpleValueType RetTy = getSimpleType(Ty: Call->getType()); |
784 | switch (RetTy) { |
785 | case MVT::i1: |
786 | case MVT::i8: |
787 | case MVT::i16: |
788 | case MVT::i32: |
789 | ResultReg = createResultReg(&WebAssembly::I32RegClass); |
790 | break; |
791 | case MVT::i64: |
792 | ResultReg = createResultReg(&WebAssembly::I64RegClass); |
793 | break; |
794 | case MVT::f32: |
795 | ResultReg = createResultReg(&WebAssembly::F32RegClass); |
796 | break; |
797 | case MVT::f64: |
798 | ResultReg = createResultReg(&WebAssembly::F64RegClass); |
799 | break; |
800 | case MVT::v16i8: |
801 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
802 | break; |
803 | case MVT::v8i16: |
804 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
805 | break; |
806 | case MVT::v4i32: |
807 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
808 | break; |
809 | case MVT::v2i64: |
810 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
811 | break; |
812 | case MVT::v4f32: |
813 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
814 | break; |
815 | case MVT::v2f64: |
816 | ResultReg = createResultReg(&WebAssembly::V128RegClass); |
817 | break; |
818 | case MVT::funcref: |
819 | ResultReg = createResultReg(&WebAssembly::FUNCREFRegClass); |
820 | break; |
821 | case MVT::externref: |
822 | ResultReg = createResultReg(&WebAssembly::EXTERNREFRegClass); |
823 | break; |
824 | default: |
825 | return false; |
826 | } |
827 | } |
828 | |
829 | SmallVector<unsigned, 8> Args; |
830 | for (unsigned I = 0, E = Call->arg_size(); I < E; ++I) { |
831 | Value *V = Call->getArgOperand(i: I); |
832 | MVT::SimpleValueType ArgTy = getSimpleType(Ty: V->getType()); |
833 | if (ArgTy == MVT::INVALID_SIMPLE_VALUE_TYPE) |
834 | return false; |
835 | |
836 | const AttributeList &Attrs = Call->getAttributes(); |
837 | if (Attrs.hasParamAttr(I, Attribute::ByVal) || |
838 | Attrs.hasParamAttr(I, Attribute::SwiftSelf) || |
839 | Attrs.hasParamAttr(I, Attribute::SwiftError) || |
840 | Attrs.hasParamAttr(I, Attribute::InAlloca) || |
841 | Attrs.hasParamAttr(I, Attribute::Nest)) |
842 | return false; |
843 | |
844 | unsigned Reg; |
845 | |
846 | if (Call->paramHasAttr(I, Attribute::SExt)) |
847 | Reg = getRegForSignedValue(V); |
848 | else if (Call->paramHasAttr(I, Attribute::ZExt)) |
849 | Reg = getRegForUnsignedValue(V); |
850 | else |
851 | Reg = getRegForValue(V); |
852 | |
853 | if (Reg == 0) |
854 | return false; |
855 | |
856 | Args.push_back(Elt: Reg); |
857 | } |
858 | |
859 | unsigned CalleeReg = 0; |
860 | if (!IsDirect) { |
861 | CalleeReg = getRegForValue(V: Call->getCalledOperand()); |
862 | if (!CalleeReg) |
863 | return false; |
864 | } |
865 | |
866 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)); |
867 | |
868 | if (!IsVoid) |
869 | MIB.addReg(RegNo: ResultReg, flags: RegState::Define); |
870 | |
871 | if (IsDirect) { |
872 | MIB.addGlobalAddress(GV: Func); |
873 | } else { |
874 | // Placeholder for the type index. |
875 | MIB.addImm(Val: 0); |
876 | // The table into which this call_indirect indexes. |
877 | MCSymbolWasm *Table = WebAssembly::getOrCreateFunctionTableSymbol( |
878 | Ctx&: MF->getMMI().getContext(), Subtarget); |
879 | if (Subtarget->hasReferenceTypes()) { |
880 | MIB.addSym(Sym: Table); |
881 | } else { |
882 | // Otherwise for the MVP there is at most one table whose number is 0, but |
883 | // we can't write a table symbol or issue relocations. Instead we just |
884 | // ensure the table is live. |
885 | Table->setNoStrip(); |
886 | MIB.addImm(Val: 0); |
887 | } |
888 | // See if we must truncate the function pointer. |
889 | // CALL_INDIRECT takes an i32, but in wasm64 we represent function pointers |
890 | // as 64-bit for uniformity with other pointer types. |
891 | // See also: WebAssemblyISelLowering.cpp: LowerCallResults |
892 | if (Subtarget->hasAddr64()) { |
893 | auto Wrap = BuildMI(*FuncInfo.MBB, std::prev(FuncInfo.InsertPt), MIMD, |
894 | TII.get(WebAssembly::I32_WRAP_I64)); |
895 | Register Reg32 = createResultReg(&WebAssembly::I32RegClass); |
896 | Wrap.addReg(Reg32, RegState::Define); |
897 | Wrap.addReg(CalleeReg); |
898 | CalleeReg = Reg32; |
899 | } |
900 | } |
901 | |
902 | for (unsigned ArgReg : Args) |
903 | MIB.addReg(RegNo: ArgReg); |
904 | |
905 | if (!IsDirect) |
906 | MIB.addReg(RegNo: CalleeReg); |
907 | |
908 | if (!IsVoid) |
909 | updateValueMap(I: Call, Reg: ResultReg); |
910 | return true; |
911 | } |
912 | |
913 | bool WebAssemblyFastISel::selectSelect(const Instruction *I) { |
914 | const auto *Select = cast<SelectInst>(Val: I); |
915 | |
916 | bool Not; |
917 | unsigned CondReg = |
918 | getRegForI1Value(V: Select->getCondition(), BB: I->getParent(), Not); |
919 | if (CondReg == 0) |
920 | return false; |
921 | |
922 | Register TrueReg = getRegForValue(V: Select->getTrueValue()); |
923 | if (TrueReg == 0) |
924 | return false; |
925 | |
926 | Register FalseReg = getRegForValue(V: Select->getFalseValue()); |
927 | if (FalseReg == 0) |
928 | return false; |
929 | |
930 | if (Not) |
931 | std::swap(a&: TrueReg, b&: FalseReg); |
932 | |
933 | unsigned Opc; |
934 | const TargetRegisterClass *RC; |
935 | switch (getSimpleType(Ty: Select->getType())) { |
936 | case MVT::i1: |
937 | case MVT::i8: |
938 | case MVT::i16: |
939 | case MVT::i32: |
940 | Opc = WebAssembly::SELECT_I32; |
941 | RC = &WebAssembly::I32RegClass; |
942 | break; |
943 | case MVT::i64: |
944 | Opc = WebAssembly::SELECT_I64; |
945 | RC = &WebAssembly::I64RegClass; |
946 | break; |
947 | case MVT::f32: |
948 | Opc = WebAssembly::SELECT_F32; |
949 | RC = &WebAssembly::F32RegClass; |
950 | break; |
951 | case MVT::f64: |
952 | Opc = WebAssembly::SELECT_F64; |
953 | RC = &WebAssembly::F64RegClass; |
954 | break; |
955 | case MVT::funcref: |
956 | Opc = WebAssembly::SELECT_FUNCREF; |
957 | RC = &WebAssembly::FUNCREFRegClass; |
958 | break; |
959 | case MVT::externref: |
960 | Opc = WebAssembly::SELECT_EXTERNREF; |
961 | RC = &WebAssembly::EXTERNREFRegClass; |
962 | break; |
963 | default: |
964 | return false; |
965 | } |
966 | |
967 | Register ResultReg = createResultReg(RC); |
968 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
969 | .addReg(RegNo: TrueReg) |
970 | .addReg(RegNo: FalseReg) |
971 | .addReg(RegNo: CondReg); |
972 | |
973 | updateValueMap(I: Select, Reg: ResultReg); |
974 | return true; |
975 | } |
976 | |
977 | bool WebAssemblyFastISel::selectTrunc(const Instruction *I) { |
978 | const auto *Trunc = cast<TruncInst>(Val: I); |
979 | |
980 | Register Reg = getRegForValue(V: Trunc->getOperand(i_nocapture: 0)); |
981 | if (Reg == 0) |
982 | return false; |
983 | |
984 | if (Trunc->getOperand(i_nocapture: 0)->getType()->isIntegerTy(Bitwidth: 64)) { |
985 | Register Result = createResultReg(&WebAssembly::I32RegClass); |
986 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
987 | TII.get(WebAssembly::I32_WRAP_I64), Result) |
988 | .addReg(Reg); |
989 | Reg = Result; |
990 | } |
991 | |
992 | updateValueMap(I: Trunc, Reg); |
993 | return true; |
994 | } |
995 | |
996 | bool WebAssemblyFastISel::selectZExt(const Instruction *I) { |
997 | const auto *ZExt = cast<ZExtInst>(Val: I); |
998 | |
999 | const Value *Op = ZExt->getOperand(i_nocapture: 0); |
1000 | MVT::SimpleValueType From = getSimpleType(Ty: Op->getType()); |
1001 | MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: ZExt->getType())); |
1002 | Register In = getRegForValue(V: Op); |
1003 | if (In == 0) |
1004 | return false; |
1005 | unsigned Reg = zeroExtend(Reg: In, V: Op, From, To); |
1006 | if (Reg == 0) |
1007 | return false; |
1008 | |
1009 | updateValueMap(I: ZExt, Reg); |
1010 | return true; |
1011 | } |
1012 | |
1013 | bool WebAssemblyFastISel::selectSExt(const Instruction *I) { |
1014 | const auto *SExt = cast<SExtInst>(Val: I); |
1015 | |
1016 | const Value *Op = SExt->getOperand(i_nocapture: 0); |
1017 | MVT::SimpleValueType From = getSimpleType(Ty: Op->getType()); |
1018 | MVT::SimpleValueType To = getLegalType(VT: getSimpleType(Ty: SExt->getType())); |
1019 | Register In = getRegForValue(V: Op); |
1020 | if (In == 0) |
1021 | return false; |
1022 | unsigned Reg = signExtend(Reg: In, V: Op, From, To); |
1023 | if (Reg == 0) |
1024 | return false; |
1025 | |
1026 | updateValueMap(I: SExt, Reg); |
1027 | return true; |
1028 | } |
1029 | |
1030 | bool WebAssemblyFastISel::selectICmp(const Instruction *I) { |
1031 | const auto *ICmp = cast<ICmpInst>(Val: I); |
1032 | |
1033 | bool I32 = getSimpleType(ICmp->getOperand(0)->getType()) != MVT::i64; |
1034 | unsigned Opc; |
1035 | bool IsSigned = false; |
1036 | switch (ICmp->getPredicate()) { |
1037 | case ICmpInst::ICMP_EQ: |
1038 | Opc = I32 ? WebAssembly::EQ_I32 : WebAssembly::EQ_I64; |
1039 | break; |
1040 | case ICmpInst::ICMP_NE: |
1041 | Opc = I32 ? WebAssembly::NE_I32 : WebAssembly::NE_I64; |
1042 | break; |
1043 | case ICmpInst::ICMP_UGT: |
1044 | Opc = I32 ? WebAssembly::GT_U_I32 : WebAssembly::GT_U_I64; |
1045 | break; |
1046 | case ICmpInst::ICMP_UGE: |
1047 | Opc = I32 ? WebAssembly::GE_U_I32 : WebAssembly::GE_U_I64; |
1048 | break; |
1049 | case ICmpInst::ICMP_ULT: |
1050 | Opc = I32 ? WebAssembly::LT_U_I32 : WebAssembly::LT_U_I64; |
1051 | break; |
1052 | case ICmpInst::ICMP_ULE: |
1053 | Opc = I32 ? WebAssembly::LE_U_I32 : WebAssembly::LE_U_I64; |
1054 | break; |
1055 | case ICmpInst::ICMP_SGT: |
1056 | Opc = I32 ? WebAssembly::GT_S_I32 : WebAssembly::GT_S_I64; |
1057 | IsSigned = true; |
1058 | break; |
1059 | case ICmpInst::ICMP_SGE: |
1060 | Opc = I32 ? WebAssembly::GE_S_I32 : WebAssembly::GE_S_I64; |
1061 | IsSigned = true; |
1062 | break; |
1063 | case ICmpInst::ICMP_SLT: |
1064 | Opc = I32 ? WebAssembly::LT_S_I32 : WebAssembly::LT_S_I64; |
1065 | IsSigned = true; |
1066 | break; |
1067 | case ICmpInst::ICMP_SLE: |
1068 | Opc = I32 ? WebAssembly::LE_S_I32 : WebAssembly::LE_S_I64; |
1069 | IsSigned = true; |
1070 | break; |
1071 | default: |
1072 | return false; |
1073 | } |
1074 | |
1075 | unsigned LHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 0), IsSigned); |
1076 | if (LHS == 0) |
1077 | return false; |
1078 | |
1079 | unsigned RHS = getRegForPromotedValue(V: ICmp->getOperand(i_nocapture: 1), IsSigned); |
1080 | if (RHS == 0) |
1081 | return false; |
1082 | |
1083 | Register ResultReg = createResultReg(&WebAssembly::I32RegClass); |
1084 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
1085 | .addReg(RegNo: LHS) |
1086 | .addReg(RegNo: RHS); |
1087 | updateValueMap(I: ICmp, Reg: ResultReg); |
1088 | return true; |
1089 | } |
1090 | |
1091 | bool WebAssemblyFastISel::selectFCmp(const Instruction *I) { |
1092 | const auto *FCmp = cast<FCmpInst>(Val: I); |
1093 | |
1094 | Register LHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 0)); |
1095 | if (LHS == 0) |
1096 | return false; |
1097 | |
1098 | Register RHS = getRegForValue(V: FCmp->getOperand(i_nocapture: 1)); |
1099 | if (RHS == 0) |
1100 | return false; |
1101 | |
1102 | bool F32 = getSimpleType(FCmp->getOperand(0)->getType()) != MVT::f64; |
1103 | unsigned Opc; |
1104 | bool Not = false; |
1105 | switch (FCmp->getPredicate()) { |
1106 | case FCmpInst::FCMP_OEQ: |
1107 | Opc = F32 ? WebAssembly::EQ_F32 : WebAssembly::EQ_F64; |
1108 | break; |
1109 | case FCmpInst::FCMP_UNE: |
1110 | Opc = F32 ? WebAssembly::NE_F32 : WebAssembly::NE_F64; |
1111 | break; |
1112 | case FCmpInst::FCMP_OGT: |
1113 | Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
1114 | break; |
1115 | case FCmpInst::FCMP_OGE: |
1116 | Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
1117 | break; |
1118 | case FCmpInst::FCMP_OLT: |
1119 | Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
1120 | break; |
1121 | case FCmpInst::FCMP_OLE: |
1122 | Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
1123 | break; |
1124 | case FCmpInst::FCMP_UGT: |
1125 | Opc = F32 ? WebAssembly::LE_F32 : WebAssembly::LE_F64; |
1126 | Not = true; |
1127 | break; |
1128 | case FCmpInst::FCMP_UGE: |
1129 | Opc = F32 ? WebAssembly::LT_F32 : WebAssembly::LT_F64; |
1130 | Not = true; |
1131 | break; |
1132 | case FCmpInst::FCMP_ULT: |
1133 | Opc = F32 ? WebAssembly::GE_F32 : WebAssembly::GE_F64; |
1134 | Not = true; |
1135 | break; |
1136 | case FCmpInst::FCMP_ULE: |
1137 | Opc = F32 ? WebAssembly::GT_F32 : WebAssembly::GT_F64; |
1138 | Not = true; |
1139 | break; |
1140 | default: |
1141 | return false; |
1142 | } |
1143 | |
1144 | Register ResultReg = createResultReg(&WebAssembly::I32RegClass); |
1145 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), DestReg: ResultReg) |
1146 | .addReg(RegNo: LHS) |
1147 | .addReg(RegNo: RHS); |
1148 | |
1149 | if (Not) |
1150 | ResultReg = notValue(Reg: ResultReg); |
1151 | |
1152 | updateValueMap(I: FCmp, Reg: ResultReg); |
1153 | return true; |
1154 | } |
1155 | |
1156 | bool WebAssemblyFastISel::selectBitCast(const Instruction *I) { |
1157 | // Target-independent code can handle this, except it doesn't set the dead |
1158 | // flag on the ARGUMENTS clobber, so we have to do that manually in order |
1159 | // to satisfy code that expects this of isBitcast() instructions. |
1160 | EVT VT = TLI.getValueType(DL, Ty: I->getOperand(i: 0)->getType()); |
1161 | EVT RetVT = TLI.getValueType(DL, Ty: I->getType()); |
1162 | if (!VT.isSimple() || !RetVT.isSimple()) |
1163 | return false; |
1164 | |
1165 | Register In = getRegForValue(V: I->getOperand(i: 0)); |
1166 | if (In == 0) |
1167 | return false; |
1168 | |
1169 | if (VT == RetVT) { |
1170 | // No-op bitcast. |
1171 | updateValueMap(I, Reg: In); |
1172 | return true; |
1173 | } |
1174 | |
1175 | Register Reg = fastEmit_ISD_BITCAST_r(VT.getSimpleVT(), RetVT.getSimpleVT(), |
1176 | In); |
1177 | if (!Reg) |
1178 | return false; |
1179 | MachineBasicBlock::iterator Iter = FuncInfo.InsertPt; |
1180 | --Iter; |
1181 | assert(Iter->isBitcast()); |
1182 | Iter->setPhysRegsDeadExcept(UsedRegs: ArrayRef<Register>(), TRI); |
1183 | updateValueMap(I, Reg); |
1184 | return true; |
1185 | } |
1186 | |
1187 | bool WebAssemblyFastISel::selectLoad(const Instruction *I) { |
1188 | const auto *Load = cast<LoadInst>(Val: I); |
1189 | if (Load->isAtomic()) |
1190 | return false; |
1191 | if (!WebAssembly::isDefaultAddressSpace(AS: Load->getPointerAddressSpace())) |
1192 | return false; |
1193 | if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy()) |
1194 | return false; |
1195 | |
1196 | Address Addr; |
1197 | if (!computeAddress(Obj: Load->getPointerOperand(), Addr)) |
1198 | return false; |
1199 | |
1200 | // TODO: Fold a following sign-/zero-extend into the load instruction. |
1201 | |
1202 | unsigned Opc; |
1203 | const TargetRegisterClass *RC; |
1204 | bool A64 = Subtarget->hasAddr64(); |
1205 | switch (getSimpleType(Ty: Load->getType())) { |
1206 | case MVT::i1: |
1207 | case MVT::i8: |
1208 | Opc = A64 ? WebAssembly::LOAD8_U_I32_A64 : WebAssembly::LOAD8_U_I32_A32; |
1209 | RC = &WebAssembly::I32RegClass; |
1210 | break; |
1211 | case MVT::i16: |
1212 | Opc = A64 ? WebAssembly::LOAD16_U_I32_A64 : WebAssembly::LOAD16_U_I32_A32; |
1213 | RC = &WebAssembly::I32RegClass; |
1214 | break; |
1215 | case MVT::i32: |
1216 | Opc = A64 ? WebAssembly::LOAD_I32_A64 : WebAssembly::LOAD_I32_A32; |
1217 | RC = &WebAssembly::I32RegClass; |
1218 | break; |
1219 | case MVT::i64: |
1220 | Opc = A64 ? WebAssembly::LOAD_I64_A64 : WebAssembly::LOAD_I64_A32; |
1221 | RC = &WebAssembly::I64RegClass; |
1222 | break; |
1223 | case MVT::f32: |
1224 | Opc = A64 ? WebAssembly::LOAD_F32_A64 : WebAssembly::LOAD_F32_A32; |
1225 | RC = &WebAssembly::F32RegClass; |
1226 | break; |
1227 | case MVT::f64: |
1228 | Opc = A64 ? WebAssembly::LOAD_F64_A64 : WebAssembly::LOAD_F64_A32; |
1229 | RC = &WebAssembly::F64RegClass; |
1230 | break; |
1231 | default: |
1232 | return false; |
1233 | } |
1234 | |
1235 | materializeLoadStoreOperands(Addr); |
1236 | |
1237 | Register ResultReg = createResultReg(RC); |
1238 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc), |
1239 | DestReg: ResultReg); |
1240 | |
1241 | addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Load)); |
1242 | |
1243 | updateValueMap(I: Load, Reg: ResultReg); |
1244 | return true; |
1245 | } |
1246 | |
1247 | bool WebAssemblyFastISel::selectStore(const Instruction *I) { |
1248 | const auto *Store = cast<StoreInst>(Val: I); |
1249 | if (Store->isAtomic()) |
1250 | return false; |
1251 | if (!WebAssembly::isDefaultAddressSpace(AS: Store->getPointerAddressSpace())) |
1252 | return false; |
1253 | if (!Subtarget->hasSIMD128() && |
1254 | Store->getValueOperand()->getType()->isVectorTy()) |
1255 | return false; |
1256 | |
1257 | Address Addr; |
1258 | if (!computeAddress(Obj: Store->getPointerOperand(), Addr)) |
1259 | return false; |
1260 | |
1261 | unsigned Opc; |
1262 | bool VTIsi1 = false; |
1263 | bool A64 = Subtarget->hasAddr64(); |
1264 | switch (getSimpleType(Ty: Store->getValueOperand()->getType())) { |
1265 | case MVT::i1: |
1266 | VTIsi1 = true; |
1267 | [[fallthrough]]; |
1268 | case MVT::i8: |
1269 | Opc = A64 ? WebAssembly::STORE8_I32_A64 : WebAssembly::STORE8_I32_A32; |
1270 | break; |
1271 | case MVT::i16: |
1272 | Opc = A64 ? WebAssembly::STORE16_I32_A64 : WebAssembly::STORE16_I32_A32; |
1273 | break; |
1274 | case MVT::i32: |
1275 | Opc = A64 ? WebAssembly::STORE_I32_A64 : WebAssembly::STORE_I32_A32; |
1276 | break; |
1277 | case MVT::i64: |
1278 | Opc = A64 ? WebAssembly::STORE_I64_A64 : WebAssembly::STORE_I64_A32; |
1279 | break; |
1280 | case MVT::f32: |
1281 | Opc = A64 ? WebAssembly::STORE_F32_A64 : WebAssembly::STORE_F32_A32; |
1282 | break; |
1283 | case MVT::f64: |
1284 | Opc = A64 ? WebAssembly::STORE_F64_A64 : WebAssembly::STORE_F64_A32; |
1285 | break; |
1286 | default: |
1287 | return false; |
1288 | } |
1289 | |
1290 | materializeLoadStoreOperands(Addr); |
1291 | |
1292 | Register ValueReg = getRegForValue(V: Store->getValueOperand()); |
1293 | if (ValueReg == 0) |
1294 | return false; |
1295 | if (VTIsi1) |
1296 | ValueReg = maskI1Value(Reg: ValueReg, V: Store->getValueOperand()); |
1297 | |
1298 | auto MIB = BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)); |
1299 | |
1300 | addLoadStoreOperands(Addr, MIB, MMO: createMachineMemOperandFor(I: Store)); |
1301 | |
1302 | MIB.addReg(RegNo: ValueReg); |
1303 | return true; |
1304 | } |
1305 | |
1306 | bool WebAssemblyFastISel::selectBr(const Instruction *I) { |
1307 | const auto *Br = cast<BranchInst>(Val: I); |
1308 | if (Br->isUnconditional()) { |
1309 | MachineBasicBlock *MSucc = FuncInfo.MBBMap[Br->getSuccessor(i: 0)]; |
1310 | fastEmitBranch(MSucc, DbgLoc: Br->getDebugLoc()); |
1311 | return true; |
1312 | } |
1313 | |
1314 | MachineBasicBlock *TBB = FuncInfo.MBBMap[Br->getSuccessor(i: 0)]; |
1315 | MachineBasicBlock *FBB = FuncInfo.MBBMap[Br->getSuccessor(i: 1)]; |
1316 | |
1317 | bool Not; |
1318 | unsigned CondReg = getRegForI1Value(V: Br->getCondition(), BB: Br->getParent(), Not); |
1319 | if (CondReg == 0) |
1320 | return false; |
1321 | |
1322 | unsigned Opc = WebAssembly::BR_IF; |
1323 | if (Not) |
1324 | Opc = WebAssembly::BR_UNLESS; |
1325 | |
1326 | BuildMI(BB&: *FuncInfo.MBB, I: FuncInfo.InsertPt, MIMD, MCID: TII.get(Opcode: Opc)) |
1327 | .addMBB(MBB: TBB) |
1328 | .addReg(RegNo: CondReg); |
1329 | |
1330 | finishCondBranch(BranchBB: Br->getParent(), TrueMBB: TBB, FalseMBB: FBB); |
1331 | return true; |
1332 | } |
1333 | |
1334 | bool WebAssemblyFastISel::selectRet(const Instruction *I) { |
1335 | if (!FuncInfo.CanLowerReturn) |
1336 | return false; |
1337 | |
1338 | const auto *Ret = cast<ReturnInst>(Val: I); |
1339 | |
1340 | if (Ret->getNumOperands() == 0) { |
1341 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
1342 | TII.get(WebAssembly::RETURN)); |
1343 | return true; |
1344 | } |
1345 | |
1346 | // TODO: support multiple return in FastISel |
1347 | if (Ret->getNumOperands() > 1) |
1348 | return false; |
1349 | |
1350 | Value *RV = Ret->getOperand(i_nocapture: 0); |
1351 | if (!Subtarget->hasSIMD128() && RV->getType()->isVectorTy()) |
1352 | return false; |
1353 | |
1354 | switch (getSimpleType(Ty: RV->getType())) { |
1355 | case MVT::i1: |
1356 | case MVT::i8: |
1357 | case MVT::i16: |
1358 | case MVT::i32: |
1359 | case MVT::i64: |
1360 | case MVT::f32: |
1361 | case MVT::f64: |
1362 | case MVT::v16i8: |
1363 | case MVT::v8i16: |
1364 | case MVT::v4i32: |
1365 | case MVT::v2i64: |
1366 | case MVT::v4f32: |
1367 | case MVT::v2f64: |
1368 | case MVT::funcref: |
1369 | case MVT::externref: |
1370 | break; |
1371 | default: |
1372 | return false; |
1373 | } |
1374 | |
1375 | unsigned Reg; |
1376 | if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::SExt)) |
1377 | Reg = getRegForSignedValue(V: RV); |
1378 | else if (FuncInfo.Fn->getAttributes().hasRetAttr(Attribute::ZExt)) |
1379 | Reg = getRegForUnsignedValue(V: RV); |
1380 | else |
1381 | Reg = getRegForValue(V: RV); |
1382 | |
1383 | if (Reg == 0) |
1384 | return false; |
1385 | |
1386 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
1387 | TII.get(WebAssembly::RETURN)) |
1388 | .addReg(Reg); |
1389 | return true; |
1390 | } |
1391 | |
1392 | bool WebAssemblyFastISel::selectUnreachable(const Instruction *I) { |
1393 | BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, |
1394 | TII.get(WebAssembly::UNREACHABLE)); |
1395 | return true; |
1396 | } |
1397 | |
1398 | bool WebAssemblyFastISel::fastSelectInstruction(const Instruction *I) { |
1399 | switch (I->getOpcode()) { |
1400 | case Instruction::Call: |
1401 | if (selectCall(I)) |
1402 | return true; |
1403 | break; |
1404 | case Instruction::Select: |
1405 | return selectSelect(I); |
1406 | case Instruction::Trunc: |
1407 | return selectTrunc(I); |
1408 | case Instruction::ZExt: |
1409 | return selectZExt(I); |
1410 | case Instruction::SExt: |
1411 | return selectSExt(I); |
1412 | case Instruction::ICmp: |
1413 | return selectICmp(I); |
1414 | case Instruction::FCmp: |
1415 | return selectFCmp(I); |
1416 | case Instruction::BitCast: |
1417 | return selectBitCast(I); |
1418 | case Instruction::Load: |
1419 | return selectLoad(I); |
1420 | case Instruction::Store: |
1421 | return selectStore(I); |
1422 | case Instruction::Br: |
1423 | return selectBr(I); |
1424 | case Instruction::Ret: |
1425 | return selectRet(I); |
1426 | case Instruction::Unreachable: |
1427 | return selectUnreachable(I); |
1428 | default: |
1429 | break; |
1430 | } |
1431 | |
1432 | // Fall back to target-independent instruction selection. |
1433 | return selectOperator(I, Opcode: I->getOpcode()); |
1434 | } |
1435 | |
1436 | FastISel *WebAssembly::createFastISel(FunctionLoweringInfo &FuncInfo, |
1437 | const TargetLibraryInfo *LibInfo) { |
1438 | return new WebAssemblyFastISel(FuncInfo, LibInfo); |
1439 | } |
1440 | |