LLVM 24.0.0git
WebAssemblyISelLowering.cpp
Go to the documentation of this file.
1//=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering 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 implements the WebAssemblyTargetLowering class.
11///
12//===----------------------------------------------------------------------===//
13
32#include "llvm/IR/Function.h"
33#include "llvm/IR/Intrinsics.h"
34#include "llvm/IR/IntrinsicsWebAssembly.h"
39using namespace llvm;
40
41#define DEBUG_TYPE "wasm-lower"
42
44 const TargetMachine &TM, const WebAssemblySubtarget &STI)
45 : TargetLowering(TM, STI), Subtarget(&STI) {
46 auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32;
47
48 // Set the load count for memcmp expand optimization
51
52 // Booleans always contain 0 or 1.
54 // Except in SIMD vectors
56 // We don't know the microarchitecture here, so just reduce register pressure.
58 // Tell ISel that we have a stack pointer.
60 Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32);
61 // Set up the register classes.
62 addRegisterClass(MVT::i32, &WebAssembly::I32RegClass);
63 addRegisterClass(MVT::i64, &WebAssembly::I64RegClass);
64 addRegisterClass(MVT::f32, &WebAssembly::F32RegClass);
65 addRegisterClass(MVT::f64, &WebAssembly::F64RegClass);
66 if (Subtarget->hasSIMD128()) {
67 addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass);
68 addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass);
69 addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass);
70 addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass);
71 addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass);
72 addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass);
73 }
74 if (Subtarget->hasFP16()) {
75 addRegisterClass(MVT::v8f16, &WebAssembly::V128RegClass);
76 }
77 if (Subtarget->hasReferenceTypes()) {
78 addRegisterClass(MVT::externref, &WebAssembly::EXTERNREFRegClass);
79 addRegisterClass(MVT::funcref, &WebAssembly::FUNCREFRegClass);
80 if (Subtarget->hasExceptionHandling()) {
81 addRegisterClass(MVT::exnref, &WebAssembly::EXNREFRegClass);
82 }
83 }
84 // Compute derived properties from the register classes.
85 computeRegisterProperties(Subtarget->getRegisterInfo());
86
87 // Transform loads and stores to pointers in address space 1 to loads and
88 // stores to WebAssembly global variables, outside linear memory.
89 for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) {
92 }
93 if (Subtarget->hasSIMD128()) {
94 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
95 MVT::v2f64}) {
98 }
99 }
100 if (Subtarget->hasFP16()) {
101 setOperationAction(ISD::LOAD, MVT::v8f16, Custom);
103 }
104 if (Subtarget->hasReferenceTypes()) {
105 // We need custom load and store lowering for both externref, funcref and
106 // Other. The MVT::Other here represents tables of reference types.
107 for (auto T : {MVT::externref, MVT::funcref, MVT::Other}) {
110 }
111 }
112
120
121 // Take the default expansion for va_arg, va_copy, and va_end. There is no
122 // default action for va_start, so we do that custom.
127
128 for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64, MVT::v8f16}) {
129 if (!Subtarget->hasFP16() && T == MVT::v8f16) {
130 continue;
131 }
132 // Don't expand the floating-point types to constant pools.
134 // Expand floating-point comparisons.
138 // Expand floating-point library function operators.
141 // Expand vector FREM, but use a libcall rather than an expansion for scalar
142 if (MVT(T).isVector())
144 else
146 // Note supported floating-point library function operators that otherwise
147 // default to expand.
151 // Support minimum and maximum, which otherwise default to expand.
154 if (Subtarget->hasSIMD128() && MVT(T).isVector()) {
157 }
158 // When experimental v8f16 support is enabled these instructions don't need
159 // to be expanded.
160 if (T != MVT::v8f16) {
163 }
164 if (Subtarget->hasFP16() && T == MVT::f32) {
166 setTruncStoreAction(T, MVT::f16, Legal);
167 } else {
169 setTruncStoreAction(T, MVT::f16, Expand);
170 }
171 }
172
173 // Expand unavailable integer operations.
174 for (auto Op :
178 for (auto T : {MVT::i32, MVT::i64})
180 if (Subtarget->hasSIMD128())
181 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64})
183 }
184
185 if (Subtarget->hasWideArithmetic()) {
191 }
192
193 if (Subtarget->hasNontrappingFPToInt())
195 for (auto T : {MVT::i32, MVT::i64})
197
198 if (Subtarget->hasRelaxedSIMD()) {
201 {MVT::v4f32, MVT::v2f64}, Custom);
202 }
203
204 // Combine expands these operations, because wasi-libc and emscripten do not
205 // yet have the dedicated libcalls.
208
209 // SIMD-specific configuration
210 if (Subtarget->hasSIMD128()) {
211
213
214 // Combine wide-vector muls, with extend inputs, to extmul_half.
217
218 // Combine vector mask reductions into alltrue/anytrue
220
221 // Convert vector to integer bitcasts to bitmask
223
224 // Hoist bitcasts out of shuffles
226
227 // Combine extends of extract_subvectors into widening ops
229
230 // Combine int_to_fp or fp_extend of extract_vectors and vice versa into
231 // conversions ops
234
235 // Combine fp_to_{s,u}int_sat or fp_round of concat_vectors or vice versa
236 // into conversion ops
240
242
243 // Support saturating add/sub for i8x16 and i16x8
245 for (auto T : {MVT::v16i8, MVT::v8i16})
247
248 // Support integer abs
249 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64})
251
252 // Custom lower BUILD_VECTORs to minimize number of replace_lanes
253 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
254 MVT::v2f64})
256
257 if (Subtarget->hasFP16()) {
261 }
262
263 // We have custom shuffle lowering to expose the shuffle mask
264 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
265 MVT::v2f64})
267
268 if (Subtarget->hasFP16())
270
271 // Support splatting
272 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
273 MVT::v2f64})
275
276 setOperationAction(ISD::AVGCEILU, {MVT::v8i16, MVT::v16i8}, Legal);
277
278 // Custom lowering since wasm shifts must have a scalar shift amount
279 for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL})
280 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64})
282
283 // Custom lower lane accesses to expand out variable indices
285 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
286 MVT::v2f64})
288
289 // There is no i8x16.mul instruction
290 setOperationAction(ISD::MUL, MVT::v16i8, Expand);
291
292 // Expand integer operations supported for scalars but not SIMD
293 for (auto Op :
295 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64})
297
298 // But we do have integer min and max operations
299 for (auto Op : {ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX})
300 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32})
302
303 // And we have popcnt for i8x16. It can be used to expand ctlz/cttz.
304 setOperationAction(ISD::CTPOP, MVT::v16i8, Legal);
305 setOperationAction(ISD::CTLZ, MVT::v16i8, Expand);
306 setOperationAction(ISD::CTTZ, MVT::v16i8, Expand);
307
308 // Custom lower bit counting operations for other types to scalarize them.
309 for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP})
310 for (auto T : {MVT::v8i16, MVT::v4i32, MVT::v2i64})
312
313 // Expand float operations supported for scalars but not SIMD
316 for (auto T : {MVT::v4f32, MVT::v2f64})
318
319 // Unsigned comparison operations are unavailable for i64x2 vectors.
321 setCondCodeAction(CC, MVT::v2i64, Custom);
322
323 // 64x2 conversions are not in the spec
324 for (auto Op :
326 for (auto T : {MVT::v2i64, MVT::v2f64})
328
329 // But saturating fp_to_int conversions are
331 setOperationAction(Op, MVT::v4i32, Custom);
332 if (Subtarget->hasFP16()) {
333 setOperationAction(Op, MVT::v8i16, Custom);
334 }
335 }
336
337 // Support vector extending
342 }
343
344 if (Subtarget->hasFP16()) {
345 setOperationAction(ISD::FMA, MVT::v8f16, Legal);
346 }
347
348 if (Subtarget->hasRelaxedSIMD()) {
351 }
352
353 // Partial MLA reductions.
355 setPartialReduceMLAAction(Op, MVT::v4i32, MVT::v16i8, Legal);
356 setPartialReduceMLAAction(Op, MVT::v4i32, MVT::v8i16, Legal);
357 }
358 }
359
360 // As a special case, these operators use the type to mean the type to
361 // sign-extend from.
363 if (!Subtarget->hasSignExt()) {
364 // Sign extends are legal only when extending a vector extract
365 auto Action = Subtarget->hasSIMD128() ? Custom : Expand;
366 for (auto T : {MVT::i8, MVT::i16, MVT::i32})
368 }
371
372 // Dynamic stack allocation: use the default expansion.
376
380
381 // Expand these forms; we pattern-match the forms that we can handle in isel.
382 for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
383 for (auto Op : {ISD::BR_CC, ISD::SELECT_CC})
385
386 if (Subtarget->hasReferenceTypes())
387 for (auto Op : {ISD::BR_CC, ISD::SELECT_CC})
388 for (auto T : {MVT::externref, MVT::funcref})
390
391 // There is no vector conditional select instruction
392 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
393 MVT::v2f64, MVT::v8f16})
395
396 // We have custom switch handling.
398
399 // WebAssembly doesn't have:
400 // - Floating-point extending loads.
401 // - Floating-point truncating stores.
402 // - i1 extending loads.
403 // - truncating SIMD stores and most extending loads
404 setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand);
405 setTruncStoreAction(MVT::f64, MVT::f32, Expand);
406 for (auto T : MVT::integer_valuetypes())
407 for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
408 setLoadExtAction(Ext, T, MVT::i1, Promote);
409 if (Subtarget->hasSIMD128()) {
410 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64, MVT::v4f32,
411 MVT::v2f64}) {
412 for (auto MemT : MVT::fixedlen_vector_valuetypes()) {
413 if (MVT(T) != MemT) {
415 for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
416 setLoadExtAction(Ext, T, MemT, Expand);
417 }
418 }
419 }
420 // But some vector extending loads are legal
421 for (auto Ext : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) {
422 setLoadExtAction(Ext, MVT::v8i16, MVT::v8i8, Legal);
423 setLoadExtAction(Ext, MVT::v4i32, MVT::v4i16, Legal);
424 setLoadExtAction(Ext, MVT::v2i64, MVT::v2i32, Legal);
425 }
426 setLoadExtAction(ISD::EXTLOAD, MVT::v2f64, MVT::v2f32, Legal);
427 }
428
429 // Don't do anything clever with build_pairs
431
432 // Trap lowers to wasm unreachable
433 setOperationAction(ISD::TRAP, MVT::Other, Legal);
435
436 // Exception handling intrinsics
440
442
443 // Always convert switches to br_tables unless there is only one case, which
444 // is equivalent to a simple branch. This reduces code size for wasm, and we
445 // defer possible jump table optimizations to the VM.
447}
448
450WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(
451 const AtomicRMWInst *AI) const {
452 // We have wasm instructions for these
453 switch (AI->getOperation()) {
461 default:
462 break;
463 }
465}
466
467bool WebAssemblyTargetLowering::shouldScalarizeBinop(SDValue VecOp) const {
468 // Implementation copied from X86TargetLowering.
469 unsigned Opc = VecOp.getOpcode();
470
471 // Assume target opcodes can't be scalarized.
472 // TODO - do we have any exceptions?
474 return false;
475
476 // If the vector op is not supported, try to convert to scalar.
477 EVT VecVT = VecOp.getValueType();
479 return true;
480
481 // If the vector op is supported, but the scalar op is not, the transform may
482 // not be worthwhile.
483 EVT ScalarVT = VecVT.getScalarType();
484 return isOperationLegalOrCustomOrPromote(Opc, ScalarVT);
485}
486
487FastISel *WebAssemblyTargetLowering::createFastISel(
488 FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo,
489 const LibcallLoweringInfo *LibcallLowering) const {
490 return WebAssembly::createFastISel(FuncInfo, LibInfo, LibcallLowering);
491}
492
493MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/,
494 EVT VT) const {
495 unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1);
496 if (BitWidth > 1 && BitWidth < 8)
497 BitWidth = 8;
498
499 if (BitWidth > 64) {
500 // The shift will be lowered to a libcall, and compiler-rt libcalls expect
501 // the count to be an i32.
502 BitWidth = 32;
504 "32-bit shift counts ought to be enough for anyone");
505 }
506
509 "Unable to represent scalar shift amount type");
510 return Result;
511}
512
513// Lower an fp-to-int conversion operator from the LLVM opcode, which has an
514// undefined result on invalid/overflow, to the WebAssembly opcode, which
515// traps on invalid/overflow.
518 const TargetInstrInfo &TII,
519 bool IsUnsigned, bool Int64,
520 bool Float64, unsigned LoweredOpcode) {
522
523 Register OutReg = MI.getOperand(0).getReg();
524 Register InReg = MI.getOperand(1).getReg();
525
526 unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32;
527 unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32;
528 unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32;
529 unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32;
530 unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
531 unsigned Eqz = WebAssembly::EQZ_I32;
532 unsigned And = WebAssembly::AND_I32;
533 int64_t Limit = Int64 ? INT64_MIN : INT32_MIN;
534 int64_t Substitute = IsUnsigned ? 0 : Limit;
535 double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit;
536 auto &Context = BB->getParent()->getFunction().getContext();
537 Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context);
538
539 const BasicBlock *LLVMBB = BB->getBasicBlock();
540 MachineFunction *F = BB->getParent();
541 MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB);
542 MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVMBB);
543 MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB);
544
546 F->insert(It, FalseMBB);
547 F->insert(It, TrueMBB);
548 F->insert(It, DoneMBB);
549
550 // Transfer the remainder of BB and its successor edges to DoneMBB.
551 DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end());
553
554 BB->addSuccessor(TrueMBB);
555 BB->addSuccessor(FalseMBB);
556 TrueMBB->addSuccessor(DoneMBB);
557 FalseMBB->addSuccessor(DoneMBB);
558
559 unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg;
560 Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
561 Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
562 CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
563 EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
564 FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
565 TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg));
566
567 MI.eraseFromParent();
568 // For signed numbers, we can do a single comparison to determine whether
569 // fabs(x) is within range.
570 if (IsUnsigned) {
571 Tmp0 = InReg;
572 } else {
573 BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg);
574 }
575 BuildMI(BB, DL, TII.get(FConst), Tmp1)
576 .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal)));
577 BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1);
578
579 // For unsigned numbers, we have to do a separate comparison with zero.
580 if (IsUnsigned) {
581 Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg));
582 Register SecondCmpReg =
583 MRI.createVirtualRegister(&WebAssembly::I32RegClass);
584 Register AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
585 BuildMI(BB, DL, TII.get(FConst), Tmp1)
586 .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0)));
587 BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1);
588 BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg);
589 CmpReg = AndReg;
590 }
591
592 BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg);
593
594 // Create the CFG diamond to select between doing the conversion or using
595 // the substitute value.
596 BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg);
597 BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg);
598 BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB);
599 BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute);
600 BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg)
601 .addReg(FalseReg)
602 .addMBB(FalseMBB)
603 .addReg(TrueReg)
604 .addMBB(TrueMBB);
605
606 return DoneMBB;
607}
608
609// Lower a `MEMCPY` instruction into a CFG triangle around a `MEMORY_COPY`
610// instruction to handle the zero-length case.
613 const TargetInstrInfo &TII, bool Int64) {
615
616 MachineOperand DstMem = MI.getOperand(0);
617 MachineOperand SrcMem = MI.getOperand(1);
618 MachineOperand Dst = MI.getOperand(2);
619 MachineOperand Src = MI.getOperand(3);
620 MachineOperand Len = MI.getOperand(4);
621
622 // If the length is a constant, we don't actually need the check.
623 if (MachineInstr *Def = MRI.getVRegDef(Len.getReg())) {
624 if (Def->getOpcode() == WebAssembly::CONST_I32 ||
625 Def->getOpcode() == WebAssembly::CONST_I64) {
626 if (Def->getOperand(1).getImm() == 0) {
627 // A zero-length memcpy is a no-op.
628 MI.eraseFromParent();
629 return BB;
630 }
631 // A non-zero-length memcpy doesn't need a zero check.
632 unsigned MemoryCopy =
633 Int64 ? WebAssembly::MEMORY_COPY_A64 : WebAssembly::MEMORY_COPY_A32;
634 BuildMI(*BB, MI, DL, TII.get(MemoryCopy))
635 .add(DstMem)
636 .add(SrcMem)
637 .add(Dst)
638 .add(Src)
639 .add(Len);
640 MI.eraseFromParent();
641 return BB;
642 }
643 }
644
645 // We're going to add an extra use to `Len` to test if it's zero; that
646 // use shouldn't be a kill, even if the original use is.
647 MachineOperand NoKillLen = Len;
648 NoKillLen.setIsKill(false);
649
650 // Decide on which `MachineInstr` opcode we're going to use.
651 unsigned Eqz = Int64 ? WebAssembly::EQZ_I64 : WebAssembly::EQZ_I32;
652 unsigned MemoryCopy =
653 Int64 ? WebAssembly::MEMORY_COPY_A64 : WebAssembly::MEMORY_COPY_A32;
654
655 // Create two new basic blocks; one for the new `memory.fill` that we can
656 // branch over, and one for the rest of the instructions after the original
657 // `memory.fill`.
658 const BasicBlock *LLVMBB = BB->getBasicBlock();
659 MachineFunction *F = BB->getParent();
660 MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB);
661 MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB);
662
664 F->insert(It, TrueMBB);
665 F->insert(It, DoneMBB);
666
667 // Transfer the remainder of BB and its successor edges to DoneMBB.
668 DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end());
670
671 // Connect the CFG edges.
672 BB->addSuccessor(TrueMBB);
673 BB->addSuccessor(DoneMBB);
674 TrueMBB->addSuccessor(DoneMBB);
675
676 // Create a virtual register for the `Eqz` result.
677 unsigned EqzReg;
678 EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
679
680 // Erase the original `memory.copy`.
681 MI.eraseFromParent();
682
683 // Test if `Len` is zero.
684 BuildMI(BB, DL, TII.get(Eqz), EqzReg).add(NoKillLen);
685
686 // Insert a new `memory.copy`.
687 BuildMI(TrueMBB, DL, TII.get(MemoryCopy))
688 .add(DstMem)
689 .add(SrcMem)
690 .add(Dst)
691 .add(Src)
692 .add(Len);
693
694 // Create the CFG triangle.
695 BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(DoneMBB).addReg(EqzReg);
696 BuildMI(TrueMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB);
697
698 return DoneMBB;
699}
700
701// Lower a `MEMSET` instruction into a CFG triangle around a `MEMORY_FILL`
702// instruction to handle the zero-length case.
705 const TargetInstrInfo &TII, bool Int64) {
707
708 MachineOperand Mem = MI.getOperand(0);
709 MachineOperand Dst = MI.getOperand(1);
710 MachineOperand Val = MI.getOperand(2);
711 MachineOperand Len = MI.getOperand(3);
712
713 // If the length is a constant, we don't actually need the check.
714 if (MachineInstr *Def = MRI.getVRegDef(Len.getReg())) {
715 if (Def->getOpcode() == WebAssembly::CONST_I32 ||
716 Def->getOpcode() == WebAssembly::CONST_I64) {
717 if (Def->getOperand(1).getImm() == 0) {
718 // A zero-length memset is a no-op.
719 MI.eraseFromParent();
720 return BB;
721 }
722 // A non-zero-length memset doesn't need a zero check.
723 unsigned MemoryFill =
724 Int64 ? WebAssembly::MEMORY_FILL_A64 : WebAssembly::MEMORY_FILL_A32;
725 BuildMI(*BB, MI, DL, TII.get(MemoryFill))
726 .add(Mem)
727 .add(Dst)
728 .add(Val)
729 .add(Len);
730 MI.eraseFromParent();
731 return BB;
732 }
733 }
734
735 // We're going to add an extra use to `Len` to test if it's zero; that
736 // use shouldn't be a kill, even if the original use is.
737 MachineOperand NoKillLen = Len;
738 NoKillLen.setIsKill(false);
739
740 // Decide on which `MachineInstr` opcode we're going to use.
741 unsigned Eqz = Int64 ? WebAssembly::EQZ_I64 : WebAssembly::EQZ_I32;
742 unsigned MemoryFill =
743 Int64 ? WebAssembly::MEMORY_FILL_A64 : WebAssembly::MEMORY_FILL_A32;
744
745 // Create two new basic blocks; one for the new `memory.fill` that we can
746 // branch over, and one for the rest of the instructions after the original
747 // `memory.fill`.
748 const BasicBlock *LLVMBB = BB->getBasicBlock();
749 MachineFunction *F = BB->getParent();
750 MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB);
751 MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB);
752
754 F->insert(It, TrueMBB);
755 F->insert(It, DoneMBB);
756
757 // Transfer the remainder of BB and its successor edges to DoneMBB.
758 DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end());
760
761 // Connect the CFG edges.
762 BB->addSuccessor(TrueMBB);
763 BB->addSuccessor(DoneMBB);
764 TrueMBB->addSuccessor(DoneMBB);
765
766 // Create a virtual register for the `Eqz` result.
767 unsigned EqzReg;
768 EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
769
770 // Erase the original `memory.fill`.
771 MI.eraseFromParent();
772
773 // Test if `Len` is zero.
774 BuildMI(BB, DL, TII.get(Eqz), EqzReg).add(NoKillLen);
775
776 // Insert a new `memory.copy`.
777 BuildMI(TrueMBB, DL, TII.get(MemoryFill)).add(Mem).add(Dst).add(Val).add(Len);
778
779 // Create the CFG triangle.
780 BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(DoneMBB).addReg(EqzReg);
781 BuildMI(TrueMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB);
782
783 return DoneMBB;
784}
785
786static MachineBasicBlock *
789 const TargetInstrInfo &TII) {
790 assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS ||
791 CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS);
792 bool IsRetCall = CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS;
793 MachineInstr &CallParams = *CallResults.getPrevNode();
794 assert(CallParams.getOpcode() ==
795 (IsRetCall ? WebAssembly::RET_CALL_PARAMS : WebAssembly::CALL_PARAMS));
796
797 bool IsIndirect =
798 CallParams.getOperand(0).isReg() || CallParams.getOperand(0).isFI();
799
800 bool IsFuncrefCall = false;
801 if (IsIndirect && CallParams.getOperand(0).isReg()) {
802 Register Reg = CallParams.getOperand(0).getReg();
803 const MachineFunction *MF = BB->getParent();
804 const MachineRegisterInfo &MRI = MF->getRegInfo();
805 const TargetRegisterClass *TRC = MRI.getRegClass(Reg);
806 IsFuncrefCall = (TRC == &WebAssembly::FUNCREFRegClass);
807 assert(!IsFuncrefCall || Subtarget->hasReferenceTypes());
808 }
809
810 unsigned CallOp;
811 if (IsIndirect && IsRetCall) {
812 CallOp = WebAssembly::RET_CALL_INDIRECT;
813 } else if (IsIndirect) {
814 CallOp = WebAssembly::CALL_INDIRECT;
815 } else if (IsRetCall) {
816 CallOp = WebAssembly::RET_CALL;
817 } else {
818 CallOp = WebAssembly::CALL;
819 }
820
821 MachineFunction &MF = *BB->getParent();
822 const MCInstrDesc &MCID = TII.get(CallOp);
823 MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL));
824
825 // Move the function pointer to the end of the arguments for indirect calls
826 if (IsIndirect) {
827 auto FnPtr = CallParams.getOperand(0);
828 CallParams.removeOperand(0);
829
830 // For funcrefs, call_indirect is done through __funcref_call_table and the
831 // funcref is always installed in slot 0 of the table, therefore instead of
832 // having the function pointer added at the end of the params list, a zero
833 // (the index in
834 // __funcref_call_table is added).
835 if (IsFuncrefCall) {
836 Register RegZero =
837 MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass);
838 MachineInstrBuilder MIBC0 =
839 BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0);
840
841 BB->insert(CallResults.getIterator(), MIBC0);
842 MachineInstrBuilder(MF, CallParams).addReg(RegZero);
843 } else
844 CallParams.addOperand(FnPtr);
845 }
846
847 for (auto Def : CallResults.defs())
848 MIB.add(Def);
849
850 if (IsIndirect) {
851 // Placeholder for the type index.
852 // This gets replaced with the correct value in WebAssemblyMCInstLower.cpp
853 MIB.addImm(0);
854 // The table into which this call_indirect indexes.
855 MCSymbolWasm *Table = IsFuncrefCall
857 MF.getContext(), Subtarget)
859 MF.getContext(), Subtarget);
860 if (Subtarget->hasCallIndirectOverlong()) {
861 MIB.addSym(Table);
862 } else {
863 // For the MVP there is at most one table whose number is 0, but we can't
864 // write a table symbol or issue relocations. Instead we just ensure the
865 // table is live and write a zero.
866 Table->setNoStrip();
867 MIB.addImm(0);
868 }
869 }
870
871 // Avoid duplicating the implicit operands.
872 for (auto Use : CallParams.explicit_uses())
873 MIB.add(Use);
874
875 BB->insert(CallResults.getIterator(), MIB);
876 CallParams.eraseFromParent();
877 CallResults.eraseFromParent();
878
879 // If this is a funcref call, to avoid hidden GC roots, we need to clear the
880 // table slot with ref.null upon call_indirect return.
881 //
882 // This generates the following code, which comes right after a call_indirect
883 // of a funcref:
884 //
885 // i32.const 0
886 // ref.null func
887 // table.set __funcref_call_table
888 if (IsIndirect && IsFuncrefCall) {
890 MF.getContext(), Subtarget);
891 Register RegZero =
892 MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass);
893 MachineInstr *Const0 =
894 BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0);
895 BB->insertAfter(MIB.getInstr()->getIterator(), Const0);
896
897 Register RegFuncref =
898 MF.getRegInfo().createVirtualRegister(&WebAssembly::FUNCREFRegClass);
899 MachineInstr *RefNull =
900 BuildMI(MF, DL, TII.get(WebAssembly::REF_NULL_FUNCREF), RegFuncref);
901 BB->insertAfter(Const0->getIterator(), RefNull);
902
903 MachineInstr *TableSet =
904 BuildMI(MF, DL, TII.get(WebAssembly::TABLE_SET_FUNCREF))
905 .addSym(Table)
906 .addReg(RegZero)
907 .addReg(RegFuncref);
908 BB->insertAfter(RefNull->getIterator(), TableSet);
909 }
910
911 return BB;
912}
913
914MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter(
915 MachineInstr &MI, MachineBasicBlock *BB) const {
916 const TargetInstrInfo &TII = *Subtarget->getInstrInfo();
917 DebugLoc DL = MI.getDebugLoc();
918
919 switch (MI.getOpcode()) {
920 default:
921 llvm_unreachable("Unexpected instr type to insert");
922 case WebAssembly::FP_TO_SINT_I32_F32:
923 return LowerFPToInt(MI, DL, BB, TII, false, false, false,
924 WebAssembly::I32_TRUNC_S_F32);
925 case WebAssembly::FP_TO_UINT_I32_F32:
926 return LowerFPToInt(MI, DL, BB, TII, true, false, false,
927 WebAssembly::I32_TRUNC_U_F32);
928 case WebAssembly::FP_TO_SINT_I64_F32:
929 return LowerFPToInt(MI, DL, BB, TII, false, true, false,
930 WebAssembly::I64_TRUNC_S_F32);
931 case WebAssembly::FP_TO_UINT_I64_F32:
932 return LowerFPToInt(MI, DL, BB, TII, true, true, false,
933 WebAssembly::I64_TRUNC_U_F32);
934 case WebAssembly::FP_TO_SINT_I32_F64:
935 return LowerFPToInt(MI, DL, BB, TII, false, false, true,
936 WebAssembly::I32_TRUNC_S_F64);
937 case WebAssembly::FP_TO_UINT_I32_F64:
938 return LowerFPToInt(MI, DL, BB, TII, true, false, true,
939 WebAssembly::I32_TRUNC_U_F64);
940 case WebAssembly::FP_TO_SINT_I64_F64:
941 return LowerFPToInt(MI, DL, BB, TII, false, true, true,
942 WebAssembly::I64_TRUNC_S_F64);
943 case WebAssembly::FP_TO_UINT_I64_F64:
944 return LowerFPToInt(MI, DL, BB, TII, true, true, true,
945 WebAssembly::I64_TRUNC_U_F64);
946 case WebAssembly::MEMCPY_A32:
947 return LowerMemcpy(MI, DL, BB, TII, false);
948 case WebAssembly::MEMCPY_A64:
949 return LowerMemcpy(MI, DL, BB, TII, true);
950 case WebAssembly::MEMSET_A32:
951 return LowerMemset(MI, DL, BB, TII, false);
952 case WebAssembly::MEMSET_A64:
953 return LowerMemset(MI, DL, BB, TII, true);
954 case WebAssembly::CALL_RESULTS:
955 case WebAssembly::RET_CALL_RESULTS:
956 return LowerCallResults(MI, DL, BB, Subtarget, TII);
957 }
958}
959
960std::pair<unsigned, const TargetRegisterClass *>
961WebAssemblyTargetLowering::getRegForInlineAsmConstraint(
962 const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
963 // First, see if this is a constraint that directly corresponds to a
964 // WebAssembly register class.
965 if (Constraint.size() == 1) {
966 switch (Constraint[0]) {
967 case 'r':
968 assert(VT != MVT::iPTR && "Pointer MVT not expected here");
969 if (Subtarget->hasSIMD128() && VT.isVector()) {
970 if (VT.getSizeInBits() == 128)
971 return std::make_pair(0U, &WebAssembly::V128RegClass);
972 }
973 if (VT.isInteger() && !VT.isVector()) {
974 if (VT.getSizeInBits() <= 32)
975 return std::make_pair(0U, &WebAssembly::I32RegClass);
976 if (VT.getSizeInBits() <= 64)
977 return std::make_pair(0U, &WebAssembly::I64RegClass);
978 }
979 if (VT.isFloatingPoint() && !VT.isVector()) {
980 switch (VT.getSizeInBits()) {
981 case 32:
982 return std::make_pair(0U, &WebAssembly::F32RegClass);
983 case 64:
984 return std::make_pair(0U, &WebAssembly::F64RegClass);
985 default:
986 break;
987 }
988 }
989 break;
990 default:
991 break;
992 }
993 }
994
996}
997
998bool WebAssemblyTargetLowering::isCheapToSpeculateCttz(Type *Ty) const {
999 // Assume ctz is a relatively cheap operation.
1000 return true;
1001}
1002
1003bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz(Type *Ty) const {
1004 // Assume clz is a relatively cheap operation.
1005 return true;
1006}
1007
1008bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL,
1009 const AddrMode &AM,
1010 Type *Ty, unsigned AS,
1011 Instruction *I) const {
1012 // WebAssembly offsets are added as unsigned without wrapping. The
1013 // isLegalAddressingMode gives us no way to determine if wrapping could be
1014 // happening, so we approximate this by accepting only non-negative offsets.
1015 if (AM.BaseOffs < 0)
1016 return false;
1017
1018 // WebAssembly has no scale register operands.
1019 if (AM.Scale != 0)
1020 return false;
1021
1022 // Everything else is legal.
1023 return true;
1024}
1025
1026bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses(
1027 EVT /*VT*/, unsigned /*AddrSpace*/, Align /*Align*/,
1028 MachineMemOperand::Flags /*Flags*/, unsigned *Fast) const {
1029 // WebAssembly supports unaligned accesses, though it should be declared
1030 // with the p2align attribute on loads and stores which do so, and there
1031 // may be a performance impact. We tell LLVM they're "fast" because
1032 // for the kinds of things that LLVM uses this for (merging adjacent stores
1033 // of constants, etc.), WebAssembly implementations will either want the
1034 // unaligned access or they'll split anyway.
1035 if (Fast)
1036 *Fast = 1;
1037 return true;
1038}
1039
1040bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT,
1041 AttributeList Attr) const {
1042 // The current thinking is that wasm engines will perform this optimization,
1043 // so we can save on code size.
1044 return true;
1045}
1046
1047bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const {
1048 EVT ExtT = ExtVal.getValueType();
1049 SDValue N0 = peekThroughFreeze(ExtVal->getOperand(0));
1050 auto *Load = dyn_cast<LoadSDNode>(N0);
1051 if (!Load)
1052 return false;
1053 EVT MemT = Load->getValueType(0);
1054 return (ExtT == MVT::v8i16 && MemT == MVT::v8i8) ||
1055 (ExtT == MVT::v4i32 && MemT == MVT::v4i16) ||
1056 (ExtT == MVT::v2i64 && MemT == MVT::v2i32);
1057}
1058
1059bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
1060 const GlobalAddressSDNode *GA) const {
1061 // Wasm doesn't support function addresses with offsets
1062 const GlobalValue *GV = GA->getGlobal();
1064}
1065
1066EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL,
1067 LLVMContext &C,
1068 EVT VT) const {
1069 if (VT.isVector()) {
1070 if (VT.getVectorElementType() == MVT::f16 && !Subtarget->hasFP16())
1071 return VT.changeElementType(C, MVT::i1);
1072
1074 }
1075
1076 // So far, all branch instructions in Wasm take an I32 condition.
1077 // The default TargetLowering::getSetCCResultType returns the pointer size,
1078 // which would be useful to reduce instruction counts when testing
1079 // against 64-bit pointers/values if at some point Wasm supports that.
1080 return EVT::getIntegerVT(C, 32);
1081}
1082
1083void WebAssemblyTargetLowering::getTgtMemIntrinsic(
1085 MachineFunction &MF, unsigned Intrinsic) const {
1087 switch (Intrinsic) {
1088 case Intrinsic::wasm_memory_atomic_notify:
1090 Info.memVT = MVT::i32;
1091 Info.ptrVal = I.getArgOperand(0);
1092 Info.offset = 0;
1093 Info.align = Align(4);
1094 // atomic.notify instruction does not really load the memory specified with
1095 // this argument, but MachineMemOperand should either be load or store, so
1096 // we set this to a load.
1097 // FIXME Volatile isn't really correct, but currently all LLVM atomic
1098 // instructions are treated as volatiles in the backend, so we should be
1099 // consistent. The same applies for wasm_atomic_wait intrinsics too.
1101 Infos.push_back(Info);
1102 return;
1103 case Intrinsic::wasm_memory_atomic_wait32:
1105 Info.memVT = MVT::i32;
1106 Info.ptrVal = I.getArgOperand(0);
1107 Info.offset = 0;
1108 Info.align = Align(4);
1110 Infos.push_back(Info);
1111 return;
1112 case Intrinsic::wasm_memory_atomic_wait64:
1114 Info.memVT = MVT::i64;
1115 Info.ptrVal = I.getArgOperand(0);
1116 Info.offset = 0;
1117 Info.align = Align(8);
1119 Infos.push_back(Info);
1120 return;
1121 case Intrinsic::wasm_loadf16_f32:
1123 Info.memVT = MVT::f16;
1124 Info.ptrVal = I.getArgOperand(0);
1125 Info.offset = 0;
1126 Info.align = Align(2);
1128 Infos.push_back(Info);
1129 return;
1130 case Intrinsic::wasm_storef16_f32:
1132 Info.memVT = MVT::f16;
1133 Info.ptrVal = I.getArgOperand(1);
1134 Info.offset = 0;
1135 Info.align = Align(2);
1137 Infos.push_back(Info);
1138 return;
1139 default:
1140 return;
1141 }
1142}
1143
1144void WebAssemblyTargetLowering::computeKnownBitsForTargetNode(
1145 const SDValue Op, KnownBits &Known, const APInt &DemandedElts,
1146 const SelectionDAG &DAG, unsigned Depth) const {
1147 switch (Op.getOpcode()) {
1148 default:
1149 break;
1151 unsigned IntNo = Op.getConstantOperandVal(0);
1152 switch (IntNo) {
1153 default:
1154 break;
1155 case Intrinsic::wasm_bitmask: {
1156 unsigned BitWidth = Known.getBitWidth();
1157 EVT VT = Op.getOperand(1).getSimpleValueType();
1158 unsigned PossibleBits = VT.getVectorNumElements();
1159 APInt ZeroMask = APInt::getHighBitsSet(BitWidth, BitWidth - PossibleBits);
1160 Known.Zero |= ZeroMask;
1161 break;
1162 }
1163 }
1164 break;
1165 }
1166 case WebAssemblyISD::EXTEND_LOW_U:
1167 case WebAssemblyISD::EXTEND_HIGH_U: {
1168 // We know the high half, of each destination vector element, will be zero.
1169 SDValue SrcOp = Op.getOperand(0);
1170 EVT VT = SrcOp.getSimpleValueType();
1171 unsigned BitWidth = Known.getBitWidth();
1172 if (VT == MVT::v8i8 || VT == MVT::v16i8) {
1173 assert(BitWidth >= 8 && "Unexpected width!");
1175 Known.Zero |= Mask;
1176 } else if (VT == MVT::v4i16 || VT == MVT::v8i16) {
1177 assert(BitWidth >= 16 && "Unexpected width!");
1179 Known.Zero |= Mask;
1180 } else if (VT == MVT::v2i32 || VT == MVT::v4i32) {
1181 assert(BitWidth >= 32 && "Unexpected width!");
1183 Known.Zero |= Mask;
1184 }
1185 break;
1186 }
1187 // For 128-bit addition if the upper bits are all zero then it's known that
1188 // the upper bits of the result will have all bits guaranteed zero except the
1189 // first.
1190 case WebAssemblyISD::I64_ADD128:
1191 if (Op.getResNo() == 1) {
1192 SDValue LHS_HI = Op.getOperand(1);
1193 SDValue RHS_HI = Op.getOperand(3);
1194 if (isNullConstant(LHS_HI) && isNullConstant(RHS_HI))
1195 Known.Zero.setBitsFrom(1);
1196 }
1197 break;
1198 }
1199}
1200
1202WebAssemblyTargetLowering::getPreferredVectorAction(MVT VT) const {
1203 if (VT.isFixedLengthVector()) {
1204 MVT EltVT = VT.getVectorElementType();
1205 // We have legal vector types with these lane types, so widening the
1206 // vector would let us use some of the lanes directly without having to
1207 // extend or truncate values.
1208 if (EltVT == MVT::i8 || EltVT == MVT::i16 || EltVT == MVT::i32 ||
1209 EltVT == MVT::i64 || EltVT == MVT::f32 || EltVT == MVT::f64)
1210 return TypeWidenVector;
1211 }
1212
1214}
1215
1216bool WebAssemblyTargetLowering::isFMAFasterThanFMulAndFAdd(
1217 const MachineFunction &MF, EVT VT) const {
1218 if (!Subtarget->hasFP16() || !VT.isVector())
1219 return false;
1220
1221 EVT ScalarVT = VT.getScalarType();
1222 if (!ScalarVT.isSimple())
1223 return false;
1224
1225 return ScalarVT.getSimpleVT().SimpleTy == MVT::f16;
1226}
1227
1228bool WebAssemblyTargetLowering::shouldSimplifyDemandedVectorElts(
1229 SDValue Op, const TargetLoweringOpt &TLO) const {
1230 // ISel process runs DAGCombiner after legalization; this step is called
1231 // SelectionDAG optimization phase. This post-legalization combining process
1232 // runs DAGCombiner on each node, and if there was a change to be made,
1233 // re-runs legalization again on it and its user nodes to make sure
1234 // everythiing is in a legalized state.
1235 //
1236 // The legalization calls lowering routines, and we do our custom lowering for
1237 // build_vectors (LowerBUILD_VECTOR), which converts undef vector elements
1238 // into zeros. But there is a set of routines in DAGCombiner that turns unused
1239 // (= not demanded) nodes into undef, among which SimplifyDemandedVectorElts
1240 // turns unused vector elements into undefs. But this routine does not work
1241 // with our custom LowerBUILD_VECTOR, which turns undefs into zeros. This
1242 // combination can result in a infinite loop, in which undefs are converted to
1243 // zeros in legalization and back to undefs in combining.
1244 //
1245 // So after DAG is legalized, we prevent SimplifyDemandedVectorElts from
1246 // running for build_vectors.
1247 if (Op.getOpcode() == ISD::BUILD_VECTOR && TLO.LegalOps && TLO.LegalTys)
1248 return false;
1249 return true;
1250}
1251
1252//===----------------------------------------------------------------------===//
1253// WebAssembly Lowering private implementation.
1254//===----------------------------------------------------------------------===//
1255
1256//===----------------------------------------------------------------------===//
1257// Lowering Code
1258//===----------------------------------------------------------------------===//
1259
1260static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) {
1262 DAG.getContext()->diagnose(
1263 DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc()));
1264}
1265
1266// Test whether the given calling convention is supported.
1268 // We currently support the language-independent target-independent
1269 // conventions. We don't yet have a way to annotate calls with properties like
1270 // "cold", and we don't have any call-clobbered registers, so these are mostly
1271 // all handled the same.
1272 return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
1273 CallConv == CallingConv::Cold ||
1274 CallConv == CallingConv::PreserveMost ||
1275 CallConv == CallingConv::PreserveAll ||
1276 CallConv == CallingConv::CXX_FAST_TLS ||
1278 CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail;
1279}
1280
1281SDValue
1282WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI,
1283 SmallVectorImpl<SDValue> &InVals) const {
1284 SelectionDAG &DAG = CLI.DAG;
1285 SDLoc DL = CLI.DL;
1286 SDValue Chain = CLI.Chain;
1287 SDValue Callee = CLI.Callee;
1289 auto Layout = MF.getDataLayout();
1290
1291 // A call through a funcref is expressed in IR as a call through the pointer
1292 // produced by the llvm.wasm.funcref.to_ptr intrinsic. Detect this here and
1293 // recover the underlying funcref value so the call can be lowered to a
1294 // table.set + call_indirect through the dedicated __funcref_call_table.
1295 bool IsFuncrefCall = false;
1296 if (Callee.getOpcode() == ISD::INTRINSIC_WO_CHAIN &&
1297 Callee.getConstantOperandVal(0) == Intrinsic::wasm_funcref_to_ptr) {
1298 Callee = Callee.getOperand(1);
1299 IsFuncrefCall = true;
1300 }
1301
1302 CallingConv::ID CallConv = CLI.CallConv;
1303 if (!callingConvSupported(CallConv))
1304 fail(DL, DAG,
1305 "WebAssembly doesn't support language-specific or target-specific "
1306 "calling conventions yet");
1307 if (CLI.IsPatchPoint)
1308 fail(DL, DAG, "WebAssembly doesn't support patch point yet");
1309
1310 if (CLI.IsTailCall) {
1311 auto NoTail = [&](const char *Msg) {
1312 if (CLI.CB && CLI.CB->isMustTailCall())
1313 fail(DL, DAG, Msg);
1314 CLI.IsTailCall = false;
1315 };
1316
1317 if (!Subtarget->hasTailCall())
1318 NoTail("WebAssembly 'tail-call' feature not enabled");
1319
1320 // Varargs calls cannot be tail calls because the buffer is on the stack
1321 if (CLI.IsVarArg)
1322 NoTail("WebAssembly does not support varargs tail calls");
1323
1324 // Do not tail call unless caller and callee return types match
1325 const Function &F = MF.getFunction();
1326 const TargetMachine &TM = getTargetMachine();
1327 Type *RetTy = F.getReturnType();
1328 SmallVector<MVT, 4> CallerRetTys;
1329 SmallVector<MVT, 4> CalleeRetTys;
1330 computeLegalValueVTs(F, TM, RetTy, CallerRetTys);
1331 computeLegalValueVTs(F, TM, CLI.RetTy, CalleeRetTys);
1332 bool TypesMatch = CallerRetTys.size() == CalleeRetTys.size() &&
1333 std::equal(CallerRetTys.begin(), CallerRetTys.end(),
1334 CalleeRetTys.begin());
1335 if (!TypesMatch)
1336 NoTail("WebAssembly tail call requires caller and callee return types to "
1337 "match");
1338
1339 // If pointers to local stack values are passed, we cannot tail call
1340 if (CLI.CB) {
1341 for (auto &Arg : CLI.CB->args()) {
1342 Value *Val = Arg.get();
1343 // Trace the value back through pointer operations
1344 while (true) {
1345 Value *Src = Val->stripPointerCastsAndAliases();
1346 if (auto *GEP = dyn_cast<GetElementPtrInst>(Src))
1347 Src = GEP->getPointerOperand();
1348 if (Val == Src)
1349 break;
1350 Val = Src;
1351 }
1352 if (isa<AllocaInst>(Val)) {
1353 NoTail(
1354 "WebAssembly does not support tail calling with stack arguments");
1355 break;
1356 }
1357 }
1358 }
1359
1360 // A byval argument is copied into this function's stack frame below, and
1361 // a tail call releases that frame before the callee reads the copy.
1362 if (llvm::any_of(CLI.Outs, [](const ISD::OutputArg &Out) {
1363 return Out.Flags.isByVal() && Out.Flags.getByValSize() != 0;
1364 }))
1365 NoTail("WebAssembly does not support tail calling with byval arguments");
1366 }
1367
1368 SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
1369 SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs;
1370 SmallVectorImpl<SDValue> &OutVals = CLI.OutVals;
1371
1372 // The generic code may have added an sret argument. If we're lowering an
1373 // invoke function, the ABI requires that the function pointer be the first
1374 // argument, so we may have to swap the arguments.
1375 if (CallConv == CallingConv::WASM_EmscriptenInvoke && Outs.size() >= 2 &&
1376 Outs[0].Flags.isSRet()) {
1377 std::swap(Outs[0], Outs[1]);
1378 std::swap(OutVals[0], OutVals[1]);
1379 }
1380
1381 bool HasSwiftSelfArg = false;
1382 bool HasSwiftErrorArg = false;
1383 std::optional<unsigned> SwiftAsyncIdx;
1384 unsigned NumFixedArgs = 0;
1385 for (unsigned I = 0; I < Outs.size(); ++I) {
1386 const ISD::OutputArg &Out = Outs[I];
1387 SDValue &OutVal = OutVals[I];
1388 HasSwiftSelfArg |= Out.Flags.isSwiftSelf();
1389 HasSwiftErrorArg |= Out.Flags.isSwiftError();
1390 if (Out.Flags.isSwiftAsync())
1391 SwiftAsyncIdx = I;
1392 if (Out.Flags.isNest())
1393 fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
1394 if (Out.Flags.isInAlloca())
1395 fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
1396 if (Out.Flags.isInConsecutiveRegs())
1397 fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
1398 if (Out.Flags.isInConsecutiveRegsLast())
1399 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
1400 if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) {
1401 auto &MFI = MF.getFrameInfo();
1402 int FI = MFI.CreateStackObject(Out.Flags.getByValSize(),
1403 Out.Flags.getNonZeroByValAlign(),
1404 /*isSS=*/false);
1405 SDValue SizeNode =
1406 DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32);
1407 SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout));
1408 Align Alignment = Out.Flags.getNonZeroByValAlign();
1409 Chain = DAG.getMemcpy(Chain, DL, FINode, OutVal, SizeNode, Alignment,
1410 Alignment,
1411 /*isVolatile*/ false, /*AlwaysInline=*/false,
1412 /*CI=*/nullptr, std::nullopt, MachinePointerInfo(),
1413 MachinePointerInfo());
1414 OutVal = FINode;
1415 }
1416 // Count the number of fixed args *after* legalization.
1417 NumFixedArgs += !Out.Flags.isVarArg();
1418 }
1419
1420 bool IsVarArg = CLI.IsVarArg;
1421 auto PtrVT = getPointerTy(Layout);
1422
1423 // As in computeSignatureVTs, swiftasync comes first.
1424 if (SwiftAsyncIdx) {
1425 WebAssembly::moveToFront(Outs, *SwiftAsyncIdx);
1426 WebAssembly::moveToFront(OutVals, *SwiftAsyncIdx);
1427 }
1428
1429 // For swiftcc and swifttailcc, emit additional swiftself, swifterror, and
1430 // (for swifttailcc) swiftasync arguments if there aren't. These additional
1431 // arguments are also added for callee signature. They are necessary to match
1432 // callee and caller signature for indirect call.
1433 if (CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail) {
1434 Type *PtrTy = PointerType::getUnqual(*DAG.getContext());
1435 if (!HasSwiftSelfArg) {
1436 NumFixedArgs++;
1437 ISD::ArgFlagsTy Flags;
1438 Flags.setSwiftSelf();
1439 ISD::OutputArg Arg(Flags, PtrVT, EVT(PtrVT), PtrTy, 0, 0);
1440 CLI.Outs.push_back(Arg);
1441 SDValue ArgVal = DAG.getUNDEF(PtrVT);
1442 CLI.OutVals.push_back(ArgVal);
1443 }
1444 if (!HasSwiftErrorArg) {
1445 NumFixedArgs++;
1446 ISD::ArgFlagsTy Flags;
1447 Flags.setSwiftError();
1448 ISD::OutputArg Arg(Flags, PtrVT, EVT(PtrVT), PtrTy, 0, 0);
1449 CLI.Outs.push_back(Arg);
1450 SDValue ArgVal = DAG.getUNDEF(PtrVT);
1451 CLI.OutVals.push_back(ArgVal);
1452 }
1453 if (CallConv == CallingConv::SwiftTail && SwiftAsyncIdx == std::nullopt) {
1454 NumFixedArgs++;
1455 ISD::ArgFlagsTy Flags;
1456 Flags.setSwiftAsync();
1457 ISD::OutputArg Arg(Flags, PtrVT, EVT(PtrVT), PtrTy, 0, 0);
1458 CLI.Outs.insert(CLI.Outs.begin(), Arg);
1459 SDValue ArgVal = DAG.getUNDEF(PtrVT);
1460 CLI.OutVals.insert(CLI.OutVals.begin(), ArgVal);
1461 }
1462 }
1463
1464 // Analyze operands of the call, assigning locations to each operand.
1466 CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
1467
1468 if (IsVarArg) {
1469 // Outgoing non-fixed arguments are placed in a buffer. First
1470 // compute their offsets and the total amount of buffer space needed.
1471 for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) {
1472 const ISD::OutputArg &Out = Outs[I];
1473 SDValue &Arg = OutVals[I];
1474 EVT VT = Arg.getValueType();
1475 assert(VT != MVT::iPTR && "Legalized args should be concrete");
1476 Type *Ty = VT.getTypeForEVT(*DAG.getContext());
1478 std::max(Out.Flags.getNonZeroOrigAlign(), Layout.getABITypeAlign(Ty));
1479 unsigned Offset =
1480 CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), Alignment);
1481 CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(),
1482 Offset, VT.getSimpleVT(),
1484 }
1485 }
1486
1487 unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
1488
1489 SDValue FINode;
1490 if (IsVarArg && NumBytes) {
1491 // For non-fixed arguments, next emit stores to store the argument values
1492 // to the stack buffer at the offsets computed above.
1493 MaybeAlign StackAlign = Layout.getStackAlignment();
1494 assert(StackAlign && "data layout string is missing stack alignment");
1495 int FI = MF.getFrameInfo().CreateStackObject(NumBytes, *StackAlign,
1496 /*isSS=*/false);
1497 unsigned ValNo = 0;
1499 for (SDValue Arg : drop_begin(OutVals, NumFixedArgs)) {
1500 assert(ArgLocs[ValNo].getValNo() == ValNo &&
1501 "ArgLocs should remain in order and only hold varargs args");
1502 unsigned Offset = ArgLocs[ValNo++].getLocMemOffset();
1503 FINode = DAG.getFrameIndex(FI, getPointerTy(Layout));
1504 SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode,
1505 DAG.getConstant(Offset, DL, PtrVT));
1506 Chains.push_back(
1507 DAG.getStore(Chain, DL, Arg, Add,
1509 }
1510 if (!Chains.empty())
1511 Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
1512 } else if (IsVarArg) {
1513 FINode = DAG.getIntPtrConstant(0, DL);
1514 }
1515
1516 if (Callee->getOpcode() == ISD::GlobalAddress) {
1517 // If the callee is a GlobalAddress node (quite common, every direct call
1518 // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress
1519 // doesn't at MO_GOT which is not needed for direct calls.
1520 GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Callee);
1523 GA->getOffset());
1524 Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL,
1525 getPointerTy(DAG.getDataLayout()), Callee);
1526 }
1527
1528 // Compute the operands for the CALLn node.
1530 Ops.push_back(Chain);
1531 Ops.push_back(Callee);
1532
1533 // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs
1534 // isn't reliable.
1535 Ops.append(OutVals.begin(),
1536 IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end());
1537 // Add a pointer to the vararg buffer.
1538 if (IsVarArg)
1539 Ops.push_back(FINode);
1540
1541 SmallVector<EVT, 8> InTys;
1542 for (const auto &In : Ins) {
1543 assert(!In.Flags.isByVal() && "byval is not valid for return values");
1544 assert(!In.Flags.isNest() && "nest is not valid for return values");
1545 if (In.Flags.isInAlloca())
1546 fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values");
1547 if (In.Flags.isInConsecutiveRegs())
1548 fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values");
1549 if (In.Flags.isInConsecutiveRegsLast())
1550 fail(DL, DAG,
1551 "WebAssembly hasn't implemented cons regs last return values");
1552 // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in
1553 // registers.
1554 InTys.push_back(In.VT);
1555 }
1556
1557 // Lastly, if this is a call to a funcref we need to add an instruction
1558 // table.set to the chain and transform the call.
1559 if (IsFuncrefCall) {
1560 // In the absence of function references proposal where a funcref call is
1561 // lowered to call_ref, using reference types we generate a table.set to set
1562 // the funcref to a special table used solely for this purpose, followed by
1563 // a call_indirect. Here we just generate the table set, and return the
1564 // SDValue of the table.set so that LowerCall can finalize the lowering by
1565 // generating the call_indirect.
1566 SDValue Chain = Ops[0];
1567
1569 MF.getContext(), Subtarget);
1570 SDValue Sym = DAG.getMCSymbol(Table, PtrVT);
1571 SDValue TableSlot = DAG.getConstant(0, DL, MVT::i32);
1572 SDValue TableSetOps[] = {Chain, Sym, TableSlot, Callee};
1573 SDValue TableSet = DAG.getMemIntrinsicNode(
1574 WebAssemblyISD::TABLE_SET, DL, DAG.getVTList(MVT::Other), TableSetOps,
1575 MVT::funcref, MachinePointerInfo(), Align(1),
1577
1578 Ops[0] = TableSet; // The new chain is the TableSet itself
1579 }
1580
1581 if (CLI.IsTailCall) {
1582 // ret_calls do not return values to the current frame
1583 SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
1584 return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops);
1585 }
1586
1587 InTys.push_back(MVT::Other);
1588 SDVTList InTyList = DAG.getVTList(InTys);
1589 SDValue Res = DAG.getNode(WebAssemblyISD::CALL, DL, InTyList, Ops);
1590
1591 for (size_t I = 0; I < Ins.size(); ++I)
1592 InVals.push_back(Res.getValue(I));
1593
1594 // Return the chain
1595 return Res.getValue(Ins.size());
1596}
1597
1598bool WebAssemblyTargetLowering::CanLowerReturn(
1599 CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/,
1600 const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext & /*Context*/,
1601 const Type *RetTy) const {
1602 // WebAssembly can only handle returning tuples with multivalue enabled
1603 return WebAssembly::canLowerReturn(Outs.size(), Subtarget);
1604}
1605
1606SDValue WebAssemblyTargetLowering::LowerReturn(
1607 SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
1609 const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
1610 SelectionDAG &DAG) const {
1611 assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) &&
1612 "MVP WebAssembly can only return up to one value");
1613 if (!callingConvSupported(CallConv))
1614 fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
1615
1616 SmallVector<SDValue, 4> RetOps(1, Chain);
1617 RetOps.append(OutVals.begin(), OutVals.end());
1618 Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps);
1619
1620 // Record the number and types of the return values.
1621 for (const ISD::OutputArg &Out : Outs) {
1622 assert(!Out.Flags.isByVal() && "byval is not valid for return values");
1623 assert(!Out.Flags.isNest() && "nest is not valid for return values");
1624 assert(!Out.Flags.isVarArg() && "non-fixed return value is not valid");
1625 if (Out.Flags.isInAlloca())
1626 fail(DL, DAG, "WebAssembly hasn't implemented inalloca results");
1627 if (Out.Flags.isInConsecutiveRegs())
1628 fail(DL, DAG, "WebAssembly hasn't implemented cons regs results");
1629 if (Out.Flags.isInConsecutiveRegsLast())
1630 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results");
1631 }
1632
1633 return Chain;
1634}
1635
1636SDValue WebAssemblyTargetLowering::LowerFormalArguments(
1637 SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
1638 const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
1639 SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
1640 if (!callingConvSupported(CallConv))
1641 fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
1642
1644 auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
1645
1646 // Set up the incoming ARGUMENTS value, which serves to represent the liveness
1647 // of the incoming values before they're represented by virtual registers.
1648 MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS);
1649
1650 bool HasSwiftErrorArg = false;
1651 bool HasSwiftSelfArg = false;
1652 std::optional<unsigned> SwiftAsyncIdx;
1653 for (auto [I, In] : enumerate(Ins))
1654 if (In.Flags.isSwiftAsync())
1655 SwiftAsyncIdx = I;
1656 // As in computeSignatureVTs, swiftasync or its padding comes first.
1657 bool PadSwiftAsync = CallConv == CallingConv::SwiftTail && !SwiftAsyncIdx;
1658 auto GetParamIdx = [&](unsigned InIdx) -> unsigned {
1659 if (SwiftAsyncIdx)
1660 return InIdx == *SwiftAsyncIdx ? 0 : InIdx + (InIdx < *SwiftAsyncIdx);
1661 return InIdx + PadSwiftAsync;
1662 };
1663
1664 SmallVector<MVT, 4> ParamVTs;
1665 for (auto [I, In] : enumerate(Ins)) {
1666 HasSwiftSelfArg |= In.Flags.isSwiftSelf();
1667 HasSwiftErrorArg |= In.Flags.isSwiftError();
1668 if (In.Flags.isInAlloca())
1669 fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments");
1670 if (In.Flags.isNest())
1671 fail(DL, DAG, "WebAssembly hasn't implemented nest arguments");
1672 if (In.Flags.isInConsecutiveRegs())
1673 fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments");
1674 if (In.Flags.isInConsecutiveRegsLast())
1675 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments");
1676 // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in
1677 // registers.
1678 InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT,
1679 DAG.getTargetConstant(GetParamIdx(I),
1680 DL, MVT::i32))
1681 : DAG.getUNDEF(In.VT));
1682
1683 // Record the number and types of arguments.
1684 ParamVTs.push_back(In.VT);
1685 }
1686
1687 if (SwiftAsyncIdx)
1688 WebAssembly::moveToFront(ParamVTs, *SwiftAsyncIdx);
1689 else if (PadSwiftAsync)
1690 ParamVTs.insert(ParamVTs.begin(), getPointerTy(MF.getDataLayout()));
1691 for (MVT VT : ParamVTs)
1692 MFI->addParam(VT);
1693
1694 // For swiftcc and swifttailcc, emit additional swiftself and swifterror
1695 // arguments if there aren't. These additional arguments are also added for
1696 // callee signature. They are necessary to match callee and caller signature
1697 // for indirect call.
1698 auto PtrVT = getPointerTy(MF.getDataLayout());
1699 if (CallConv == CallingConv::Swift || CallConv == CallingConv::SwiftTail) {
1700 if (!HasSwiftSelfArg) {
1701 MFI->addParam(PtrVT);
1702 }
1703 if (!HasSwiftErrorArg) {
1704 MFI->addParam(PtrVT);
1705 }
1706 }
1707 // Varargs are copied into a buffer allocated by the caller, and a pointer to
1708 // the buffer is passed as an argument.
1709 if (IsVarArg) {
1710 MVT PtrVT = getPointerTy(MF.getDataLayout());
1711 Register VarargVreg =
1713 MFI->setVarargBufferVreg(VarargVreg);
1714 Chain = DAG.getCopyToReg(
1715 Chain, DL, VarargVreg,
1716 DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT,
1717 DAG.getTargetConstant(ParamVTs.size(), DL, MVT::i32)));
1718 MFI->addParam(PtrVT);
1719 }
1720
1721 // Record the number and types of arguments and results.
1722 SmallVector<MVT, 4> Params;
1725 MF.getFunction(), DAG.getTarget(), Params, Results);
1726 for (MVT VT : Results)
1727 MFI->addResult(VT);
1728 // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify
1729 // the param logic here with ComputeSignatureVTs
1730 assert(MFI->getParams().size() == Params.size() &&
1731 std::equal(MFI->getParams().begin(), MFI->getParams().end(),
1732 Params.begin()));
1733
1734 return Chain;
1735}
1736
1737void WebAssemblyTargetLowering::ReplaceNodeResults(
1739 switch (N->getOpcode()) {
1741 // Do not add any results, signifying that N should not be custom lowered
1742 // after all. This happens because simd128 turns on custom lowering for
1743 // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an
1744 // illegal type.
1745 break;
1749 // Do not add any results, signifying that N should not be custom lowered.
1750 // EXTEND_VECTOR_INREG is implemented for some vectors, but not all.
1751 break;
1752 case ISD::FP_ROUND: {
1753 EVT VT = N->getValueType(0);
1754 SDValue Src = N->getOperand(0);
1755 if (VT == MVT::v4f16 && Src.getValueType() == MVT::v4f32) {
1756 Results.push_back(
1757 DAG.getNode(WebAssemblyISD::DEMOTE_ZERO, SDLoc(N), MVT::v8f16, Src));
1758 }
1759 break;
1760 }
1761 case ISD::ADD:
1762 case ISD::SUB:
1763 Results.push_back(Replace128Op(N, DAG));
1764 break;
1765 default:
1767 "ReplaceNodeResults not implemented for this op for WebAssembly!");
1768 }
1769}
1770
1771//===----------------------------------------------------------------------===//
1772// Custom lowering hooks.
1773//===----------------------------------------------------------------------===//
1774
1775SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
1776 SelectionDAG &DAG) const {
1777 SDLoc DL(Op);
1778 switch (Op.getOpcode()) {
1779 default:
1780 llvm_unreachable("unimplemented operation lowering");
1781 return SDValue();
1782 case ISD::FrameIndex:
1783 return LowerFrameIndex(Op, DAG);
1784 case ISD::GlobalAddress:
1785 return LowerGlobalAddress(Op, DAG);
1787 return LowerGlobalTLSAddress(Op, DAG);
1789 return LowerExternalSymbol(Op, DAG);
1790 case ISD::JumpTable:
1791 return LowerJumpTable(Op, DAG);
1792 case ISD::BR_JT:
1793 return LowerBR_JT(Op, DAG);
1794 case ISD::VASTART:
1795 return LowerVASTART(Op, DAG);
1796 case ISD::BlockAddress:
1797 case ISD::BRIND:
1798 fail(DL, DAG, "WebAssembly hasn't implemented computed gotos");
1799 return SDValue();
1800 case ISD::RETURNADDR:
1801 return LowerRETURNADDR(Op, DAG);
1802 case ISD::FRAMEADDR:
1803 return LowerFRAMEADDR(Op, DAG);
1804 case ISD::CopyToReg:
1805 return LowerCopyToReg(Op, DAG);
1808 return LowerAccessVectorElement(Op, DAG);
1812 return LowerIntrinsic(Op, DAG);
1814 return LowerSIGN_EXTEND_INREG(Op, DAG);
1818 return LowerEXTEND_VECTOR_INREG(Op, DAG);
1819 case ISD::BUILD_VECTOR:
1820 return LowerBUILD_VECTOR(Op, DAG);
1822 return LowerVECTOR_SHUFFLE(Op, DAG);
1823 case ISD::SETCC:
1824 return LowerSETCC(Op, DAG);
1825 case ISD::SHL:
1826 case ISD::SRA:
1827 case ISD::SRL:
1828 return LowerShift(Op, DAG);
1831 return LowerFP_TO_INT_SAT(Op, DAG);
1832 case ISD::FMINNUM:
1833 case ISD::FMINIMUMNUM:
1834 return LowerFMIN(Op, DAG);
1835 case ISD::FMAXNUM:
1836 case ISD::FMAXIMUMNUM:
1837 return LowerFMAX(Op, DAG);
1838 case ISD::LOAD:
1839 return LowerLoad(Op, DAG);
1840 case ISD::STORE:
1841 return LowerStore(Op, DAG);
1842 case ISD::CTPOP:
1843 case ISD::CTLZ:
1844 case ISD::CTTZ:
1845 return DAG.UnrollVectorOp(Op.getNode());
1846 case ISD::CLEAR_CACHE:
1847 // Report this as a diagnostic rather than aborting, like the other
1848 // unsupported features in this target. Pass the chain through so that
1849 // codegen can reach the point where the diagnostic is emitted.
1850 fail(SDLoc(Op), DAG, "llvm.clear_cache is not supported on wasm");
1851 return Op.getOperand(0);
1852 case ISD::SMUL_LOHI:
1853 case ISD::UMUL_LOHI:
1854 return LowerMUL_LOHI(Op, DAG);
1855 case ISD::UADDO:
1856 return LowerUADDO(Op, DAG);
1857 }
1858}
1859
1863
1864 return false;
1865}
1866
1867static std::optional<unsigned> IsWebAssemblyLocal(SDValue Op,
1868 SelectionDAG &DAG) {
1870 if (!FI)
1871 return std::nullopt;
1872
1873 auto &MF = DAG.getMachineFunction();
1875}
1876
1877SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
1878 SelectionDAG &DAG) const {
1879 SDLoc DL(Op);
1880 StoreSDNode *SN = cast<StoreSDNode>(Op.getNode());
1881 const SDValue &Value = SN->getValue();
1882 const SDValue &Base = SN->getBasePtr();
1883 const SDValue &Offset = SN->getOffset();
1884
1886 if (!Offset->isUndef())
1887 report_fatal_error("unexpected offset when storing to webassembly global",
1888 false);
1889
1890 SDVTList Tys = DAG.getVTList(MVT::Other);
1891 SDValue Ops[] = {SN->getChain(), Value, Base};
1892 return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops,
1893 SN->getMemoryVT(), SN->getMemOperand());
1894 }
1895
1896 if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) {
1897 if (!Offset->isUndef())
1898 report_fatal_error("unexpected offset when storing to webassembly local",
1899 false);
1900
1901 SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32);
1902 SDVTList Tys = DAG.getVTList(MVT::Other); // The chain.
1903 SDValue Ops[] = {SN->getChain(), Idx, Value};
1904 return DAG.getNode(WebAssemblyISD::LOCAL_SET, DL, Tys, Ops);
1905 }
1906
1909 "Encountered an unlowerable store to the wasm_var address space",
1910 false);
1911
1912 return Op;
1913}
1914
1915SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
1916 SelectionDAG &DAG) const {
1917 SDLoc DL(Op);
1918 LoadSDNode *LN = cast<LoadSDNode>(Op.getNode());
1919 const SDValue &Base = LN->getBasePtr();
1920 const SDValue &Offset = LN->getOffset();
1921
1923 if (!Offset->isUndef())
1925 "unexpected offset when loading from webassembly global", false);
1926
1927 SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other);
1928 SDValue Ops[] = {LN->getChain(), Base};
1929 return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops,
1930 LN->getMemoryVT(), LN->getMemOperand());
1931 }
1932
1933 if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) {
1934 if (!Offset->isUndef())
1936 "unexpected offset when loading from webassembly local", false);
1937
1938 SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32);
1939 EVT LocalVT = LN->getValueType(0);
1940 return DAG.getNode(WebAssemblyISD::LOCAL_GET, DL, {LocalVT, MVT::Other},
1941 {LN->getChain(), Idx});
1942 }
1943
1946 "Encountered an unlowerable load from the wasm_var address space",
1947 false);
1948
1949 return Op;
1950}
1951
1952SDValue WebAssemblyTargetLowering::LowerMUL_LOHI(SDValue Op,
1953 SelectionDAG &DAG) const {
1954 assert(Subtarget->hasWideArithmetic());
1955 assert(Op.getValueType() == MVT::i64);
1956 SDLoc DL(Op);
1957 unsigned Opcode;
1958 switch (Op.getOpcode()) {
1959 case ISD::UMUL_LOHI:
1960 Opcode = WebAssemblyISD::I64_MUL_WIDE_U;
1961 break;
1962 case ISD::SMUL_LOHI:
1963 Opcode = WebAssemblyISD::I64_MUL_WIDE_S;
1964 break;
1965 default:
1966 llvm_unreachable("unexpected opcode");
1967 }
1968 SDValue LHS = Op.getOperand(0);
1969 SDValue RHS = Op.getOperand(1);
1970 SDValue Lo =
1971 DAG.getNode(Opcode, DL, DAG.getVTList(MVT::i64, MVT::i64), LHS, RHS);
1972 SDValue Hi(Lo.getNode(), 1);
1973 SDValue Ops[] = {Lo, Hi};
1974 return DAG.getMergeValues(Ops, DL);
1975}
1976
1977// Lowers `UADDO` intrinsics to an `i64.add128` instruction when it's enabled.
1978//
1979// This enables generating a single wasm instruction for this operation where
1980// the upper half of both operands are constant zeros. The upper half of the
1981// result is then whether the overflow happened.
1982SDValue WebAssemblyTargetLowering::LowerUADDO(SDValue Op,
1983 SelectionDAG &DAG) const {
1984 assert(Subtarget->hasWideArithmetic());
1985 assert(Op.getValueType() == MVT::i64);
1986 assert(Op.getOpcode() == ISD::UADDO);
1987 SDLoc DL(Op);
1988 SDValue LHS = Op.getOperand(0);
1989 SDValue RHS = Op.getOperand(1);
1990 SDValue Zero = DAG.getConstant(0, DL, MVT::i64);
1991 SDValue Result =
1992 DAG.getNode(WebAssemblyISD::I64_ADD128, DL,
1993 DAG.getVTList(MVT::i64, MVT::i64), LHS, Zero, RHS, Zero);
1994 SDValue CarryI64(Result.getNode(), 1);
1995 SDValue CarryI32 = DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, CarryI64);
1996 SDValue Ops[] = {Result, CarryI32};
1997 return DAG.getMergeValues(Ops, DL);
1998}
1999
2000SDValue WebAssemblyTargetLowering::Replace128Op(SDNode *N,
2001 SelectionDAG &DAG) const {
2002 assert(Subtarget->hasWideArithmetic());
2003 assert(N->getValueType(0) == MVT::i128);
2004 SDLoc DL(N);
2005 unsigned Opcode;
2006 switch (N->getOpcode()) {
2007 case ISD::ADD:
2008 Opcode = WebAssemblyISD::I64_ADD128;
2009 break;
2010 case ISD::SUB:
2011 Opcode = WebAssemblyISD::I64_SUB128;
2012 break;
2013 default:
2014 llvm_unreachable("unexpected opcode");
2015 }
2016 SDValue LHS = N->getOperand(0);
2017 SDValue RHS = N->getOperand(1);
2018
2019 SDValue C0 = DAG.getConstant(0, DL, MVT::i64);
2020 SDValue C1 = DAG.getConstant(1, DL, MVT::i64);
2021 SDValue LHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, LHS, C0);
2022 SDValue LHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, LHS, C1);
2023 SDValue RHS_0 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, RHS, C0);
2024 SDValue RHS_1 = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i64, RHS, C1);
2025 SDValue Result_LO = DAG.getNode(Opcode, DL, DAG.getVTList(MVT::i64, MVT::i64),
2026 LHS_0, LHS_1, RHS_0, RHS_1);
2027 SDValue Result_HI(Result_LO.getNode(), 1);
2028 return DAG.getNode(ISD::BUILD_PAIR, DL, N->getVTList(), Result_LO, Result_HI);
2029}
2030
2031SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
2032 SelectionDAG &DAG) const {
2033 SDValue Src = Op.getOperand(2);
2034 if (isa<FrameIndexSDNode>(Src.getNode())) {
2035 // CopyToReg nodes don't support FrameIndex operands. Other targets select
2036 // the FI to some LEA-like instruction, but since we don't have that, we
2037 // need to insert some kind of instruction that can take an FI operand and
2038 // produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy
2039 // local.copy between Op and its FI operand.
2040 SDValue Chain = Op.getOperand(0);
2041 SDLoc DL(Op);
2042 Register Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg();
2043 EVT VT = Src.getValueType();
2044 SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32
2045 : WebAssembly::COPY_I64,
2046 DL, VT, Src),
2047 0);
2048 return Op.getNode()->getNumValues() == 1
2049 ? DAG.getCopyToReg(Chain, DL, Reg, Copy)
2050 : DAG.getCopyToReg(Chain, DL, Reg, Copy,
2051 Op.getNumOperands() == 4 ? Op.getOperand(3)
2052 : SDValue());
2053 }
2054 return SDValue();
2055}
2056
2057SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op,
2058 SelectionDAG &DAG) const {
2059 int FI = cast<FrameIndexSDNode>(Op)->getIndex();
2060 return DAG.getTargetFrameIndex(FI, Op.getValueType());
2061}
2062
2063SDValue WebAssemblyTargetLowering::LowerRETURNADDR(SDValue Op,
2064 SelectionDAG &DAG) const {
2065 SDLoc DL(Op);
2066
2067 if (!Subtarget->getTargetTriple().isOSEmscripten()) {
2068 fail(DL, DAG,
2069 "Non-Emscripten WebAssembly hasn't implemented "
2070 "__builtin_return_address");
2071 return SDValue();
2072 }
2073
2074 unsigned Depth = Op.getConstantOperandVal(0);
2075 MakeLibCallOptions CallOptions;
2076 return makeLibCall(DAG, RTLIB::RETURN_ADDRESS, Op.getValueType(),
2077 {DAG.getConstant(Depth, DL, MVT::i32)}, CallOptions, DL)
2078 .first;
2079}
2080
2081SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op,
2082 SelectionDAG &DAG) const {
2083 // Non-zero depths are not supported by WebAssembly currently. Use the
2084 // legalizer's default expansion, which is to return 0 (what this function is
2085 // documented to do).
2086 if (Op.getConstantOperandVal(0) > 0)
2087 return SDValue();
2088
2090 EVT VT = Op.getValueType();
2091 Register FP =
2092 Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction());
2093 return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT);
2094}
2095
2096SDValue
2097WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op,
2098 SelectionDAG &DAG) const {
2099 SDLoc DL(Op);
2100 const auto *GA = cast<GlobalAddressSDNode>(Op);
2101
2103 if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory())
2104 report_fatal_error("cannot use thread-local storage without bulk memory",
2105 false);
2106
2107 const GlobalValue *GV = GA->getGlobal();
2108
2109 // Currently only Emscripten supports dynamic linking with threads. Therefore,
2110 // on other targets, if we have thread-local storage, only the local-exec
2111 // model is possible.
2112 auto model = Subtarget->getTargetTriple().isOSEmscripten()
2113 ? GV->getThreadLocalMode()
2115
2116 // Unsupported TLS modes
2119
2120 if (model == GlobalValue::LocalExecTLSModel ||
2123 getTargetMachine().shouldAssumeDSOLocal(GV))) {
2124 // For DSO-local TLS variables we use offset from __tls_base, or
2125 // __wasm_get_tls_base() if using libcall thread context.
2126
2127 MVT PtrVT = getPointerTy(DAG.getDataLayout());
2128 SDValue BaseAddr(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
2129
2130 SDValue TLSOffset = DAG.getTargetGlobalAddress(
2131 GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL);
2132 SDValue SymOffset =
2133 DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset);
2134
2135 return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset);
2136 }
2137
2139
2140 EVT VT = Op.getValueType();
2141 return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
2142 DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
2143 GA->getOffset(),
2145}
2146
2147SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
2148 SelectionDAG &DAG) const {
2149 SDLoc DL(Op);
2150 const auto *GA = cast<GlobalAddressSDNode>(Op);
2151 EVT VT = Op.getValueType();
2152 assert(GA->getTargetFlags() == 0 &&
2153 "Unexpected target flags on generic GlobalAddressSDNode");
2155 fail(DL, DAG, "Invalid address space for WebAssembly target");
2156
2157 unsigned OperandFlags = 0;
2158 const GlobalValue *GV = GA->getGlobal();
2159 // Since WebAssembly tables cannot yet be shared across modules, we don't
2160 // need special treatment for tables in PIC mode.
2161 if (isPositionIndependent() &&
2163 if (getTargetMachine().shouldAssumeDSOLocal(GV)) {
2165 MVT PtrVT = getPointerTy(MF.getDataLayout());
2166 const char *BaseName;
2167 if (GV->getValueType()->isFunctionTy()) {
2168 BaseName = MF.createExternalSymbolName("__table_base");
2170 } else {
2171 BaseName = MF.createExternalSymbolName("__memory_base");
2173 }
2175 DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
2176 DAG.getTargetExternalSymbol(BaseName, PtrVT));
2177
2178 SDValue SymAddr = DAG.getNode(
2179 WebAssemblyISD::WrapperREL, DL, VT,
2180 DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(),
2181 OperandFlags));
2182
2183 return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr);
2184 }
2186 }
2187
2188 return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
2189 DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT,
2190 GA->getOffset(), OperandFlags));
2191}
2192
2193SDValue
2194WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op,
2195 SelectionDAG &DAG) const {
2196 SDLoc DL(Op);
2197 const auto *ES = cast<ExternalSymbolSDNode>(Op);
2198 EVT VT = Op.getValueType();
2199 assert(ES->getTargetFlags() == 0 &&
2200 "Unexpected target flags on generic ExternalSymbolSDNode");
2201 return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT,
2202 DAG.getTargetExternalSymbol(ES->getSymbol(), VT));
2203}
2204
2205SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op,
2206 SelectionDAG &DAG) const {
2207 // There's no need for a Wrapper node because we always incorporate a jump
2208 // table operand into a BR_TABLE instruction, rather than ever
2209 // materializing it in a register.
2210 const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op);
2211 return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(),
2212 JT->getTargetFlags());
2213}
2214
2215SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op,
2216 SelectionDAG &DAG) const {
2217 SDLoc DL(Op);
2218 SDValue Chain = Op.getOperand(0);
2219 const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1));
2220 SDValue Index = Op.getOperand(2);
2221 assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags");
2222
2224 Ops.push_back(Chain);
2225 Ops.push_back(Index);
2226
2227 MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo();
2228 const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs;
2229
2230 // Add an operand for each case.
2231 for (auto *MBB : MBBs)
2232 Ops.push_back(DAG.getBasicBlock(MBB));
2233
2234 // Add the first MBB as a dummy default target for now. This will be replaced
2235 // with the proper default target (and the preceding range check eliminated)
2236 // if possible by WebAssemblyFixBrTableDefaults.
2237 Ops.push_back(DAG.getBasicBlock(*MBBs.begin()));
2238 return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops);
2239}
2240
2241SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op,
2242 SelectionDAG &DAG) const {
2243 SDLoc DL(Op);
2244 EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout());
2245
2246 auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>();
2247 const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
2248
2249 SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL,
2250 MFI->getVarargBufferVreg(), PtrVT);
2251 return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1),
2252 MachinePointerInfo(SV));
2253}
2254
2255SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op,
2256 SelectionDAG &DAG) const {
2258 unsigned IntNo;
2259 switch (Op.getOpcode()) {
2262 IntNo = Op.getConstantOperandVal(1);
2263 break;
2265 IntNo = Op.getConstantOperandVal(0);
2266 break;
2267 default:
2268 llvm_unreachable("Invalid intrinsic");
2269 }
2270 SDLoc DL(Op);
2271
2272 switch (IntNo) {
2273 default:
2274 return SDValue(); // Don't custom lower most intrinsics.
2275
2276 case Intrinsic::wasm_lsda: {
2277 auto PtrVT = getPointerTy(MF.getDataLayout());
2278 const char *SymName = MF.createExternalSymbolName(
2279 "GCC_except_table" + std::to_string(MF.getFunctionNumber()));
2280 if (isPositionIndependent()) {
2282 SymName, PtrVT, WebAssemblyII::MO_MEMORY_BASE_REL);
2283 const char *BaseName = MF.createExternalSymbolName("__memory_base");
2285 DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT,
2286 DAG.getTargetExternalSymbol(BaseName, PtrVT));
2287 SDValue SymAddr =
2288 DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, Node);
2289 return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymAddr);
2290 }
2291 SDValue Node = DAG.getTargetExternalSymbol(SymName, PtrVT);
2292 return DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, Node);
2293 }
2294
2295 case Intrinsic::wasm_shuffle: {
2296 // Drop in-chain and replace undefs, but otherwise pass through unchanged
2297 SDValue Ops[18];
2298 size_t OpIdx = 0;
2299 Ops[OpIdx++] = Op.getOperand(1);
2300 Ops[OpIdx++] = Op.getOperand(2);
2301 while (OpIdx < 18) {
2302 const SDValue &MaskIdx = Op.getOperand(OpIdx + 1);
2303 if (MaskIdx.isUndef() || MaskIdx.getNode()->getAsZExtVal() >= 32) {
2304 bool isTarget = MaskIdx.getNode()->getOpcode() == ISD::TargetConstant;
2305 Ops[OpIdx++] = DAG.getConstant(0, DL, MVT::i32, isTarget);
2306 } else {
2307 Ops[OpIdx++] = MaskIdx;
2308 }
2309 }
2310 return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops);
2311 }
2312
2313 case Intrinsic::wasm_funcref_to_ptr: {
2314 // llvm.wasm.funcref.to_ptr only has a defined lowering when its result
2315 // feeds directly into an indirect call. Reaching here means the pointer
2316 // escapes a direct call. We haven't implemented conversion of a funcref
2317 // into a real function pointer so we crash if we get here.
2318 fail(DL, DAG,
2319 "a funcref can only be converted to a pointer to be directly called; "
2320 "the resulting pointer cannot otherwise be used");
2321 return DAG.getPOISON(Op.getValueType());
2322 }
2323
2324 case Intrinsic::thread_pointer: {
2325 return SDValue(WebAssembly::getTLSBase(DAG, DL, Subtarget), 0);
2326 }
2327 }
2328}
2329
2330SDValue
2331WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op,
2332 SelectionDAG &DAG) const {
2333 SDLoc DL(Op);
2334 // If sign extension operations are disabled, allow sext_inreg only if operand
2335 // is a vector extract of an i8 or i16 lane. SIMD does not depend on sign
2336 // extension operations, but allowing sext_inreg in this context lets us have
2337 // simple patterns to select extract_lane_s instructions. Expanding sext_inreg
2338 // everywhere would be simpler in this file, but would necessitate large and
2339 // brittle patterns to undo the expansion and select extract_lane_s
2340 // instructions.
2341 assert(!Subtarget->hasSignExt() && Subtarget->hasSIMD128());
2342 if (Op.getOperand(0).getOpcode() != ISD::EXTRACT_VECTOR_ELT)
2343 return SDValue();
2344
2345 const SDValue &Extract = Op.getOperand(0);
2346 MVT VecT = Extract.getOperand(0).getSimpleValueType();
2347 if (VecT.getVectorElementType().getSizeInBits() > 32)
2348 return SDValue();
2349 MVT ExtractedLaneT =
2350 cast<VTSDNode>(Op.getOperand(1).getNode())->getVT().getSimpleVT();
2351 MVT ExtractedVecT =
2352 MVT::getVectorVT(ExtractedLaneT, 128 / ExtractedLaneT.getSizeInBits());
2353 if (ExtractedVecT == VecT)
2354 return Op;
2355
2356 // Bitcast vector to appropriate type to ensure ISel pattern coverage
2357 const SDNode *Index = Extract.getOperand(1).getNode();
2358 if (!isa<ConstantSDNode>(Index))
2359 return SDValue();
2360 unsigned IndexVal = Index->getAsZExtVal();
2361 unsigned Scale =
2362 ExtractedVecT.getVectorNumElements() / VecT.getVectorNumElements();
2363 assert(Scale > 1);
2364 SDValue NewIndex =
2365 DAG.getConstant(IndexVal * Scale, DL, Index->getValueType(0));
2366 SDValue NewExtract = DAG.getNode(
2368 DAG.getBitcast(ExtractedVecT, Extract.getOperand(0)), NewIndex);
2369 return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Op.getValueType(), NewExtract,
2370 Op.getOperand(1));
2371}
2372
2373static SDValue GetExtendHigh(SDValue Op, unsigned UserOpc, EVT VT,
2374 SelectionDAG &DAG) {
2375 SDValue Source = peekThroughBitcasts(Op);
2376 if (Source.getOpcode() != ISD::VECTOR_SHUFFLE)
2377 return SDValue();
2378
2379 assert((UserOpc == WebAssemblyISD::EXTEND_LOW_U ||
2380 UserOpc == WebAssemblyISD::EXTEND_LOW_S) &&
2381 "expected extend_low");
2382 auto *Shuffle = cast<ShuffleVectorSDNode>(Source.getNode());
2383
2384 ArrayRef<int> Mask = Shuffle->getMask();
2385 // Look for a shuffle which moves from the high half to the low half.
2386 size_t FirstIdx = Mask.size() / 2;
2387 for (size_t i = 0; i < Mask.size() / 2; ++i) {
2388 if (Mask[i] != static_cast<int>(FirstIdx + i)) {
2389 return SDValue();
2390 }
2391 }
2392
2393 SDLoc DL(Op);
2394 unsigned Opc = UserOpc == WebAssemblyISD::EXTEND_LOW_S
2395 ? WebAssemblyISD::EXTEND_HIGH_S
2396 : WebAssemblyISD::EXTEND_HIGH_U;
2397 SDValue ShuffleSrc = Shuffle->getOperand(0);
2398 if (Op.getOpcode() == ISD::BITCAST)
2399 ShuffleSrc = DAG.getBitcast(Op.getValueType(), ShuffleSrc);
2400
2401 return DAG.getNode(Opc, DL, VT, ShuffleSrc);
2402}
2403
2404SDValue
2405WebAssemblyTargetLowering::LowerEXTEND_VECTOR_INREG(SDValue Op,
2406 SelectionDAG &DAG) const {
2407 SDLoc DL(Op);
2408 EVT VT = Op.getValueType();
2409 SDValue Src = Op.getOperand(0);
2410 EVT SrcVT = Src.getValueType();
2411
2412 if (SrcVT.getVectorElementType() == MVT::i1 ||
2413 SrcVT.getVectorElementType() == MVT::i64)
2414 return SDValue();
2415
2416 assert(VT.getScalarSizeInBits() % SrcVT.getScalarSizeInBits() == 0 &&
2417 "Unexpected extension factor.");
2418 unsigned Scale = VT.getScalarSizeInBits() / SrcVT.getScalarSizeInBits();
2419
2420 if (Scale != 2 && Scale != 4 && Scale != 8)
2421 return SDValue();
2422
2423 unsigned Ext;
2424 switch (Op.getOpcode()) {
2425 default:
2426 llvm_unreachable("unexpected opcode");
2429 Ext = WebAssemblyISD::EXTEND_LOW_U;
2430 break;
2432 Ext = WebAssemblyISD::EXTEND_LOW_S;
2433 break;
2434 }
2435
2436 if (Scale == 2) {
2437 // See if we can use EXTEND_HIGH.
2438 if (auto ExtendHigh = GetExtendHigh(Op.getOperand(0), Ext, VT, DAG))
2439 return ExtendHigh;
2440 }
2441
2442 SDValue Ret = Src;
2443 while (Scale != 1) {
2444 Ret = DAG.getNode(Ext, DL,
2445 Ret.getValueType()
2448 Ret);
2449 Scale /= 2;
2450 }
2451 assert(Ret.getValueType() == VT);
2452 return Ret;
2453}
2454
2456 SDLoc DL(Op);
2457 if (Op.getValueType() != MVT::v2f64 && Op.getValueType() != MVT::v4f32)
2458 return SDValue();
2459
2460 auto GetConvertedLane = [](SDValue Op, unsigned &Opcode, SDValue &SrcVec,
2461 unsigned &Index) -> bool {
2462 switch (Op.getOpcode()) {
2463 case ISD::SINT_TO_FP:
2464 Opcode = WebAssemblyISD::CONVERT_LOW_S;
2465 break;
2466 case ISD::UINT_TO_FP:
2467 Opcode = WebAssemblyISD::CONVERT_LOW_U;
2468 break;
2469 case ISD::FP_EXTEND:
2470 case ISD::FP16_TO_FP:
2471 Opcode = WebAssemblyISD::PROMOTE_LOW;
2472 break;
2473 default:
2474 return false;
2475 }
2476
2477 auto ExtractVector = Op.getOperand(0);
2478 if (ExtractVector.getOpcode() != ISD::EXTRACT_VECTOR_ELT)
2479 return false;
2480
2481 if (!isa<ConstantSDNode>(ExtractVector.getOperand(1).getNode()))
2482 return false;
2483
2484 SrcVec = ExtractVector.getOperand(0);
2485 Index = ExtractVector.getConstantOperandVal(1);
2486 return true;
2487 };
2488
2489 unsigned NumLanes = Op.getValueType() == MVT::v2f64 ? 2 : 4;
2490 unsigned FirstOpcode = 0, SecondOpcode = 0, ThirdOpcode = 0, FourthOpcode = 0;
2491 unsigned FirstIndex = 0, SecondIndex = 0, ThirdIndex = 0, FourthIndex = 0;
2492 SDValue FirstSrcVec, SecondSrcVec, ThirdSrcVec, FourthSrcVec;
2493
2494 if (!GetConvertedLane(Op.getOperand(0), FirstOpcode, FirstSrcVec,
2495 FirstIndex) ||
2496 !GetConvertedLane(Op.getOperand(1), SecondOpcode, SecondSrcVec,
2497 SecondIndex))
2498 return SDValue();
2499
2500 // If we're converting to v4f32, check the third and fourth lanes, too.
2501 if (NumLanes == 4 && (!GetConvertedLane(Op.getOperand(2), ThirdOpcode,
2502 ThirdSrcVec, ThirdIndex) ||
2503 !GetConvertedLane(Op.getOperand(3), FourthOpcode,
2504 FourthSrcVec, FourthIndex)))
2505 return SDValue();
2506
2507 if (FirstOpcode != SecondOpcode)
2508 return SDValue();
2509
2510 // TODO Add an optimization similar to the v2f64 below for shuffling the
2511 // vectors when the lanes are in the wrong order or come from different src
2512 // vectors.
2513 if (NumLanes == 4 &&
2514 (FirstOpcode != ThirdOpcode || FirstOpcode != FourthOpcode ||
2515 FirstSrcVec != SecondSrcVec || FirstSrcVec != ThirdSrcVec ||
2516 FirstSrcVec != FourthSrcVec || FirstIndex != 0 || SecondIndex != 1 ||
2517 ThirdIndex != 2 || FourthIndex != 3))
2518 return SDValue();
2519
2520 MVT ExpectedSrcVT;
2521 switch (FirstOpcode) {
2522 case WebAssemblyISD::CONVERT_LOW_S:
2523 case WebAssemblyISD::CONVERT_LOW_U:
2524 ExpectedSrcVT = MVT::v4i32;
2525 break;
2526 case WebAssemblyISD::PROMOTE_LOW:
2527 ExpectedSrcVT = NumLanes == 2 ? MVT::v4f32 : MVT::v8i16;
2528 break;
2529 }
2530 if (FirstSrcVec.getValueType() != ExpectedSrcVT)
2531 return SDValue();
2532
2533 auto Src = FirstSrcVec;
2534 if (NumLanes == 2 &&
2535 (FirstIndex != 0 || SecondIndex != 1 || FirstSrcVec != SecondSrcVec)) {
2536 // Shuffle the source vector so that the converted lanes are the low lanes.
2537 Src = DAG.getVectorShuffle(ExpectedSrcVT, DL, FirstSrcVec, SecondSrcVec,
2538 {static_cast<int>(FirstIndex),
2539 static_cast<int>(SecondIndex) + 4, -1, -1});
2540 }
2541 return DAG.getNode(FirstOpcode, DL, NumLanes == 2 ? MVT::v2f64 : MVT::v4f32,
2542 Src);
2543}
2544
2545SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op,
2546 SelectionDAG &DAG) const {
2547 MVT VT = Op.getSimpleValueType();
2548 if (VT == MVT::v8f16) {
2549 // BUILD_VECTOR can't handle FP16 operands since Wasm doesn't have a scalar
2550 // FP16 type, so cast them to I16s.
2551 MVT IVT = VT.changeVectorElementType(MVT::i16);
2553 for (unsigned I = 0, E = Op.getNumOperands(); I < E; ++I)
2554 NewOps.push_back(DAG.getBitcast(MVT::i16, Op.getOperand(I)));
2555 SDValue Res = DAG.getNode(ISD::BUILD_VECTOR, SDLoc(), IVT, NewOps);
2556 return DAG.getBitcast(VT, Res);
2557 }
2558
2559 if (auto ConvertLow = LowerConvertLow(Op, DAG))
2560 return ConvertLow;
2561
2562 SDLoc DL(Op);
2563 const EVT VecT = Op.getValueType();
2564 const EVT LaneT = Op.getOperand(0).getValueType();
2565 const size_t Lanes = Op.getNumOperands();
2566 bool CanSwizzle = VecT == MVT::v16i8;
2567
2568 // BUILD_VECTORs are lowered to the instruction that initializes the highest
2569 // possible number of lanes at once followed by a sequence of replace_lane
2570 // instructions to individually initialize any remaining lanes.
2571
2572 // TODO: Tune this. For example, lanewise swizzling is very expensive, so
2573 // swizzled lanes should be given greater weight.
2574
2575 // TODO: Investigate looping rather than always extracting/replacing specific
2576 // lanes to fill gaps.
2577
2578 auto IsConstant = [](const SDValue &V) {
2579 return V.getOpcode() == ISD::Constant || V.getOpcode() == ISD::ConstantFP;
2580 };
2581
2582 // Returns the source vector and index vector pair if they exist. Checks for:
2583 // (extract_vector_elt
2584 // $src,
2585 // (sign_extend_inreg (extract_vector_elt $indices, $i))
2586 // )
2587 auto GetSwizzleSrcs = [](size_t I, const SDValue &Lane) {
2588 auto Bail = std::make_pair(SDValue(), SDValue());
2589 if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
2590 return Bail;
2591 const SDValue &SwizzleSrc = Lane->getOperand(0);
2592 const SDValue &IndexExt = Lane->getOperand(1);
2593 if (IndexExt->getOpcode() != ISD::SIGN_EXTEND_INREG)
2594 return Bail;
2595 const SDValue &Index = IndexExt->getOperand(0);
2596 if (Index->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
2597 return Bail;
2598 const SDValue &SwizzleIndices = Index->getOperand(0);
2599 if (SwizzleSrc.getValueType() != MVT::v16i8 ||
2600 SwizzleIndices.getValueType() != MVT::v16i8 ||
2601 Index->getOperand(1)->getOpcode() != ISD::Constant ||
2602 Index->getConstantOperandVal(1) != I)
2603 return Bail;
2604 return std::make_pair(SwizzleSrc, SwizzleIndices);
2605 };
2606
2607 // If the lane is extracted from another vector at a constant index, return
2608 // that vector. The source vector must not have more lanes than the dest
2609 // because the shufflevector indices are in terms of the destination lanes and
2610 // would not be able to address the smaller individual source lanes.
2611 auto GetShuffleSrc = [&](const SDValue &Lane) {
2612 if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT)
2613 return SDValue();
2614 if (!isa<ConstantSDNode>(Lane->getOperand(1).getNode()))
2615 return SDValue();
2616 if (Lane->getOperand(0).getValueType().getVectorNumElements() >
2617 VecT.getVectorNumElements())
2618 return SDValue();
2619 return Lane->getOperand(0);
2620 };
2621
2622 using ValueEntry = std::pair<SDValue, size_t>;
2623 SmallVector<ValueEntry, 16> SplatValueCounts;
2624
2625 using SwizzleEntry = std::pair<std::pair<SDValue, SDValue>, size_t>;
2626 SmallVector<SwizzleEntry, 16> SwizzleCounts;
2627
2628 using ShuffleEntry = std::pair<SDValue, size_t>;
2629 SmallVector<ShuffleEntry, 16> ShuffleCounts;
2630
2631 auto AddCount = [](auto &Counts, const auto &Val) {
2632 auto CountIt =
2633 llvm::find_if(Counts, [&Val](auto E) { return E.first == Val; });
2634 if (CountIt == Counts.end()) {
2635 Counts.emplace_back(Val, 1);
2636 } else {
2637 CountIt->second++;
2638 }
2639 };
2640
2641 auto GetMostCommon = [](auto &Counts) {
2642 auto CommonIt = llvm::max_element(Counts, llvm::less_second());
2643 assert(CommonIt != Counts.end() && "Unexpected all-undef build_vector");
2644 return *CommonIt;
2645 };
2646
2647 size_t NumConstantLanes = 0;
2648
2649 // Count eligible lanes for each type of vector creation op
2650 for (size_t I = 0; I < Lanes; ++I) {
2651 const SDValue &Lane = Op->getOperand(I);
2652 if (Lane.isUndef())
2653 continue;
2654
2655 AddCount(SplatValueCounts, Lane);
2656
2657 if (IsConstant(Lane))
2658 NumConstantLanes++;
2659 if (auto ShuffleSrc = GetShuffleSrc(Lane))
2660 AddCount(ShuffleCounts, ShuffleSrc);
2661 if (CanSwizzle) {
2662 auto SwizzleSrcs = GetSwizzleSrcs(I, Lane);
2663 if (SwizzleSrcs.first)
2664 AddCount(SwizzleCounts, SwizzleSrcs);
2665 }
2666 }
2667
2668 SDValue SplatValue;
2669 size_t NumSplatLanes;
2670 std::tie(SplatValue, NumSplatLanes) = GetMostCommon(SplatValueCounts);
2671
2672 SDValue SwizzleSrc;
2673 SDValue SwizzleIndices;
2674 size_t NumSwizzleLanes = 0;
2675 if (SwizzleCounts.size())
2676 std::forward_as_tuple(std::tie(SwizzleSrc, SwizzleIndices),
2677 NumSwizzleLanes) = GetMostCommon(SwizzleCounts);
2678
2679 // Shuffles can draw from up to two vectors, so find the two most common
2680 // sources.
2681 SDValue ShuffleSrc1, ShuffleSrc2;
2682 size_t NumShuffleLanes = 0;
2683 if (ShuffleCounts.size()) {
2684 std::tie(ShuffleSrc1, NumShuffleLanes) = GetMostCommon(ShuffleCounts);
2685 llvm::erase_if(ShuffleCounts,
2686 [&](const auto &Pair) { return Pair.first == ShuffleSrc1; });
2687 }
2688 if (ShuffleCounts.size()) {
2689 size_t AdditionalShuffleLanes;
2690 std::tie(ShuffleSrc2, AdditionalShuffleLanes) =
2691 GetMostCommon(ShuffleCounts);
2692 NumShuffleLanes += AdditionalShuffleLanes;
2693 }
2694
2695 // Predicate returning true if the lane is properly initialized by the
2696 // original instruction
2697 std::function<bool(size_t, const SDValue &)> IsLaneConstructed;
2699 // Prefer swizzles over shuffles over vector consts over splats
2700 if (NumSwizzleLanes >= NumShuffleLanes &&
2701 NumSwizzleLanes >= NumConstantLanes && NumSwizzleLanes >= NumSplatLanes) {
2702 Result = DAG.getNode(WebAssemblyISD::SWIZZLE, DL, VecT, SwizzleSrc,
2703 SwizzleIndices);
2704 auto Swizzled = std::make_pair(SwizzleSrc, SwizzleIndices);
2705 IsLaneConstructed = [&, Swizzled](size_t I, const SDValue &Lane) {
2706 return Swizzled == GetSwizzleSrcs(I, Lane);
2707 };
2708 } else if (NumShuffleLanes >= NumConstantLanes &&
2709 NumShuffleLanes >= NumSplatLanes) {
2710 size_t DestLaneSize = VecT.getVectorElementType().getFixedSizeInBits() / 8;
2711 size_t DestLaneCount = VecT.getVectorNumElements();
2712 size_t Scale1 = 1;
2713 size_t Scale2 = 1;
2714 SDValue Src1 = ShuffleSrc1;
2715 SDValue Src2 = ShuffleSrc2 ? ShuffleSrc2 : DAG.getUNDEF(VecT);
2716 if (Src1.getValueType() != VecT) {
2717 size_t LaneSize =
2718 Src1.getValueType().getVectorElementType().getFixedSizeInBits() / 8;
2719 assert(LaneSize > DestLaneSize);
2720 Scale1 = LaneSize / DestLaneSize;
2721 Src1 = DAG.getBitcast(VecT, Src1);
2722 }
2723 if (Src2.getValueType() != VecT) {
2724 size_t LaneSize =
2725 Src2.getValueType().getVectorElementType().getFixedSizeInBits() / 8;
2726 assert(LaneSize > DestLaneSize);
2727 Scale2 = LaneSize / DestLaneSize;
2728 Src2 = DAG.getBitcast(VecT, Src2);
2729 }
2730
2731 int Mask[16];
2732 assert(DestLaneCount <= 16);
2733 for (size_t I = 0; I < DestLaneCount; ++I) {
2734 const SDValue &Lane = Op->getOperand(I);
2735 SDValue Src = GetShuffleSrc(Lane);
2736 if (Src == ShuffleSrc1) {
2737 Mask[I] = Lane->getConstantOperandVal(1) * Scale1;
2738 } else if (Src && Src == ShuffleSrc2) {
2739 Mask[I] = DestLaneCount + Lane->getConstantOperandVal(1) * Scale2;
2740 } else {
2741 Mask[I] = -1;
2742 }
2743 }
2744 ArrayRef<int> MaskRef(Mask, DestLaneCount);
2745 Result = DAG.getVectorShuffle(VecT, DL, Src1, Src2, MaskRef);
2746 IsLaneConstructed = [&](size_t, const SDValue &Lane) {
2747 auto Src = GetShuffleSrc(Lane);
2748 return Src == ShuffleSrc1 || (Src && Src == ShuffleSrc2);
2749 };
2750 } else if (NumConstantLanes >= NumSplatLanes) {
2751 SmallVector<SDValue, 16> ConstLanes;
2752 for (const SDValue &Lane : Op->op_values()) {
2753 if (IsConstant(Lane)) {
2754 // Values may need to be fixed so that they will sign extend to be
2755 // within the expected range during ISel. Check whether the value is in
2756 // bounds based on the lane bit width and if it is out of bounds, lop
2757 // off the extra bits.
2758 uint64_t LaneBits = 128 / Lanes;
2759 if (auto *Const = dyn_cast<ConstantSDNode>(Lane.getNode())) {
2760 ConstLanes.push_back(DAG.getConstant(
2761 Const->getAPIntValue().trunc(LaneBits).getZExtValue(),
2762 SDLoc(Lane), LaneT));
2763 } else {
2764 ConstLanes.push_back(Lane);
2765 }
2766 } else if (LaneT.isFloatingPoint()) {
2767 ConstLanes.push_back(DAG.getConstantFP(0, DL, LaneT));
2768 } else {
2769 ConstLanes.push_back(DAG.getConstant(0, DL, LaneT));
2770 }
2771 }
2772 Result = DAG.getBuildVector(VecT, DL, ConstLanes);
2773 IsLaneConstructed = [&IsConstant](size_t _, const SDValue &Lane) {
2774 return IsConstant(Lane);
2775 };
2776 } else {
2777 size_t DestLaneSize = VecT.getVectorElementType().getFixedSizeInBits();
2778 if (NumSplatLanes == 1 && Op->getOperand(0) == SplatValue &&
2779 (DestLaneSize == 32 || DestLaneSize == 64)) {
2780 // Could be selected to load_zero.
2781 Result = DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, VecT, SplatValue);
2782 } else {
2783 // Use a splat (which might be selected as a load splat)
2784 Result = DAG.getSplatBuildVector(VecT, DL, SplatValue);
2785 }
2786 IsLaneConstructed = [&SplatValue](size_t _, const SDValue &Lane) {
2787 return Lane == SplatValue;
2788 };
2789 }
2790
2791 assert(Result);
2792 assert(IsLaneConstructed);
2793
2794 // Add replace_lane instructions for any unhandled values
2795 for (size_t I = 0; I < Lanes; ++I) {
2796 const SDValue &Lane = Op->getOperand(I);
2797 if (!Lane.isUndef() && !IsLaneConstructed(I, Lane))
2798 Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane,
2799 DAG.getConstant(I, DL, MVT::i32));
2800 }
2801
2802 return Result;
2803}
2804
2805SDValue
2806WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op,
2807 SelectionDAG &DAG) const {
2808 SDLoc DL(Op);
2809 ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask();
2810 MVT VecType = Op.getOperand(0).getSimpleValueType();
2811 assert(VecType.is128BitVector() && "Unexpected shuffle vector type");
2812 size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8;
2813
2814 // Space for two vector args and sixteen mask indices
2815 SDValue Ops[18];
2816 size_t OpIdx = 0;
2817 Ops[OpIdx++] = Op.getOperand(0);
2818 Ops[OpIdx++] = Op.getOperand(1);
2819
2820 // Expand mask indices to byte indices and materialize them as operands
2821 for (int M : Mask) {
2822 for (size_t J = 0; J < LaneBytes; ++J) {
2823 // Lower undefs (represented by -1 in mask) to {0..J}, which use a
2824 // whole lane of vector input, to allow further reduction at VM. E.g.
2825 // match an 8x16 byte shuffle to an equivalent cheaper 32x4 shuffle.
2826 uint64_t ByteIndex = M == -1 ? J : (uint64_t)M * LaneBytes + J;
2827 Ops[OpIdx++] = DAG.getConstant(ByteIndex, DL, MVT::i32);
2828 }
2829 }
2830
2831 return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops);
2832}
2833
2834SDValue WebAssemblyTargetLowering::LowerSETCC(SDValue Op,
2835 SelectionDAG &DAG) const {
2836 SDLoc DL(Op);
2837 // The legalizer does not know how to expand the unsupported comparison modes
2838 // of i64x2 vectors, so we manually unroll them here.
2839 assert(Op->getOperand(0)->getSimpleValueType(0) == MVT::v2i64);
2841 DAG.ExtractVectorElements(Op->getOperand(0), LHS);
2842 DAG.ExtractVectorElements(Op->getOperand(1), RHS);
2843 const SDValue &CC = Op->getOperand(2);
2844 auto MakeLane = [&](unsigned I) {
2845 return DAG.getNode(ISD::SELECT_CC, DL, MVT::i64, LHS[I], RHS[I],
2846 DAG.getConstant(uint64_t(-1), DL, MVT::i64),
2847 DAG.getConstant(uint64_t(0), DL, MVT::i64), CC);
2848 };
2849 return DAG.getBuildVector(Op->getValueType(0), DL,
2850 {MakeLane(0), MakeLane(1)});
2851}
2852
2853SDValue
2854WebAssemblyTargetLowering::LowerAccessVectorElement(SDValue Op,
2855 SelectionDAG &DAG) const {
2856 if (Op.getOpcode() == ISD::INSERT_VECTOR_ELT &&
2857 Op.getValueType() == MVT::v8f16) {
2858 // INSERT_VECTOR_ELT can't handle FP16 operands since Wasm doesn't have a
2859 // scalar FP16 type, so cast them to I16s.
2860 SDLoc DL(Op);
2861 SDValue IntVector = DAG.getBitcast(MVT::v8i16, Op.getOperand(0));
2862 SDValue IntElement = DAG.getBitcast(MVT::i16, Op.getOperand(1));
2864 IntVector, IntElement, Op.getOperand(2));
2865 return DAG.getBitcast(MVT::v8f16, Inserted);
2866 }
2867
2868 // Allow constant lane indices, expand variable lane indices
2869 SDNode *IdxNode = Op.getOperand(Op.getNumOperands() - 1).getNode();
2870 if (isa<ConstantSDNode>(IdxNode)) {
2871 // Ensure the index type is i32 to match the tablegen patterns
2872 uint64_t Idx = IdxNode->getAsZExtVal();
2873 SmallVector<SDValue, 3> Ops(Op.getNode()->ops());
2874 Ops[Op.getNumOperands() - 1] =
2875 DAG.getConstant(Idx, SDLoc(IdxNode), MVT::i32);
2876 return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(), Ops);
2877 }
2878 // Perform default expansion
2879 return SDValue();
2880}
2881
2883 EVT LaneT = Op.getSimpleValueType().getVectorElementType();
2884 // 32-bit and 64-bit unrolled shifts will have proper semantics
2885 if (LaneT.bitsGE(MVT::i32))
2886 return DAG.UnrollVectorOp(Op.getNode());
2887 // Otherwise mask the shift value to get proper semantics from 32-bit shift
2888 SDLoc DL(Op);
2889 size_t NumLanes = Op.getSimpleValueType().getVectorNumElements();
2890 SDValue Mask = DAG.getConstant(LaneT.getSizeInBits() - 1, DL, MVT::i32);
2891 unsigned ShiftOpcode = Op.getOpcode();
2892 SmallVector<SDValue, 16> ShiftedElements;
2893 DAG.ExtractVectorElements(Op.getOperand(0), ShiftedElements, 0, 0, MVT::i32);
2894 SmallVector<SDValue, 16> ShiftElements;
2895 DAG.ExtractVectorElements(Op.getOperand(1), ShiftElements, 0, 0, MVT::i32);
2896 SmallVector<SDValue, 16> UnrolledOps;
2897 for (size_t i = 0; i < NumLanes; ++i) {
2898 SDValue MaskedShiftValue =
2899 DAG.getNode(ISD::AND, DL, MVT::i32, ShiftElements[i], Mask);
2900 SDValue ShiftedValue = ShiftedElements[i];
2901 if (ShiftOpcode == ISD::SRA)
2902 ShiftedValue = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, MVT::i32,
2903 ShiftedValue, DAG.getValueType(LaneT));
2904 UnrolledOps.push_back(
2905 DAG.getNode(ShiftOpcode, DL, MVT::i32, ShiftedValue, MaskedShiftValue));
2906 }
2907 return DAG.getBuildVector(Op.getValueType(), DL, UnrolledOps);
2908}
2909
2910SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op,
2911 SelectionDAG &DAG) const {
2912 SDLoc DL(Op);
2913 // Only manually lower vector shifts
2914 assert(Op.getSimpleValueType().isVector());
2915
2916 uint64_t LaneBits = Op.getValueType().getScalarSizeInBits();
2917 auto ShiftVal = Op.getOperand(1);
2918
2919 // Try to skip bitmask operation since it is implied inside shift instruction
2920 auto SkipImpliedMask = [](SDValue MaskOp, uint64_t MaskBits) {
2921 if (MaskOp.getOpcode() != ISD::AND)
2922 return MaskOp;
2923 SDValue LHS = MaskOp.getOperand(0);
2924 SDValue RHS = MaskOp.getOperand(1);
2925 if (MaskOp.getValueType().isVector()) {
2926 APInt MaskVal;
2927 if (!ISD::isConstantSplatVector(RHS.getNode(), MaskVal))
2928 std::swap(LHS, RHS);
2929
2930 if (ISD::isConstantSplatVector(RHS.getNode(), MaskVal) &&
2931 MaskVal == MaskBits)
2932 MaskOp = LHS;
2933 } else {
2934 if (!isa<ConstantSDNode>(RHS.getNode()))
2935 std::swap(LHS, RHS);
2936
2937 auto ConstantRHS = dyn_cast<ConstantSDNode>(RHS.getNode());
2938 if (ConstantRHS && ConstantRHS->getAPIntValue() == MaskBits)
2939 MaskOp = LHS;
2940 }
2941
2942 return MaskOp;
2943 };
2944
2945 // Skip vector and operation
2946 ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1);
2947 ShiftVal = DAG.getSplatValue(ShiftVal);
2948 if (!ShiftVal)
2949 return unrollVectorShift(Op, DAG);
2950
2951 // Skip scalar and operation
2952 ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1);
2953 // Use anyext because none of the high bits can affect the shift
2954 ShiftVal = DAG.getAnyExtOrTrunc(ShiftVal, DL, MVT::i32);
2955
2956 unsigned Opcode;
2957 switch (Op.getOpcode()) {
2958 case ISD::SHL:
2959 Opcode = WebAssemblyISD::VEC_SHL;
2960 break;
2961 case ISD::SRA:
2962 Opcode = WebAssemblyISD::VEC_SHR_S;
2963 break;
2964 case ISD::SRL:
2965 Opcode = WebAssemblyISD::VEC_SHR_U;
2966 break;
2967 default:
2968 llvm_unreachable("unexpected opcode");
2969 }
2970
2971 return DAG.getNode(Opcode, DL, Op.getValueType(), Op.getOperand(0), ShiftVal);
2972}
2973
2974SDValue WebAssemblyTargetLowering::LowerFP_TO_INT_SAT(SDValue Op,
2975 SelectionDAG &DAG) const {
2976 EVT ResT = Op.getValueType();
2977 EVT SatVT = cast<VTSDNode>(Op.getOperand(1))->getVT();
2978
2979 if ((ResT == MVT::i32 || ResT == MVT::i64) &&
2980 (SatVT == MVT::i32 || SatVT == MVT::i64))
2981 return Op;
2982
2983 if (ResT == MVT::v4i32 && SatVT == MVT::i32)
2984 return Op;
2985
2986 if (ResT == MVT::v8i16 && SatVT == MVT::i16)
2987 return Op;
2988
2989 return SDValue();
2990}
2991
2993 return (Op->getFlags().hasNoNaNs() ||
2994 (DAG.isKnownNeverNaN(Op->getOperand(0)) &&
2995 DAG.isKnownNeverNaN(Op->getOperand(1)))) &&
2996 (Op->getFlags().hasNoSignedZeros() ||
2997 DAG.isKnownNeverLogicalZero(Op->getOperand(0)) ||
2998 DAG.isKnownNeverLogicalZero(Op->getOperand(1)));
2999}
3000
3001SDValue WebAssemblyTargetLowering::LowerFMIN(SDValue Op,
3002 SelectionDAG &DAG) const {
3003 if (Subtarget->hasRelaxedSIMD() && HasNoSignedZerosOrNaNs(Op, DAG)) {
3004 return DAG.getNode(WebAssemblyISD::RELAXED_FMIN, SDLoc(Op),
3005 Op.getValueType(), Op.getOperand(0), Op.getOperand(1));
3006 }
3007 return SDValue();
3008}
3009
3010SDValue WebAssemblyTargetLowering::LowerFMAX(SDValue Op,
3011 SelectionDAG &DAG) const {
3012 if (Subtarget->hasRelaxedSIMD() && HasNoSignedZerosOrNaNs(Op, DAG)) {
3013 return DAG.getNode(WebAssemblyISD::RELAXED_FMAX, SDLoc(Op),
3014 Op.getValueType(), Op.getOperand(0), Op.getOperand(1));
3015 }
3016 return SDValue();
3017}
3018
3019//===----------------------------------------------------------------------===//
3020// Custom DAG combine hooks
3021//===----------------------------------------------------------------------===//
3022static SDValue
3024 auto &DAG = DCI.DAG;
3025 auto Shuffle = cast<ShuffleVectorSDNode>(N);
3026
3027 // Hoist vector bitcasts that don't change the number of lanes out of unary
3028 // shuffles, where they are less likely to get in the way of other combines.
3029 // (shuffle (vNxT1 (bitcast (vNxT0 x))), undef, mask) ->
3030 // (vNxT1 (bitcast (vNxT0 (shuffle x, undef, mask))))
3031 SDValue Bitcast = N->getOperand(0);
3032 if (Bitcast.getOpcode() != ISD::BITCAST)
3033 return SDValue();
3034 if (!N->getOperand(1).isUndef())
3035 return SDValue();
3036 SDValue CastOp = Bitcast.getOperand(0);
3037 EVT SrcType = CastOp.getValueType();
3038 EVT DstType = Bitcast.getValueType();
3039 if (!SrcType.is128BitVector() ||
3040 SrcType.getVectorNumElements() != DstType.getVectorNumElements())
3041 return SDValue();
3042 SDValue NewShuffle = DAG.getVectorShuffle(
3043 SrcType, SDLoc(N), CastOp, DAG.getUNDEF(SrcType), Shuffle->getMask());
3044 return DAG.getBitcast(DstType, NewShuffle);
3045}
3046
3047/// Convert ({u,s}itofp vec) --> ({u,s}itofp ({s,z}ext vec)) so it doesn't get
3048/// split up into scalar instructions during legalization, and the vector
3049/// extending instructions are selected in performVectorExtendCombine below.
3050static SDValue
3053 auto &DAG = DCI.DAG;
3054 assert(N->getOpcode() == ISD::UINT_TO_FP ||
3055 N->getOpcode() == ISD::SINT_TO_FP);
3056
3057 EVT InVT = N->getOperand(0)->getValueType(0);
3058 EVT ResVT = N->getValueType(0);
3059 MVT ExtVT;
3060 if (ResVT == MVT::v4f32 && (InVT == MVT::v4i16 || InVT == MVT::v4i8))
3061 ExtVT = MVT::v4i32;
3062 else if (ResVT == MVT::v2f64 && (InVT == MVT::v2i16 || InVT == MVT::v2i8))
3063 ExtVT = MVT::v2i32;
3064 else if (Subtarget->hasFP16() && ResVT == MVT::v8f16 && InVT == MVT::v8i8)
3065 ExtVT = MVT::v8i16;
3066 else
3067 return SDValue();
3068
3069 unsigned Op =
3071 SDValue Conv = DAG.getNode(Op, SDLoc(N), ExtVT, N->getOperand(0));
3072 return DAG.getNode(N->getOpcode(), SDLoc(N), ResVT, Conv);
3073}
3074
3075static SDValue
3078 auto &DAG = DCI.DAG;
3079
3080 SDNodeFlags Flags = N->getFlags();
3081 SDValue Op0 = N->getOperand(0);
3082 EVT VT = N->getValueType(0);
3083
3084 // Optimize uitofp to sitofp when the sign bit is known to be zero.
3085 // Depending on the target (runtime) backend, this might be performance
3086 // neutral (e.g. AArch64) or a significant improvement (e.g. x86_64).
3087 if (VT.isVector() && (Flags.hasNonNeg() || DAG.SignBitIsZero(Op0))) {
3088 return DAG.getNode(ISD::SINT_TO_FP, SDLoc(N), VT, Op0);
3089 }
3090
3091 return SDValue();
3092}
3093
3094static SDValue DoubleVectorWidth(SDValue In, unsigned RequiredNumElems,
3095 SelectionDAG &DAG) {
3096 SDLoc DL(In);
3097 LLVMContext &Ctx = *DAG.getContext();
3098 EVT InVT = In.getValueType();
3099 unsigned NumElems = InVT.getVectorNumElements() * 2;
3100 EVT OutVT = EVT::getVectorVT(Ctx, InVT.getVectorElementType(), NumElems);
3101 SDValue Concat =
3102 DAG.getNode(ISD::CONCAT_VECTORS, DL, OutVT, In, DAG.getPOISON(InVT));
3103 if (NumElems < RequiredNumElems) {
3104 return DoubleVectorWidth(Concat, RequiredNumElems, DAG);
3105 }
3106 return Concat;
3107}
3108
3109static SDValue ExtendLowAndHigh(SDValue Input, bool IsSext, EVT SrcVT,
3110 EVT ResVT, SelectionDAG &DAG) {
3111 SDLoc DL(Input.getNode());
3112 unsigned LowOp =
3113 IsSext ? WebAssemblyISD::EXTEND_LOW_S : WebAssemblyISD::EXTEND_LOW_U;
3114 unsigned HighOp =
3115 IsSext ? WebAssemblyISD::EXTEND_HIGH_S : WebAssemblyISD::EXTEND_HIGH_U;
3116
3117 if (SrcVT == MVT::v8i8) {
3118 assert(ResVT == MVT::v8i32);
3119 // extend_low and then extend_low and extend_high.
3120 SDValue LowHalf =
3121 DAG.getNode(LowOp, DL, MVT::v8i16, DoubleVectorWidth(Input, 16, DAG));
3122 return DAG.getNode(ISD::CONCAT_VECTORS, DL, ResVT,
3123 {
3124 DAG.getNode(LowOp, DL, MVT::v4i32, LowHalf),
3125 DAG.getNode(HighOp, DL, MVT::v4i32, LowHalf),
3126 });
3127 } else {
3128 assert(ResVT == MVT::v16i32);
3129 assert(SrcVT == MVT::v16i8);
3130 SDValue LowHalf = DAG.getNode(LowOp, DL, MVT::v8i16, Input);
3131 SDValue HighHalf = DAG.getNode(HighOp, DL, MVT::v8i16, Input);
3132 return DAG.getNode(ISD::CONCAT_VECTORS, DL, ResVT,
3133 {
3134 DAG.getNode(LowOp, DL, MVT::v4i32, LowHalf),
3135 DAG.getNode(HighOp, DL, MVT::v4i32, LowHalf),
3136 DAG.getNode(LowOp, DL, MVT::v4i32, HighHalf),
3137 DAG.getNode(HighOp, DL, MVT::v4i32, HighHalf),
3138 });
3139 }
3140}
3141
3142static SDValue
3144 auto &DAG = DCI.DAG;
3145 assert(N->getOpcode() == ISD::SIGN_EXTEND ||
3146 N->getOpcode() == ISD::ZERO_EXTEND);
3147
3148 EVT ResVT = N->getValueType(0);
3149 EVT SrcVT = N->getOperand(0)->getValueType(0);
3150 bool IsSext = N->getOpcode() == ISD::SIGN_EXTEND;
3151 SDLoc DL(N);
3152
3153 if ((ResVT == MVT::v16i32 && SrcVT == MVT::v16i8) ||
3154 (ResVT == MVT::v8i32 && SrcVT == MVT::v8i8)) {
3155 // Use a tree of extend low/high to split and extend the input in two
3156 // layers to avoid doing several shuffles and even more extends.
3157 return ExtendLowAndHigh(N->getOperand(0), IsSext, SrcVT, ResVT, DAG);
3158 }
3159
3160 // Combine ({s,z}ext (extract_subvector src, i)) into a widening operation if
3161 // possible before the extract_subvector can be expanded.
3162 auto Extract = N->getOperand(0);
3163 if (Extract.getOpcode() != ISD::EXTRACT_SUBVECTOR)
3164 return SDValue();
3165 auto Source = Extract.getOperand(0);
3166 auto *IndexNode = dyn_cast<ConstantSDNode>(Extract.getOperand(1));
3167 if (IndexNode == nullptr)
3168 return SDValue();
3169 auto Index = IndexNode->getZExtValue();
3170
3171 // Only v8i8, v4i16, and v2i32 extracts can be widened, and only if the
3172 // extracted subvector is the low or high half of its source.
3173 if (ResVT == MVT::v8i16) {
3174 if (Extract.getValueType() != MVT::v8i8 ||
3175 Source.getValueType() != MVT::v16i8 || (Index != 0 && Index != 8))
3176 return SDValue();
3177 } else if (ResVT == MVT::v4i32) {
3178 if (Extract.getValueType() != MVT::v4i16 ||
3179 Source.getValueType() != MVT::v8i16 || (Index != 0 && Index != 4))
3180 return SDValue();
3181 } else if (ResVT == MVT::v2i64) {
3182 if (Extract.getValueType() != MVT::v2i32 ||
3183 Source.getValueType() != MVT::v4i32 || (Index != 0 && Index != 2))
3184 return SDValue();
3185 } else {
3186 return SDValue();
3187 }
3188
3189 bool IsLow = Index == 0;
3190
3191 unsigned Op = IsSext ? (IsLow ? WebAssemblyISD::EXTEND_LOW_S
3192 : WebAssemblyISD::EXTEND_HIGH_S)
3193 : (IsLow ? WebAssemblyISD::EXTEND_LOW_U
3194 : WebAssemblyISD::EXTEND_HIGH_U);
3195
3196 return DAG.getNode(Op, DL, ResVT, Source);
3197}
3198
3199static SDValue
3201 auto &DAG = DCI.DAG;
3202
3203 auto GetWasmConversionOp = [](unsigned Op) {
3204 switch (Op) {
3206 return WebAssemblyISD::TRUNC_SAT_ZERO_S;
3208 return WebAssemblyISD::TRUNC_SAT_ZERO_U;
3209 case ISD::FP_ROUND:
3210 return WebAssemblyISD::DEMOTE_ZERO;
3211 }
3212 llvm_unreachable("unexpected op");
3213 };
3214
3215 auto IsZeroSplat = [](SDValue SplatVal) {
3216 auto *Splat = dyn_cast<BuildVectorSDNode>(SplatVal.getNode());
3217 APInt SplatValue, SplatUndef;
3218 unsigned SplatBitSize;
3219 bool HasAnyUndefs;
3220 // Endianness doesn't matter in this context because we are looking for
3221 // an all-zero value.
3222 return Splat &&
3223 Splat->isConstantSplat(SplatValue, SplatUndef, SplatBitSize,
3224 HasAnyUndefs) &&
3225 SplatValue == 0;
3226 };
3227
3228 if (N->getOpcode() == ISD::CONCAT_VECTORS) {
3229 // Combine this:
3230 //
3231 // (concat_vectors (v2i32 (fp_to_{s,u}int_sat $x, 32)), (v2i32 (splat 0)))
3232 //
3233 // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x).
3234 //
3235 // Or this:
3236 //
3237 // (concat_vectors ({v2f32, v4f16} (fp_round ({v2f64, v4f32} $x))),
3238 // ({v2f32, v4f16} (splat 0)))
3239 //
3240 // into ({f32x4, f16x8}.demote_zero_{f64x2, f32x4} $x).
3241 EVT ResVT;
3242 EVT ExpectedConversionType;
3243 auto Conversion = N->getOperand(0);
3244 auto ConversionOp = Conversion.getOpcode();
3245 switch (ConversionOp) {
3248 ResVT = MVT::v4i32;
3249 ExpectedConversionType = MVT::v2i32;
3250 break;
3251 case ISD::FP_ROUND:
3252 if (Conversion.getValueType() == MVT::v2f32) {
3253 ResVT = MVT::v4f32;
3254 ExpectedConversionType = MVT::v2f32;
3255 } else if (Conversion.getValueType() == MVT::v4f16) {
3256 ResVT = MVT::v8f16;
3257 ExpectedConversionType = MVT::v4f16;
3258 } else {
3259 return SDValue();
3260 }
3261 break;
3262 default:
3263 return SDValue();
3264 }
3265
3266 if (N->getValueType(0) != ResVT)
3267 return SDValue();
3268
3269 if (Conversion.getValueType() != ExpectedConversionType)
3270 return SDValue();
3271
3272 auto Source = Conversion.getOperand(0);
3273 if (!((Source.getValueType() == MVT::v2f64 && ResVT == MVT::v4f32) ||
3274 (Source.getValueType() == MVT::v2f64 && ResVT == MVT::v4i32) ||
3275 (Source.getValueType() == MVT::v4f32 && ResVT == MVT::v8f16)))
3276 return SDValue();
3277
3278 if (!IsZeroSplat(N->getOperand(1)) ||
3279 N->getOperand(1).getValueType() != ExpectedConversionType)
3280 return SDValue();
3281
3282 unsigned Op = GetWasmConversionOp(ConversionOp);
3283 return DAG.getNode(Op, SDLoc(N), ResVT, Source);
3284 }
3285
3286 // Combine this:
3287 //
3288 // (fp_to_{s,u}int_sat (concat_vectors $x, (v2f64 (splat 0))), 32)
3289 //
3290 // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x).
3291 //
3292 // Or this:
3293 //
3294 // ({v4f32, v8f16} (fp_round (concat_vectors $x,
3295 // ({v2f64, v4f32} (splat 0)))))
3296 //
3297 // into ({f32x4, f16x8}.demote_zero_{f64x2, f32x4} $x).
3298 EVT ResVT;
3299 auto ConversionOp = N->getOpcode();
3300 switch (ConversionOp) {
3303 ResVT = MVT::v4i32;
3304 break;
3305 case ISD::FP_ROUND:
3306 ResVT = N->getValueType(0);
3307 break;
3308 default:
3309 llvm_unreachable("unexpected op");
3310 }
3311
3312 if (N->getValueType(0) != ResVT)
3313 return SDValue();
3314
3315 auto Concat = N->getOperand(0);
3316 if (Concat.getOpcode() != ISD::CONCAT_VECTORS)
3317 return SDValue();
3318 EVT ConcatVT = Concat.getValueType();
3319 EVT SourceVT = Concat.getOperand(0).getValueType();
3320
3321 if (!IsZeroSplat(Concat.getOperand(1)))
3322 return SDValue();
3323
3324 if (ConversionOp == ISD::FP_ROUND) {
3325 bool IsF64ToF32 =
3326 ConcatVT == MVT::v4f64 && SourceVT == MVT::v2f64 && ResVT == MVT::v4f32;
3327 bool IsF32ToF16 =
3328 ConcatVT == MVT::v8f32 && SourceVT == MVT::v4f32 && ResVT == MVT::v8f16;
3329 if (!(IsF64ToF32 || IsF32ToF16))
3330 return SDValue();
3331 } else {
3332 if (ConcatVT != MVT::v4f64 || SourceVT != MVT::v2f64 || ResVT != MVT::v4i32)
3333 return SDValue();
3334 }
3335
3336 unsigned Op = GetWasmConversionOp(ConversionOp);
3337 return DAG.getNode(Op, SDLoc(N), ResVT, Concat.getOperand(0));
3338}
3339
3340// Helper to extract VectorWidth bits from Vec, starting from IdxVal.
3341static SDValue extractSubVector(SDValue Vec, unsigned IdxVal, SelectionDAG &DAG,
3342 const SDLoc &DL, unsigned VectorWidth) {
3343 EVT VT = Vec.getValueType();
3344 EVT ElVT = VT.getVectorElementType();
3345 unsigned Factor = VT.getSizeInBits() / VectorWidth;
3346 EVT ResultVT = EVT::getVectorVT(*DAG.getContext(), ElVT,
3347 VT.getVectorNumElements() / Factor);
3348
3349 // Extract the relevant VectorWidth bits. Generate an EXTRACT_SUBVECTOR
3350 unsigned ElemsPerChunk = VectorWidth / ElVT.getSizeInBits();
3351 assert(isPowerOf2_32(ElemsPerChunk) && "Elements per chunk not power of 2");
3352
3353 // This is the index of the first element of the VectorWidth-bit chunk
3354 // we want. Since ElemsPerChunk is a power of 2 just need to clear bits.
3355 IdxVal &= ~(ElemsPerChunk - 1);
3356
3357 // If the input is a buildvector just emit a smaller one.
3358 if (Vec.getOpcode() == ISD::BUILD_VECTOR)
3359 return DAG.getBuildVector(ResultVT, DL,
3360 Vec->ops().slice(IdxVal, ElemsPerChunk));
3361
3362 SDValue VecIdx = DAG.getIntPtrConstant(IdxVal, DL);
3363 return DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ResultVT, Vec, VecIdx);
3364}
3365
3366// Helper to recursively truncate vector elements in half with NARROW_U. DstVT
3367// is the expected destination value type after recursion. In is the initial
3368// input. Note that the input should have enough leading zero bits to prevent
3369// NARROW_U from saturating results.
3371 SelectionDAG &DAG) {
3372 EVT SrcVT = In.getValueType();
3373
3374 if (In.isUndef())
3375 return DAG.getUNDEF(DstVT);
3376
3377 // No truncation required, we might get here due to recursive calls.
3378 if (SrcVT == DstVT)
3379 return In;
3380
3381 unsigned SrcSizeInBits = SrcVT.getSizeInBits();
3382 unsigned NumElems = SrcVT.getVectorNumElements();
3383 if (!isPowerOf2_32(NumElems))
3384 return SDValue();
3385 assert(DstVT.getVectorNumElements() == NumElems && "Illegal truncation");
3386 assert(SrcSizeInBits > DstVT.getSizeInBits() && "Illegal truncation");
3387
3388 LLVMContext &Ctx = *DAG.getContext();
3389
3390 if (SrcVT == MVT::v8i16 && DstVT == MVT::v8i8) {
3391 // Use a narrow to avoid a shuffle, which can also prevent other shuffles
3392 // degrading to extract/replace_lane.
3393 SDValue Poison = DAG.getPOISON(SrcVT);
3394 SDValue Res =
3395 DAG.getNode(WebAssemblyISD::NARROW_U, DL, MVT::v16i8, In, Poison);
3396 return extractSubVector(Res, 0, DAG, DL, 64);
3397 }
3398
3399 if (SrcVT == MVT::v4i32 && DstVT == MVT::v4i8) {
3400 SDValue Poison = DAG.getPOISON(SrcVT);
3401 SDValue Narrow =
3402 DAG.getNode(WebAssemblyISD::NARROW_U, DL, MVT::v8i16, In, Poison);
3403 SDValue Extract = extractSubVector(Narrow, 0, DAG, DL, 64);
3404 return truncateVectorWithNARROW(DstVT, Extract, DL, DAG);
3405 }
3406
3407 if (SrcVT == MVT::v4i32 && DstVT == MVT::v4i16) {
3408 SDValue Poison = DAG.getPOISON(SrcVT);
3409 SDValue Narrow =
3410 DAG.getNode(WebAssemblyISD::NARROW_U, DL, MVT::v8i16, In, Poison);
3411 return extractSubVector(Narrow, 0, DAG, DL, 64);
3412 }
3413
3414 if (SrcVT == MVT::v4i16 && DstVT == MVT::v4i8) {
3415 In = DAG.getNode(ISD::CONCAT_VECTORS, DL, MVT::v8i16,
3416 {In, DAG.getPOISON(SrcVT)});
3417 SDValue Res = DAG.getNode(WebAssemblyISD::NARROW_U, DL, MVT::v16i8, In,
3418 DAG.getPOISON(MVT::v8i16));
3419 return extractSubVector(Res, 0, DAG, DL, 32);
3420 }
3421
3422 unsigned SubSizeInBits = SrcSizeInBits / 2;
3423 // Narrow to the largest type possible:
3424 // vXi64/vXi32 -> i16x8.narrow_i32x4_u and vXi16 -> i8x16.narrow_i16x8_u.
3425 EVT OutVT = SrcVT.getScalarSizeInBits() > 16 ? MVT::i16 : MVT::i8;
3426 OutVT = EVT::getVectorVT(Ctx, OutVT, SubSizeInBits / OutVT.getSizeInBits());
3427 EVT InVT =
3429
3430 // Split lower/upper subvectors.
3431 SDValue Lo = extractSubVector(In, 0, DAG, DL, SubSizeInBits);
3432 SDValue Hi = extractSubVector(In, NumElems / 2, DAG, DL, SubSizeInBits);
3433
3434 if (SrcVT.is256BitVector()) {
3435 if (DstVT.is128BitVector()) {
3436 // 256bit -> 128bit truncate - Narrow lower/upper 128-bit subvectors.
3437 Lo = DAG.getBitcast(InVT, Lo);
3438 Hi = DAG.getBitcast(InVT, Hi);
3439 SDValue Res = DAG.getNode(WebAssemblyISD::NARROW_U, DL, OutVT, Lo, Hi);
3440 return DAG.getBitcast(DstVT, Res);
3441 }
3442 if (DstVT.is64BitVector() || DstVT.is32BitVector()) {
3443 Lo = DAG.getBitcast(InVT, Lo);
3444 Hi = DAG.getBitcast(InVT, Hi);
3445 SDValue Res = DAG.getNode(WebAssemblyISD::NARROW_U, DL, OutVT, Lo, Hi);
3446 if (OutVT.getVectorNumElements() > DstVT.getVectorNumElements()) {
3448 Res = DAG.getBitcast(InVT, Res);
3449 }
3450 return truncateVectorWithNARROW(DstVT, Res, DL, DAG);
3451 }
3452 }
3453
3454 // Recursively narrow lower/upper subvectors, concat result and narrow again.
3455 EVT PackedSVT = EVT::getIntegerVT(Ctx, SrcVT.getScalarSizeInBits() / 2);
3456 EVT PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems / 2);
3457 Lo = truncateVectorWithNARROW(PackedVT, Lo, DL, DAG);
3458 Hi = truncateVectorWithNARROW(PackedVT, Hi, DL, DAG);
3459
3460 PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems);
3461 SDValue Res = DAG.getNode(ISD::CONCAT_VECTORS, DL, PackedVT, Lo, Hi);
3462 return truncateVectorWithNARROW(DstVT, Res, DL, DAG);
3463}
3464
3467 auto &DAG = DCI.DAG;
3468
3469 SDValue In = N->getOperand(0);
3470 EVT InVT = In.getValueType();
3471 if (!InVT.isSimple())
3472 return SDValue();
3473
3474 EVT OutVT = N->getValueType(0);
3475 if (!OutVT.isVector())
3476 return SDValue();
3477
3478 if (OutVT != MVT::v16i16 && OutVT != MVT::v16i8 && OutVT != MVT::v8i16 &&
3479 OutVT != MVT::v8i8 && OutVT != MVT::v4i16 && OutVT != MVT::v4i8)
3480 return SDValue();
3481
3482 SDLoc DL(N);
3484 OutVT.getScalarSizeInBits());
3485 In = DAG.getNode(ISD::AND, DL, InVT, In, DAG.getConstant(Mask, DL, InVT));
3486 return truncateVectorWithNARROW(OutVT, In, DL, DAG);
3487}
3488
3491 using namespace llvm::SDPatternMatch;
3492 auto &DAG = DCI.DAG;
3493 SDLoc DL(N);
3494 SDValue Src = N->getOperand(0);
3495 EVT VT = N->getValueType(0);
3496 EVT SrcVT = Src.getValueType();
3497
3498 if (!(DCI.isBeforeLegalize() && VT.isScalarInteger() &&
3499 SrcVT.isFixedLengthVectorOf(MVT::i1)))
3500 return SDValue();
3501
3502 unsigned NumElts = SrcVT.getVectorNumElements();
3503 EVT Width = MVT::getIntegerVT(128 / NumElts);
3504
3505 // bitcast <N x i1> to iN, where N = 2, 4, 8, 16 (legal)
3506 // ==> bitmask
3507 if (NumElts == 2 || NumElts == 4 || NumElts == 8 || NumElts == 16) {
3508 return DAG.getZExtOrTrunc(
3509 DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32,
3510 {DAG.getConstant(Intrinsic::wasm_bitmask, DL, MVT::i32),
3511 DAG.getSExtOrTrunc(N->getOperand(0), DL,
3512 SrcVT.changeVectorElementType(
3513 *DAG.getContext(), Width))}),
3514 DL, VT);
3515 }
3516
3517 // bitcast <N x i1>(setcc ...) to concat iN, where N = 32 and 64 (illegal)
3518 if (NumElts == 32 || NumElts == 64) {
3519 SDValue Concat, SetCCVector;
3520 ISD::CondCode SetCond;
3521
3522 if (!sd_match(N, m_BitCast(m_c_SetCC(SetCond, m_Value(Concat),
3523 m_Value(SetCCVector)))))
3524 return SDValue();
3525 if (Concat.getOpcode() != ISD::CONCAT_VECTORS)
3526 return SDValue();
3527
3528 // Reconstruct the wide bitmask from each CONCAT_VECTORS operand.
3529 // Derive the per-chunk mask/integer types from the actual operand type
3530 // instead of hardcoding v16i1 / i16 for every chunk.
3531 EVT ConcatOperandVT = Concat.getOperand(0).getValueType();
3532 unsigned ConcatOperandNumElts = ConcatOperandVT.getVectorNumElements();
3533
3534 EVT ConcatOperandMaskVT =
3535 EVT::getVectorVT(*DAG.getContext(), MVT::i1,
3536 ElementCount::getFixed(ConcatOperandNumElts));
3537 EVT ConcatOperandBitmaskVT =
3538 EVT::getIntegerVT(*DAG.getContext(), ConcatOperandNumElts);
3539 EVT ReturnVT = N->getValueType(0);
3540 SDValue ReconstructedBitmask = DAG.getConstant(0, DL, ReturnVT);
3541 // Example:
3542 // v32i16 = concat(v8i16, v8i16, v8i16, v8i16)
3543 // -> v8i1 + v8i1 + v8i1 + v8i1
3544 // -> i8 + i8 + i8 + i8
3545 // -> reconstructed i32 bitmask
3546 for (size_t I = 0; I < Concat->ops().size(); ++I) {
3547 SDValue ConcatOperand = Concat.getOperand(I);
3548 assert(ConcatOperand.getValueType() == ConcatOperandVT &&
3549 "concat_vectors operands must have the same type");
3550
3551 SDValue SetCCVectorOperand =
3552 extractSubVector(SetCCVector, I * ConcatOperandNumElts, DAG, DL, 128);
3553 if (!SetCCVectorOperand ||
3554 SetCCVectorOperand.getValueType() != ConcatOperandVT)
3555 return SDValue();
3556
3557 // Build the per-chunk mask using the correct chunk type:
3558 // v16i8 -> v16i1 -> i16
3559 // v8i16 -> v8i1 -> i8
3560 // v4i32 -> v4i1 -> i4
3561 // v2i64 -> v2i1 -> i2
3562 SDValue ConcatOperandMask = DAG.getSetCC(
3563 DL, ConcatOperandMaskVT, ConcatOperand, SetCCVectorOperand, SetCond);
3564 SDValue ConcatOperandBitmask =
3565 DAG.getBitcast(ConcatOperandBitmaskVT, ConcatOperandMask);
3566 SDValue ExtendedConcatOperandBitmask =
3567 DAG.getZExtOrTrunc(ConcatOperandBitmask, DL, ReturnVT);
3568
3569 // Shift each chunk's mask to its original lane position before merging it
3570 // into the result:
3571 // Result bit index = I * ConcatOperandNumElts + local lane index
3572 //
3573 // Example: four chunks, each containing 8 mask bits:
3574 // result = M0 | (M1 << 8) | (M2 << 16) | (M3 << 24)
3575 //
3576 SDValue PositionedChunkBitmask = ExtendedConcatOperandBitmask;
3577 if (I != 0) {
3578 PositionedChunkBitmask = DAG.getNode(
3579 ISD::SHL, DL, ReturnVT, ExtendedConcatOperandBitmask,
3580 DAG.getShiftAmountConstant(I * ConcatOperandNumElts, ReturnVT, DL));
3581 }
3582
3583 ReconstructedBitmask = DAG.getNode(
3584 ISD::OR, DL, ReturnVT, ReconstructedBitmask, PositionedChunkBitmask);
3585 }
3586
3587 return ReconstructedBitmask;
3588 }
3589
3590 return SDValue();
3591}
3592
3594 // bitmask (setcc <X>, 0, setlt) => bitmask X
3595 assert(N->getOpcode() == ISD::INTRINSIC_WO_CHAIN);
3596 using namespace llvm::SDPatternMatch;
3597
3598 if (N->getConstantOperandVal(0) != Intrinsic::wasm_bitmask)
3599 return SDValue();
3600
3601 SDValue LHS;
3602 if (!sd_match(N->getOperand(1),
3604 return SDValue();
3605
3606 SDLoc DL(N);
3607 return DAG.getNode(
3608 ISD::INTRINSIC_WO_CHAIN, DL, N->getValueType(0),
3609 {DAG.getConstant(Intrinsic::wasm_bitmask, DL, MVT::i32), LHS});
3610}
3611
3613 // any_true (setcc <X>, 0, eq) => (not (all_true X))
3614 // all_true (setcc <X>, 0, eq) => (not (any_true X))
3615 // any_true (setcc <X>, 0, ne) => (any_true X)
3616 // all_true (setcc <X>, 0, ne) => (all_true X)
3617 assert(N->getOpcode() == ISD::INTRINSIC_WO_CHAIN);
3618 using namespace llvm::SDPatternMatch;
3619
3620 SDValue LHS;
3621 if (N->getNumOperands() < 2 ||
3622 !sd_match(N->getOperand(1), m_c_SetCC(m_Value(LHS), m_Zero())))
3623 return SDValue();
3624 EVT LT = LHS.getValueType();
3625 if (LT.getScalarSizeInBits() > 128 / LT.getVectorNumElements())
3626 return SDValue();
3627
3628 auto CombineSetCC = [&N, &DAG](Intrinsic::WASMIntrinsics InPre,
3629 ISD::CondCode SetType,
3630 Intrinsic::WASMIntrinsics InPost) {
3631 if (N->getConstantOperandVal(0) != InPre)
3632 return SDValue();
3633
3634 SDValue LHS;
3635 if (!sd_match(N->getOperand(1),
3636 m_c_SpecificSetCC(SetType, m_Value(LHS), m_Zero())))
3637 return SDValue();
3638
3639 SDLoc DL(N);
3640 SDValue Ret = DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32,
3641 {DAG.getConstant(InPost, DL, MVT::i32), LHS});
3642 if (SetType == ISD::SETEQ)
3643 Ret = DAG.getNode(ISD::XOR, DL, MVT::i32, Ret,
3644 DAG.getConstant(1, DL, MVT::i32));
3645 return DAG.getZExtOrTrunc(Ret, DL, N->getValueType(0));
3646 };
3647
3648 if (SDValue AnyTrueEQ = CombineSetCC(Intrinsic::wasm_anytrue, ISD::SETEQ,
3649 Intrinsic::wasm_alltrue))
3650 return AnyTrueEQ;
3651 if (SDValue AllTrueEQ = CombineSetCC(Intrinsic::wasm_alltrue, ISD::SETEQ,
3652 Intrinsic::wasm_anytrue))
3653 return AllTrueEQ;
3654 if (SDValue AnyTrueNE = CombineSetCC(Intrinsic::wasm_anytrue, ISD::SETNE,
3655 Intrinsic::wasm_anytrue))
3656 return AnyTrueNE;
3657 if (SDValue AllTrueNE = CombineSetCC(Intrinsic::wasm_alltrue, ISD::SETNE,
3658 Intrinsic::wasm_alltrue))
3659 return AllTrueNE;
3660
3661 return SDValue();
3662}
3663
3669
3671 unsigned NumElts,
3672 const MaskReduceInfo &Info,
3673 SelectionDAG &DAG) {
3674 EVT VecVT = FromVT.changeVectorElementType(*DAG.getContext(),
3675 MVT::getIntegerVT(128 / NumElts));
3676 assert(VecVT.getSizeInBits() == 128 &&
3677 "mask reduction should be widened to a 128-bit vector");
3678
3679 SDLoc DL(N);
3680 SDValue Mask = N->getOperand(0)->getOperand(0);
3681 SDValue Ret = DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32,
3682 {DAG.getConstant(Info.IID, DL, MVT::i32),
3683 DAG.getSExtOrTrunc(Mask, DL, VecVT)});
3684 if (Info.Invert)
3685 Ret = DAG.getNode(ISD::XOR, DL, MVT::i32, Ret,
3686 DAG.getConstant(1, DL, MVT::i32));
3687 return DAG.getZExtOrTrunc(Ret, DL, N->getValueType(0));
3688}
3689
3691 unsigned NumElts,
3692 const MaskReduceInfo &Info,
3693 SelectionDAG &DAG) {
3694 assert((NumElts == 32 || NumElts == 64) &&
3695 "combineWideMaskReduction is only for wide masks");
3696 assert(MaskVT.isFixedLengthVector() &&
3697 MaskVT.getVectorElementType() == MVT::i1);
3698 SDLoc DL(N);
3699 unsigned ChunkElts = 16;
3700 EVT ChunkMaskVT = EVT::getVectorVT(*DAG.getContext(), MVT::i1,
3701 ElementCount::getFixed(ChunkElts));
3702 EVT LegalVecVT = ChunkMaskVT.changeVectorElementType(
3703 *DAG.getContext(), MVT::getIntegerVT(128 / ChunkElts));
3704
3705 SmallVector<SDValue, 4> ChunkResults;
3706 // Split the wide mask into v16i1 chunks and reduce each chunk separately.
3707 // For example:
3708 // v32i1: [0..15] [16..31]
3709 // | |
3710 // v v
3711 // chunk0 chunk1
3712 //
3713 // v64i1: [0..15] [16..31] [32..47] [48..63]
3714 // | | | |
3715 // v v v v
3716 // chunk0 chunk1 chunk2 chunk3
3717 //
3718 // each chunk:
3719 // v16i1 -> v16i8 -> wasm_anytrue/alltrue -> i32 0/1
3720 for (unsigned I = 0; I < NumElts; I += ChunkElts) {
3721 SDValue ChunkMask = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ChunkMaskVT,
3722 Mask, DAG.getVectorIdxConstant(I, DL));
3723 SDValue LegalMask = DAG.getSExtOrTrunc(ChunkMask, DL, LegalVecVT);
3724 SDValue Reduced =
3725 DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32,
3726 DAG.getConstant(Info.IID, DL, MVT::i32), LegalMask);
3727 ChunkResults.push_back(Reduced);
3728 }
3729
3730 SDValue Acc = ChunkResults[0];
3731 for (unsigned I = 1; I < ChunkResults.size(); ++I)
3732 Acc =
3733 DAG.getNode(Info.WideCombineOpcode, DL, MVT::i32, Acc, ChunkResults[I]);
3734
3735 if (Info.Invert)
3736 Acc = DAG.getNode(ISD::XOR, DL, MVT::i32, Acc,
3737 DAG.getConstant(1, DL, MVT::i32));
3738
3739 return DAG.getZExtOrTrunc(Acc, DL, N->getValueType(0));
3740}
3741
3742static std::optional<MaskReduceInfo> classifyMaskReduction(SDNode *N) {
3743 auto *C = dyn_cast<ConstantSDNode>(N->getOperand(1));
3744 if (!C)
3745 return std::nullopt;
3746
3747 ISD::CondCode CC = N->getOperandCondCode(2);
3748
3749 // setcc (bitcast mask), 0, ne -> any_true(mask)
3750 if (C->isZero() && CC == ISD::SETNE)
3751 return MaskReduceInfo{Intrinsic::wasm_anytrue, ISD::OR, false};
3752
3753 // setcc (bitcast mask), 0, eq -> !any_true(mask)
3754 if (C->isZero() && CC == ISD::SETEQ)
3755 return MaskReduceInfo{Intrinsic::wasm_anytrue, ISD::OR, true};
3756
3757 // setcc (bitcast mask), -1, eq -> all_true(mask)
3758 if (C->isAllOnes() && CC == ISD::SETEQ)
3759 return MaskReduceInfo{Intrinsic::wasm_alltrue, ISD::AND, false};
3760
3761 // setcc (bitcast mask), -1, ne -> !all_true(mask)
3762 if (C->isAllOnes() && CC == ISD::SETNE)
3763 return MaskReduceInfo{Intrinsic::wasm_alltrue, ISD::AND, true};
3764
3765 return std::nullopt;
3766}
3767
3768/// Try to convert a i128 comparison to a v16i8 comparison before type
3769/// legalization splits it up into chunks
3770static SDValue
3773
3774 SDLoc DL(N);
3775 SDValue X = N->getOperand(0);
3776 SDValue Y = N->getOperand(1);
3777 EVT VT = N->getValueType(0);
3778 EVT OpVT = X.getValueType();
3779
3780 SelectionDAG &DAG = DCI.DAG;
3782 Attribute::NoImplicitFloat))
3783 return SDValue();
3784
3785 ISD::CondCode CC = N->getOperandCondCode(2);
3786 // We're looking for an oversized integer equality comparison with SIMD
3787 if (!OpVT.isScalarInteger() || !OpVT.isByteSized() || OpVT != MVT::i128 ||
3788 !Subtarget->hasSIMD128() || !isIntEqualitySetCC(CC))
3789 return SDValue();
3790
3791 // Don't perform this combine if constructing the vector will be expensive.
3792 auto IsVectorBitCastCheap = [](SDValue X) {
3794 return isa<ConstantSDNode>(X) || X.getOpcode() == ISD::LOAD;
3795 };
3796
3797 if (!IsVectorBitCastCheap(X) || !IsVectorBitCastCheap(Y))
3798 return SDValue();
3799
3800 SDValue VecX = DAG.getBitcast(MVT::v16i8, X);
3801 SDValue VecY = DAG.getBitcast(MVT::v16i8, Y);
3802 SDValue Cmp = DAG.getSetCC(DL, MVT::v16i8, VecX, VecY, CC);
3803
3804 SDValue Intr =
3805 DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32,
3806 {DAG.getConstant(CC == ISD::SETEQ ? Intrinsic::wasm_alltrue
3807 : Intrinsic::wasm_anytrue,
3808 DL, MVT::i32),
3809 Cmp});
3810
3811 return DAG.getSetCC(DL, VT, Intr, DAG.getConstant(0, DL, MVT::i32),
3812 ISD::SETNE);
3813}
3814
3818 if (!DCI.isBeforeLegalize())
3819 return SDValue();
3820
3821 EVT VT = N->getValueType(0);
3822 if (!VT.isScalarInteger())
3823 return SDValue();
3824
3826 return V;
3827
3828 SDValue LHS = N->getOperand(0);
3829 if (LHS->getOpcode() != ISD::BITCAST)
3830 return SDValue();
3831
3832 EVT FromVT = LHS->getOperand(0).getValueType();
3833 if (!FromVT.isFixedLengthVectorOf(MVT::i1))
3834 return SDValue();
3835
3836 unsigned NumElts = FromVT.getVectorNumElements();
3837 auto Info = classifyMaskReduction(N);
3838 if (!Info)
3839 return SDValue();
3840
3841 auto &DAG = DCI.DAG;
3842 if (NumElts == 2 || NumElts == 4 || NumElts == 8 || NumElts == 16)
3843 return combineSmallMaskReduction(N, FromVT, NumElts, *Info, DAG);
3844
3845 if (NumElts == 32 || NumElts == 64)
3846 return combineWideMaskReduction(N, LHS.getOperand(0), FromVT, NumElts,
3847 *Info, DAG);
3848
3849 return SDValue();
3850}
3851
3853 EVT VT = N->getValueType(0);
3854 if (VT != MVT::v4i32 && VT != MVT::v8i32 && VT != MVT::v16i32)
3855 return SDValue();
3856
3857 // Mul with extending inputs.
3858 SDValue LHS = N->getOperand(0);
3859 SDValue RHS = N->getOperand(1);
3860 if (LHS.getOpcode() != RHS.getOpcode())
3861 return SDValue();
3862
3863 if (LHS.getOpcode() != ISD::SIGN_EXTEND &&
3864 LHS.getOpcode() != ISD::ZERO_EXTEND)
3865 return SDValue();
3866
3867 if (LHS->getOperand(0).getValueType() != RHS->getOperand(0).getValueType())
3868 return SDValue();
3869
3870 EVT FromVT = LHS->getOperand(0).getValueType();
3871 EVT EltTy = FromVT.getVectorElementType();
3872 if (EltTy != MVT::i8)
3873 return SDValue();
3874
3875 // For an input DAG that looks like this
3876 // %a = input_type
3877 // %b = input_type
3878 // %lhs = extend %a to output_type
3879 // %rhs = extend %b to output_type
3880 // %mul = mul %lhs, %rhs
3881
3882 // input_type | output_type | instructions
3883 // v16i8 | v16i32 | %low = i16x8.extmul_low_i8x16_ %a, %b
3884 // | | %high = i16x8.extmul_high_i8x16_, %a, %b
3885 // | | %low_low = i32x4.ext_low_i16x8_ %low
3886 // | | %low_high = i32x4.ext_high_i16x8_ %low
3887 // | | %high_low = i32x4.ext_low_i16x8_ %high
3888 // | | %high_high = i32x4.ext_high_i16x8_ %high
3889 // | | %res = concat_vector(...)
3890 // v8i8 | v8i32 | %low = i16x8.extmul_low_i8x16_ %a, %b
3891 // | | %low_low = i32x4.ext_low_i16x8_ %low
3892 // | | %low_high = i32x4.ext_high_i16x8_ %low
3893 // | | %res = concat_vector(%low_low, %low_high)
3894 // v4i8 | v4i32 | %low = i16x8.extmul_low_i8x16_ %a, %b
3895 // | %res = i32x4.ext_low_i16x8_ %low
3896
3897 SDLoc DL(N);
3898 unsigned NumElts = VT.getVectorNumElements();
3899 SDValue ExtendInLHS = LHS->getOperand(0);
3900 SDValue ExtendInRHS = RHS->getOperand(0);
3901 bool IsSigned = LHS->getOpcode() == ISD::SIGN_EXTEND;
3902 unsigned ExtendLowOpc =
3903 IsSigned ? WebAssemblyISD::EXTEND_LOW_S : WebAssemblyISD::EXTEND_LOW_U;
3904 unsigned ExtendHighOpc =
3905 IsSigned ? WebAssemblyISD::EXTEND_HIGH_S : WebAssemblyISD::EXTEND_HIGH_U;
3906
3907 auto GetExtendLow = [&DAG, &DL, &ExtendLowOpc](EVT VT, SDValue Op) {
3908 return DAG.getNode(ExtendLowOpc, DL, VT, Op);
3909 };
3910 auto GetExtendHigh = [&DAG, &DL, &ExtendHighOpc](EVT VT, SDValue Op) {
3911 return DAG.getNode(ExtendHighOpc, DL, VT, Op);
3912 };
3913
3914 if (NumElts == 16) {
3915 SDValue LowLHS = GetExtendLow(MVT::v8i16, ExtendInLHS);
3916 SDValue LowRHS = GetExtendLow(MVT::v8i16, ExtendInRHS);
3917 SDValue MulLow = DAG.getNode(ISD::MUL, DL, MVT::v8i16, LowLHS, LowRHS);
3918 SDValue HighLHS = GetExtendHigh(MVT::v8i16, ExtendInLHS);
3919 SDValue HighRHS = GetExtendHigh(MVT::v8i16, ExtendInRHS);
3920 SDValue MulHigh = DAG.getNode(ISD::MUL, DL, MVT::v8i16, HighLHS, HighRHS);
3921 SDValue SubVectors[] = {
3922 GetExtendLow(MVT::v4i32, MulLow),
3923 GetExtendHigh(MVT::v4i32, MulLow),
3924 GetExtendLow(MVT::v4i32, MulHigh),
3925 GetExtendHigh(MVT::v4i32, MulHigh),
3926 };
3927 return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, SubVectors);
3928 } else if (NumElts == 8) {
3929 SDValue LowLHS = DAG.getNode(LHS->getOpcode(), DL, MVT::v8i16, ExtendInLHS);
3930 SDValue LowRHS = DAG.getNode(RHS->getOpcode(), DL, MVT::v8i16, ExtendInRHS);
3931 SDValue MulLow = DAG.getNode(ISD::MUL, DL, MVT::v8i16, LowLHS, LowRHS);
3932 SDValue Lo = GetExtendLow(MVT::v4i32, MulLow);
3933 SDValue Hi = GetExtendHigh(MVT::v4i32, MulLow);
3934 return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, Lo, Hi);
3935 } else {
3936 assert(NumElts == 4);
3937 SDValue LowLHS =
3938 GetExtendLow(MVT::v8i16, DoubleVectorWidth(ExtendInLHS, 16, DAG));
3939 SDValue LowRHS =
3940 GetExtendLow(MVT::v8i16, DoubleVectorWidth(ExtendInRHS, 16, DAG));
3941 SDValue MulLow = DAG.getNode(ISD::MUL, DL, MVT::v8i16, LowLHS, LowRHS);
3942 return GetExtendLow(MVT::v4i32, MulLow);
3943 }
3944 return SDValue();
3945}
3946
3949 assert(N->getOpcode() == ISD::MUL);
3950 EVT VT = N->getValueType(0);
3951 if (!VT.isVector())
3952 return SDValue();
3953
3954 if (auto Res = TryWideExtMulCombine(N, DCI.DAG))
3955 return Res;
3956
3957 // We don't natively support v16i8 or v8i8 mul, but we do support v8i16. So,
3958 // extend them to v8i16.
3959 if (VT != MVT::v8i8 && VT != MVT::v16i8)
3960 return SDValue();
3961
3962 SDLoc DL(N);
3963 SelectionDAG &DAG = DCI.DAG;
3964 SDValue LHS = N->getOperand(0);
3965 SDValue RHS = N->getOperand(1);
3966 EVT MulVT = MVT::v8i16;
3967
3968 if (VT == MVT::v8i8) {
3969 SDValue PromotedLHS = DAG.getNode(ISD::CONCAT_VECTORS, DL, MVT::v16i8, LHS,
3970 DAG.getUNDEF(MVT::v8i8));
3971 SDValue PromotedRHS = DAG.getNode(ISD::CONCAT_VECTORS, DL, MVT::v16i8, RHS,
3972 DAG.getUNDEF(MVT::v8i8));
3973 SDValue LowLHS =
3974 DAG.getNode(WebAssemblyISD::EXTEND_LOW_U, DL, MulVT, PromotedLHS);
3975 SDValue LowRHS =
3976 DAG.getNode(WebAssemblyISD::EXTEND_LOW_U, DL, MulVT, PromotedRHS);
3977 SDValue MulLow = DAG.getBitcast(
3978 MVT::v16i8, DAG.getNode(ISD::MUL, DL, MulVT, LowLHS, LowRHS));
3979 // Take the low byte of each lane.
3980 SDValue Shuffle = DAG.getVectorShuffle(
3981 MVT::v16i8, DL, MulLow, DAG.getUNDEF(MVT::v16i8),
3982 {0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1});
3983 return extractSubVector(Shuffle, 0, DAG, DL, 64);
3984 } else {
3985 assert(VT == MVT::v16i8 && "Expected v16i8");
3986 SDValue LowLHS = DAG.getNode(WebAssemblyISD::EXTEND_LOW_U, DL, MulVT, LHS);
3987 SDValue LowRHS = DAG.getNode(WebAssemblyISD::EXTEND_LOW_U, DL, MulVT, RHS);
3988 SDValue HighLHS =
3989 DAG.getNode(WebAssemblyISD::EXTEND_HIGH_U, DL, MulVT, LHS);
3990 SDValue HighRHS =
3991 DAG.getNode(WebAssemblyISD::EXTEND_HIGH_U, DL, MulVT, RHS);
3992
3993 SDValue MulLow =
3994 DAG.getBitcast(VT, DAG.getNode(ISD::MUL, DL, MulVT, LowLHS, LowRHS));
3995 SDValue MulHigh =
3996 DAG.getBitcast(VT, DAG.getNode(ISD::MUL, DL, MulVT, HighLHS, HighRHS));
3997
3998 // Take the low byte of each lane.
3999 return DAG.getVectorShuffle(
4000 VT, DL, MulLow, MulHigh,
4001 {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30});
4002 }
4003}
4004
4006 EVT OutVT = N->getValueType(0);
4007 if (!OutVT.isVector())
4008 return SDValue();
4009
4010 EVT OutElTy = OutVT.getVectorElementType();
4011 if (OutElTy != MVT::i8 && OutElTy != MVT::i16)
4012 return SDValue();
4013
4014 unsigned NumElems = OutVT.getVectorNumElements();
4015 if (!isPowerOf2_32(NumElems))
4016 return SDValue();
4017
4018 EVT FPVT = N->getOperand(0)->getValueType(0);
4019 if (FPVT.getVectorElementType() != MVT::f32)
4020 return SDValue();
4021
4022 SDLoc DL(N);
4023
4024 // First, convert to i32.
4025 LLVMContext &Ctx = *DAG.getContext();
4026 EVT IntVT = EVT::getVectorVT(Ctx, MVT::i32, NumElems);
4027 SDValue ToInt = DAG.getNode(N->getOpcode(), DL, IntVT, N->getOperand(0));
4029 OutVT.getScalarSizeInBits());
4030 // Mask out the top MSBs.
4031 SDValue Masked =
4032 DAG.getNode(ISD::AND, DL, IntVT, ToInt, DAG.getConstant(Mask, DL, IntVT));
4033
4034 if (OutVT.getSizeInBits() < 128) {
4035 // Create a wide enough vector that we can use narrow.
4036 EVT NarrowedVT = OutElTy == MVT::i8 ? MVT::v16i8 : MVT::v8i16;
4037 unsigned NumRequiredElems = NarrowedVT.getVectorNumElements();
4038 SDValue WideVector = DoubleVectorWidth(Masked, NumRequiredElems, DAG);
4039 SDValue Trunc = truncateVectorWithNARROW(NarrowedVT, WideVector, DL, DAG);
4040 return DAG.getBitcast(
4041 OutVT, extractSubVector(Trunc, 0, DAG, DL, OutVT.getSizeInBits()));
4042 } else {
4043 return truncateVectorWithNARROW(OutVT, Masked, DL, DAG);
4044 }
4045 return SDValue();
4046}
4047
4048// Wide vector shift operations such as v8i32 with sign-extended
4049// operands cause Type Legalizer crashes because the target-specific
4050// extension nodes cannot be directly mapped to the 256-bit size.
4051//
4052// To resolve the crash and optimize performance, we intercept the
4053// illegal v8i32 shift in DAGCombine. We convert the shift amounts
4054// into multipliers and manually split the vector into two v4i32 halves.
4055//
4056// Before: t1: v8i32 = shl (sign_extend v8i16), const_vec
4057// After : t2: v4i32 = mul (ext_low_s v8i16), (ext_low_s narrow_vec)
4058// t3: v4i32 = mul (ext_high_s v8i16), (ext_high_s narrow_vec)
4059// t4: v8i32 = concat_vectors t2, t3
4062 SelectionDAG &DAG = DCI.DAG;
4063 assert(N->getOpcode() == ISD::SHL);
4064 EVT VT = N->getValueType(0);
4065 if (VT != MVT::v8i32)
4066 return SDValue();
4067
4068 SDValue LHS = N->getOperand(0);
4069 SDValue RHS = N->getOperand(1);
4070 unsigned ExtOpc = LHS.getOpcode();
4071 if (ExtOpc != ISD::SIGN_EXTEND && ExtOpc != ISD::ZERO_EXTEND)
4072 return SDValue();
4073
4074 if (RHS.getOpcode() != ISD::BUILD_VECTOR)
4075 return SDValue();
4076
4077 SDLoc DL(N);
4078 SDValue ExtendIn = LHS.getOperand(0);
4079 EVT FromVT = ExtendIn.getValueType();
4080 if (FromVT != MVT::v8i16)
4081 return SDValue();
4082
4083 unsigned NumElts = VT.getVectorNumElements();
4084 unsigned BitWidth = FromVT.getScalarSizeInBits();
4085 bool IsSigned = (ExtOpc == ISD::SIGN_EXTEND);
4086 unsigned MaxValidShift = IsSigned ? (BitWidth - 1) : BitWidth;
4087 SmallVector<SDValue, 16> MulConsts;
4088 for (unsigned I = 0; I < NumElts; ++I) {
4089 auto *C = dyn_cast<ConstantSDNode>(RHS.getOperand(I));
4090 if (!C)
4091 return SDValue();
4092
4093 const APInt &ShiftAmt = C->getAPIntValue();
4094 if (ShiftAmt.uge(MaxValidShift))
4095 return SDValue();
4096
4097 APInt MulAmt = APInt::getOneBitSet(BitWidth, ShiftAmt.getZExtValue());
4098 MulConsts.push_back(DAG.getConstant(MulAmt, DL, FromVT.getScalarType(),
4099 /*isTarget=*/false, /*isOpaque=*/true));
4100 }
4101
4102 SDValue NarrowConst = DAG.getBuildVector(FromVT, DL, MulConsts);
4103 unsigned ExtLowOpc =
4104 IsSigned ? WebAssemblyISD::EXTEND_LOW_S : WebAssemblyISD::EXTEND_LOW_U;
4105 unsigned ExtHighOpc =
4106 IsSigned ? WebAssemblyISD::EXTEND_HIGH_S : WebAssemblyISD::EXTEND_HIGH_U;
4107
4108 EVT HalfVT = MVT::v4i32;
4109 SDValue LHSLo = DAG.getNode(ExtLowOpc, DL, HalfVT, ExtendIn);
4110 SDValue LHSHi = DAG.getNode(ExtHighOpc, DL, HalfVT, ExtendIn);
4111 SDValue RHSLo = DAG.getNode(ExtLowOpc, DL, HalfVT, NarrowConst);
4112 SDValue RHSHi = DAG.getNode(ExtHighOpc, DL, HalfVT, NarrowConst);
4113 SDValue MulLo = DAG.getNode(ISD::MUL, DL, HalfVT, LHSLo, RHSLo);
4114 SDValue MulHi = DAG.getNode(ISD::MUL, DL, HalfVT, LHSHi, RHSHi);
4115 return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, MulLo, MulHi);
4116}
4117
4119 if (N->getValueType(0) != MVT::f128)
4120 return SDValue();
4121
4123 switch (N->getOpcode()) {
4124 // wasi-libc and emscripten do not currently define fminimuml and fmaximuml.
4125 case ISD::FMINIMUM:
4126 case ISD::FMAXIMUM:
4127 return TLI.expandFMINIMUM_FMAXIMUM(N, DAG);
4128
4129 // wasi-libc and emscripten do not currently define fminimum_numl and
4130 // fmaximum_numl.
4131 case ISD::FMINIMUMNUM:
4132 case ISD::FMAXIMUMNUM:
4133 return TLI.expandFMINIMUMNUM_FMAXIMUMNUM(N, DAG);
4134
4135 default:
4136 return SDValue();
4137 }
4138}
4139
4141 if (N->getOperand(0).getOpcode() == WebAssemblyISD::EXTEND_LOW_U &&
4142 N->getOperand(1).getOpcode() == WebAssemblyISD::EXTEND_HIGH_U &&
4143 N->getOperand(0)->getOperand(0) == N->getOperand(1)->getOperand(0))
4144 return N->getOperand(0)->getOperand(0);
4145 return SDValue();
4146}
4147
4148SDValue
4149WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N,
4150 DAGCombinerInfo &DCI) const {
4151 switch (N->getOpcode()) {
4152 default:
4153 return SDValue();
4154 case ISD::BITCAST:
4155 return performBitcastCombine(N, DCI);
4156 case ISD::SETCC:
4157 return performSETCCCombine(N, DCI, Subtarget);
4159 return performVECTOR_SHUFFLECombine(N, DCI);
4160 case ISD::SIGN_EXTEND:
4161 case ISD::ZERO_EXTEND:
4162 return performVectorExtendCombine(N, DCI);
4163 case ISD::UINT_TO_FP:
4164 if (auto ExtCombine = performVectorExtendToFPCombine(N, DCI, Subtarget))
4165 return ExtCombine;
4166 return performVectorNonNegToFPCombine(N, DCI);
4167 case ISD::SINT_TO_FP:
4168 return performVectorExtendToFPCombine(N, DCI, Subtarget);
4171 case ISD::FP_ROUND:
4173 return performVectorTruncZeroCombine(N, DCI);
4174 case ISD::FP_TO_SINT:
4175 case ISD::FP_TO_UINT:
4176 return performConvertFPCombine(N, DCI.DAG);
4177 case ISD::TRUNCATE:
4178 return performTruncateCombine(N, DCI);
4180 if (SDValue V = performBitmaskCombine(N, DCI.DAG))
4181 return V;
4182 return performAnyAllCombine(N, DCI.DAG);
4183 }
4184 case ISD::MUL:
4185 return performMulCombine(N, DCI);
4186 case ISD::SHL:
4187 return performShiftCombine(N, DCI);
4188 case ISD::FMINIMUM:
4189 case ISD::FMAXIMUM:
4190 case ISD::FMINIMUMNUM:
4191 case ISD::FMAXIMUMNUM:
4192 return performMinMaxF128Combine(N, DCI.DAG);
4193 case WebAssemblyISD::NARROW_U:
4194 return performNarrowCombine(N);
4195 }
4196}
static SDValue performMulCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const AArch64Subtarget *Subtarget)
static SDValue performTruncateCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI)
return SDValue()
static SDValue performSETCCCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI, SelectionDAG &DAG)
const AArch64Subtarget & Subtarget
const TargetLowering & TLI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Function Alias Analysis false
Function Alias Analysis Results
static void fail(const SDLoc &DL, SelectionDAG &DAG, const Twine &Msg, SDValue Val={})
#define X(NUM, ENUM, NAME)
Definition ELF.h:665
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
Hexagon Common GEP
const HexagonInstrInfo * TII
#define _
IRTranslator LLVM IR MI
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define T
static SDValue performVECTOR_SHUFFLECombine(SDNode *N, SelectionDAG &DAG, const RISCVSubtarget &Subtarget, const RISCVTargetLowering &TLI)
static SDValue combineVectorSizedSetCCEquality(EVT VT, SDValue X, SDValue Y, ISD::CondCode CC, const SDLoc &DL, SelectionDAG &DAG, const RISCVSubtarget &Subtarget)
Try to map an integer comparison with size > XLEN to vector instructions before type legalization spl...
int64_t C1
RISCVCC::CondCode CC
MachineRegisterInfo & MRI
int64_t C0
MachineInstr *& Lo
Contains matchers for matching SelectionDAG nodes and values.
const char * Msg
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
static bool callingConvSupported(CallingConv::ID CallConv)
static MachineBasicBlock * LowerFPToInt(MachineInstr &MI, DebugLoc DL, MachineBasicBlock *BB, const TargetInstrInfo &TII, bool IsUnsigned, bool Int64, bool Float64, unsigned LoweredOpcode)
static SDValue TryWideExtMulCombine(SDNode *N, SelectionDAG &DAG)
static MachineBasicBlock * LowerMemcpy(MachineInstr &MI, DebugLoc DL, MachineBasicBlock *BB, const TargetInstrInfo &TII, bool Int64)
static SDValue performVectorExtendToFPCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI, const WebAssemblySubtarget *Subtarget)
Convert ({u,s}itofp vec) --> ({u,s}itofp ({s,z}ext vec)) so it doesn't get split up into scalar instr...
static std::optional< unsigned > IsWebAssemblyLocal(SDValue Op, SelectionDAG &DAG)
static SDValue performVectorExtendCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI)
static SDValue performVectorNonNegToFPCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI)
static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG)
static SDValue performAnyAllCombine(SDNode *N, SelectionDAG &DAG)
static MachineBasicBlock * LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, const WebAssemblySubtarget *Subtarget, const TargetInstrInfo &TII)
static std::optional< MaskReduceInfo > classifyMaskReduction(SDNode *N)
static SDValue GetExtendHigh(SDValue Op, unsigned UserOpc, EVT VT, SelectionDAG &DAG)
SDValue performConvertFPCombine(SDNode *N, SelectionDAG &DAG)
static SDValue performNarrowCombine(SDNode *N)
static SDValue performBitmaskCombine(SDNode *N, SelectionDAG &DAG)
static SDValue performVectorTruncZeroCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI)
static bool IsWebAssemblyGlobal(SDValue Op)
static SDValue combineSmallMaskReduction(SDNode *N, EVT FromVT, unsigned NumElts, const MaskReduceInfo &Info, SelectionDAG &DAG)
static MachineBasicBlock * LowerMemset(MachineInstr &MI, DebugLoc DL, MachineBasicBlock *BB, const TargetInstrInfo &TII, bool Int64)
static bool HasNoSignedZerosOrNaNs(SDValue Op, SelectionDAG &DAG)
static SDValue ExtendLowAndHigh(SDValue Input, bool IsSext, EVT SrcVT, EVT ResVT, SelectionDAG &DAG)
static SDValue performShiftCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI)
static SDValue LowerConvertLow(SDValue Op, SelectionDAG &DAG)
static SDValue DoubleVectorWidth(SDValue In, unsigned RequiredNumElems, SelectionDAG &DAG)
static SDValue extractSubVector(SDValue Vec, unsigned IdxVal, SelectionDAG &DAG, const SDLoc &DL, unsigned VectorWidth)
static SDValue performBitcastCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI)
static SDValue truncateVectorWithNARROW(EVT DstVT, SDValue In, const SDLoc &DL, SelectionDAG &DAG)
static SDValue performMinMaxF128Combine(SDNode *N, SelectionDAG &DAG)
static SDValue combineWideMaskReduction(SDNode *N, SDValue Mask, EVT MaskVT, unsigned NumElts, const MaskReduceInfo &Info, SelectionDAG &DAG)
This file defines the interfaces that WebAssembly uses to lower LLVM code into a selection DAG.
This file provides WebAssembly-specific target descriptions.
This file declares WebAssembly-specific per-machine-function information.
This file declares the WebAssembly-specific subclass of TargetSubtarget.
This file declares the WebAssembly-specific subclass of TargetMachine.
This file contains the declaration of the WebAssembly-specific type parsing utility functions.
This file contains the declaration of the WebAssembly-specific utility functions.
X86 cmov Conversion
static constexpr int Concat[]
Value * RHS
Value * LHS
The Input class is used to parse a yaml document into in-memory structs and vectors.
Class for arbitrary precision integers.
Definition APInt.h:78
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1560
static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet)
Constructs an APInt value that has the bottom loBitsSet bits set.
Definition APInt.h:302
static APInt getHighBitsSet(unsigned numBits, unsigned hiBitsSet)
Constructs an APInt value that has the top hiBitsSet bits set.
Definition APInt.h:292
static APInt getOneBitSet(unsigned numBits, unsigned BitNo)
Return an APInt with exactly one bit set in the result.
Definition APInt.h:235
bool uge(const APInt &RHS) const
Unsigned greater or equal comparison.
Definition APInt.h:1225
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
an instruction that atomically reads a memory location, combines it with another value,...
@ Add
*p = old + v
@ Sub
*p = old - v
@ And
*p = old & v
@ Xor
*p = old ^ v
BinOp getOperation() const
LLVM Basic Block Representation.
Definition BasicBlock.h:62
static CCValAssign getMem(unsigned ValNo, MVT ValVT, int64_t Offset, MVT LocVT, LocInfo HTP, bool IsCustom=false)
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
A debug info location.
Definition DebugLoc.h:126
Diagnostic information for unsupported feature in backend.
static constexpr ElementCount getFixed(ScalarTy MinVal)
Definition TypeSize.h:306
This is a fast-path instruction selection class that generates poor code and doesn't support illegal ...
Definition FastISel.h:67
FunctionLoweringInfo - This contains information that is global to a function that is used when lower...
FunctionType * getFunctionType() const
Returns the FunctionType for me.
Definition Function.h:212
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:352
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:731
LLVM_ABI unsigned getAddressSpace() const
const GlobalValue * getGlobal() const
ThreadLocalMode getThreadLocalMode() const
Type * getValueType() const
unsigned getTargetFlags() const
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
LLVM_ABI void diagnose(const DiagnosticInfo &DI)
Report a message to the currently installed diagnostic handler.
Tracks which library functions to use for a particular subtarget or function.
const SDValue & getBasePtr() const
const SDValue & getOffset() const
Describe properties that are true of each instruction in the target description file.
Machine Value Type.
bool is128BitVector() const
Return true if this is a 128-bit vector type.
@ INVALID_SIMPLE_VALUE_TYPE
static auto integer_fixedlen_vector_valuetypes()
SimpleValueType SimpleTy
MVT changeVectorElementType(MVT EltVT) const
Return a VT for a vector type whose attributes match ourselves with the exception of the element type...
unsigned getVectorNumElements() const
bool isVector() const
Return true if this is a vector value type.
bool isInteger() const
Return true if this is an integer or a vector integer type.
static auto integer_valuetypes()
TypeSize getSizeInBits() const
Returns the size of the specified MVT in bits.
static auto fixedlen_vector_valuetypes()
bool isFixedLengthVector() const
static MVT getVectorVT(MVT VT, unsigned NumElements)
MVT getVectorElementType() const
bool isFloatingPoint() const
Return true if this is a FP or a vector FP type.
static MVT getIntegerVT(unsigned BitWidth)
LLVM_ABI void transferSuccessorsAndUpdatePHIs(MachineBasicBlock *FromMBB)
Transfers all the successors, as in transferSuccessors, and update PHI operands in the successor bloc...
LLVM_ABI instr_iterator insert(instr_iterator I, MachineInstr *M)
Insert MI into the instruction list before I, possibly inside a bundle.
const BasicBlock * getBasicBlock() const
Return the LLVM basic block that this instance corresponded to originally.
LLVM_ABI void addSuccessor(MachineBasicBlock *Succ, BranchProbability Prob=BranchProbability::getUnknown())
Add Succ as a successor of this MachineBasicBlock.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator insertAfter(iterator I, MachineInstr *MI)
Insert MI into the instruction list after I.
void splice(iterator Where, MachineBasicBlock *Other, iterator From)
Take an instruction from MBB 'Other' at the position From, and insert it into this MBB right before '...
LLVM_ABI int CreateStackObject(uint64_t Size, Align Alignment, bool isSpillSlot, const AllocaInst *Alloca=nullptr, uint8_t ID=0)
Create a new statically sized stack object, returning a nonnegative identifier to represent it.
void setFrameAddressIsTaken(bool T)
unsigned getFunctionNumber() const
getFunctionNumber - Return a unique ID for the current function.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineFrameInfo & getFrameInfo()
getFrameInfo - Return the frame info object for the current function.
const char * createExternalSymbolName(StringRef Name)
Allocate a string and populate it with the given external symbol name.
MCContext & getContext() const
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
const DataLayout & getDataLayout() const
Return the DataLayout attached to the Module associated to this MF.
Function & getFunction()
Return the LLVM function that this machine code represents.
BasicBlockListType::iterator iterator
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineJumpTableInfo * getJumpTableInfo() const
getJumpTableInfo - Return the jump table info object for the current function.
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
const MachineInstrBuilder & addSym(MCSymbol *Sym, unsigned char TargetFlags=0) const
const MachineInstrBuilder & addFPImm(const ConstantFP *Val) const
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
MachineInstr * getInstr() const
If conversion operators fail, use this method to get the MachineInstr explicitly.
Representation of each machine instruction.
mop_range defs()
Returns all explicit operands that are register definitions.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
LLVM_ABI void addOperand(MachineFunction &MF, const MachineOperand &Op)
Add the specified operand to the instruction.
mop_range explicit_uses()
LLVM_ABI void removeOperand(unsigned OpNo)
Erase an operand from an instruction, leaving it with one fewer operand than it started with.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
const std::vector< MachineJumpTableEntry > & getJumpTables() const
Flags
Flags values. These may be or'd together.
@ MOVolatile
The memory access is volatile.
@ MOLoad
The memory access reads data.
@ MOStore
The memory access writes data.
MachineOperand class - Representation of each machine instruction operand.
bool isReg() const
isReg - Tests if this is a MO_Register operand.
void setIsKill(bool Val=true)
Register getReg() const
getReg - Returns the register number.
bool isFI() const
isFI - Tests if this is a MO_FrameIndex operand.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
void addLiveIn(MCRegister Reg, Register vreg=Register())
addLiveIn - Add the specified register as a live-in.
unsigned getAddressSpace() const
Return the address space for the associated pointer.
MachineMemOperand * getMemOperand() const
Return the unique MachineMemOperand object describing the memory reference performed by operation.
const SDValue & getChain() const
EVT getMemoryVT() const
Return the type of the in-memory value.
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
Wrapper class representing virtual and physical registers.
Definition Register.h:20
Wrapper class for IR location info (IR ordering and DebugLoc) to be passed into SDNode creation funct...
Represents one node in the SelectionDAG.
ArrayRef< SDUse > ops() const
unsigned getOpcode() const
Return the SelectionDAG opcode value for this node.
uint64_t getAsZExtVal() const
Helper method returns the zero-extended integer value of a ConstantSDNode.
const SDValue & getOperand(unsigned Num) const
uint64_t getConstantOperandVal(unsigned Num) const
Helper method returns the integer value of a ConstantSDNode operand.
EVT getValueType(unsigned ResNo) const
Return the type of a specified result.
Unlike LLVM values, Selection DAG nodes may return multiple values as the result of a computation.
bool isUndef() const
SDNode * getNode() const
get the SDNode which holds the desired result
SDValue getValue(unsigned R) const
EVT getValueType() const
Return the ValueType of the referenced return value.
const SDValue & getOperand(unsigned i) const
MVT getSimpleValueType() const
Return the simple ValueType of the referenced return value.
unsigned getOpcode() const
This is used to represent a portion of an LLVM function in a low-level Data Dependence DAG representa...
LLVM_ABI bool isKnownNeverLogicalZero(SDValue Op, const APInt &DemandedElts, unsigned Depth=0) const
Test whether the given floating point SDValue (or all elements of it, if it is a vector) is known to ...
SDValue getTargetGlobalAddress(const GlobalValue *GV, const SDLoc &DL, EVT VT, int64_t offset=0, unsigned TargetFlags=0)
SDValue getCopyToReg(SDValue Chain, const SDLoc &dl, Register Reg, SDValue N)
LLVM_ABI SDValue getMergeValues(ArrayRef< SDValue > Ops, const SDLoc &dl)
Create a MERGE_VALUES node from the given operands.
LLVM_ABI SDVTList getVTList(EVT VT)
Return an SDVTList that represents the list of values specified.
LLVM_ABI SDValue getShiftAmountConstant(uint64_t Val, EVT VT, const SDLoc &DL)
LLVM_ABI SDValue getSplatValue(SDValue V, bool LegalTypes=false)
If V is a splat vector, return its scalar source operand by extracting that element from the source v...
LLVM_ABI MachineSDNode * getMachineNode(unsigned Opcode, const SDLoc &dl, EVT VT)
These are used for target selectors to create a new node with specified return type(s),...
LLVM_ABI void ExtractVectorElements(SDValue Op, SmallVectorImpl< SDValue > &Args, unsigned Start=0, unsigned Count=0, EVT EltVT=EVT())
Append the extracted elements from Start to Count out of the vector Op in Args.
LLVM_ABI SDValue UnrollVectorOp(SDNode *N, unsigned ResNE=0)
Utility function used by legalize and lowering to "unroll" a vector operation by splitting out the sc...
LLVM_ABI SDValue getConstantFP(double Val, const SDLoc &DL, EVT VT, bool isTarget=false)
Create a ConstantFPSDNode wrapping a constant value.
LLVM_ABI SDValue getMemIntrinsicNode(unsigned Opcode, const SDLoc &dl, SDVTList VTList, ArrayRef< SDValue > Ops, EVT MemVT, MachinePointerInfo PtrInfo, Align Alignment, MachineMemOperand::Flags Flags=MachineMemOperand::MOLoad|MachineMemOperand::MOStore, LocationSize Size=LocationSize::precise(0), const AAMDNodes &AAInfo=AAMDNodes())
Creates a MemIntrinsicNode that may produce a result and takes a list of operands.
SDValue getSetCC(const SDLoc &DL, EVT VT, SDValue LHS, SDValue RHS, ISD::CondCode Cond, SDValue Chain=SDValue(), bool IsSignaling=false, SDNodeFlags Flags={})
Helper function to make it easier to build SetCC's if you just have an ISD::CondCode instead of an SD...
LLVM_ABI SDValue getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src, SDValue Size, Align DstAlign, Align SrcAlign, bool isVol, bool AlwaysInline, const CallInst *CI, std::optional< bool > OverrideTailCall, MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo, const AAMDNodes &AAInfo=AAMDNodes(), BatchAAResults *BatchAA=nullptr)
const TargetLowering & getTargetLoweringInfo() const
SDValue getTargetJumpTable(int JTI, EVT VT, unsigned TargetFlags=0)
SDValue getUNDEF(EVT VT)
Return an UNDEF node. UNDEF does not have a useful SDLoc.
SDValue getBuildVector(EVT VT, const SDLoc &DL, ArrayRef< SDValue > Ops)
Return an ISD::BUILD_VECTOR node.
LLVM_ABI SDValue getBitcast(EVT VT, SDValue V)
Return a bitcast using the SDLoc of the value operand, and casting to the provided type.
SDValue getCopyFromReg(SDValue Chain, const SDLoc &dl, Register Reg, EVT VT)
const DataLayout & getDataLayout() const
SDValue getTargetFrameIndex(int FI, EVT VT)
LLVM_ABI SDValue getStore(SDValue Chain, const SDLoc &dl, SDValue Val, SDValue Ptr, MachinePointerInfo PtrInfo, Align Alignment, MachineMemOperand::Flags MMOFlags=MachineMemOperand::MONone, const MMOMetadata &Metadata=MMOMetadata())
Helper function to build ISD::STORE nodes.
LLVM_ABI SDValue getConstant(uint64_t Val, const SDLoc &DL, EVT VT, bool isTarget=false, bool isOpaque=false)
Create a ConstantSDNode wrapping a constant value.
LLVM_ABI bool SignBitIsZero(SDValue Op, unsigned Depth=0) const
Return true if the sign bit of Op is known to be zero.
LLVM_ABI SDValue getBasicBlock(MachineBasicBlock *MBB)
LLVM_ABI SDValue getSExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT)
Convert Op, which must be of integer type, to the integer type VT, by either sign-extending or trunca...
const TargetMachine & getTarget() const
LLVM_ABI SDValue getAnyExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT)
Convert Op, which must be of integer type, to the integer type VT, by either any-extending or truncat...
LLVM_ABI SDValue getIntPtrConstant(uint64_t Val, const SDLoc &DL, bool isTarget=false)
LLVM_ABI SDValue getValueType(EVT)
LLVM_ABI SDValue getNode(unsigned Opcode, const SDLoc &DL, EVT VT, ArrayRef< SDUse > Ops)
Gets or creates the specified node.
LLVM_ABI bool isKnownNeverNaN(SDValue Op, const APInt &DemandedElts, bool SNaN=false, unsigned Depth=0) const
Test whether the given SDValue (or all elements of it, if it is a vector) is known to never be NaN in...
SDValue getTargetConstant(uint64_t Val, const SDLoc &DL, EVT VT, bool isOpaque=false)
LLVM_ABI SDValue getVectorIdxConstant(uint64_t Val, const SDLoc &DL, bool isTarget=false)
MachineFunction & getMachineFunction() const
SDValue getPOISON(EVT VT)
Return a POISON node. POISON does not have a useful SDLoc.
SDValue getSplatBuildVector(EVT VT, const SDLoc &DL, SDValue Op)
Return a splat ISD::BUILD_VECTOR node, consisting of Op splatted to all elements.
LLVM_ABI SDValue getFrameIndex(int FI, EVT VT, bool isTarget=false)
LLVM_ABI SDValue getZExtOrTrunc(SDValue Op, const SDLoc &DL, EVT VT)
Convert Op, which must be of integer type, to the integer type VT, by either zero-extending or trunca...
LLVMContext * getContext() const
LLVM_ABI SDValue getTargetExternalSymbol(const char *Sym, EVT VT, unsigned TargetFlags=0)
LLVM_ABI SDValue getMCSymbol(MCSymbol *Sym, EVT VT)
SDValue getEntryNode() const
Return the token chain corresponding to the entry of the function.
LLVM_ABI SDValue getVectorShuffle(EVT VT, const SDLoc &dl, SDValue N1, SDValue N2, ArrayRef< int > Mask)
Return an ISD::VECTOR_SHUFFLE node.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
iterator insert(iterator I, T &&Elt)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
const SDValue & getBasePtr() const
const SDValue & getOffset() const
const SDValue & getValue() const
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
TargetInstrInfo - Interface to description of machine instruction set.
Provides information about what library functions are available for the current target.
void setBooleanVectorContents(BooleanContent Ty)
Specify how the target extends the result of a vector boolean value from a vector of i1 to a wider ty...
void setOperationAction(unsigned Op, MVT VT, LegalizeAction Action)
Indicate that the specified operation does not work with the specified type and indicate what to do a...
virtual const TargetRegisterClass * getRegClassFor(MVT VT, bool isDivergent=false) const
Return the register class that should be used for the specified value type.
const TargetMachine & getTargetMachine() const
unsigned MaxLoadsPerMemcmp
Specify maximum number of load instructions per memcmp call.
LegalizeTypeAction
This enum indicates whether a types are legal for a target, and if not, what action should be used to...
void setMaxAtomicSizeInBitsSupported(unsigned SizeInBits)
Set the maximum atomic operation size supported by the backend.
virtual TargetLoweringBase::LegalizeTypeAction getPreferredVectorAction(MVT VT) const
Return the preferred vector type legalization action.
void setBooleanContents(BooleanContent Ty)
Specify how the target extends the result of integer and floating point boolean values from i1 to a w...
void computeRegisterProperties(const TargetRegisterInfo *TRI)
Once all of the register classes are added, this allows us to compute derived properties we expose.
void addRegisterClass(MVT VT, const TargetRegisterClass *RC)
Add the specified register class as an available regclass for the specified value type.
virtual MVT getPointerTy(const DataLayout &DL, uint32_t AS=0) const
Return the pointer type for the given address space, defaults to the pointer type from the data layou...
void setMinimumJumpTableEntries(unsigned Val)
Indicate the minimum number of blocks to generate jump tables.
void setPartialReduceMLAAction(unsigned Opc, MVT AccVT, MVT InputVT, LegalizeAction Action)
Indicate how a PARTIAL_REDUCE_U/SMLA node with Acc type AccVT and Input type InputVT should be treate...
void setTruncStoreAction(MVT ValVT, MVT MemVT, LegalizeAction Action)
Indicate that the specified truncating store does not work with the specified type and indicate what ...
unsigned MaxLoadsPerMemcmpOptSize
Likewise for functions with the OptSize attribute.
virtual bool isBinOp(unsigned Opcode) const
Return true if the node is a math/logic binary operator.
void setStackPointerRegisterToSaveRestore(Register R)
If set to a physical register, this specifies the register that llvm.savestack/llvm....
AtomicExpansionKind
Enum that specifies what an atomic load/AtomicRMWInst is expanded to, if at all.
void setCondCodeAction(ArrayRef< ISD::CondCode > CCs, MVT VT, LegalizeAction Action)
Indicate that the specified condition code is or isn't supported on the target and indicate what to d...
void setTargetDAGCombine(ArrayRef< ISD::NodeType > NTs)
Targets should invoke this method for each target independent node that they want to provide a custom...
void setLoadExtAction(unsigned ExtType, MVT ValVT, MVT MemVT, LegalizeAction Action)
Indicate that the specified load with extension does not work with the specified type and indicate wh...
void setSchedulingPreference(Sched::Preference Pref)
Specify the target scheduling preference.
bool isOperationLegalOrCustomOrPromote(unsigned Op, EVT VT, bool LegalOnly=false) const
Return true if the specified operation is legal on this target or can be made legal with custom lower...
This class defines information used to lower LLVM code to legal SelectionDAG operators that the targe...
bool isPositionIndependent() const
virtual std::pair< unsigned, const TargetRegisterClass * > getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const
Given a physical register constraint (e.g.
TargetLowering(const TargetLowering &)=delete
virtual bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const
Return true if folding a constant offset with the given GlobalAddress is legal.
std::pair< SDValue, SDValue > makeLibCall(SelectionDAG &DAG, RTLIB::LibcallImpl LibcallImpl, EVT RetVT, ArrayRef< SDValue > Ops, MakeLibCallOptions CallOptions, const SDLoc &dl, SDValue Chain=SDValue()) const
Returns a pair of (return value, chain).
Primary interface to the complete machine description for the target machine.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
bool isFunctionTy() const
True if this is an instance of FunctionType.
Definition Type.h:268
static LLVM_ABI Type * getDoubleTy(LLVMContext &C)
Definition Type.cpp:277
static LLVM_ABI Type * getFloatTy(LLVMContext &C)
Definition Type.cpp:276
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
LLVM_ABI const Value * stripPointerCastsAndAliases() const
Strip off pointer casts, all-zero GEPs, address space casts, and aliases.
Definition Value.cpp:711
static std::optional< unsigned > getLocalForStackObject(MachineFunction &MF, int FrameIndex)
WebAssemblyTargetLowering(const TargetMachine &TM, const WebAssemblySubtarget &STI)
self_iterator getIterator()
Definition ilist_node.h:123
#define INT64_MIN
Definition DataTypes.h:74
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ Swift
Calling convention for Swift.
Definition CallingConv.h:69
@ PreserveMost
Used for runtime calls that preserves most registers.
Definition CallingConv.h:63
@ CXX_FAST_TLS
Used for access functions.
Definition CallingConv.h:72
@ WASM_EmscriptenInvoke
For emscripten __invoke_* functions.
@ Cold
Attempts to make code in the caller as efficient as possible under the assumption that the call is no...
Definition CallingConv.h:47
@ PreserveAll
Used for runtime calls that preserves (almost) all registers.
Definition CallingConv.h:66
@ Fast
Attempts to make calls as fast as possible (e.g.
Definition CallingConv.h:41
@ SwiftTail
This follows the Swift calling convention in how arguments are passed but guarantees tail calls will ...
Definition CallingConv.h:87
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
@ SETCC
SetCC operator - This evaluates to a true value iff the condition is true.
Definition ISDOpcodes.h:851
@ STACKRESTORE
STACKRESTORE has two operands, an input chain and a pointer to restore to it returns an output chain.
@ STACKSAVE
STACKSAVE - STACKSAVE has one operand, an input chain.
@ PARTIAL_REDUCE_SMLA
PARTIAL_REDUCE_[U|S]MLA(Accumulator, Input1, Input2) The partial reduction nodes sign or zero extend ...
@ SMUL_LOHI
SMUL_LOHI/UMUL_LOHI - Multiply two integers of type iN, producing a signed/unsigned value of type i[2...
Definition ISDOpcodes.h:277
@ BSWAP
Byte Swap and Counting operators.
Definition ISDOpcodes.h:811
@ VAEND
VAEND, VASTART - VAEND and VASTART have three operands: an input chain, pointer, and a SRCVALUE.
@ ADDC
Carry-setting nodes for multiple precision addition and subtraction.
Definition ISDOpcodes.h:296
@ ADD
Simple integer binary arithmetic operators.
Definition ISDOpcodes.h:266
@ LOAD
LOAD and STORE have token chains as their first operand, then the same operands as an LLVM load/store...
@ FMA
FMA - Perform a * b + c with no intermediate rounding step.
Definition ISDOpcodes.h:523
@ PSEUDO_FMIN
PSEUDO_FMIN is strictly equivalent to op0 olt op1 ?
@ INTRINSIC_VOID
OUTCHAIN = INTRINSIC_VOID(INCHAIN, INTRINSICID, arg1, arg2, ...) This node represents a target intrin...
Definition ISDOpcodes.h:222
@ GlobalAddress
Definition ISDOpcodes.h:90
@ SINT_TO_FP
[SU]INT_TO_FP - These operators convert integers (whose interpreted sign depends on the first letter)...
Definition ISDOpcodes.h:912
@ CONCAT_VECTORS
CONCAT_VECTORS(VECTOR0, VECTOR1, ...) - Given a number of values of vector type with the same length ...
Definition ISDOpcodes.h:589
@ ABS
ABS - Determine the unsigned absolute value of a signed integer value of the same bitwidth.
Definition ISDOpcodes.h:771
@ SIGN_EXTEND_VECTOR_INREG
SIGN_EXTEND_VECTOR_INREG(Vector) - This operator represents an in-register sign-extension of the low ...
Definition ISDOpcodes.h:942
@ SDIVREM
SDIVREM/UDIVREM - Divide two integers and produce both a quotient and remainder result.
Definition ISDOpcodes.h:282
@ FP16_TO_FP
FP16_TO_FP, FP_TO_FP16 - These operators are used to perform promotions and truncation for half-preci...
@ FMULADD
FMULADD - Performs a * b + c, with, or without, intermediate rounding.
Definition ISDOpcodes.h:533
@ BITCAST
BITCAST - This operator converts between integer, vector and FP values, as if the value was stored to...
@ BUILD_PAIR
BUILD_PAIR - This is the opposite of EXTRACT_ELEMENT in some ways.
Definition ISDOpcodes.h:256
@ BUILTIN_OP_END
BUILTIN_OP_END - This must be the last enum value in this list.
@ GlobalTLSAddress
Definition ISDOpcodes.h:91
@ PARTIAL_REDUCE_UMLA
@ SIGN_EXTEND
Conversion operators.
Definition ISDOpcodes.h:876
@ SCALAR_TO_VECTOR
SCALAR_TO_VECTOR(VAL) - This represents the operation of loading a scalar value into element 0 of the...
Definition ISDOpcodes.h:681
@ FSINCOS
FSINCOS - Compute both fsin and fcos as a single operation.
@ BR_CC
BR_CC - Conditional branch.
@ BRIND
BRIND - Indirect branch.
@ BR_JT
BR_JT - Jumptable branch.
@ SSUBSAT
RESULT = [US]SUBSAT(LHS, RHS) - Perform saturation subtraction on 2 integers with the same bit width ...
Definition ISDOpcodes.h:377
@ EXTRACT_ELEMENT
EXTRACT_ELEMENT - This is used to get the lower or upper (determined by a Constant,...
Definition ISDOpcodes.h:249
@ SPLAT_VECTOR
SPLAT_VECTOR(VAL) - Returns a vector with the scalar value VAL duplicated in all lanes.
Definition ISDOpcodes.h:688
@ VACOPY
VACOPY - VACOPY has 5 operands: an input chain, a destination pointer, a source pointer,...
@ MULHU
MULHU/MULHS - Multiply high - Multiply two integers of type iN, producing an unsigned/signed value of...
Definition ISDOpcodes.h:720
@ SHL
Shift and rotation operations.
Definition ISDOpcodes.h:793
@ VECTOR_SHUFFLE
VECTOR_SHUFFLE(VEC1, VEC2) - Returns a vector, of the same type as VEC1/VEC2.
Definition ISDOpcodes.h:659
@ EXTRACT_SUBVECTOR
EXTRACT_SUBVECTOR(VECTOR, IDX) - Returns a subvector from VECTOR.
Definition ISDOpcodes.h:619
@ EXTRACT_VECTOR_ELT
EXTRACT_VECTOR_ELT(VECTOR, IDX) - Returns a single element from VECTOR identified by the (potentially...
Definition ISDOpcodes.h:581
@ CopyToReg
CopyToReg - This node has three operands: a chain, a register number to set to this value,...
Definition ISDOpcodes.h:226
@ ZERO_EXTEND
ZERO_EXTEND - Used for integer types, zeroing the new bits.
Definition ISDOpcodes.h:882
@ DEBUGTRAP
DEBUGTRAP - Trap intended to get the attention of a debugger.
@ SELECT_CC
Select with condition operator - This selects between a true value and a false value (ops #2 and #3) ...
Definition ISDOpcodes.h:843
@ FMINNUM
FMINNUM/FMAXNUM - Same semantics as the llvm.minnum/maxnum intrinsics.
@ DYNAMIC_STACKALLOC
DYNAMIC_STACKALLOC - Allocate some number of bytes on the stack aligned to a specified boundary.
@ ANY_EXTEND_VECTOR_INREG
ANY_EXTEND_VECTOR_INREG(Vector) - This operator represents an in-register any-extension of the low la...
Definition ISDOpcodes.h:931
@ SIGN_EXTEND_INREG
SIGN_EXTEND_INREG - This operator atomically performs a SHL/SRA pair to sign extend a small value in ...
Definition ISDOpcodes.h:920
@ SMIN
[US]{MIN/MAX} - Binary minimum or maximum of signed or unsigned integers.
Definition ISDOpcodes.h:751
@ FP_EXTEND
X = FP_EXTEND(Y) - Extend a smaller FP type into a larger FP type.
@ FRAMEADDR
FRAMEADDR, RETURNADDR - These nodes represent llvm.frameaddress and llvm.returnaddress on the DAG.
Definition ISDOpcodes.h:112
@ FMINIMUM
FMINIMUM/FMAXIMUM - Same semantics as the llvm.minimum/maximum intrinsics.
@ FP_TO_SINT
FP_TO_[US]INT - Convert a floating point value to a signed or unsigned integer.
Definition ISDOpcodes.h:958
@ TargetConstant
TargetConstant* - Like Constant*, but the DAG does not do any folding, simplification,...
Definition ISDOpcodes.h:181
@ AND
Bitwise operators - logical and, logical or, logical xor.
Definition ISDOpcodes.h:763
@ TRAP
TRAP - Trapping instruction.
@ INTRINSIC_WO_CHAIN
RESULT = INTRINSIC_WO_CHAIN(INTRINSICID, arg1, arg2, ...) This node represents a target intrinsic fun...
Definition ISDOpcodes.h:207
@ ADDE
Carry-using nodes for multiple precision addition and subtraction.
Definition ISDOpcodes.h:306
@ INSERT_VECTOR_ELT
INSERT_VECTOR_ELT(VECTOR, VAL, IDX) - Returns VECTOR with the element at IDX replaced with VAL.
Definition ISDOpcodes.h:570
@ TokenFactor
TokenFactor - This node takes multiple tokens as input and produces a single token result.
Definition ISDOpcodes.h:55
@ ExternalSymbol
Definition ISDOpcodes.h:95
@ FP_ROUND
X = FP_ROUND(Y, TRUNC) - Rounding 'Y' from a larger floating point type down to the precision of the ...
Definition ISDOpcodes.h:991
@ CLEAR_CACHE
llvm.clear_cache intrinsic Operands: Input Chain, Start Addres, End Address Outputs: Output Chain
@ ZERO_EXTEND_VECTOR_INREG
ZERO_EXTEND_VECTOR_INREG(Vector) - This operator represents an in-register zero-extension of the low ...
Definition ISDOpcodes.h:953
@ FP_TO_SINT_SAT
FP_TO_[US]INT_SAT - Convert floating point value in operand 0 to a signed or unsigned scalar integer ...
Definition ISDOpcodes.h:977
@ TRUNCATE
TRUNCATE - Completely drop the high bits.
Definition ISDOpcodes.h:888
@ VAARG
VAARG - VAARG has four operands: an input chain, a pointer, a SRCVALUE, and the alignment.
@ SHL_PARTS
SHL_PARTS/SRA_PARTS/SRL_PARTS - These operators are used for expanded integer shift operations.
Definition ISDOpcodes.h:865
@ FCOPYSIGN
FCOPYSIGN(X, Y) - Return the value of X with the sign of Y.
Definition ISDOpcodes.h:539
@ SADDSAT
RESULT = [US]ADDSAT(LHS, RHS) - Perform saturation addition on 2 integers with the same bit width (W)...
Definition ISDOpcodes.h:368
@ FMINIMUMNUM
FMINIMUMNUM/FMAXIMUMNUM - Same semantics as the llvm.minimumnum/maximumnum intrinsics.
@ INTRINSIC_W_CHAIN
RESULT,OUTCHAIN = INTRINSIC_W_CHAIN(INCHAIN, INTRINSICID, arg1, ...) This node represents a target in...
Definition ISDOpcodes.h:215
@ BUILD_VECTOR
BUILD_VECTOR(ELT0, ELT1, ELT2, ELT3,...) - Return a fixed-width vector with the specified,...
Definition ISDOpcodes.h:561
LLVM_ABI bool isConstantSplatVector(const SDNode *N, APInt &SplatValue)
Node predicates.
CondCode
ISD::CondCode enum - These are ordered carefully to make the bitfields below work out,...
This namespace contains an enum with a value for every intrinsic/builtin function known by LLVM.
OperandFlags
These are flags set on operands, but should be considered private, all access should go through the M...
Definition MCInstrDesc.h:51
auto m_Value()
Match an arbitrary value and ignore it.
is_zero< true > m_Zero()
Match any null constant or a vector with all elements equal to 0.
CastOperator_match< OpTy, Instruction::BitCast > m_BitCast(const OpTy &Op)
Matches BitCast.
TernaryOpc_match< T0_P, T1_P, CondCode_match, true, false > m_c_SetCC(const T0_P &LHS, const T1_P &RHS)
Match a SETCC with any condition code, allowing the operands to be commuted.
TernaryOpc_match< T0_P, T1_P, CondCode_match, true, false > m_c_SpecificSetCC(ISD::CondCode CC, const T0_P &LHS, const T1_P &RHS)
Match a SETCC with a specific condition code, allowing the operands to be commuted.
bool sd_match(SDValue N, Pattern &&P)
MCSymbolWasm * getOrCreateFunctionTableSymbol(MCContext &Ctx, const WebAssemblySubtarget *Subtarget)
Returns the __indirect_function_table, for use in call_indirect and in function bitcasts.
void moveToFront(RangeT &Range, size_t Idx)
Moves the element at Idx of Range to the front, keeping the order of the other elements.
bool isWebAssemblyTableType(const Type *Ty)
Return true if the table represents a WebAssembly table type.
MCSymbolWasm * getOrCreateFuncrefCallTableSymbol(MCContext &Ctx, const WebAssemblySubtarget *Subtarget)
Returns the __funcref_call_table, for use in funcref calls when lowered to table.set + call_indirect.
bool isValidAddressSpace(unsigned AS)
FastISel * createFastISel(FunctionLoweringInfo &funcInfo, const TargetLibraryInfo *libInfo, const LibcallLoweringInfo *libcallLowering)
bool canLowerReturn(size_t ResultSize, const WebAssemblySubtarget *Subtarget)
Returns true if the function's return value(s) can be lowered directly, i.e., not indirectly via a po...
MachineSDNode * getTLSBase(SelectionDAG &DAG, const SDLoc &DL, const WebAssemblySubtarget *Subtarget, const SDValue Chain=SDValue())
bool isWasmVarAddressSpace(unsigned AS)
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:316
unsigned Log2_32_Ceil(uint32_t Value)
Return the ceil log base 2 of the specified value, 32 if the value is zero.
Definition MathExtras.h:339
@ Offset
Definition DWP.cpp:577
void computeSignatureVTs(const FunctionType *Ty, const Function *TargetFunc, const Function &ContextFunc, const TargetMachine &TM, SmallVectorImpl< MVT > &Params, SmallVectorImpl< MVT > &Results)
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1685
SDValue peekThroughFreeze(SDValue V)
Return the non-frozen source operand of V if it exists.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
LLVM_ABI bool isNullConstant(SDValue V)
Returns true if V is a constant integer zero.
@ Known
Known to have no common set bits.
LLVM_ABI SDValue peekThroughBitcasts(SDValue V)
Return the non-bitcasted source operand of V if it exists.
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Definition STLExtras.h:2570
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ Load
The value being inserted comes from a load (InsertElement only).
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1762
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
@ Add
Sum of integers.
@ Fast
Assign the register banks as fast as possible (default).
DWARFExpression::Operation Op
auto max_element(R &&Range)
Provide wrappers to std::max_element which take ranges instead of having to pass begin/end explicitly...
Definition STLExtras.h:2104
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1788
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
Definition STLExtras.h:2208
void computeLegalValueVTs(const WebAssemblyTargetLowering &TLI, LLVMContext &Ctx, const DataLayout &DL, Type *Ty, SmallVectorImpl< MVT > &ValueVTs)
constexpr uint64_t NextPowerOf2(uint64_t A)
Returns the next power of two (in 64-bits) that is strictly greater than A.
Definition MathExtras.h:368
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:881
#define N
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
Extended Value Type.
Definition ValueTypes.h:35
EVT changeVectorElementTypeToInteger() const
Return a vector with the same number of elements as this vector, but with the element type converted ...
Definition ValueTypes.h:90
bool isSimple() const
Test if the given EVT is simple (as opposed to being extended).
Definition ValueTypes.h:160
static EVT getVectorVT(LLVMContext &Context, EVT VT, unsigned NumElements, bool IsScalable=false)
Returns the EVT that represents a vector NumElements in length, where each element is of type VT.
Definition ValueTypes.h:70
bool isFloatingPoint() const
Return true if this is a FP or a vector FP type.
Definition ValueTypes.h:170
bool is32BitVector() const
Return true if this is a 32-bit vector type.
Definition ValueTypes.h:235
TypeSize getSizeInBits() const
Return the size of the specified value type in bits.
Definition ValueTypes.h:411
bool isByteSized() const
Return true if the bit size is a multiple of 8.
Definition ValueTypes.h:281
uint64_t getScalarSizeInBits() const
Definition ValueTypes.h:423
EVT changeVectorElementType(LLVMContext &Context, EVT EltVT) const
Return a VT for a vector type whose attributes match ourselves with the exception of the element type...
Definition ValueTypes.h:98
MVT getSimpleVT() const
Return the SimpleValueType held in the specified simple EVT.
Definition ValueTypes.h:354
bool is128BitVector() const
Return true if this is a 128-bit vector type.
Definition ValueTypes.h:245
static EVT getIntegerVT(LLVMContext &Context, unsigned BitWidth)
Returns the EVT that represents an integer with the given number of bits.
Definition ValueTypes.h:61
uint64_t getFixedSizeInBits() const
Return the size of the specified fixed width value type in bits.
Definition ValueTypes.h:419
EVT widenIntegerVectorElementType(LLVMContext &Context) const
Return a VT for an integer vector type with the size of the elements doubled.
Definition ValueTypes.h:490
bool isFixedLengthVector() const
Definition ValueTypes.h:214
bool isFixedLengthVectorOf(EVT EltVT) const
Return true if this is a fixed length vector with matching element type.
Definition ValueTypes.h:220
bool isVector() const
Return true if this is a vector value type.
Definition ValueTypes.h:191
EVT getScalarType() const
If this is a vector type, return the element type, otherwise return this.
Definition ValueTypes.h:361
bool bitsGE(EVT VT) const
Return true if this has no less bits than VT.
Definition ValueTypes.h:330
bool is256BitVector() const
Return true if this is a 256-bit vector type.
Definition ValueTypes.h:250
LLVM_ABI Type * getTypeForEVT(LLVMContext &Context) const
This method returns an LLVM type corresponding to the specified EVT.
EVT getVectorElementType() const
Given a vector type, return the type of each element.
Definition ValueTypes.h:366
EVT changeElementType(LLVMContext &Context, EVT EltVT) const
Return a VT for a type whose attributes match ourselves with the exception of the element type that i...
Definition ValueTypes.h:136
bool isScalarInteger() const
Return true if this is an integer, but not a vector.
Definition ValueTypes.h:180
unsigned getVectorNumElements() const
Given a vector type, return the number of elements it contains.
Definition ValueTypes.h:374
EVT getHalfNumVectorElementsVT(LLVMContext &Context) const
Definition ValueTypes.h:499
bool is64BitVector() const
Return true if this is a 64-bit vector type.
Definition ValueTypes.h:240
Matching combinators.
static LLVM_ABI MachinePointerInfo getFixedStack(MachineFunction &MF, int FI, int64_t Offset=0)
Return a MachinePointerInfo record that refers to the specified FrameIndex.
These are IR-level optimization flags that may be propagated to SDNodes.
This structure is used to pass arguments to makeLibCall function.