1 //=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// This file implements the WebAssemblyTargetLowering class. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "WebAssemblyISelLowering.h" 16 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 17 #include "WebAssemblyMachineFunctionInfo.h" 18 #include "WebAssemblySubtarget.h" 19 #include "WebAssemblyTargetMachine.h" 20 #include "llvm/CodeGen/Analysis.h" 21 #include "llvm/CodeGen/CallingConvLower.h" 22 #include "llvm/CodeGen/MachineInstrBuilder.h" 23 #include "llvm/CodeGen/MachineJumpTableInfo.h" 24 #include "llvm/CodeGen/MachineRegisterInfo.h" 25 #include "llvm/CodeGen/SelectionDAG.h" 26 #include "llvm/IR/DiagnosticInfo.h" 27 #include "llvm/IR/DiagnosticPrinter.h" 28 #include "llvm/IR/Function.h" 29 #include "llvm/IR/Intrinsics.h" 30 #include "llvm/Support/Debug.h" 31 #include "llvm/Support/ErrorHandling.h" 32 #include "llvm/Support/raw_ostream.h" 33 #include "llvm/Target/TargetOptions.h" 34 using namespace llvm; 35 36 #define DEBUG_TYPE "wasm-lower" 37 38 // Emit proposed instructions that may not have been implemented in engines 39 cl::opt<bool> EnableUnimplementedWasmSIMDInstrs( 40 "wasm-enable-unimplemented-simd", 41 cl::desc("Emit potentially-unimplemented WebAssembly SIMD instructions"), 42 cl::init(false)); 43 44 WebAssemblyTargetLowering::WebAssemblyTargetLowering( 45 const TargetMachine &TM, const WebAssemblySubtarget &STI) 46 : TargetLowering(TM), Subtarget(&STI) { 47 auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; 48 49 // Booleans always contain 0 or 1. 50 setBooleanContents(ZeroOrOneBooleanContent); 51 // WebAssembly does not produce floating-point exceptions on normal floating 52 // point operations. 53 setHasFloatingPointExceptions(false); 54 // We don't know the microarchitecture here, so just reduce register pressure. 55 setSchedulingPreference(Sched::RegPressure); 56 // Tell ISel that we have a stack pointer. 57 setStackPointerRegisterToSaveRestore( 58 Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32); 59 // Set up the register classes. 60 addRegisterClass(MVT::i32, &WebAssembly::I32RegClass); 61 addRegisterClass(MVT::i64, &WebAssembly::I64RegClass); 62 addRegisterClass(MVT::f32, &WebAssembly::F32RegClass); 63 addRegisterClass(MVT::f64, &WebAssembly::F64RegClass); 64 if (Subtarget->hasSIMD128()) { 65 addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass); 66 addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass); 67 addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass); 68 addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass); 69 if (EnableUnimplementedWasmSIMDInstrs) { 70 addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); 71 addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); 72 } 73 } 74 // Compute derived properties from the register classes. 75 computeRegisterProperties(Subtarget->getRegisterInfo()); 76 77 setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); 78 setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); 79 setOperationAction(ISD::JumpTable, MVTPtr, Custom); 80 setOperationAction(ISD::BlockAddress, MVTPtr, Custom); 81 setOperationAction(ISD::BRIND, MVT::Other, Custom); 82 83 // Take the default expansion for va_arg, va_copy, and va_end. There is no 84 // default action for va_start, so we do that custom. 85 setOperationAction(ISD::VASTART, MVT::Other, Custom); 86 setOperationAction(ISD::VAARG, MVT::Other, Expand); 87 setOperationAction(ISD::VACOPY, MVT::Other, Expand); 88 setOperationAction(ISD::VAEND, MVT::Other, Expand); 89 90 for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64}) { 91 // Don't expand the floating-point types to constant pools. 92 setOperationAction(ISD::ConstantFP, T, Legal); 93 // Expand floating-point comparisons. 94 for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE, 95 ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE}) 96 setCondCodeAction(CC, T, Expand); 97 // Expand floating-point library function operators. 98 for (auto Op : 99 {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FMA}) 100 setOperationAction(Op, T, Expand); 101 // Note supported floating-point library function operators that otherwise 102 // default to expand. 103 for (auto Op : 104 {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) 105 setOperationAction(Op, T, Legal); 106 // Support minnan and maxnan, which otherwise default to expand. 107 setOperationAction(ISD::FMINNAN, T, Legal); 108 setOperationAction(ISD::FMAXNAN, T, Legal); 109 // WebAssembly currently has no builtin f16 support. 110 setOperationAction(ISD::FP16_TO_FP, T, Expand); 111 setOperationAction(ISD::FP_TO_FP16, T, Expand); 112 setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand); 113 setTruncStoreAction(T, MVT::f16, Expand); 114 } 115 116 for (auto T : {MVT::i32, MVT::i64}) { 117 // Expand unavailable integer operations. 118 for (auto Op : 119 {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI, ISD::MULHS, ISD::MULHU, 120 ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, ISD::SRA_PARTS, 121 ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) { 122 setOperationAction(Op, T, Expand); 123 } 124 } 125 126 // There is no i64x2.mul instruction 127 setOperationAction(ISD::MUL, MVT::v2i64, Expand); 128 129 // We have custom shuffle lowering to expose the shuffle mask 130 if (Subtarget->hasSIMD128()) { 131 for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) { 132 setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); 133 } 134 if (EnableUnimplementedWasmSIMDInstrs) { 135 setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v2i64, Custom); 136 setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v2f64, Custom); 137 } 138 } 139 140 // As a special case, these operators use the type to mean the type to 141 // sign-extend from. 142 setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); 143 if (!Subtarget->hasSignExt()) { 144 for (auto T : {MVT::i8, MVT::i16, MVT::i32}) 145 setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand); 146 } 147 148 // Dynamic stack allocation: use the default expansion. 149 setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); 150 setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); 151 setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); 152 153 setOperationAction(ISD::FrameIndex, MVT::i32, Custom); 154 setOperationAction(ISD::CopyToReg, MVT::Other, Custom); 155 156 // Expand these forms; we pattern-match the forms that we can handle in isel. 157 for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) 158 for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) 159 setOperationAction(Op, T, Expand); 160 161 // We have custom switch handling. 162 setOperationAction(ISD::BR_JT, MVT::Other, Custom); 163 164 // WebAssembly doesn't have: 165 // - Floating-point extending loads. 166 // - Floating-point truncating stores. 167 // - i1 extending loads. 168 setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); 169 setTruncStoreAction(MVT::f64, MVT::f32, Expand); 170 for (auto T : MVT::integer_valuetypes()) 171 for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) 172 setLoadExtAction(Ext, T, MVT::i1, Promote); 173 174 // Trap lowers to wasm unreachable 175 setOperationAction(ISD::TRAP, MVT::Other, Legal); 176 177 // Exception handling intrinsics 178 setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); 179 180 setMaxAtomicSizeInBitsSupported(64); 181 } 182 183 TargetLowering::AtomicExpansionKind 184 WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { 185 // We have wasm instructions for these 186 switch (AI->getOperation()) { 187 case AtomicRMWInst::Add: 188 case AtomicRMWInst::Sub: 189 case AtomicRMWInst::And: 190 case AtomicRMWInst::Or: 191 case AtomicRMWInst::Xor: 192 case AtomicRMWInst::Xchg: 193 return AtomicExpansionKind::None; 194 default: 195 break; 196 } 197 return AtomicExpansionKind::CmpXChg; 198 } 199 200 FastISel *WebAssemblyTargetLowering::createFastISel( 201 FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const { 202 return WebAssembly::createFastISel(FuncInfo, LibInfo); 203 } 204 205 bool WebAssemblyTargetLowering::isOffsetFoldingLegal( 206 const GlobalAddressSDNode * /*GA*/) const { 207 // All offsets can be folded. 208 return true; 209 } 210 211 MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, 212 EVT VT) const { 213 unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1); 214 if (BitWidth > 1 && BitWidth < 8) 215 BitWidth = 8; 216 217 if (BitWidth > 64) { 218 // The shift will be lowered to a libcall, and compiler-rt libcalls expect 219 // the count to be an i32. 220 BitWidth = 32; 221 assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && 222 "32-bit shift counts ought to be enough for anyone"); 223 } 224 225 MVT Result = MVT::getIntegerVT(BitWidth); 226 assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE && 227 "Unable to represent scalar shift amount type"); 228 return Result; 229 } 230 231 // Lower an fp-to-int conversion operator from the LLVM opcode, which has an 232 // undefined result on invalid/overflow, to the WebAssembly opcode, which 233 // traps on invalid/overflow. 234 static MachineBasicBlock *LowerFPToInt(MachineInstr &MI, DebugLoc DL, 235 MachineBasicBlock *BB, 236 const TargetInstrInfo &TII, 237 bool IsUnsigned, bool Int64, 238 bool Float64, unsigned LoweredOpcode) { 239 MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); 240 241 unsigned OutReg = MI.getOperand(0).getReg(); 242 unsigned InReg = MI.getOperand(1).getReg(); 243 244 unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; 245 unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; 246 unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; 247 unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; 248 unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; 249 unsigned Eqz = WebAssembly::EQZ_I32; 250 unsigned And = WebAssembly::AND_I32; 251 int64_t Limit = Int64 ? INT64_MIN : INT32_MIN; 252 int64_t Substitute = IsUnsigned ? 0 : Limit; 253 double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; 254 auto &Context = BB->getParent()->getFunction().getContext(); 255 Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); 256 257 const BasicBlock *LLVM_BB = BB->getBasicBlock(); 258 MachineFunction *F = BB->getParent(); 259 MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVM_BB); 260 MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVM_BB); 261 MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVM_BB); 262 263 MachineFunction::iterator It = ++BB->getIterator(); 264 F->insert(It, FalseMBB); 265 F->insert(It, TrueMBB); 266 F->insert(It, DoneMBB); 267 268 // Transfer the remainder of BB and its successor edges to DoneMBB. 269 DoneMBB->splice(DoneMBB->begin(), BB, 270 std::next(MachineBasicBlock::iterator(MI)), BB->end()); 271 DoneMBB->transferSuccessorsAndUpdatePHIs(BB); 272 273 BB->addSuccessor(TrueMBB); 274 BB->addSuccessor(FalseMBB); 275 TrueMBB->addSuccessor(DoneMBB); 276 FalseMBB->addSuccessor(DoneMBB); 277 278 unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; 279 Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 280 Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 281 CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 282 EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 283 FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 284 TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 285 286 MI.eraseFromParent(); 287 // For signed numbers, we can do a single comparison to determine whether 288 // fabs(x) is within range. 289 if (IsUnsigned) { 290 Tmp0 = InReg; 291 } else { 292 BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg); 293 } 294 BuildMI(BB, DL, TII.get(FConst), Tmp1) 295 .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal))); 296 BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1); 297 298 // For unsigned numbers, we have to do a separate comparison with zero. 299 if (IsUnsigned) { 300 Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 301 unsigned SecondCmpReg = 302 MRI.createVirtualRegister(&WebAssembly::I32RegClass); 303 unsigned AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 304 BuildMI(BB, DL, TII.get(FConst), Tmp1) 305 .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0))); 306 BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1); 307 BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg); 308 CmpReg = AndReg; 309 } 310 311 BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg); 312 313 // Create the CFG diamond to select between doing the conversion or using 314 // the substitute value. 315 BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg); 316 BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg); 317 BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); 318 BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute); 319 BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) 320 .addReg(FalseReg) 321 .addMBB(FalseMBB) 322 .addReg(TrueReg) 323 .addMBB(TrueMBB); 324 325 return DoneMBB; 326 } 327 328 MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter( 329 MachineInstr &MI, MachineBasicBlock *BB) const { 330 const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); 331 DebugLoc DL = MI.getDebugLoc(); 332 333 switch (MI.getOpcode()) { 334 default: 335 llvm_unreachable("Unexpected instr type to insert"); 336 case WebAssembly::FP_TO_SINT_I32_F32: 337 return LowerFPToInt(MI, DL, BB, TII, false, false, false, 338 WebAssembly::I32_TRUNC_S_F32); 339 case WebAssembly::FP_TO_UINT_I32_F32: 340 return LowerFPToInt(MI, DL, BB, TII, true, false, false, 341 WebAssembly::I32_TRUNC_U_F32); 342 case WebAssembly::FP_TO_SINT_I64_F32: 343 return LowerFPToInt(MI, DL, BB, TII, false, true, false, 344 WebAssembly::I64_TRUNC_S_F32); 345 case WebAssembly::FP_TO_UINT_I64_F32: 346 return LowerFPToInt(MI, DL, BB, TII, true, true, false, 347 WebAssembly::I64_TRUNC_U_F32); 348 case WebAssembly::FP_TO_SINT_I32_F64: 349 return LowerFPToInt(MI, DL, BB, TII, false, false, true, 350 WebAssembly::I32_TRUNC_S_F64); 351 case WebAssembly::FP_TO_UINT_I32_F64: 352 return LowerFPToInt(MI, DL, BB, TII, true, false, true, 353 WebAssembly::I32_TRUNC_U_F64); 354 case WebAssembly::FP_TO_SINT_I64_F64: 355 return LowerFPToInt(MI, DL, BB, TII, false, true, true, 356 WebAssembly::I64_TRUNC_S_F64); 357 case WebAssembly::FP_TO_UINT_I64_F64: 358 return LowerFPToInt(MI, DL, BB, TII, true, true, true, 359 WebAssembly::I64_TRUNC_U_F64); 360 llvm_unreachable("Unexpected instruction to emit with custom inserter"); 361 } 362 } 363 364 const char * 365 WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { 366 switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) { 367 case WebAssemblyISD::FIRST_NUMBER: 368 break; 369 #define HANDLE_NODETYPE(NODE) \ 370 case WebAssemblyISD::NODE: \ 371 return "WebAssemblyISD::" #NODE; 372 #include "WebAssemblyISD.def" 373 #undef HANDLE_NODETYPE 374 } 375 return nullptr; 376 } 377 378 std::pair<unsigned, const TargetRegisterClass *> 379 WebAssemblyTargetLowering::getRegForInlineAsmConstraint( 380 const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { 381 // First, see if this is a constraint that directly corresponds to a 382 // WebAssembly register class. 383 if (Constraint.size() == 1) { 384 switch (Constraint[0]) { 385 case 'r': 386 assert(VT != MVT::iPTR && "Pointer MVT not expected here"); 387 if (Subtarget->hasSIMD128() && VT.isVector()) { 388 if (VT.getSizeInBits() == 128) 389 return std::make_pair(0U, &WebAssembly::V128RegClass); 390 } 391 if (VT.isInteger() && !VT.isVector()) { 392 if (VT.getSizeInBits() <= 32) 393 return std::make_pair(0U, &WebAssembly::I32RegClass); 394 if (VT.getSizeInBits() <= 64) 395 return std::make_pair(0U, &WebAssembly::I64RegClass); 396 } 397 break; 398 default: 399 break; 400 } 401 } 402 403 return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); 404 } 405 406 bool WebAssemblyTargetLowering::isCheapToSpeculateCttz() const { 407 // Assume ctz is a relatively cheap operation. 408 return true; 409 } 410 411 bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const { 412 // Assume clz is a relatively cheap operation. 413 return true; 414 } 415 416 bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL, 417 const AddrMode &AM, 418 Type *Ty, unsigned AS, 419 Instruction *I) const { 420 // WebAssembly offsets are added as unsigned without wrapping. The 421 // isLegalAddressingMode gives us no way to determine if wrapping could be 422 // happening, so we approximate this by accepting only non-negative offsets. 423 if (AM.BaseOffs < 0) 424 return false; 425 426 // WebAssembly has no scale register operands. 427 if (AM.Scale != 0) 428 return false; 429 430 // Everything else is legal. 431 return true; 432 } 433 434 bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses( 435 EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/, bool *Fast) const { 436 // WebAssembly supports unaligned accesses, though it should be declared 437 // with the p2align attribute on loads and stores which do so, and there 438 // may be a performance impact. We tell LLVM they're "fast" because 439 // for the kinds of things that LLVM uses this for (merging adjacent stores 440 // of constants, etc.), WebAssembly implementations will either want the 441 // unaligned access or they'll split anyway. 442 if (Fast) 443 *Fast = true; 444 return true; 445 } 446 447 bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, 448 AttributeList Attr) const { 449 // The current thinking is that wasm engines will perform this optimization, 450 // so we can save on code size. 451 return true; 452 } 453 454 EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL, 455 LLVMContext &C, 456 EVT VT) const { 457 if (VT.isVector()) 458 return VT.changeVectorElementTypeToInteger(); 459 460 return TargetLowering::getSetCCResultType(DL, C, VT); 461 } 462 463 bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, 464 const CallInst &I, 465 MachineFunction &MF, 466 unsigned Intrinsic) const { 467 switch (Intrinsic) { 468 case Intrinsic::wasm_atomic_notify: 469 Info.opc = ISD::INTRINSIC_W_CHAIN; 470 Info.memVT = MVT::i32; 471 Info.ptrVal = I.getArgOperand(0); 472 Info.offset = 0; 473 Info.align = 4; 474 // atomic.notify instruction does not really load the memory specified with 475 // this argument, but MachineMemOperand should either be load or store, so 476 // we set this to a load. 477 // FIXME Volatile isn't really correct, but currently all LLVM atomic 478 // instructions are treated as volatiles in the backend, so we should be 479 // consistent. The same applies for wasm_atomic_wait intrinsics too. 480 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 481 return true; 482 case Intrinsic::wasm_atomic_wait_i32: 483 Info.opc = ISD::INTRINSIC_W_CHAIN; 484 Info.memVT = MVT::i32; 485 Info.ptrVal = I.getArgOperand(0); 486 Info.offset = 0; 487 Info.align = 4; 488 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 489 return true; 490 case Intrinsic::wasm_atomic_wait_i64: 491 Info.opc = ISD::INTRINSIC_W_CHAIN; 492 Info.memVT = MVT::i64; 493 Info.ptrVal = I.getArgOperand(0); 494 Info.offset = 0; 495 Info.align = 8; 496 Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 497 return true; 498 default: 499 return false; 500 } 501 } 502 503 //===----------------------------------------------------------------------===// 504 // WebAssembly Lowering private implementation. 505 //===----------------------------------------------------------------------===// 506 507 //===----------------------------------------------------------------------===// 508 // Lowering Code 509 //===----------------------------------------------------------------------===// 510 511 static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *msg) { 512 MachineFunction &MF = DAG.getMachineFunction(); 513 DAG.getContext()->diagnose( 514 DiagnosticInfoUnsupported(MF.getFunction(), msg, DL.getDebugLoc())); 515 } 516 517 // Test whether the given calling convention is supported. 518 static bool CallingConvSupported(CallingConv::ID CallConv) { 519 // We currently support the language-independent target-independent 520 // conventions. We don't yet have a way to annotate calls with properties like 521 // "cold", and we don't have any call-clobbered registers, so these are mostly 522 // all handled the same. 523 return CallConv == CallingConv::C || CallConv == CallingConv::Fast || 524 CallConv == CallingConv::Cold || 525 CallConv == CallingConv::PreserveMost || 526 CallConv == CallingConv::PreserveAll || 527 CallConv == CallingConv::CXX_FAST_TLS; 528 } 529 530 SDValue 531 WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, 532 SmallVectorImpl<SDValue> &InVals) const { 533 SelectionDAG &DAG = CLI.DAG; 534 SDLoc DL = CLI.DL; 535 SDValue Chain = CLI.Chain; 536 SDValue Callee = CLI.Callee; 537 MachineFunction &MF = DAG.getMachineFunction(); 538 auto Layout = MF.getDataLayout(); 539 540 CallingConv::ID CallConv = CLI.CallConv; 541 if (!CallingConvSupported(CallConv)) 542 fail(DL, DAG, 543 "WebAssembly doesn't support language-specific or target-specific " 544 "calling conventions yet"); 545 if (CLI.IsPatchPoint) 546 fail(DL, DAG, "WebAssembly doesn't support patch point yet"); 547 548 // WebAssembly doesn't currently support explicit tail calls. If they are 549 // required, fail. Otherwise, just disable them. 550 if ((CallConv == CallingConv::Fast && CLI.IsTailCall && 551 MF.getTarget().Options.GuaranteedTailCallOpt) || 552 (CLI.CS && CLI.CS.isMustTailCall())) 553 fail(DL, DAG, "WebAssembly doesn't support tail call yet"); 554 CLI.IsTailCall = false; 555 556 SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; 557 if (Ins.size() > 1) 558 fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); 559 560 SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; 561 SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; 562 unsigned NumFixedArgs = 0; 563 for (unsigned i = 0; i < Outs.size(); ++i) { 564 const ISD::OutputArg &Out = Outs[i]; 565 SDValue &OutVal = OutVals[i]; 566 if (Out.Flags.isNest()) 567 fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 568 if (Out.Flags.isInAlloca()) 569 fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 570 if (Out.Flags.isInConsecutiveRegs()) 571 fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 572 if (Out.Flags.isInConsecutiveRegsLast()) 573 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 574 if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) { 575 auto &MFI = MF.getFrameInfo(); 576 int FI = MFI.CreateStackObject(Out.Flags.getByValSize(), 577 Out.Flags.getByValAlign(), 578 /*isSS=*/false); 579 SDValue SizeNode = 580 DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); 581 SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 582 Chain = DAG.getMemcpy( 583 Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getByValAlign(), 584 /*isVolatile*/ false, /*AlwaysInline=*/false, 585 /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo()); 586 OutVal = FINode; 587 } 588 // Count the number of fixed args *after* legalization. 589 NumFixedArgs += Out.IsFixed; 590 } 591 592 bool IsVarArg = CLI.IsVarArg; 593 auto PtrVT = getPointerTy(Layout); 594 595 // Analyze operands of the call, assigning locations to each operand. 596 SmallVector<CCValAssign, 16> ArgLocs; 597 CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); 598 599 if (IsVarArg) { 600 // Outgoing non-fixed arguments are placed in a buffer. First 601 // compute their offsets and the total amount of buffer space needed. 602 for (SDValue Arg : 603 make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { 604 EVT VT = Arg.getValueType(); 605 assert(VT != MVT::iPTR && "Legalized args should be concrete"); 606 Type *Ty = VT.getTypeForEVT(*DAG.getContext()); 607 unsigned Offset = CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), 608 Layout.getABITypeAlignment(Ty)); 609 CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), 610 Offset, VT.getSimpleVT(), 611 CCValAssign::Full)); 612 } 613 } 614 615 unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); 616 617 SDValue FINode; 618 if (IsVarArg && NumBytes) { 619 // For non-fixed arguments, next emit stores to store the argument values 620 // to the stack buffer at the offsets computed above. 621 int FI = MF.getFrameInfo().CreateStackObject(NumBytes, 622 Layout.getStackAlignment(), 623 /*isSS=*/false); 624 unsigned ValNo = 0; 625 SmallVector<SDValue, 8> Chains; 626 for (SDValue Arg : 627 make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { 628 assert(ArgLocs[ValNo].getValNo() == ValNo && 629 "ArgLocs should remain in order and only hold varargs args"); 630 unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); 631 FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 632 SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode, 633 DAG.getConstant(Offset, DL, PtrVT)); 634 Chains.push_back( 635 DAG.getStore(Chain, DL, Arg, Add, 636 MachinePointerInfo::getFixedStack(MF, FI, Offset), 0)); 637 } 638 if (!Chains.empty()) 639 Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); 640 } else if (IsVarArg) { 641 FINode = DAG.getIntPtrConstant(0, DL); 642 } 643 644 // Compute the operands for the CALLn node. 645 SmallVector<SDValue, 16> Ops; 646 Ops.push_back(Chain); 647 Ops.push_back(Callee); 648 649 // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs 650 // isn't reliable. 651 Ops.append(OutVals.begin(), 652 IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); 653 // Add a pointer to the vararg buffer. 654 if (IsVarArg) 655 Ops.push_back(FINode); 656 657 SmallVector<EVT, 8> InTys; 658 for (const auto &In : Ins) { 659 assert(!In.Flags.isByVal() && "byval is not valid for return values"); 660 assert(!In.Flags.isNest() && "nest is not valid for return values"); 661 if (In.Flags.isInAlloca()) 662 fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values"); 663 if (In.Flags.isInConsecutiveRegs()) 664 fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values"); 665 if (In.Flags.isInConsecutiveRegsLast()) 666 fail(DL, DAG, 667 "WebAssembly hasn't implemented cons regs last return values"); 668 // Ignore In.getOrigAlign() because all our arguments are passed in 669 // registers. 670 InTys.push_back(In.VT); 671 } 672 InTys.push_back(MVT::Other); 673 SDVTList InTyList = DAG.getVTList(InTys); 674 SDValue Res = 675 DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1, 676 DL, InTyList, Ops); 677 if (Ins.empty()) { 678 Chain = Res; 679 } else { 680 InVals.push_back(Res); 681 Chain = Res.getValue(1); 682 } 683 684 return Chain; 685 } 686 687 bool WebAssemblyTargetLowering::CanLowerReturn( 688 CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, 689 const SmallVectorImpl<ISD::OutputArg> &Outs, 690 LLVMContext & /*Context*/) const { 691 // WebAssembly can't currently handle returning tuples. 692 return Outs.size() <= 1; 693 } 694 695 SDValue WebAssemblyTargetLowering::LowerReturn( 696 SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, 697 const SmallVectorImpl<ISD::OutputArg> &Outs, 698 const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, 699 SelectionDAG &DAG) const { 700 assert(Outs.size() <= 1 && "WebAssembly can only return up to one value"); 701 if (!CallingConvSupported(CallConv)) 702 fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 703 704 SmallVector<SDValue, 4> RetOps(1, Chain); 705 RetOps.append(OutVals.begin(), OutVals.end()); 706 Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps); 707 708 // Record the number and types of the return values. 709 for (const ISD::OutputArg &Out : Outs) { 710 assert(!Out.Flags.isByVal() && "byval is not valid for return values"); 711 assert(!Out.Flags.isNest() && "nest is not valid for return values"); 712 assert(Out.IsFixed && "non-fixed return value is not valid"); 713 if (Out.Flags.isInAlloca()) 714 fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); 715 if (Out.Flags.isInConsecutiveRegs()) 716 fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); 717 if (Out.Flags.isInConsecutiveRegsLast()) 718 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); 719 } 720 721 return Chain; 722 } 723 724 SDValue WebAssemblyTargetLowering::LowerFormalArguments( 725 SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, 726 const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, 727 SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { 728 if (!CallingConvSupported(CallConv)) 729 fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 730 731 MachineFunction &MF = DAG.getMachineFunction(); 732 auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); 733 734 // Set up the incoming ARGUMENTS value, which serves to represent the liveness 735 // of the incoming values before they're represented by virtual registers. 736 MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); 737 738 for (const ISD::InputArg &In : Ins) { 739 if (In.Flags.isInAlloca()) 740 fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 741 if (In.Flags.isNest()) 742 fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 743 if (In.Flags.isInConsecutiveRegs()) 744 fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 745 if (In.Flags.isInConsecutiveRegsLast()) 746 fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 747 // Ignore In.getOrigAlign() because all our arguments are passed in 748 // registers. 749 InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, 750 DAG.getTargetConstant(InVals.size(), 751 DL, MVT::i32)) 752 : DAG.getUNDEF(In.VT)); 753 754 // Record the number and types of arguments. 755 MFI->addParam(In.VT); 756 } 757 758 // Varargs are copied into a buffer allocated by the caller, and a pointer to 759 // the buffer is passed as an argument. 760 if (IsVarArg) { 761 MVT PtrVT = getPointerTy(MF.getDataLayout()); 762 unsigned VarargVreg = 763 MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); 764 MFI->setVarargBufferVreg(VarargVreg); 765 Chain = DAG.getCopyToReg( 766 Chain, DL, VarargVreg, 767 DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, 768 DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); 769 MFI->addParam(PtrVT); 770 } 771 772 // Record the number and types of results. 773 SmallVector<MVT, 4> Params; 774 SmallVector<MVT, 4> Results; 775 ComputeSignatureVTs(MF.getFunction(), DAG.getTarget(), Params, Results); 776 for (MVT VT : Results) 777 MFI->addResult(VT); 778 779 return Chain; 780 } 781 782 //===----------------------------------------------------------------------===// 783 // Custom lowering hooks. 784 //===----------------------------------------------------------------------===// 785 786 SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, 787 SelectionDAG &DAG) const { 788 SDLoc DL(Op); 789 switch (Op.getOpcode()) { 790 default: 791 llvm_unreachable("unimplemented operation lowering"); 792 return SDValue(); 793 case ISD::FrameIndex: 794 return LowerFrameIndex(Op, DAG); 795 case ISD::GlobalAddress: 796 return LowerGlobalAddress(Op, DAG); 797 case ISD::ExternalSymbol: 798 return LowerExternalSymbol(Op, DAG); 799 case ISD::JumpTable: 800 return LowerJumpTable(Op, DAG); 801 case ISD::BR_JT: 802 return LowerBR_JT(Op, DAG); 803 case ISD::VASTART: 804 return LowerVASTART(Op, DAG); 805 case ISD::BlockAddress: 806 case ISD::BRIND: 807 fail(DL, DAG, "WebAssembly hasn't implemented computed gotos"); 808 return SDValue(); 809 case ISD::RETURNADDR: // Probably nothing meaningful can be returned here. 810 fail(DL, DAG, "WebAssembly hasn't implemented __builtin_return_address"); 811 return SDValue(); 812 case ISD::FRAMEADDR: 813 return LowerFRAMEADDR(Op, DAG); 814 case ISD::CopyToReg: 815 return LowerCopyToReg(Op, DAG); 816 case ISD::INTRINSIC_WO_CHAIN: 817 return LowerINTRINSIC_WO_CHAIN(Op, DAG); 818 case ISD::VECTOR_SHUFFLE: 819 return LowerVECTOR_SHUFFLE(Op, DAG); 820 } 821 } 822 823 SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, 824 SelectionDAG &DAG) const { 825 SDValue Src = Op.getOperand(2); 826 if (isa<FrameIndexSDNode>(Src.getNode())) { 827 // CopyToReg nodes don't support FrameIndex operands. Other targets select 828 // the FI to some LEA-like instruction, but since we don't have that, we 829 // need to insert some kind of instruction that can take an FI operand and 830 // produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy 831 // copy_local between Op and its FI operand. 832 SDValue Chain = Op.getOperand(0); 833 SDLoc DL(Op); 834 unsigned Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg(); 835 EVT VT = Src.getValueType(); 836 SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32 837 : WebAssembly::COPY_I64, 838 DL, VT, Src), 839 0); 840 return Op.getNode()->getNumValues() == 1 841 ? DAG.getCopyToReg(Chain, DL, Reg, Copy) 842 : DAG.getCopyToReg(Chain, DL, Reg, Copy, 843 Op.getNumOperands() == 4 ? Op.getOperand(3) 844 : SDValue()); 845 } 846 return SDValue(); 847 } 848 849 SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op, 850 SelectionDAG &DAG) const { 851 int FI = cast<FrameIndexSDNode>(Op)->getIndex(); 852 return DAG.getTargetFrameIndex(FI, Op.getValueType()); 853 } 854 855 SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, 856 SelectionDAG &DAG) const { 857 // Non-zero depths are not supported by WebAssembly currently. Use the 858 // legalizer's default expansion, which is to return 0 (what this function is 859 // documented to do). 860 if (Op.getConstantOperandVal(0) > 0) 861 return SDValue(); 862 863 DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); 864 EVT VT = Op.getValueType(); 865 unsigned FP = 866 Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); 867 return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); 868 } 869 870 SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, 871 SelectionDAG &DAG) const { 872 SDLoc DL(Op); 873 const auto *GA = cast<GlobalAddressSDNode>(Op); 874 EVT VT = Op.getValueType(); 875 assert(GA->getTargetFlags() == 0 && 876 "Unexpected target flags on generic GlobalAddressSDNode"); 877 if (GA->getAddressSpace() != 0) 878 fail(DL, DAG, "WebAssembly only expects the 0 address space"); 879 return DAG.getNode( 880 WebAssemblyISD::Wrapper, DL, VT, 881 DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset())); 882 } 883 884 SDValue 885 WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, 886 SelectionDAG &DAG) const { 887 SDLoc DL(Op); 888 const auto *ES = cast<ExternalSymbolSDNode>(Op); 889 EVT VT = Op.getValueType(); 890 assert(ES->getTargetFlags() == 0 && 891 "Unexpected target flags on generic ExternalSymbolSDNode"); 892 // Set the TargetFlags to 0x1 which indicates that this is a "function" 893 // symbol rather than a data symbol. We do this unconditionally even though 894 // we don't know anything about the symbol other than its name, because all 895 // external symbols used in target-independent SelectionDAG code are for 896 // functions. 897 return DAG.getNode( 898 WebAssemblyISD::Wrapper, DL, VT, 899 DAG.getTargetExternalSymbol(ES->getSymbol(), VT, 900 WebAssemblyII::MO_SYMBOL_FUNCTION)); 901 } 902 903 SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, 904 SelectionDAG &DAG) const { 905 // There's no need for a Wrapper node because we always incorporate a jump 906 // table operand into a BR_TABLE instruction, rather than ever 907 // materializing it in a register. 908 const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); 909 return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), 910 JT->getTargetFlags()); 911 } 912 913 SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, 914 SelectionDAG &DAG) const { 915 SDLoc DL(Op); 916 SDValue Chain = Op.getOperand(0); 917 const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1)); 918 SDValue Index = Op.getOperand(2); 919 assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); 920 921 SmallVector<SDValue, 8> Ops; 922 Ops.push_back(Chain); 923 Ops.push_back(Index); 924 925 MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); 926 const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; 927 928 // Add an operand for each case. 929 for (auto MBB : MBBs) 930 Ops.push_back(DAG.getBasicBlock(MBB)); 931 932 // TODO: For now, we just pick something arbitrary for a default case for now. 933 // We really want to sniff out the guard and put in the real default case (and 934 // delete the guard). 935 Ops.push_back(DAG.getBasicBlock(MBBs[0])); 936 937 return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops); 938 } 939 940 SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, 941 SelectionDAG &DAG) const { 942 SDLoc DL(Op); 943 EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); 944 945 auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>(); 946 const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); 947 948 SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, 949 MFI->getVarargBufferVreg(), PtrVT); 950 return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), 951 MachinePointerInfo(SV), 0); 952 } 953 954 SDValue 955 WebAssemblyTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, 956 SelectionDAG &DAG) const { 957 unsigned IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); 958 SDLoc DL(Op); 959 switch (IntNo) { 960 default: 961 return {}; // Don't custom lower most intrinsics. 962 963 case Intrinsic::wasm_lsda: 964 // TODO For now, just return 0 not to crash 965 return DAG.getConstant(0, DL, Op.getValueType()); 966 } 967 } 968 969 SDValue 970 WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op, 971 SelectionDAG &DAG) const { 972 SDLoc DL(Op); 973 ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask(); 974 MVT VecType = Op.getOperand(0).getSimpleValueType(); 975 assert(VecType.is128BitVector() && "Unexpected shuffle vector type"); 976 size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8; 977 978 // Space for two vector args and sixteen mask indices 979 SDValue Ops[18]; 980 size_t OpIdx = 0; 981 Ops[OpIdx++] = Op.getOperand(0); 982 Ops[OpIdx++] = Op.getOperand(1); 983 984 // Expand mask indices to byte indices and materialize them as operands 985 for (size_t I = 0, Lanes = Mask.size(); I < Lanes; ++I) { 986 for (size_t J = 0; J < LaneBytes; ++J) { 987 Ops[OpIdx++] = 988 DAG.getConstant((uint64_t)Mask[I] * LaneBytes + J, DL, MVT::i32); 989 } 990 } 991 992 return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, MVT::v16i8, Ops); 993 } 994 995 //===----------------------------------------------------------------------===// 996 // WebAssembly Optimization Hooks 997 //===----------------------------------------------------------------------===// 998