10b57cec5SDimitry Andric //=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric /// This file implements the WebAssemblyTargetLowering class. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "WebAssemblyISelLowering.h" 150b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 16fe6060f1SDimitry Andric #include "Utils/WebAssemblyTypeUtilities.h" 170b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 180b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 190b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 205f757f3fSDimitry Andric #include "WebAssemblyUtilities.h" 210b57cec5SDimitry Andric #include "llvm/CodeGen/CallingConvLower.h" 2281ad6265SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 2381ad6265SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 240b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 250b57cec5SDimitry Andric #include "llvm/CodeGen/MachineJumpTableInfo.h" 260b57cec5SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 270b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 280b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAG.h" 29fe6060f1SDimitry Andric #include "llvm/CodeGen/SelectionDAGNodes.h" 300b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 310b57cec5SDimitry Andric #include "llvm/IR/DiagnosticPrinter.h" 320b57cec5SDimitry Andric #include "llvm/IR/Function.h" 330b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h" 34480093f4SDimitry Andric #include "llvm/IR/IntrinsicsWebAssembly.h" 355f757f3fSDimitry Andric #include "llvm/IR/PatternMatch.h" 360b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 370b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 38349cc55cSDimitry Andric #include "llvm/Support/KnownBits.h" 39e8d8bef9SDimitry Andric #include "llvm/Support/MathExtras.h" 400b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 410b57cec5SDimitry Andric #include "llvm/Target/TargetOptions.h" 420b57cec5SDimitry Andric using namespace llvm; 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-lower" 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric WebAssemblyTargetLowering::WebAssemblyTargetLowering( 470b57cec5SDimitry Andric const TargetMachine &TM, const WebAssemblySubtarget &STI) 480b57cec5SDimitry Andric : TargetLowering(TM), Subtarget(&STI) { 490b57cec5SDimitry Andric auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric // Booleans always contain 0 or 1. 520b57cec5SDimitry Andric setBooleanContents(ZeroOrOneBooleanContent); 530b57cec5SDimitry Andric // Except in SIMD vectors 540b57cec5SDimitry Andric setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); 550b57cec5SDimitry Andric // We don't know the microarchitecture here, so just reduce register pressure. 560b57cec5SDimitry Andric setSchedulingPreference(Sched::RegPressure); 570b57cec5SDimitry Andric // Tell ISel that we have a stack pointer. 580b57cec5SDimitry Andric setStackPointerRegisterToSaveRestore( 590b57cec5SDimitry Andric Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32); 600b57cec5SDimitry Andric // Set up the register classes. 610b57cec5SDimitry Andric addRegisterClass(MVT::i32, &WebAssembly::I32RegClass); 620b57cec5SDimitry Andric addRegisterClass(MVT::i64, &WebAssembly::I64RegClass); 630b57cec5SDimitry Andric addRegisterClass(MVT::f32, &WebAssembly::F32RegClass); 640b57cec5SDimitry Andric addRegisterClass(MVT::f64, &WebAssembly::F64RegClass); 650b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 660b57cec5SDimitry Andric addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass); 670b57cec5SDimitry Andric addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass); 680b57cec5SDimitry Andric addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass); 690b57cec5SDimitry Andric addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass); 700b57cec5SDimitry Andric addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); 710b57cec5SDimitry Andric addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); 720b57cec5SDimitry Andric } 73*0fca6ea1SDimitry Andric if (Subtarget->hasHalfPrecision()) { 74*0fca6ea1SDimitry Andric addRegisterClass(MVT::v8f16, &WebAssembly::V128RegClass); 75*0fca6ea1SDimitry Andric } 76fe6060f1SDimitry Andric if (Subtarget->hasReferenceTypes()) { 77fe6060f1SDimitry Andric addRegisterClass(MVT::externref, &WebAssembly::EXTERNREFRegClass); 78fe6060f1SDimitry Andric addRegisterClass(MVT::funcref, &WebAssembly::FUNCREFRegClass); 79*0fca6ea1SDimitry Andric if (Subtarget->hasExceptionHandling()) { 80*0fca6ea1SDimitry Andric addRegisterClass(MVT::exnref, &WebAssembly::EXNREFRegClass); 81*0fca6ea1SDimitry Andric } 82fe6060f1SDimitry Andric } 830b57cec5SDimitry Andric // Compute derived properties from the register classes. 840b57cec5SDimitry Andric computeRegisterProperties(Subtarget->getRegisterInfo()); 850b57cec5SDimitry Andric 86fe6060f1SDimitry Andric // Transform loads and stores to pointers in address space 1 to loads and 87fe6060f1SDimitry Andric // stores to WebAssembly global variables, outside linear memory. 88fe6060f1SDimitry Andric for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) { 89fe6060f1SDimitry Andric setOperationAction(ISD::LOAD, T, Custom); 90fe6060f1SDimitry Andric setOperationAction(ISD::STORE, T, Custom); 91fe6060f1SDimitry Andric } 92fe6060f1SDimitry Andric if (Subtarget->hasSIMD128()) { 93fe6060f1SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 94fe6060f1SDimitry Andric MVT::v2f64}) { 95fe6060f1SDimitry Andric setOperationAction(ISD::LOAD, T, Custom); 96fe6060f1SDimitry Andric setOperationAction(ISD::STORE, T, Custom); 97fe6060f1SDimitry Andric } 98fe6060f1SDimitry Andric } 99fe6060f1SDimitry Andric if (Subtarget->hasReferenceTypes()) { 100349cc55cSDimitry Andric // We need custom load and store lowering for both externref, funcref and 101349cc55cSDimitry Andric // Other. The MVT::Other here represents tables of reference types. 102349cc55cSDimitry Andric for (auto T : {MVT::externref, MVT::funcref, MVT::Other}) { 103fe6060f1SDimitry Andric setOperationAction(ISD::LOAD, T, Custom); 104fe6060f1SDimitry Andric setOperationAction(ISD::STORE, T, Custom); 105fe6060f1SDimitry Andric } 106fe6060f1SDimitry Andric } 107fe6060f1SDimitry Andric 1080b57cec5SDimitry Andric setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); 109e8d8bef9SDimitry Andric setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); 1100b57cec5SDimitry Andric setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); 1110b57cec5SDimitry Andric setOperationAction(ISD::JumpTable, MVTPtr, Custom); 1120b57cec5SDimitry Andric setOperationAction(ISD::BlockAddress, MVTPtr, Custom); 1130b57cec5SDimitry Andric setOperationAction(ISD::BRIND, MVT::Other, Custom); 114*0fca6ea1SDimitry Andric setOperationAction(ISD::CLEAR_CACHE, MVT::Other, Custom); 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric // Take the default expansion for va_arg, va_copy, and va_end. There is no 1170b57cec5SDimitry Andric // default action for va_start, so we do that custom. 1180b57cec5SDimitry Andric setOperationAction(ISD::VASTART, MVT::Other, Custom); 1190b57cec5SDimitry Andric setOperationAction(ISD::VAARG, MVT::Other, Expand); 1200b57cec5SDimitry Andric setOperationAction(ISD::VACOPY, MVT::Other, Expand); 1210b57cec5SDimitry Andric setOperationAction(ISD::VAEND, MVT::Other, Expand); 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64}) { 1240b57cec5SDimitry Andric // Don't expand the floating-point types to constant pools. 1250b57cec5SDimitry Andric setOperationAction(ISD::ConstantFP, T, Legal); 1260b57cec5SDimitry Andric // Expand floating-point comparisons. 1270b57cec5SDimitry Andric for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE, 1280b57cec5SDimitry Andric ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE}) 1290b57cec5SDimitry Andric setCondCodeAction(CC, T, Expand); 1300b57cec5SDimitry Andric // Expand floating-point library function operators. 1310b57cec5SDimitry Andric for (auto Op : 1320b57cec5SDimitry Andric {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FMA}) 1330b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1340b57cec5SDimitry Andric // Note supported floating-point library function operators that otherwise 1350b57cec5SDimitry Andric // default to expand. 13606c3fb27SDimitry Andric for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, 13706c3fb27SDimitry Andric ISD::FRINT, ISD::FROUNDEVEN}) 1380b57cec5SDimitry Andric setOperationAction(Op, T, Legal); 1390b57cec5SDimitry Andric // Support minimum and maximum, which otherwise default to expand. 1400b57cec5SDimitry Andric setOperationAction(ISD::FMINIMUM, T, Legal); 1410b57cec5SDimitry Andric setOperationAction(ISD::FMAXIMUM, T, Legal); 1420b57cec5SDimitry Andric // WebAssembly currently has no builtin f16 support. 1430b57cec5SDimitry Andric setOperationAction(ISD::FP16_TO_FP, T, Expand); 1440b57cec5SDimitry Andric setOperationAction(ISD::FP_TO_FP16, T, Expand); 1450b57cec5SDimitry Andric setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand); 1460b57cec5SDimitry Andric setTruncStoreAction(T, MVT::f16, Expand); 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric 149*0fca6ea1SDimitry Andric if (Subtarget->hasHalfPrecision()) { 150*0fca6ea1SDimitry Andric setOperationAction(ISD::FMINIMUM, MVT::v8f16, Legal); 151*0fca6ea1SDimitry Andric setOperationAction(ISD::FMAXIMUM, MVT::v8f16, Legal); 152*0fca6ea1SDimitry Andric } 153*0fca6ea1SDimitry Andric 1540b57cec5SDimitry Andric // Expand unavailable integer operations. 1550b57cec5SDimitry Andric for (auto Op : 1560b57cec5SDimitry Andric {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI, ISD::MULHS, ISD::MULHU, 1570b57cec5SDimitry Andric ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, ISD::SRA_PARTS, 1580b57cec5SDimitry Andric ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) { 1590b57cec5SDimitry Andric for (auto T : {MVT::i32, MVT::i64}) 1600b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1610b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) 1625ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) 1630b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1640b57cec5SDimitry Andric } 1650b57cec5SDimitry Andric 166fe6060f1SDimitry Andric if (Subtarget->hasNontrappingFPToInt()) 167fe6060f1SDimitry Andric for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}) 168fe6060f1SDimitry Andric for (auto T : {MVT::i32, MVT::i64}) 169fe6060f1SDimitry Andric setOperationAction(Op, T, Custom); 170fe6060f1SDimitry Andric 1710b57cec5SDimitry Andric // SIMD-specific configuration 1720b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 17306c3fb27SDimitry Andric // Combine vector mask reductions into alltrue/anytrue 17406c3fb27SDimitry Andric setTargetDAGCombine(ISD::SETCC); 17506c3fb27SDimitry Andric 17606c3fb27SDimitry Andric // Convert vector to integer bitcasts to bitmask 17706c3fb27SDimitry Andric setTargetDAGCombine(ISD::BITCAST); 17806c3fb27SDimitry Andric 1795ffd83dbSDimitry Andric // Hoist bitcasts out of shuffles 1805ffd83dbSDimitry Andric setTargetDAGCombine(ISD::VECTOR_SHUFFLE); 1815ffd83dbSDimitry Andric 182e8d8bef9SDimitry Andric // Combine extends of extract_subvectors into widening ops 18381ad6265SDimitry Andric setTargetDAGCombine({ISD::SIGN_EXTEND, ISD::ZERO_EXTEND}); 184e8d8bef9SDimitry Andric 185fe6060f1SDimitry Andric // Combine int_to_fp or fp_extend of extract_vectors and vice versa into 186fe6060f1SDimitry Andric // conversions ops 18781ad6265SDimitry Andric setTargetDAGCombine({ISD::SINT_TO_FP, ISD::UINT_TO_FP, ISD::FP_EXTEND, 18881ad6265SDimitry Andric ISD::EXTRACT_SUBVECTOR}); 189fe6060f1SDimitry Andric 190fe6060f1SDimitry Andric // Combine fp_to_{s,u}int_sat or fp_round of concat_vectors or vice versa 191fe6060f1SDimitry Andric // into conversion ops 19281ad6265SDimitry Andric setTargetDAGCombine({ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT, 19381ad6265SDimitry Andric ISD::FP_ROUND, ISD::CONCAT_VECTORS}); 194fe6060f1SDimitry Andric 1950eae32dcSDimitry Andric setTargetDAGCombine(ISD::TRUNCATE); 1960eae32dcSDimitry Andric 1970b57cec5SDimitry Andric // Support saturating add for i8x16 and i16x8 1980b57cec5SDimitry Andric for (auto Op : {ISD::SADDSAT, ISD::UADDSAT}) 1990b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16}) 2000b57cec5SDimitry Andric setOperationAction(Op, T, Legal); 2010b57cec5SDimitry Andric 2025ffd83dbSDimitry Andric // Support integer abs 203fe6060f1SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) 2045ffd83dbSDimitry Andric setOperationAction(ISD::ABS, T, Legal); 2055ffd83dbSDimitry Andric 2060b57cec5SDimitry Andric // Custom lower BUILD_VECTORs to minimize number of replace_lanes 2075ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 2085ffd83dbSDimitry Andric MVT::v2f64}) 2090b57cec5SDimitry Andric setOperationAction(ISD::BUILD_VECTOR, T, Custom); 2100b57cec5SDimitry Andric 2110b57cec5SDimitry Andric // We have custom shuffle lowering to expose the shuffle mask 2125ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 2135ffd83dbSDimitry Andric MVT::v2f64}) 2140b57cec5SDimitry Andric setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); 2150b57cec5SDimitry Andric 216bdd1243dSDimitry Andric // Support splatting 217bdd1243dSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 218bdd1243dSDimitry Andric MVT::v2f64}) 219bdd1243dSDimitry Andric setOperationAction(ISD::SPLAT_VECTOR, T, Legal); 220bdd1243dSDimitry Andric 2210b57cec5SDimitry Andric // Custom lowering since wasm shifts must have a scalar shift amount 2225ffd83dbSDimitry Andric for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL}) 2235ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) 2240b57cec5SDimitry Andric setOperationAction(Op, T, Custom); 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric // Custom lower lane accesses to expand out variable indices 2275ffd83dbSDimitry Andric for (auto Op : {ISD::EXTRACT_VECTOR_ELT, ISD::INSERT_VECTOR_ELT}) 2285ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 2295ffd83dbSDimitry Andric MVT::v2f64}) 2300b57cec5SDimitry Andric setOperationAction(Op, T, Custom); 2310b57cec5SDimitry Andric 2325ffd83dbSDimitry Andric // There is no i8x16.mul instruction 2335ffd83dbSDimitry Andric setOperationAction(ISD::MUL, MVT::v16i8, Expand); 2340b57cec5SDimitry Andric 235e8d8bef9SDimitry Andric // There is no vector conditional select instruction 2365ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, 2375ffd83dbSDimitry Andric MVT::v2f64}) 238e8d8bef9SDimitry Andric setOperationAction(ISD::SELECT_CC, T, Expand); 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric // Expand integer operations supported for scalars but not SIMD 241349cc55cSDimitry Andric for (auto Op : 242349cc55cSDimitry Andric {ISD::SDIV, ISD::UDIV, ISD::SREM, ISD::UREM, ISD::ROTL, ISD::ROTR}) 2435ffd83dbSDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) 2440b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 2450b57cec5SDimitry Andric 246480093f4SDimitry Andric // But we do have integer min and max operations 247480093f4SDimitry Andric for (auto Op : {ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX}) 248480093f4SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) 249480093f4SDimitry Andric setOperationAction(Op, T, Legal); 250480093f4SDimitry Andric 251349cc55cSDimitry Andric // And we have popcnt for i8x16. It can be used to expand ctlz/cttz. 252fe6060f1SDimitry Andric setOperationAction(ISD::CTPOP, MVT::v16i8, Legal); 253349cc55cSDimitry Andric setOperationAction(ISD::CTLZ, MVT::v16i8, Expand); 254349cc55cSDimitry Andric setOperationAction(ISD::CTTZ, MVT::v16i8, Expand); 255349cc55cSDimitry Andric 256349cc55cSDimitry Andric // Custom lower bit counting operations for other types to scalarize them. 257349cc55cSDimitry Andric for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP}) 258349cc55cSDimitry Andric for (auto T : {MVT::v8i16, MVT::v4i32, MVT::v2i64}) 259349cc55cSDimitry Andric setOperationAction(Op, T, Custom); 260fe6060f1SDimitry Andric 2610b57cec5SDimitry Andric // Expand float operations supported for scalars but not SIMD 262fe6060f1SDimitry Andric for (auto Op : {ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, 26306c3fb27SDimitry Andric ISD::FEXP, ISD::FEXP2}) 2645ffd83dbSDimitry Andric for (auto T : {MVT::v4f32, MVT::v2f64}) 2655ffd83dbSDimitry Andric setOperationAction(Op, T, Expand); 2660b57cec5SDimitry Andric 267fe6060f1SDimitry Andric // Unsigned comparison operations are unavailable for i64x2 vectors. 268fe6060f1SDimitry Andric for (auto CC : {ISD::SETUGT, ISD::SETUGE, ISD::SETULT, ISD::SETULE}) 269fe6060f1SDimitry Andric setCondCodeAction(CC, MVT::v2i64, Custom); 270480093f4SDimitry Andric 2715ffd83dbSDimitry Andric // 64x2 conversions are not in the spec 2725ffd83dbSDimitry Andric for (auto Op : 2735ffd83dbSDimitry Andric {ISD::SINT_TO_FP, ISD::UINT_TO_FP, ISD::FP_TO_SINT, ISD::FP_TO_UINT}) 2745ffd83dbSDimitry Andric for (auto T : {MVT::v2i64, MVT::v2f64}) 2755ffd83dbSDimitry Andric setOperationAction(Op, T, Expand); 276fe6060f1SDimitry Andric 277fe6060f1SDimitry Andric // But saturating fp_to_int converstions are 278fe6060f1SDimitry Andric for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}) 279fe6060f1SDimitry Andric setOperationAction(Op, MVT::v4i32, Custom); 28006c3fb27SDimitry Andric 28106c3fb27SDimitry Andric // Support vector extending 28206c3fb27SDimitry Andric for (auto T : MVT::integer_fixedlen_vector_valuetypes()) { 28306c3fb27SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_VECTOR_INREG, T, Custom); 28406c3fb27SDimitry Andric setOperationAction(ISD::ZERO_EXTEND_VECTOR_INREG, T, Custom); 28506c3fb27SDimitry Andric } 2860b57cec5SDimitry Andric } 2870b57cec5SDimitry Andric 2880b57cec5SDimitry Andric // As a special case, these operators use the type to mean the type to 2890b57cec5SDimitry Andric // sign-extend from. 2900b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); 2910b57cec5SDimitry Andric if (!Subtarget->hasSignExt()) { 2920b57cec5SDimitry Andric // Sign extends are legal only when extending a vector extract 2930b57cec5SDimitry Andric auto Action = Subtarget->hasSIMD128() ? Custom : Expand; 2940b57cec5SDimitry Andric for (auto T : {MVT::i8, MVT::i16, MVT::i32}) 2950b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, T, Action); 2960b57cec5SDimitry Andric } 2978bcb0991SDimitry Andric for (auto T : MVT::integer_fixedlen_vector_valuetypes()) 2980b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand); 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric // Dynamic stack allocation: use the default expansion. 3010b57cec5SDimitry Andric setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); 3020b57cec5SDimitry Andric setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); 3030b57cec5SDimitry Andric setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric setOperationAction(ISD::FrameIndex, MVT::i32, Custom); 3065ffd83dbSDimitry Andric setOperationAction(ISD::FrameIndex, MVT::i64, Custom); 3070b57cec5SDimitry Andric setOperationAction(ISD::CopyToReg, MVT::Other, Custom); 3080b57cec5SDimitry Andric 3090b57cec5SDimitry Andric // Expand these forms; we pattern-match the forms that we can handle in isel. 3100b57cec5SDimitry Andric for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) 3110b57cec5SDimitry Andric for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) 3120b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 3130b57cec5SDimitry Andric 3140b57cec5SDimitry Andric // We have custom switch handling. 3150b57cec5SDimitry Andric setOperationAction(ISD::BR_JT, MVT::Other, Custom); 3160b57cec5SDimitry Andric 3170b57cec5SDimitry Andric // WebAssembly doesn't have: 3180b57cec5SDimitry Andric // - Floating-point extending loads. 3190b57cec5SDimitry Andric // - Floating-point truncating stores. 3200b57cec5SDimitry Andric // - i1 extending loads. 3218bcb0991SDimitry Andric // - truncating SIMD stores and most extending loads 3220b57cec5SDimitry Andric setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); 3230b57cec5SDimitry Andric setTruncStoreAction(MVT::f64, MVT::f32, Expand); 3240b57cec5SDimitry Andric for (auto T : MVT::integer_valuetypes()) 3250b57cec5SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) 3260b57cec5SDimitry Andric setLoadExtAction(Ext, T, MVT::i1, Promote); 3270b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 3280b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64, MVT::v4f32, 3290b57cec5SDimitry Andric MVT::v2f64}) { 3308bcb0991SDimitry Andric for (auto MemT : MVT::fixedlen_vector_valuetypes()) { 3310b57cec5SDimitry Andric if (MVT(T) != MemT) { 3320b57cec5SDimitry Andric setTruncStoreAction(T, MemT, Expand); 3330b57cec5SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) 3340b57cec5SDimitry Andric setLoadExtAction(Ext, T, MemT, Expand); 3350b57cec5SDimitry Andric } 3360b57cec5SDimitry Andric } 3370b57cec5SDimitry Andric } 3388bcb0991SDimitry Andric // But some vector extending loads are legal 3398bcb0991SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) { 3408bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v8i16, MVT::v8i8, Legal); 3418bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v4i32, MVT::v4i16, Legal); 3428bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v2i64, MVT::v2i32, Legal); 3438bcb0991SDimitry Andric } 344349cc55cSDimitry Andric setLoadExtAction(ISD::EXTLOAD, MVT::v2f64, MVT::v2f32, Legal); 3458bcb0991SDimitry Andric } 3460b57cec5SDimitry Andric 3470b57cec5SDimitry Andric // Don't do anything clever with build_pairs 3480b57cec5SDimitry Andric setOperationAction(ISD::BUILD_PAIR, MVT::i64, Expand); 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric // Trap lowers to wasm unreachable 3510b57cec5SDimitry Andric setOperationAction(ISD::TRAP, MVT::Other, Legal); 3525ffd83dbSDimitry Andric setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); 3530b57cec5SDimitry Andric 3540b57cec5SDimitry Andric // Exception handling intrinsics 3550b57cec5SDimitry Andric setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); 356e8d8bef9SDimitry Andric setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); 3570b57cec5SDimitry Andric setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); 3580b57cec5SDimitry Andric 3590b57cec5SDimitry Andric setMaxAtomicSizeInBitsSupported(64); 3600b57cec5SDimitry Andric 3610b57cec5SDimitry Andric // Override the __gnu_f2h_ieee/__gnu_h2f_ieee names so that the f32 name is 3620b57cec5SDimitry Andric // consistent with the f64 and f128 names. 3630b57cec5SDimitry Andric setLibcallName(RTLIB::FPEXT_F16_F32, "__extendhfsf2"); 3640b57cec5SDimitry Andric setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2"); 3650b57cec5SDimitry Andric 3660b57cec5SDimitry Andric // Define the emscripten name for return address helper. 367e8d8bef9SDimitry Andric // TODO: when implementing other Wasm backends, make this generic or only do 3680b57cec5SDimitry Andric // this on emscripten depending on what they end up doing. 3690b57cec5SDimitry Andric setLibcallName(RTLIB::RETURN_ADDRESS, "emscripten_return_address"); 3700b57cec5SDimitry Andric 3710b57cec5SDimitry Andric // Always convert switches to br_tables unless there is only one case, which 3720b57cec5SDimitry Andric // is equivalent to a simple branch. This reduces code size for wasm, and we 3730b57cec5SDimitry Andric // defer possible jump table optimizations to the VM. 3740b57cec5SDimitry Andric setMinimumJumpTableEntries(2); 3750b57cec5SDimitry Andric } 3760b57cec5SDimitry Andric 377349cc55cSDimitry Andric MVT WebAssemblyTargetLowering::getPointerTy(const DataLayout &DL, 378349cc55cSDimitry Andric uint32_t AS) const { 379349cc55cSDimitry Andric if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) 380349cc55cSDimitry Andric return MVT::externref; 381349cc55cSDimitry Andric if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) 382349cc55cSDimitry Andric return MVT::funcref; 383349cc55cSDimitry Andric return TargetLowering::getPointerTy(DL, AS); 384349cc55cSDimitry Andric } 385349cc55cSDimitry Andric 386349cc55cSDimitry Andric MVT WebAssemblyTargetLowering::getPointerMemTy(const DataLayout &DL, 387349cc55cSDimitry Andric uint32_t AS) const { 388349cc55cSDimitry Andric if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) 389349cc55cSDimitry Andric return MVT::externref; 390349cc55cSDimitry Andric if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) 391349cc55cSDimitry Andric return MVT::funcref; 392349cc55cSDimitry Andric return TargetLowering::getPointerMemTy(DL, AS); 393349cc55cSDimitry Andric } 394349cc55cSDimitry Andric 3950b57cec5SDimitry Andric TargetLowering::AtomicExpansionKind 3960b57cec5SDimitry Andric WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { 3970b57cec5SDimitry Andric // We have wasm instructions for these 3980b57cec5SDimitry Andric switch (AI->getOperation()) { 3990b57cec5SDimitry Andric case AtomicRMWInst::Add: 4000b57cec5SDimitry Andric case AtomicRMWInst::Sub: 4010b57cec5SDimitry Andric case AtomicRMWInst::And: 4020b57cec5SDimitry Andric case AtomicRMWInst::Or: 4030b57cec5SDimitry Andric case AtomicRMWInst::Xor: 4040b57cec5SDimitry Andric case AtomicRMWInst::Xchg: 4050b57cec5SDimitry Andric return AtomicExpansionKind::None; 4060b57cec5SDimitry Andric default: 4070b57cec5SDimitry Andric break; 4080b57cec5SDimitry Andric } 4090b57cec5SDimitry Andric return AtomicExpansionKind::CmpXChg; 4100b57cec5SDimitry Andric } 4110b57cec5SDimitry Andric 412fe6060f1SDimitry Andric bool WebAssemblyTargetLowering::shouldScalarizeBinop(SDValue VecOp) const { 413fe6060f1SDimitry Andric // Implementation copied from X86TargetLowering. 414fe6060f1SDimitry Andric unsigned Opc = VecOp.getOpcode(); 415fe6060f1SDimitry Andric 416fe6060f1SDimitry Andric // Assume target opcodes can't be scalarized. 417fe6060f1SDimitry Andric // TODO - do we have any exceptions? 418fe6060f1SDimitry Andric if (Opc >= ISD::BUILTIN_OP_END) 419fe6060f1SDimitry Andric return false; 420fe6060f1SDimitry Andric 421fe6060f1SDimitry Andric // If the vector op is not supported, try to convert to scalar. 422fe6060f1SDimitry Andric EVT VecVT = VecOp.getValueType(); 423fe6060f1SDimitry Andric if (!isOperationLegalOrCustomOrPromote(Opc, VecVT)) 424fe6060f1SDimitry Andric return true; 425fe6060f1SDimitry Andric 426fe6060f1SDimitry Andric // If the vector op is supported, but the scalar op is not, the transform may 427fe6060f1SDimitry Andric // not be worthwhile. 428fe6060f1SDimitry Andric EVT ScalarVT = VecVT.getScalarType(); 429fe6060f1SDimitry Andric return isOperationLegalOrCustomOrPromote(Opc, ScalarVT); 430fe6060f1SDimitry Andric } 431fe6060f1SDimitry Andric 4320b57cec5SDimitry Andric FastISel *WebAssemblyTargetLowering::createFastISel( 4330b57cec5SDimitry Andric FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const { 4340b57cec5SDimitry Andric return WebAssembly::createFastISel(FuncInfo, LibInfo); 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, 4380b57cec5SDimitry Andric EVT VT) const { 4390b57cec5SDimitry Andric unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1); 4400b57cec5SDimitry Andric if (BitWidth > 1 && BitWidth < 8) 4410b57cec5SDimitry Andric BitWidth = 8; 4420b57cec5SDimitry Andric 4430b57cec5SDimitry Andric if (BitWidth > 64) { 4440b57cec5SDimitry Andric // The shift will be lowered to a libcall, and compiler-rt libcalls expect 4450b57cec5SDimitry Andric // the count to be an i32. 4460b57cec5SDimitry Andric BitWidth = 32; 4470b57cec5SDimitry Andric assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && 4480b57cec5SDimitry Andric "32-bit shift counts ought to be enough for anyone"); 4490b57cec5SDimitry Andric } 4500b57cec5SDimitry Andric 4510b57cec5SDimitry Andric MVT Result = MVT::getIntegerVT(BitWidth); 4520b57cec5SDimitry Andric assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE && 4530b57cec5SDimitry Andric "Unable to represent scalar shift amount type"); 4540b57cec5SDimitry Andric return Result; 4550b57cec5SDimitry Andric } 4560b57cec5SDimitry Andric 4570b57cec5SDimitry Andric // Lower an fp-to-int conversion operator from the LLVM opcode, which has an 4580b57cec5SDimitry Andric // undefined result on invalid/overflow, to the WebAssembly opcode, which 4590b57cec5SDimitry Andric // traps on invalid/overflow. 4600b57cec5SDimitry Andric static MachineBasicBlock *LowerFPToInt(MachineInstr &MI, DebugLoc DL, 4610b57cec5SDimitry Andric MachineBasicBlock *BB, 4620b57cec5SDimitry Andric const TargetInstrInfo &TII, 4630b57cec5SDimitry Andric bool IsUnsigned, bool Int64, 4640b57cec5SDimitry Andric bool Float64, unsigned LoweredOpcode) { 4650b57cec5SDimitry Andric MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); 4660b57cec5SDimitry Andric 4678bcb0991SDimitry Andric Register OutReg = MI.getOperand(0).getReg(); 4688bcb0991SDimitry Andric Register InReg = MI.getOperand(1).getReg(); 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; 4710b57cec5SDimitry Andric unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; 4720b57cec5SDimitry Andric unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; 4730b57cec5SDimitry Andric unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; 4740b57cec5SDimitry Andric unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; 4750b57cec5SDimitry Andric unsigned Eqz = WebAssembly::EQZ_I32; 4760b57cec5SDimitry Andric unsigned And = WebAssembly::AND_I32; 4770b57cec5SDimitry Andric int64_t Limit = Int64 ? INT64_MIN : INT32_MIN; 4780b57cec5SDimitry Andric int64_t Substitute = IsUnsigned ? 0 : Limit; 4790b57cec5SDimitry Andric double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; 4800b57cec5SDimitry Andric auto &Context = BB->getParent()->getFunction().getContext(); 4810b57cec5SDimitry Andric Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); 4820b57cec5SDimitry Andric 4830b57cec5SDimitry Andric const BasicBlock *LLVMBB = BB->getBasicBlock(); 4840b57cec5SDimitry Andric MachineFunction *F = BB->getParent(); 4850b57cec5SDimitry Andric MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB); 4860b57cec5SDimitry Andric MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVMBB); 4870b57cec5SDimitry Andric MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB); 4880b57cec5SDimitry Andric 4890b57cec5SDimitry Andric MachineFunction::iterator It = ++BB->getIterator(); 4900b57cec5SDimitry Andric F->insert(It, FalseMBB); 4910b57cec5SDimitry Andric F->insert(It, TrueMBB); 4920b57cec5SDimitry Andric F->insert(It, DoneMBB); 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric // Transfer the remainder of BB and its successor edges to DoneMBB. 4950b57cec5SDimitry Andric DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end()); 4960b57cec5SDimitry Andric DoneMBB->transferSuccessorsAndUpdatePHIs(BB); 4970b57cec5SDimitry Andric 4980b57cec5SDimitry Andric BB->addSuccessor(TrueMBB); 4990b57cec5SDimitry Andric BB->addSuccessor(FalseMBB); 5000b57cec5SDimitry Andric TrueMBB->addSuccessor(DoneMBB); 5010b57cec5SDimitry Andric FalseMBB->addSuccessor(DoneMBB); 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; 5040b57cec5SDimitry Andric Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 5050b57cec5SDimitry Andric Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 5060b57cec5SDimitry Andric CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 5070b57cec5SDimitry Andric EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 5080b57cec5SDimitry Andric FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 5090b57cec5SDimitry Andric TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 5100b57cec5SDimitry Andric 5110b57cec5SDimitry Andric MI.eraseFromParent(); 5120b57cec5SDimitry Andric // For signed numbers, we can do a single comparison to determine whether 5130b57cec5SDimitry Andric // fabs(x) is within range. 5140b57cec5SDimitry Andric if (IsUnsigned) { 5150b57cec5SDimitry Andric Tmp0 = InReg; 5160b57cec5SDimitry Andric } else { 5170b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg); 5180b57cec5SDimitry Andric } 5190b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(FConst), Tmp1) 5200b57cec5SDimitry Andric .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal))); 5210b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1); 5220b57cec5SDimitry Andric 5230b57cec5SDimitry Andric // For unsigned numbers, we have to do a separate comparison with zero. 5240b57cec5SDimitry Andric if (IsUnsigned) { 5250b57cec5SDimitry Andric Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 5268bcb0991SDimitry Andric Register SecondCmpReg = 5270b57cec5SDimitry Andric MRI.createVirtualRegister(&WebAssembly::I32RegClass); 5288bcb0991SDimitry Andric Register AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 5290b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(FConst), Tmp1) 5300b57cec5SDimitry Andric .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0))); 5310b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1); 5320b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg); 5330b57cec5SDimitry Andric CmpReg = AndReg; 5340b57cec5SDimitry Andric } 5350b57cec5SDimitry Andric 5360b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg); 5370b57cec5SDimitry Andric 5380b57cec5SDimitry Andric // Create the CFG diamond to select between doing the conversion or using 5390b57cec5SDimitry Andric // the substitute value. 5400b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg); 5410b57cec5SDimitry Andric BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg); 5420b57cec5SDimitry Andric BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); 5430b57cec5SDimitry Andric BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute); 5440b57cec5SDimitry Andric BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) 5450b57cec5SDimitry Andric .addReg(FalseReg) 5460b57cec5SDimitry Andric .addMBB(FalseMBB) 5470b57cec5SDimitry Andric .addReg(TrueReg) 5480b57cec5SDimitry Andric .addMBB(TrueMBB); 5490b57cec5SDimitry Andric 5500b57cec5SDimitry Andric return DoneMBB; 5510b57cec5SDimitry Andric } 5520b57cec5SDimitry Andric 553fe6060f1SDimitry Andric static MachineBasicBlock * 554fe6060f1SDimitry Andric LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, 555fe6060f1SDimitry Andric const WebAssemblySubtarget *Subtarget, 5565ffd83dbSDimitry Andric const TargetInstrInfo &TII) { 5575ffd83dbSDimitry Andric MachineInstr &CallParams = *CallResults.getPrevNode(); 5585ffd83dbSDimitry Andric assert(CallParams.getOpcode() == WebAssembly::CALL_PARAMS); 5595ffd83dbSDimitry Andric assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS || 5605ffd83dbSDimitry Andric CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS); 5615ffd83dbSDimitry Andric 56206c3fb27SDimitry Andric bool IsIndirect = 56306c3fb27SDimitry Andric CallParams.getOperand(0).isReg() || CallParams.getOperand(0).isFI(); 5645ffd83dbSDimitry Andric bool IsRetCall = CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS; 5655ffd83dbSDimitry Andric 566fe6060f1SDimitry Andric bool IsFuncrefCall = false; 56706c3fb27SDimitry Andric if (IsIndirect && CallParams.getOperand(0).isReg()) { 568fe6060f1SDimitry Andric Register Reg = CallParams.getOperand(0).getReg(); 569fe6060f1SDimitry Andric const MachineFunction *MF = BB->getParent(); 570fe6060f1SDimitry Andric const MachineRegisterInfo &MRI = MF->getRegInfo(); 571fe6060f1SDimitry Andric const TargetRegisterClass *TRC = MRI.getRegClass(Reg); 572fe6060f1SDimitry Andric IsFuncrefCall = (TRC == &WebAssembly::FUNCREFRegClass); 573fe6060f1SDimitry Andric assert(!IsFuncrefCall || Subtarget->hasReferenceTypes()); 574fe6060f1SDimitry Andric } 575fe6060f1SDimitry Andric 5765ffd83dbSDimitry Andric unsigned CallOp; 5775ffd83dbSDimitry Andric if (IsIndirect && IsRetCall) { 5785ffd83dbSDimitry Andric CallOp = WebAssembly::RET_CALL_INDIRECT; 5795ffd83dbSDimitry Andric } else if (IsIndirect) { 5805ffd83dbSDimitry Andric CallOp = WebAssembly::CALL_INDIRECT; 5815ffd83dbSDimitry Andric } else if (IsRetCall) { 5825ffd83dbSDimitry Andric CallOp = WebAssembly::RET_CALL; 5835ffd83dbSDimitry Andric } else { 5845ffd83dbSDimitry Andric CallOp = WebAssembly::CALL; 5855ffd83dbSDimitry Andric } 5865ffd83dbSDimitry Andric 5875ffd83dbSDimitry Andric MachineFunction &MF = *BB->getParent(); 5885ffd83dbSDimitry Andric const MCInstrDesc &MCID = TII.get(CallOp); 5895ffd83dbSDimitry Andric MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); 5905ffd83dbSDimitry Andric 5915ffd83dbSDimitry Andric // Move the function pointer to the end of the arguments for indirect calls 5925ffd83dbSDimitry Andric if (IsIndirect) { 5935ffd83dbSDimitry Andric auto FnPtr = CallParams.getOperand(0); 59481ad6265SDimitry Andric CallParams.removeOperand(0); 595349cc55cSDimitry Andric 596349cc55cSDimitry Andric // For funcrefs, call_indirect is done through __funcref_call_table and the 597972a253aSDimitry Andric // funcref is always installed in slot 0 of the table, therefore instead of 598972a253aSDimitry Andric // having the function pointer added at the end of the params list, a zero 599972a253aSDimitry Andric // (the index in 600349cc55cSDimitry Andric // __funcref_call_table is added). 601349cc55cSDimitry Andric if (IsFuncrefCall) { 602349cc55cSDimitry Andric Register RegZero = 603349cc55cSDimitry Andric MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); 604349cc55cSDimitry Andric MachineInstrBuilder MIBC0 = 605349cc55cSDimitry Andric BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0); 606349cc55cSDimitry Andric 607349cc55cSDimitry Andric BB->insert(CallResults.getIterator(), MIBC0); 608349cc55cSDimitry Andric MachineInstrBuilder(MF, CallParams).addReg(RegZero); 609349cc55cSDimitry Andric } else 6105ffd83dbSDimitry Andric CallParams.addOperand(FnPtr); 6115ffd83dbSDimitry Andric } 6125ffd83dbSDimitry Andric 6135ffd83dbSDimitry Andric for (auto Def : CallResults.defs()) 6145ffd83dbSDimitry Andric MIB.add(Def); 6155ffd83dbSDimitry Andric 6165ffd83dbSDimitry Andric if (IsIndirect) { 617fe6060f1SDimitry Andric // Placeholder for the type index. 6185ffd83dbSDimitry Andric MIB.addImm(0); 619fe6060f1SDimitry Andric // The table into which this call_indirect indexes. 620fe6060f1SDimitry Andric MCSymbolWasm *Table = IsFuncrefCall 621fe6060f1SDimitry Andric ? WebAssembly::getOrCreateFuncrefCallTableSymbol( 622fe6060f1SDimitry Andric MF.getContext(), Subtarget) 623fe6060f1SDimitry Andric : WebAssembly::getOrCreateFunctionTableSymbol( 624fe6060f1SDimitry Andric MF.getContext(), Subtarget); 625fe6060f1SDimitry Andric if (Subtarget->hasReferenceTypes()) { 626fe6060f1SDimitry Andric MIB.addSym(Table); 627fe6060f1SDimitry Andric } else { 628fe6060f1SDimitry Andric // For the MVP there is at most one table whose number is 0, but we can't 629fe6060f1SDimitry Andric // write a table symbol or issue relocations. Instead we just ensure the 630fe6060f1SDimitry Andric // table is live and write a zero. 631fe6060f1SDimitry Andric Table->setNoStrip(); 6325ffd83dbSDimitry Andric MIB.addImm(0); 633fe6060f1SDimitry Andric } 6345ffd83dbSDimitry Andric } 6355ffd83dbSDimitry Andric 6365ffd83dbSDimitry Andric for (auto Use : CallParams.uses()) 6375ffd83dbSDimitry Andric MIB.add(Use); 6385ffd83dbSDimitry Andric 6395ffd83dbSDimitry Andric BB->insert(CallResults.getIterator(), MIB); 6405ffd83dbSDimitry Andric CallParams.eraseFromParent(); 6415ffd83dbSDimitry Andric CallResults.eraseFromParent(); 6425ffd83dbSDimitry Andric 643fe6060f1SDimitry Andric // If this is a funcref call, to avoid hidden GC roots, we need to clear the 644fe6060f1SDimitry Andric // table slot with ref.null upon call_indirect return. 645fe6060f1SDimitry Andric // 646fe6060f1SDimitry Andric // This generates the following code, which comes right after a call_indirect 647fe6060f1SDimitry Andric // of a funcref: 648fe6060f1SDimitry Andric // 649fe6060f1SDimitry Andric // i32.const 0 650fe6060f1SDimitry Andric // ref.null func 651fe6060f1SDimitry Andric // table.set __funcref_call_table 652fe6060f1SDimitry Andric if (IsIndirect && IsFuncrefCall) { 653fe6060f1SDimitry Andric MCSymbolWasm *Table = WebAssembly::getOrCreateFuncrefCallTableSymbol( 654fe6060f1SDimitry Andric MF.getContext(), Subtarget); 655fe6060f1SDimitry Andric Register RegZero = 656fe6060f1SDimitry Andric MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); 657fe6060f1SDimitry Andric MachineInstr *Const0 = 658fe6060f1SDimitry Andric BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0); 659fe6060f1SDimitry Andric BB->insertAfter(MIB.getInstr()->getIterator(), Const0); 660fe6060f1SDimitry Andric 661fe6060f1SDimitry Andric Register RegFuncref = 662fe6060f1SDimitry Andric MF.getRegInfo().createVirtualRegister(&WebAssembly::FUNCREFRegClass); 663fe6060f1SDimitry Andric MachineInstr *RefNull = 6640eae32dcSDimitry Andric BuildMI(MF, DL, TII.get(WebAssembly::REF_NULL_FUNCREF), RegFuncref); 665fe6060f1SDimitry Andric BB->insertAfter(Const0->getIterator(), RefNull); 666fe6060f1SDimitry Andric 667fe6060f1SDimitry Andric MachineInstr *TableSet = 668fe6060f1SDimitry Andric BuildMI(MF, DL, TII.get(WebAssembly::TABLE_SET_FUNCREF)) 669fe6060f1SDimitry Andric .addSym(Table) 670fe6060f1SDimitry Andric .addReg(RegZero) 671fe6060f1SDimitry Andric .addReg(RegFuncref); 672fe6060f1SDimitry Andric BB->insertAfter(RefNull->getIterator(), TableSet); 673fe6060f1SDimitry Andric } 674fe6060f1SDimitry Andric 6755ffd83dbSDimitry Andric return BB; 6765ffd83dbSDimitry Andric } 6775ffd83dbSDimitry Andric 6780b57cec5SDimitry Andric MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter( 6790b57cec5SDimitry Andric MachineInstr &MI, MachineBasicBlock *BB) const { 6800b57cec5SDimitry Andric const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); 6810b57cec5SDimitry Andric DebugLoc DL = MI.getDebugLoc(); 6820b57cec5SDimitry Andric 6830b57cec5SDimitry Andric switch (MI.getOpcode()) { 6840b57cec5SDimitry Andric default: 6850b57cec5SDimitry Andric llvm_unreachable("Unexpected instr type to insert"); 6860b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I32_F32: 6870b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, false, false, 6880b57cec5SDimitry Andric WebAssembly::I32_TRUNC_S_F32); 6890b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I32_F32: 6900b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, false, false, 6910b57cec5SDimitry Andric WebAssembly::I32_TRUNC_U_F32); 6920b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I64_F32: 6930b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, true, false, 6940b57cec5SDimitry Andric WebAssembly::I64_TRUNC_S_F32); 6950b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I64_F32: 6960b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, true, false, 6970b57cec5SDimitry Andric WebAssembly::I64_TRUNC_U_F32); 6980b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I32_F64: 6990b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, false, true, 7000b57cec5SDimitry Andric WebAssembly::I32_TRUNC_S_F64); 7010b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I32_F64: 7020b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, false, true, 7030b57cec5SDimitry Andric WebAssembly::I32_TRUNC_U_F64); 7040b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I64_F64: 7050b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, true, true, 7060b57cec5SDimitry Andric WebAssembly::I64_TRUNC_S_F64); 7070b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I64_F64: 7080b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, true, true, 7090b57cec5SDimitry Andric WebAssembly::I64_TRUNC_U_F64); 7105ffd83dbSDimitry Andric case WebAssembly::CALL_RESULTS: 7115ffd83dbSDimitry Andric case WebAssembly::RET_CALL_RESULTS: 712fe6060f1SDimitry Andric return LowerCallResults(MI, DL, BB, Subtarget, TII); 7130b57cec5SDimitry Andric } 7140b57cec5SDimitry Andric } 7150b57cec5SDimitry Andric 7160b57cec5SDimitry Andric const char * 7170b57cec5SDimitry Andric WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { 7180b57cec5SDimitry Andric switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) { 7190b57cec5SDimitry Andric case WebAssemblyISD::FIRST_NUMBER: 720480093f4SDimitry Andric case WebAssemblyISD::FIRST_MEM_OPCODE: 7210b57cec5SDimitry Andric break; 7220b57cec5SDimitry Andric #define HANDLE_NODETYPE(NODE) \ 7230b57cec5SDimitry Andric case WebAssemblyISD::NODE: \ 7240b57cec5SDimitry Andric return "WebAssemblyISD::" #NODE; 725480093f4SDimitry Andric #define HANDLE_MEM_NODETYPE(NODE) HANDLE_NODETYPE(NODE) 7260b57cec5SDimitry Andric #include "WebAssemblyISD.def" 727480093f4SDimitry Andric #undef HANDLE_MEM_NODETYPE 7280b57cec5SDimitry Andric #undef HANDLE_NODETYPE 7290b57cec5SDimitry Andric } 7300b57cec5SDimitry Andric return nullptr; 7310b57cec5SDimitry Andric } 7320b57cec5SDimitry Andric 7330b57cec5SDimitry Andric std::pair<unsigned, const TargetRegisterClass *> 7340b57cec5SDimitry Andric WebAssemblyTargetLowering::getRegForInlineAsmConstraint( 7350b57cec5SDimitry Andric const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { 7360b57cec5SDimitry Andric // First, see if this is a constraint that directly corresponds to a 7370b57cec5SDimitry Andric // WebAssembly register class. 7380b57cec5SDimitry Andric if (Constraint.size() == 1) { 7390b57cec5SDimitry Andric switch (Constraint[0]) { 7400b57cec5SDimitry Andric case 'r': 7410b57cec5SDimitry Andric assert(VT != MVT::iPTR && "Pointer MVT not expected here"); 7420b57cec5SDimitry Andric if (Subtarget->hasSIMD128() && VT.isVector()) { 7430b57cec5SDimitry Andric if (VT.getSizeInBits() == 128) 7440b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::V128RegClass); 7450b57cec5SDimitry Andric } 7460b57cec5SDimitry Andric if (VT.isInteger() && !VT.isVector()) { 7470b57cec5SDimitry Andric if (VT.getSizeInBits() <= 32) 7480b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::I32RegClass); 7490b57cec5SDimitry Andric if (VT.getSizeInBits() <= 64) 7500b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::I64RegClass); 7510b57cec5SDimitry Andric } 752e8d8bef9SDimitry Andric if (VT.isFloatingPoint() && !VT.isVector()) { 753e8d8bef9SDimitry Andric switch (VT.getSizeInBits()) { 754e8d8bef9SDimitry Andric case 32: 755e8d8bef9SDimitry Andric return std::make_pair(0U, &WebAssembly::F32RegClass); 756e8d8bef9SDimitry Andric case 64: 757e8d8bef9SDimitry Andric return std::make_pair(0U, &WebAssembly::F64RegClass); 758e8d8bef9SDimitry Andric default: 759e8d8bef9SDimitry Andric break; 760e8d8bef9SDimitry Andric } 761e8d8bef9SDimitry Andric } 7620b57cec5SDimitry Andric break; 7630b57cec5SDimitry Andric default: 7640b57cec5SDimitry Andric break; 7650b57cec5SDimitry Andric } 7660b57cec5SDimitry Andric } 7670b57cec5SDimitry Andric 7680b57cec5SDimitry Andric return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); 7690b57cec5SDimitry Andric } 7700b57cec5SDimitry Andric 771bdd1243dSDimitry Andric bool WebAssemblyTargetLowering::isCheapToSpeculateCttz(Type *Ty) const { 7720b57cec5SDimitry Andric // Assume ctz is a relatively cheap operation. 7730b57cec5SDimitry Andric return true; 7740b57cec5SDimitry Andric } 7750b57cec5SDimitry Andric 776bdd1243dSDimitry Andric bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz(Type *Ty) const { 7770b57cec5SDimitry Andric // Assume clz is a relatively cheap operation. 7780b57cec5SDimitry Andric return true; 7790b57cec5SDimitry Andric } 7800b57cec5SDimitry Andric 7810b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL, 7820b57cec5SDimitry Andric const AddrMode &AM, 7830b57cec5SDimitry Andric Type *Ty, unsigned AS, 7840b57cec5SDimitry Andric Instruction *I) const { 7850b57cec5SDimitry Andric // WebAssembly offsets are added as unsigned without wrapping. The 7860b57cec5SDimitry Andric // isLegalAddressingMode gives us no way to determine if wrapping could be 7870b57cec5SDimitry Andric // happening, so we approximate this by accepting only non-negative offsets. 7880b57cec5SDimitry Andric if (AM.BaseOffs < 0) 7890b57cec5SDimitry Andric return false; 7900b57cec5SDimitry Andric 7910b57cec5SDimitry Andric // WebAssembly has no scale register operands. 7920b57cec5SDimitry Andric if (AM.Scale != 0) 7930b57cec5SDimitry Andric return false; 7940b57cec5SDimitry Andric 7950b57cec5SDimitry Andric // Everything else is legal. 7960b57cec5SDimitry Andric return true; 7970b57cec5SDimitry Andric } 7980b57cec5SDimitry Andric 7990b57cec5SDimitry Andric bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses( 800fe6060f1SDimitry Andric EVT /*VT*/, unsigned /*AddrSpace*/, Align /*Align*/, 801bdd1243dSDimitry Andric MachineMemOperand::Flags /*Flags*/, unsigned *Fast) const { 8020b57cec5SDimitry Andric // WebAssembly supports unaligned accesses, though it should be declared 8030b57cec5SDimitry Andric // with the p2align attribute on loads and stores which do so, and there 8040b57cec5SDimitry Andric // may be a performance impact. We tell LLVM they're "fast" because 8050b57cec5SDimitry Andric // for the kinds of things that LLVM uses this for (merging adjacent stores 8060b57cec5SDimitry Andric // of constants, etc.), WebAssembly implementations will either want the 8070b57cec5SDimitry Andric // unaligned access or they'll split anyway. 8080b57cec5SDimitry Andric if (Fast) 809bdd1243dSDimitry Andric *Fast = 1; 8100b57cec5SDimitry Andric return true; 8110b57cec5SDimitry Andric } 8120b57cec5SDimitry Andric 8130b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, 8140b57cec5SDimitry Andric AttributeList Attr) const { 8150b57cec5SDimitry Andric // The current thinking is that wasm engines will perform this optimization, 8160b57cec5SDimitry Andric // so we can save on code size. 8170b57cec5SDimitry Andric return true; 8180b57cec5SDimitry Andric } 8190b57cec5SDimitry Andric 8208bcb0991SDimitry Andric bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { 82116d6b3b3SDimitry Andric EVT ExtT = ExtVal.getValueType(); 82216d6b3b3SDimitry Andric EVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getValueType(0); 8238bcb0991SDimitry Andric return (ExtT == MVT::v8i16 && MemT == MVT::v8i8) || 8248bcb0991SDimitry Andric (ExtT == MVT::v4i32 && MemT == MVT::v4i16) || 8258bcb0991SDimitry Andric (ExtT == MVT::v2i64 && MemT == MVT::v2i32); 8268bcb0991SDimitry Andric } 8278bcb0991SDimitry Andric 828349cc55cSDimitry Andric bool WebAssemblyTargetLowering::isOffsetFoldingLegal( 829349cc55cSDimitry Andric const GlobalAddressSDNode *GA) const { 830349cc55cSDimitry Andric // Wasm doesn't support function addresses with offsets 831349cc55cSDimitry Andric const GlobalValue *GV = GA->getGlobal(); 832349cc55cSDimitry Andric return isa<Function>(GV) ? false : TargetLowering::isOffsetFoldingLegal(GA); 833349cc55cSDimitry Andric } 834349cc55cSDimitry Andric 8355f757f3fSDimitry Andric bool WebAssemblyTargetLowering::shouldSinkOperands( 8365f757f3fSDimitry Andric Instruction *I, SmallVectorImpl<Use *> &Ops) const { 8375f757f3fSDimitry Andric using namespace llvm::PatternMatch; 8385f757f3fSDimitry Andric 8395f757f3fSDimitry Andric if (!I->getType()->isVectorTy() || !I->isShift()) 8405f757f3fSDimitry Andric return false; 8415f757f3fSDimitry Andric 8425f757f3fSDimitry Andric Value *V = I->getOperand(1); 8435f757f3fSDimitry Andric // We dont need to sink constant splat. 8445f757f3fSDimitry Andric if (dyn_cast<Constant>(V)) 8455f757f3fSDimitry Andric return false; 8465f757f3fSDimitry Andric 8475f757f3fSDimitry Andric if (match(V, m_Shuffle(m_InsertElt(m_Value(), m_Value(), m_ZeroInt()), 8485f757f3fSDimitry Andric m_Value(), m_ZeroMask()))) { 8495f757f3fSDimitry Andric // Sink insert 8505f757f3fSDimitry Andric Ops.push_back(&cast<Instruction>(V)->getOperandUse(0)); 8515f757f3fSDimitry Andric // Sink shuffle 8525f757f3fSDimitry Andric Ops.push_back(&I->getOperandUse(1)); 8535f757f3fSDimitry Andric return true; 8545f757f3fSDimitry Andric } 8555f757f3fSDimitry Andric 8565f757f3fSDimitry Andric return false; 8575f757f3fSDimitry Andric } 8585f757f3fSDimitry Andric 8590b57cec5SDimitry Andric EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL, 8600b57cec5SDimitry Andric LLVMContext &C, 8610b57cec5SDimitry Andric EVT VT) const { 8620b57cec5SDimitry Andric if (VT.isVector()) 8630b57cec5SDimitry Andric return VT.changeVectorElementTypeToInteger(); 8640b57cec5SDimitry Andric 8655ffd83dbSDimitry Andric // So far, all branch instructions in Wasm take an I32 condition. 8665ffd83dbSDimitry Andric // The default TargetLowering::getSetCCResultType returns the pointer size, 8675ffd83dbSDimitry Andric // which would be useful to reduce instruction counts when testing 8685ffd83dbSDimitry Andric // against 64-bit pointers/values if at some point Wasm supports that. 8695ffd83dbSDimitry Andric return EVT::getIntegerVT(C, 32); 8700b57cec5SDimitry Andric } 8710b57cec5SDimitry Andric 8720b57cec5SDimitry Andric bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, 8730b57cec5SDimitry Andric const CallInst &I, 8740b57cec5SDimitry Andric MachineFunction &MF, 8750b57cec5SDimitry Andric unsigned Intrinsic) const { 8760b57cec5SDimitry Andric switch (Intrinsic) { 877e8d8bef9SDimitry Andric case Intrinsic::wasm_memory_atomic_notify: 8780b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 8790b57cec5SDimitry Andric Info.memVT = MVT::i32; 8800b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 8810b57cec5SDimitry Andric Info.offset = 0; 8828bcb0991SDimitry Andric Info.align = Align(4); 8830b57cec5SDimitry Andric // atomic.notify instruction does not really load the memory specified with 8840b57cec5SDimitry Andric // this argument, but MachineMemOperand should either be load or store, so 8850b57cec5SDimitry Andric // we set this to a load. 8860b57cec5SDimitry Andric // FIXME Volatile isn't really correct, but currently all LLVM atomic 8870b57cec5SDimitry Andric // instructions are treated as volatiles in the backend, so we should be 8880b57cec5SDimitry Andric // consistent. The same applies for wasm_atomic_wait intrinsics too. 8890b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 8900b57cec5SDimitry Andric return true; 891e8d8bef9SDimitry Andric case Intrinsic::wasm_memory_atomic_wait32: 8920b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 8930b57cec5SDimitry Andric Info.memVT = MVT::i32; 8940b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 8950b57cec5SDimitry Andric Info.offset = 0; 8968bcb0991SDimitry Andric Info.align = Align(4); 8970b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 8980b57cec5SDimitry Andric return true; 899e8d8bef9SDimitry Andric case Intrinsic::wasm_memory_atomic_wait64: 9000b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 9010b57cec5SDimitry Andric Info.memVT = MVT::i64; 9020b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 9030b57cec5SDimitry Andric Info.offset = 0; 9048bcb0991SDimitry Andric Info.align = Align(8); 9050b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 9060b57cec5SDimitry Andric return true; 907*0fca6ea1SDimitry Andric case Intrinsic::wasm_loadf16_f32: 908*0fca6ea1SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 909*0fca6ea1SDimitry Andric Info.memVT = MVT::f16; 910*0fca6ea1SDimitry Andric Info.ptrVal = I.getArgOperand(0); 911*0fca6ea1SDimitry Andric Info.offset = 0; 912*0fca6ea1SDimitry Andric Info.align = Align(2); 913*0fca6ea1SDimitry Andric Info.flags = MachineMemOperand::MOLoad; 914*0fca6ea1SDimitry Andric return true; 915*0fca6ea1SDimitry Andric case Intrinsic::wasm_storef16_f32: 916*0fca6ea1SDimitry Andric Info.opc = ISD::INTRINSIC_VOID; 917*0fca6ea1SDimitry Andric Info.memVT = MVT::f16; 918*0fca6ea1SDimitry Andric Info.ptrVal = I.getArgOperand(1); 919*0fca6ea1SDimitry Andric Info.offset = 0; 920*0fca6ea1SDimitry Andric Info.align = Align(2); 921*0fca6ea1SDimitry Andric Info.flags = MachineMemOperand::MOStore; 922*0fca6ea1SDimitry Andric return true; 9230b57cec5SDimitry Andric default: 9240b57cec5SDimitry Andric return false; 9250b57cec5SDimitry Andric } 9260b57cec5SDimitry Andric } 9270b57cec5SDimitry Andric 928349cc55cSDimitry Andric void WebAssemblyTargetLowering::computeKnownBitsForTargetNode( 929349cc55cSDimitry Andric const SDValue Op, KnownBits &Known, const APInt &DemandedElts, 930349cc55cSDimitry Andric const SelectionDAG &DAG, unsigned Depth) const { 931349cc55cSDimitry Andric switch (Op.getOpcode()) { 932349cc55cSDimitry Andric default: 933349cc55cSDimitry Andric break; 934349cc55cSDimitry Andric case ISD::INTRINSIC_WO_CHAIN: { 935349cc55cSDimitry Andric unsigned IntNo = Op.getConstantOperandVal(0); 936349cc55cSDimitry Andric switch (IntNo) { 937349cc55cSDimitry Andric default: 938349cc55cSDimitry Andric break; 939349cc55cSDimitry Andric case Intrinsic::wasm_bitmask: { 940349cc55cSDimitry Andric unsigned BitWidth = Known.getBitWidth(); 941349cc55cSDimitry Andric EVT VT = Op.getOperand(1).getSimpleValueType(); 942349cc55cSDimitry Andric unsigned PossibleBits = VT.getVectorNumElements(); 943349cc55cSDimitry Andric APInt ZeroMask = APInt::getHighBitsSet(BitWidth, BitWidth - PossibleBits); 944349cc55cSDimitry Andric Known.Zero |= ZeroMask; 945349cc55cSDimitry Andric break; 946349cc55cSDimitry Andric } 947349cc55cSDimitry Andric } 948349cc55cSDimitry Andric } 949349cc55cSDimitry Andric } 950349cc55cSDimitry Andric } 951349cc55cSDimitry Andric 952349cc55cSDimitry Andric TargetLoweringBase::LegalizeTypeAction 953349cc55cSDimitry Andric WebAssemblyTargetLowering::getPreferredVectorAction(MVT VT) const { 954349cc55cSDimitry Andric if (VT.isFixedLengthVector()) { 955349cc55cSDimitry Andric MVT EltVT = VT.getVectorElementType(); 956349cc55cSDimitry Andric // We have legal vector types with these lane types, so widening the 957349cc55cSDimitry Andric // vector would let us use some of the lanes directly without having to 958349cc55cSDimitry Andric // extend or truncate values. 959349cc55cSDimitry Andric if (EltVT == MVT::i8 || EltVT == MVT::i16 || EltVT == MVT::i32 || 960349cc55cSDimitry Andric EltVT == MVT::i64 || EltVT == MVT::f32 || EltVT == MVT::f64) 961349cc55cSDimitry Andric return TypeWidenVector; 962349cc55cSDimitry Andric } 963349cc55cSDimitry Andric 964349cc55cSDimitry Andric return TargetLoweringBase::getPreferredVectorAction(VT); 965349cc55cSDimitry Andric } 966349cc55cSDimitry Andric 96781ad6265SDimitry Andric bool WebAssemblyTargetLowering::shouldSimplifyDemandedVectorElts( 96881ad6265SDimitry Andric SDValue Op, const TargetLoweringOpt &TLO) const { 96981ad6265SDimitry Andric // ISel process runs DAGCombiner after legalization; this step is called 97081ad6265SDimitry Andric // SelectionDAG optimization phase. This post-legalization combining process 97181ad6265SDimitry Andric // runs DAGCombiner on each node, and if there was a change to be made, 97281ad6265SDimitry Andric // re-runs legalization again on it and its user nodes to make sure 97381ad6265SDimitry Andric // everythiing is in a legalized state. 97481ad6265SDimitry Andric // 97581ad6265SDimitry Andric // The legalization calls lowering routines, and we do our custom lowering for 97681ad6265SDimitry Andric // build_vectors (LowerBUILD_VECTOR), which converts undef vector elements 97781ad6265SDimitry Andric // into zeros. But there is a set of routines in DAGCombiner that turns unused 97881ad6265SDimitry Andric // (= not demanded) nodes into undef, among which SimplifyDemandedVectorElts 97981ad6265SDimitry Andric // turns unused vector elements into undefs. But this routine does not work 98081ad6265SDimitry Andric // with our custom LowerBUILD_VECTOR, which turns undefs into zeros. This 98181ad6265SDimitry Andric // combination can result in a infinite loop, in which undefs are converted to 98281ad6265SDimitry Andric // zeros in legalization and back to undefs in combining. 98381ad6265SDimitry Andric // 98481ad6265SDimitry Andric // So after DAG is legalized, we prevent SimplifyDemandedVectorElts from 98581ad6265SDimitry Andric // running for build_vectors. 98681ad6265SDimitry Andric if (Op.getOpcode() == ISD::BUILD_VECTOR && TLO.LegalOps && TLO.LegalTys) 98781ad6265SDimitry Andric return false; 98881ad6265SDimitry Andric return true; 98981ad6265SDimitry Andric } 99081ad6265SDimitry Andric 9910b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9920b57cec5SDimitry Andric // WebAssembly Lowering private implementation. 9930b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9940b57cec5SDimitry Andric 9950b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9960b57cec5SDimitry Andric // Lowering Code 9970b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9980b57cec5SDimitry Andric 9990b57cec5SDimitry Andric static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) { 10000b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 10010b57cec5SDimitry Andric DAG.getContext()->diagnose( 10020b57cec5SDimitry Andric DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); 10030b57cec5SDimitry Andric } 10040b57cec5SDimitry Andric 10050b57cec5SDimitry Andric // Test whether the given calling convention is supported. 10060b57cec5SDimitry Andric static bool callingConvSupported(CallingConv::ID CallConv) { 10070b57cec5SDimitry Andric // We currently support the language-independent target-independent 10080b57cec5SDimitry Andric // conventions. We don't yet have a way to annotate calls with properties like 10090b57cec5SDimitry Andric // "cold", and we don't have any call-clobbered registers, so these are mostly 10100b57cec5SDimitry Andric // all handled the same. 10110b57cec5SDimitry Andric return CallConv == CallingConv::C || CallConv == CallingConv::Fast || 10120b57cec5SDimitry Andric CallConv == CallingConv::Cold || 10130b57cec5SDimitry Andric CallConv == CallingConv::PreserveMost || 10140b57cec5SDimitry Andric CallConv == CallingConv::PreserveAll || 10158bcb0991SDimitry Andric CallConv == CallingConv::CXX_FAST_TLS || 10165ffd83dbSDimitry Andric CallConv == CallingConv::WASM_EmscriptenInvoke || 10175ffd83dbSDimitry Andric CallConv == CallingConv::Swift; 10180b57cec5SDimitry Andric } 10190b57cec5SDimitry Andric 10200b57cec5SDimitry Andric SDValue 10210b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, 10220b57cec5SDimitry Andric SmallVectorImpl<SDValue> &InVals) const { 10230b57cec5SDimitry Andric SelectionDAG &DAG = CLI.DAG; 10240b57cec5SDimitry Andric SDLoc DL = CLI.DL; 10250b57cec5SDimitry Andric SDValue Chain = CLI.Chain; 10260b57cec5SDimitry Andric SDValue Callee = CLI.Callee; 10270b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 10280b57cec5SDimitry Andric auto Layout = MF.getDataLayout(); 10290b57cec5SDimitry Andric 10300b57cec5SDimitry Andric CallingConv::ID CallConv = CLI.CallConv; 10310b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 10320b57cec5SDimitry Andric fail(DL, DAG, 10330b57cec5SDimitry Andric "WebAssembly doesn't support language-specific or target-specific " 10340b57cec5SDimitry Andric "calling conventions yet"); 10350b57cec5SDimitry Andric if (CLI.IsPatchPoint) 10360b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support patch point yet"); 10370b57cec5SDimitry Andric 10388bcb0991SDimitry Andric if (CLI.IsTailCall) { 10395ffd83dbSDimitry Andric auto NoTail = [&](const char *Msg) { 10405ffd83dbSDimitry Andric if (CLI.CB && CLI.CB->isMustTailCall()) 10415ffd83dbSDimitry Andric fail(DL, DAG, Msg); 10425ffd83dbSDimitry Andric CLI.IsTailCall = false; 10435ffd83dbSDimitry Andric }; 10445ffd83dbSDimitry Andric 10455ffd83dbSDimitry Andric if (!Subtarget->hasTailCall()) 10465ffd83dbSDimitry Andric NoTail("WebAssembly 'tail-call' feature not enabled"); 10475ffd83dbSDimitry Andric 10485ffd83dbSDimitry Andric // Varargs calls cannot be tail calls because the buffer is on the stack 10495ffd83dbSDimitry Andric if (CLI.IsVarArg) 10505ffd83dbSDimitry Andric NoTail("WebAssembly does not support varargs tail calls"); 10515ffd83dbSDimitry Andric 10528bcb0991SDimitry Andric // Do not tail call unless caller and callee return types match 10538bcb0991SDimitry Andric const Function &F = MF.getFunction(); 10548bcb0991SDimitry Andric const TargetMachine &TM = getTargetMachine(); 10558bcb0991SDimitry Andric Type *RetTy = F.getReturnType(); 10568bcb0991SDimitry Andric SmallVector<MVT, 4> CallerRetTys; 10578bcb0991SDimitry Andric SmallVector<MVT, 4> CalleeRetTys; 10588bcb0991SDimitry Andric computeLegalValueVTs(F, TM, RetTy, CallerRetTys); 10598bcb0991SDimitry Andric computeLegalValueVTs(F, TM, CLI.RetTy, CalleeRetTys); 10608bcb0991SDimitry Andric bool TypesMatch = CallerRetTys.size() == CalleeRetTys.size() && 10618bcb0991SDimitry Andric std::equal(CallerRetTys.begin(), CallerRetTys.end(), 10628bcb0991SDimitry Andric CalleeRetTys.begin()); 10635ffd83dbSDimitry Andric if (!TypesMatch) 10645ffd83dbSDimitry Andric NoTail("WebAssembly tail call requires caller and callee return types to " 10655ffd83dbSDimitry Andric "match"); 10665ffd83dbSDimitry Andric 10675ffd83dbSDimitry Andric // If pointers to local stack values are passed, we cannot tail call 10685ffd83dbSDimitry Andric if (CLI.CB) { 10695ffd83dbSDimitry Andric for (auto &Arg : CLI.CB->args()) { 10705ffd83dbSDimitry Andric Value *Val = Arg.get(); 10715ffd83dbSDimitry Andric // Trace the value back through pointer operations 10725ffd83dbSDimitry Andric while (true) { 10735ffd83dbSDimitry Andric Value *Src = Val->stripPointerCastsAndAliases(); 10745ffd83dbSDimitry Andric if (auto *GEP = dyn_cast<GetElementPtrInst>(Src)) 10755ffd83dbSDimitry Andric Src = GEP->getPointerOperand(); 10765ffd83dbSDimitry Andric if (Val == Src) 10775ffd83dbSDimitry Andric break; 10785ffd83dbSDimitry Andric Val = Src; 10790b57cec5SDimitry Andric } 10805ffd83dbSDimitry Andric if (isa<AllocaInst>(Val)) { 10815ffd83dbSDimitry Andric NoTail( 10825ffd83dbSDimitry Andric "WebAssembly does not support tail calling with stack arguments"); 10835ffd83dbSDimitry Andric break; 10848bcb0991SDimitry Andric } 10858bcb0991SDimitry Andric } 10868bcb0991SDimitry Andric } 10878bcb0991SDimitry Andric } 10880b57cec5SDimitry Andric 10890b57cec5SDimitry Andric SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; 10900b57cec5SDimitry Andric SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; 10910b57cec5SDimitry Andric SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; 10928bcb0991SDimitry Andric 10938bcb0991SDimitry Andric // The generic code may have added an sret argument. If we're lowering an 10948bcb0991SDimitry Andric // invoke function, the ABI requires that the function pointer be the first 10958bcb0991SDimitry Andric // argument, so we may have to swap the arguments. 10968bcb0991SDimitry Andric if (CallConv == CallingConv::WASM_EmscriptenInvoke && Outs.size() >= 2 && 10978bcb0991SDimitry Andric Outs[0].Flags.isSRet()) { 10988bcb0991SDimitry Andric std::swap(Outs[0], Outs[1]); 10998bcb0991SDimitry Andric std::swap(OutVals[0], OutVals[1]); 11008bcb0991SDimitry Andric } 11018bcb0991SDimitry Andric 11025ffd83dbSDimitry Andric bool HasSwiftSelfArg = false; 11035ffd83dbSDimitry Andric bool HasSwiftErrorArg = false; 11040b57cec5SDimitry Andric unsigned NumFixedArgs = 0; 11050b57cec5SDimitry Andric for (unsigned I = 0; I < Outs.size(); ++I) { 11060b57cec5SDimitry Andric const ISD::OutputArg &Out = Outs[I]; 11070b57cec5SDimitry Andric SDValue &OutVal = OutVals[I]; 11085ffd83dbSDimitry Andric HasSwiftSelfArg |= Out.Flags.isSwiftSelf(); 11095ffd83dbSDimitry Andric HasSwiftErrorArg |= Out.Flags.isSwiftError(); 11100b57cec5SDimitry Andric if (Out.Flags.isNest()) 11110b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 11120b57cec5SDimitry Andric if (Out.Flags.isInAlloca()) 11130b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 11140b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegs()) 11150b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 11160b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegsLast()) 11170b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 11180b57cec5SDimitry Andric if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) { 11190b57cec5SDimitry Andric auto &MFI = MF.getFrameInfo(); 11200b57cec5SDimitry Andric int FI = MFI.CreateStackObject(Out.Flags.getByValSize(), 11215ffd83dbSDimitry Andric Out.Flags.getNonZeroByValAlign(), 11220b57cec5SDimitry Andric /*isSS=*/false); 11230b57cec5SDimitry Andric SDValue SizeNode = 11240b57cec5SDimitry Andric DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); 11250b57cec5SDimitry Andric SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 1126*0fca6ea1SDimitry Andric Chain = DAG.getMemcpy(Chain, DL, FINode, OutVal, SizeNode, 1127*0fca6ea1SDimitry Andric Out.Flags.getNonZeroByValAlign(), 11280b57cec5SDimitry Andric /*isVolatile*/ false, /*AlwaysInline=*/false, 1129*0fca6ea1SDimitry Andric /*CI=*/nullptr, std::nullopt, MachinePointerInfo(), 1130*0fca6ea1SDimitry Andric MachinePointerInfo()); 11310b57cec5SDimitry Andric OutVal = FINode; 11320b57cec5SDimitry Andric } 11330b57cec5SDimitry Andric // Count the number of fixed args *after* legalization. 11340b57cec5SDimitry Andric NumFixedArgs += Out.IsFixed; 11350b57cec5SDimitry Andric } 11360b57cec5SDimitry Andric 11370b57cec5SDimitry Andric bool IsVarArg = CLI.IsVarArg; 11380b57cec5SDimitry Andric auto PtrVT = getPointerTy(Layout); 11390b57cec5SDimitry Andric 11405ffd83dbSDimitry Andric // For swiftcc, emit additional swiftself and swifterror arguments 11415ffd83dbSDimitry Andric // if there aren't. These additional arguments are also added for callee 11425ffd83dbSDimitry Andric // signature They are necessary to match callee and caller signature for 11435ffd83dbSDimitry Andric // indirect call. 11445ffd83dbSDimitry Andric if (CallConv == CallingConv::Swift) { 11455ffd83dbSDimitry Andric if (!HasSwiftSelfArg) { 11465ffd83dbSDimitry Andric NumFixedArgs++; 11475ffd83dbSDimitry Andric ISD::OutputArg Arg; 11485ffd83dbSDimitry Andric Arg.Flags.setSwiftSelf(); 11495ffd83dbSDimitry Andric CLI.Outs.push_back(Arg); 11505ffd83dbSDimitry Andric SDValue ArgVal = DAG.getUNDEF(PtrVT); 11515ffd83dbSDimitry Andric CLI.OutVals.push_back(ArgVal); 11525ffd83dbSDimitry Andric } 11535ffd83dbSDimitry Andric if (!HasSwiftErrorArg) { 11545ffd83dbSDimitry Andric NumFixedArgs++; 11555ffd83dbSDimitry Andric ISD::OutputArg Arg; 11565ffd83dbSDimitry Andric Arg.Flags.setSwiftError(); 11575ffd83dbSDimitry Andric CLI.Outs.push_back(Arg); 11585ffd83dbSDimitry Andric SDValue ArgVal = DAG.getUNDEF(PtrVT); 11595ffd83dbSDimitry Andric CLI.OutVals.push_back(ArgVal); 11605ffd83dbSDimitry Andric } 11615ffd83dbSDimitry Andric } 11625ffd83dbSDimitry Andric 11630b57cec5SDimitry Andric // Analyze operands of the call, assigning locations to each operand. 11640b57cec5SDimitry Andric SmallVector<CCValAssign, 16> ArgLocs; 11650b57cec5SDimitry Andric CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); 11660b57cec5SDimitry Andric 11670b57cec5SDimitry Andric if (IsVarArg) { 11680b57cec5SDimitry Andric // Outgoing non-fixed arguments are placed in a buffer. First 11690b57cec5SDimitry Andric // compute their offsets and the total amount of buffer space needed. 11700b57cec5SDimitry Andric for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) { 11710b57cec5SDimitry Andric const ISD::OutputArg &Out = Outs[I]; 11720b57cec5SDimitry Andric SDValue &Arg = OutVals[I]; 11730b57cec5SDimitry Andric EVT VT = Arg.getValueType(); 11740b57cec5SDimitry Andric assert(VT != MVT::iPTR && "Legalized args should be concrete"); 11750b57cec5SDimitry Andric Type *Ty = VT.getTypeForEVT(*DAG.getContext()); 11765ffd83dbSDimitry Andric Align Alignment = 11775ffd83dbSDimitry Andric std::max(Out.Flags.getNonZeroOrigAlign(), Layout.getABITypeAlign(Ty)); 11785ffd83dbSDimitry Andric unsigned Offset = 11795ffd83dbSDimitry Andric CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), Alignment); 11800b57cec5SDimitry Andric CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), 11810b57cec5SDimitry Andric Offset, VT.getSimpleVT(), 11820b57cec5SDimitry Andric CCValAssign::Full)); 11830b57cec5SDimitry Andric } 11840b57cec5SDimitry Andric } 11850b57cec5SDimitry Andric 11860b57cec5SDimitry Andric unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); 11870b57cec5SDimitry Andric 11880b57cec5SDimitry Andric SDValue FINode; 11890b57cec5SDimitry Andric if (IsVarArg && NumBytes) { 11900b57cec5SDimitry Andric // For non-fixed arguments, next emit stores to store the argument values 11910b57cec5SDimitry Andric // to the stack buffer at the offsets computed above. 11920b57cec5SDimitry Andric int FI = MF.getFrameInfo().CreateStackObject(NumBytes, 11930b57cec5SDimitry Andric Layout.getStackAlignment(), 11940b57cec5SDimitry Andric /*isSS=*/false); 11950b57cec5SDimitry Andric unsigned ValNo = 0; 11960b57cec5SDimitry Andric SmallVector<SDValue, 8> Chains; 1197e8d8bef9SDimitry Andric for (SDValue Arg : drop_begin(OutVals, NumFixedArgs)) { 11980b57cec5SDimitry Andric assert(ArgLocs[ValNo].getValNo() == ValNo && 11990b57cec5SDimitry Andric "ArgLocs should remain in order and only hold varargs args"); 12000b57cec5SDimitry Andric unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); 12010b57cec5SDimitry Andric FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 12020b57cec5SDimitry Andric SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode, 12030b57cec5SDimitry Andric DAG.getConstant(Offset, DL, PtrVT)); 12040b57cec5SDimitry Andric Chains.push_back( 12050b57cec5SDimitry Andric DAG.getStore(Chain, DL, Arg, Add, 1206e8d8bef9SDimitry Andric MachinePointerInfo::getFixedStack(MF, FI, Offset))); 12070b57cec5SDimitry Andric } 12080b57cec5SDimitry Andric if (!Chains.empty()) 12090b57cec5SDimitry Andric Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); 12100b57cec5SDimitry Andric } else if (IsVarArg) { 12110b57cec5SDimitry Andric FINode = DAG.getIntPtrConstant(0, DL); 12120b57cec5SDimitry Andric } 12130b57cec5SDimitry Andric 12140b57cec5SDimitry Andric if (Callee->getOpcode() == ISD::GlobalAddress) { 12150b57cec5SDimitry Andric // If the callee is a GlobalAddress node (quite common, every direct call 12160b57cec5SDimitry Andric // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress 12170b57cec5SDimitry Andric // doesn't at MO_GOT which is not needed for direct calls. 12180b57cec5SDimitry Andric GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Callee); 12190b57cec5SDimitry Andric Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, 12200b57cec5SDimitry Andric getPointerTy(DAG.getDataLayout()), 12210b57cec5SDimitry Andric GA->getOffset()); 12220b57cec5SDimitry Andric Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL, 12230b57cec5SDimitry Andric getPointerTy(DAG.getDataLayout()), Callee); 12240b57cec5SDimitry Andric } 12250b57cec5SDimitry Andric 12260b57cec5SDimitry Andric // Compute the operands for the CALLn node. 12270b57cec5SDimitry Andric SmallVector<SDValue, 16> Ops; 12280b57cec5SDimitry Andric Ops.push_back(Chain); 12290b57cec5SDimitry Andric Ops.push_back(Callee); 12300b57cec5SDimitry Andric 12310b57cec5SDimitry Andric // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs 12320b57cec5SDimitry Andric // isn't reliable. 12330b57cec5SDimitry Andric Ops.append(OutVals.begin(), 12340b57cec5SDimitry Andric IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); 12350b57cec5SDimitry Andric // Add a pointer to the vararg buffer. 12360b57cec5SDimitry Andric if (IsVarArg) 12370b57cec5SDimitry Andric Ops.push_back(FINode); 12380b57cec5SDimitry Andric 12390b57cec5SDimitry Andric SmallVector<EVT, 8> InTys; 12400b57cec5SDimitry Andric for (const auto &In : Ins) { 12410b57cec5SDimitry Andric assert(!In.Flags.isByVal() && "byval is not valid for return values"); 12420b57cec5SDimitry Andric assert(!In.Flags.isNest() && "nest is not valid for return values"); 12430b57cec5SDimitry Andric if (In.Flags.isInAlloca()) 12440b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values"); 12450b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegs()) 12460b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values"); 12470b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegsLast()) 12480b57cec5SDimitry Andric fail(DL, DAG, 12490b57cec5SDimitry Andric "WebAssembly hasn't implemented cons regs last return values"); 12505ffd83dbSDimitry Andric // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in 12510b57cec5SDimitry Andric // registers. 12520b57cec5SDimitry Andric InTys.push_back(In.VT); 12530b57cec5SDimitry Andric } 12540b57cec5SDimitry Andric 1255fe6060f1SDimitry Andric // Lastly, if this is a call to a funcref we need to add an instruction 1256fe6060f1SDimitry Andric // table.set to the chain and transform the call. 125706c3fb27SDimitry Andric if (CLI.CB && WebAssembly::isWebAssemblyFuncrefType( 125806c3fb27SDimitry Andric CLI.CB->getCalledOperand()->getType())) { 1259fe6060f1SDimitry Andric // In the absence of function references proposal where a funcref call is 1260fe6060f1SDimitry Andric // lowered to call_ref, using reference types we generate a table.set to set 1261fe6060f1SDimitry Andric // the funcref to a special table used solely for this purpose, followed by 1262fe6060f1SDimitry Andric // a call_indirect. Here we just generate the table set, and return the 1263fe6060f1SDimitry Andric // SDValue of the table.set so that LowerCall can finalize the lowering by 1264fe6060f1SDimitry Andric // generating the call_indirect. 1265fe6060f1SDimitry Andric SDValue Chain = Ops[0]; 1266fe6060f1SDimitry Andric 1267fe6060f1SDimitry Andric MCSymbolWasm *Table = WebAssembly::getOrCreateFuncrefCallTableSymbol( 1268fe6060f1SDimitry Andric MF.getContext(), Subtarget); 1269fe6060f1SDimitry Andric SDValue Sym = DAG.getMCSymbol(Table, PtrVT); 1270fe6060f1SDimitry Andric SDValue TableSlot = DAG.getConstant(0, DL, MVT::i32); 1271fe6060f1SDimitry Andric SDValue TableSetOps[] = {Chain, Sym, TableSlot, Callee}; 1272fe6060f1SDimitry Andric SDValue TableSet = DAG.getMemIntrinsicNode( 1273fe6060f1SDimitry Andric WebAssemblyISD::TABLE_SET, DL, DAG.getVTList(MVT::Other), TableSetOps, 1274fe6060f1SDimitry Andric MVT::funcref, 1275fe6060f1SDimitry Andric // Machine Mem Operand args 1276349cc55cSDimitry Andric MachinePointerInfo( 1277349cc55cSDimitry Andric WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF), 1278fe6060f1SDimitry Andric CLI.CB->getCalledOperand()->getPointerAlignment(DAG.getDataLayout()), 1279fe6060f1SDimitry Andric MachineMemOperand::MOStore); 1280fe6060f1SDimitry Andric 1281fe6060f1SDimitry Andric Ops[0] = TableSet; // The new chain is the TableSet itself 1282fe6060f1SDimitry Andric } 1283fe6060f1SDimitry Andric 12840b57cec5SDimitry Andric if (CLI.IsTailCall) { 12850b57cec5SDimitry Andric // ret_calls do not return values to the current frame 12860b57cec5SDimitry Andric SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); 12870b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops); 12880b57cec5SDimitry Andric } 12890b57cec5SDimitry Andric 12900b57cec5SDimitry Andric InTys.push_back(MVT::Other); 12910b57cec5SDimitry Andric SDVTList InTyList = DAG.getVTList(InTys); 12925ffd83dbSDimitry Andric SDValue Res = DAG.getNode(WebAssemblyISD::CALL, DL, InTyList, Ops); 12930b57cec5SDimitry Andric 12945ffd83dbSDimitry Andric for (size_t I = 0; I < Ins.size(); ++I) 12955ffd83dbSDimitry Andric InVals.push_back(Res.getValue(I)); 12965ffd83dbSDimitry Andric 12975ffd83dbSDimitry Andric // Return the chain 12985ffd83dbSDimitry Andric return Res.getValue(Ins.size()); 12990b57cec5SDimitry Andric } 13000b57cec5SDimitry Andric 13010b57cec5SDimitry Andric bool WebAssemblyTargetLowering::CanLowerReturn( 13020b57cec5SDimitry Andric CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, 13030b57cec5SDimitry Andric const SmallVectorImpl<ISD::OutputArg> &Outs, 13040b57cec5SDimitry Andric LLVMContext & /*Context*/) const { 13058bcb0991SDimitry Andric // WebAssembly can only handle returning tuples with multivalue enabled 1306*0fca6ea1SDimitry Andric return WebAssembly::canLowerReturn(Outs.size(), Subtarget); 13070b57cec5SDimitry Andric } 13080b57cec5SDimitry Andric 13090b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerReturn( 13100b57cec5SDimitry Andric SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, 13110b57cec5SDimitry Andric const SmallVectorImpl<ISD::OutputArg> &Outs, 13120b57cec5SDimitry Andric const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, 13130b57cec5SDimitry Andric SelectionDAG &DAG) const { 1314*0fca6ea1SDimitry Andric assert(WebAssembly::canLowerReturn(Outs.size(), Subtarget) && 13158bcb0991SDimitry Andric "MVP WebAssembly can only return up to one value"); 13160b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 13170b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 13180b57cec5SDimitry Andric 13190b57cec5SDimitry Andric SmallVector<SDValue, 4> RetOps(1, Chain); 13200b57cec5SDimitry Andric RetOps.append(OutVals.begin(), OutVals.end()); 13210b57cec5SDimitry Andric Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps); 13220b57cec5SDimitry Andric 13230b57cec5SDimitry Andric // Record the number and types of the return values. 13240b57cec5SDimitry Andric for (const ISD::OutputArg &Out : Outs) { 13250b57cec5SDimitry Andric assert(!Out.Flags.isByVal() && "byval is not valid for return values"); 13260b57cec5SDimitry Andric assert(!Out.Flags.isNest() && "nest is not valid for return values"); 13270b57cec5SDimitry Andric assert(Out.IsFixed && "non-fixed return value is not valid"); 13280b57cec5SDimitry Andric if (Out.Flags.isInAlloca()) 13290b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); 13300b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegs()) 13310b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); 13320b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegsLast()) 13330b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); 13340b57cec5SDimitry Andric } 13350b57cec5SDimitry Andric 13360b57cec5SDimitry Andric return Chain; 13370b57cec5SDimitry Andric } 13380b57cec5SDimitry Andric 13390b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFormalArguments( 13400b57cec5SDimitry Andric SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, 13410b57cec5SDimitry Andric const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, 13420b57cec5SDimitry Andric SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { 13430b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 13440b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 13450b57cec5SDimitry Andric 13460b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 13470b57cec5SDimitry Andric auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); 13480b57cec5SDimitry Andric 13490b57cec5SDimitry Andric // Set up the incoming ARGUMENTS value, which serves to represent the liveness 13500b57cec5SDimitry Andric // of the incoming values before they're represented by virtual registers. 13510b57cec5SDimitry Andric MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); 13520b57cec5SDimitry Andric 13535ffd83dbSDimitry Andric bool HasSwiftErrorArg = false; 13545ffd83dbSDimitry Andric bool HasSwiftSelfArg = false; 13550b57cec5SDimitry Andric for (const ISD::InputArg &In : Ins) { 13565ffd83dbSDimitry Andric HasSwiftSelfArg |= In.Flags.isSwiftSelf(); 13575ffd83dbSDimitry Andric HasSwiftErrorArg |= In.Flags.isSwiftError(); 13580b57cec5SDimitry Andric if (In.Flags.isInAlloca()) 13590b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 13600b57cec5SDimitry Andric if (In.Flags.isNest()) 13610b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 13620b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegs()) 13630b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 13640b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegsLast()) 13650b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 13665ffd83dbSDimitry Andric // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in 13670b57cec5SDimitry Andric // registers. 13680b57cec5SDimitry Andric InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, 13690b57cec5SDimitry Andric DAG.getTargetConstant(InVals.size(), 13700b57cec5SDimitry Andric DL, MVT::i32)) 13710b57cec5SDimitry Andric : DAG.getUNDEF(In.VT)); 13720b57cec5SDimitry Andric 13730b57cec5SDimitry Andric // Record the number and types of arguments. 13740b57cec5SDimitry Andric MFI->addParam(In.VT); 13750b57cec5SDimitry Andric } 13760b57cec5SDimitry Andric 13775ffd83dbSDimitry Andric // For swiftcc, emit additional swiftself and swifterror arguments 13785ffd83dbSDimitry Andric // if there aren't. These additional arguments are also added for callee 13795ffd83dbSDimitry Andric // signature They are necessary to match callee and caller signature for 13805ffd83dbSDimitry Andric // indirect call. 13815ffd83dbSDimitry Andric auto PtrVT = getPointerTy(MF.getDataLayout()); 13825ffd83dbSDimitry Andric if (CallConv == CallingConv::Swift) { 13835ffd83dbSDimitry Andric if (!HasSwiftSelfArg) { 13845ffd83dbSDimitry Andric MFI->addParam(PtrVT); 13855ffd83dbSDimitry Andric } 13865ffd83dbSDimitry Andric if (!HasSwiftErrorArg) { 13875ffd83dbSDimitry Andric MFI->addParam(PtrVT); 13885ffd83dbSDimitry Andric } 13895ffd83dbSDimitry Andric } 13900b57cec5SDimitry Andric // Varargs are copied into a buffer allocated by the caller, and a pointer to 13910b57cec5SDimitry Andric // the buffer is passed as an argument. 13920b57cec5SDimitry Andric if (IsVarArg) { 13930b57cec5SDimitry Andric MVT PtrVT = getPointerTy(MF.getDataLayout()); 13948bcb0991SDimitry Andric Register VarargVreg = 13950b57cec5SDimitry Andric MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); 13960b57cec5SDimitry Andric MFI->setVarargBufferVreg(VarargVreg); 13970b57cec5SDimitry Andric Chain = DAG.getCopyToReg( 13980b57cec5SDimitry Andric Chain, DL, VarargVreg, 13990b57cec5SDimitry Andric DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, 14000b57cec5SDimitry Andric DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); 14010b57cec5SDimitry Andric MFI->addParam(PtrVT); 14020b57cec5SDimitry Andric } 14030b57cec5SDimitry Andric 14040b57cec5SDimitry Andric // Record the number and types of arguments and results. 14050b57cec5SDimitry Andric SmallVector<MVT, 4> Params; 14060b57cec5SDimitry Andric SmallVector<MVT, 4> Results; 14075ffd83dbSDimitry Andric computeSignatureVTs(MF.getFunction().getFunctionType(), &MF.getFunction(), 14085ffd83dbSDimitry Andric MF.getFunction(), DAG.getTarget(), Params, Results); 14090b57cec5SDimitry Andric for (MVT VT : Results) 14100b57cec5SDimitry Andric MFI->addResult(VT); 14110b57cec5SDimitry Andric // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify 14120b57cec5SDimitry Andric // the param logic here with ComputeSignatureVTs 14130b57cec5SDimitry Andric assert(MFI->getParams().size() == Params.size() && 14140b57cec5SDimitry Andric std::equal(MFI->getParams().begin(), MFI->getParams().end(), 14150b57cec5SDimitry Andric Params.begin())); 14160b57cec5SDimitry Andric 14170b57cec5SDimitry Andric return Chain; 14180b57cec5SDimitry Andric } 14190b57cec5SDimitry Andric 14200b57cec5SDimitry Andric void WebAssemblyTargetLowering::ReplaceNodeResults( 14210b57cec5SDimitry Andric SDNode *N, SmallVectorImpl<SDValue> &Results, SelectionDAG &DAG) const { 14220b57cec5SDimitry Andric switch (N->getOpcode()) { 14230b57cec5SDimitry Andric case ISD::SIGN_EXTEND_INREG: 14240b57cec5SDimitry Andric // Do not add any results, signifying that N should not be custom lowered 14250b57cec5SDimitry Andric // after all. This happens because simd128 turns on custom lowering for 14260b57cec5SDimitry Andric // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an 14270b57cec5SDimitry Andric // illegal type. 14280b57cec5SDimitry Andric break; 142906c3fb27SDimitry Andric case ISD::SIGN_EXTEND_VECTOR_INREG: 143006c3fb27SDimitry Andric case ISD::ZERO_EXTEND_VECTOR_INREG: 143106c3fb27SDimitry Andric // Do not add any results, signifying that N should not be custom lowered. 143206c3fb27SDimitry Andric // EXTEND_VECTOR_INREG is implemented for some vectors, but not all. 143306c3fb27SDimitry Andric break; 14340b57cec5SDimitry Andric default: 14350b57cec5SDimitry Andric llvm_unreachable( 14360b57cec5SDimitry Andric "ReplaceNodeResults not implemented for this op for WebAssembly!"); 14370b57cec5SDimitry Andric } 14380b57cec5SDimitry Andric } 14390b57cec5SDimitry Andric 14400b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 14410b57cec5SDimitry Andric // Custom lowering hooks. 14420b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 14430b57cec5SDimitry Andric 14440b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, 14450b57cec5SDimitry Andric SelectionDAG &DAG) const { 14460b57cec5SDimitry Andric SDLoc DL(Op); 14470b57cec5SDimitry Andric switch (Op.getOpcode()) { 14480b57cec5SDimitry Andric default: 14490b57cec5SDimitry Andric llvm_unreachable("unimplemented operation lowering"); 14500b57cec5SDimitry Andric return SDValue(); 14510b57cec5SDimitry Andric case ISD::FrameIndex: 14520b57cec5SDimitry Andric return LowerFrameIndex(Op, DAG); 14530b57cec5SDimitry Andric case ISD::GlobalAddress: 14540b57cec5SDimitry Andric return LowerGlobalAddress(Op, DAG); 1455e8d8bef9SDimitry Andric case ISD::GlobalTLSAddress: 1456e8d8bef9SDimitry Andric return LowerGlobalTLSAddress(Op, DAG); 14570b57cec5SDimitry Andric case ISD::ExternalSymbol: 14580b57cec5SDimitry Andric return LowerExternalSymbol(Op, DAG); 14590b57cec5SDimitry Andric case ISD::JumpTable: 14600b57cec5SDimitry Andric return LowerJumpTable(Op, DAG); 14610b57cec5SDimitry Andric case ISD::BR_JT: 14620b57cec5SDimitry Andric return LowerBR_JT(Op, DAG); 14630b57cec5SDimitry Andric case ISD::VASTART: 14640b57cec5SDimitry Andric return LowerVASTART(Op, DAG); 14650b57cec5SDimitry Andric case ISD::BlockAddress: 14660b57cec5SDimitry Andric case ISD::BRIND: 14670b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented computed gotos"); 14680b57cec5SDimitry Andric return SDValue(); 14690b57cec5SDimitry Andric case ISD::RETURNADDR: 14700b57cec5SDimitry Andric return LowerRETURNADDR(Op, DAG); 14710b57cec5SDimitry Andric case ISD::FRAMEADDR: 14720b57cec5SDimitry Andric return LowerFRAMEADDR(Op, DAG); 14730b57cec5SDimitry Andric case ISD::CopyToReg: 14740b57cec5SDimitry Andric return LowerCopyToReg(Op, DAG); 14750b57cec5SDimitry Andric case ISD::EXTRACT_VECTOR_ELT: 14760b57cec5SDimitry Andric case ISD::INSERT_VECTOR_ELT: 14770b57cec5SDimitry Andric return LowerAccessVectorElement(Op, DAG); 14780b57cec5SDimitry Andric case ISD::INTRINSIC_VOID: 14790b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: 14800b57cec5SDimitry Andric case ISD::INTRINSIC_W_CHAIN: 14810b57cec5SDimitry Andric return LowerIntrinsic(Op, DAG); 14820b57cec5SDimitry Andric case ISD::SIGN_EXTEND_INREG: 14830b57cec5SDimitry Andric return LowerSIGN_EXTEND_INREG(Op, DAG); 148406c3fb27SDimitry Andric case ISD::ZERO_EXTEND_VECTOR_INREG: 148506c3fb27SDimitry Andric case ISD::SIGN_EXTEND_VECTOR_INREG: 148606c3fb27SDimitry Andric return LowerEXTEND_VECTOR_INREG(Op, DAG); 14870b57cec5SDimitry Andric case ISD::BUILD_VECTOR: 14880b57cec5SDimitry Andric return LowerBUILD_VECTOR(Op, DAG); 14890b57cec5SDimitry Andric case ISD::VECTOR_SHUFFLE: 14900b57cec5SDimitry Andric return LowerVECTOR_SHUFFLE(Op, DAG); 1491480093f4SDimitry Andric case ISD::SETCC: 1492480093f4SDimitry Andric return LowerSETCC(Op, DAG); 14930b57cec5SDimitry Andric case ISD::SHL: 14940b57cec5SDimitry Andric case ISD::SRA: 14950b57cec5SDimitry Andric case ISD::SRL: 14960b57cec5SDimitry Andric return LowerShift(Op, DAG); 1497fe6060f1SDimitry Andric case ISD::FP_TO_SINT_SAT: 1498fe6060f1SDimitry Andric case ISD::FP_TO_UINT_SAT: 1499fe6060f1SDimitry Andric return LowerFP_TO_INT_SAT(Op, DAG); 1500fe6060f1SDimitry Andric case ISD::LOAD: 1501fe6060f1SDimitry Andric return LowerLoad(Op, DAG); 1502fe6060f1SDimitry Andric case ISD::STORE: 1503fe6060f1SDimitry Andric return LowerStore(Op, DAG); 1504349cc55cSDimitry Andric case ISD::CTPOP: 1505349cc55cSDimitry Andric case ISD::CTLZ: 1506349cc55cSDimitry Andric case ISD::CTTZ: 1507349cc55cSDimitry Andric return DAG.UnrollVectorOp(Op.getNode()); 1508*0fca6ea1SDimitry Andric case ISD::CLEAR_CACHE: 1509*0fca6ea1SDimitry Andric report_fatal_error("llvm.clear_cache is not supported on wasm"); 15100b57cec5SDimitry Andric } 15110b57cec5SDimitry Andric } 15120b57cec5SDimitry Andric 1513fe6060f1SDimitry Andric static bool IsWebAssemblyGlobal(SDValue Op) { 1514fe6060f1SDimitry Andric if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Op)) 1515fe6060f1SDimitry Andric return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace()); 1516fe6060f1SDimitry Andric 1517fe6060f1SDimitry Andric return false; 1518fe6060f1SDimitry Andric } 1519fe6060f1SDimitry Andric 1520bdd1243dSDimitry Andric static std::optional<unsigned> IsWebAssemblyLocal(SDValue Op, 1521bdd1243dSDimitry Andric SelectionDAG &DAG) { 1522fe6060f1SDimitry Andric const FrameIndexSDNode *FI = dyn_cast<FrameIndexSDNode>(Op); 1523fe6060f1SDimitry Andric if (!FI) 1524bdd1243dSDimitry Andric return std::nullopt; 1525fe6060f1SDimitry Andric 1526fe6060f1SDimitry Andric auto &MF = DAG.getMachineFunction(); 1527fe6060f1SDimitry Andric return WebAssemblyFrameLowering::getLocalForStackObject(MF, FI->getIndex()); 1528fe6060f1SDimitry Andric } 1529fe6060f1SDimitry Andric 1530fe6060f1SDimitry Andric SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op, 1531fe6060f1SDimitry Andric SelectionDAG &DAG) const { 1532fe6060f1SDimitry Andric SDLoc DL(Op); 1533fe6060f1SDimitry Andric StoreSDNode *SN = cast<StoreSDNode>(Op.getNode()); 1534fe6060f1SDimitry Andric const SDValue &Value = SN->getValue(); 1535fe6060f1SDimitry Andric const SDValue &Base = SN->getBasePtr(); 1536fe6060f1SDimitry Andric const SDValue &Offset = SN->getOffset(); 1537fe6060f1SDimitry Andric 1538fe6060f1SDimitry Andric if (IsWebAssemblyGlobal(Base)) { 1539fe6060f1SDimitry Andric if (!Offset->isUndef()) 1540fe6060f1SDimitry Andric report_fatal_error("unexpected offset when storing to webassembly global", 1541fe6060f1SDimitry Andric false); 1542fe6060f1SDimitry Andric 1543fe6060f1SDimitry Andric SDVTList Tys = DAG.getVTList(MVT::Other); 1544fe6060f1SDimitry Andric SDValue Ops[] = {SN->getChain(), Value, Base}; 1545fe6060f1SDimitry Andric return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops, 1546fe6060f1SDimitry Andric SN->getMemoryVT(), SN->getMemOperand()); 1547fe6060f1SDimitry Andric } 1548fe6060f1SDimitry Andric 1549bdd1243dSDimitry Andric if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) { 1550fe6060f1SDimitry Andric if (!Offset->isUndef()) 1551fe6060f1SDimitry Andric report_fatal_error("unexpected offset when storing to webassembly local", 1552fe6060f1SDimitry Andric false); 1553fe6060f1SDimitry Andric 1554fe6060f1SDimitry Andric SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32); 1555fe6060f1SDimitry Andric SDVTList Tys = DAG.getVTList(MVT::Other); // The chain. 1556fe6060f1SDimitry Andric SDValue Ops[] = {SN->getChain(), Idx, Value}; 1557fe6060f1SDimitry Andric return DAG.getNode(WebAssemblyISD::LOCAL_SET, DL, Tys, Ops); 1558fe6060f1SDimitry Andric } 1559fe6060f1SDimitry Andric 1560bdd1243dSDimitry Andric if (WebAssembly::isWasmVarAddressSpace(SN->getAddressSpace())) 1561bdd1243dSDimitry Andric report_fatal_error( 1562bdd1243dSDimitry Andric "Encountered an unlowerable store to the wasm_var address space", 1563bdd1243dSDimitry Andric false); 1564bdd1243dSDimitry Andric 1565fe6060f1SDimitry Andric return Op; 1566fe6060f1SDimitry Andric } 1567fe6060f1SDimitry Andric 1568fe6060f1SDimitry Andric SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op, 1569fe6060f1SDimitry Andric SelectionDAG &DAG) const { 1570fe6060f1SDimitry Andric SDLoc DL(Op); 1571fe6060f1SDimitry Andric LoadSDNode *LN = cast<LoadSDNode>(Op.getNode()); 1572fe6060f1SDimitry Andric const SDValue &Base = LN->getBasePtr(); 1573fe6060f1SDimitry Andric const SDValue &Offset = LN->getOffset(); 1574fe6060f1SDimitry Andric 1575fe6060f1SDimitry Andric if (IsWebAssemblyGlobal(Base)) { 1576fe6060f1SDimitry Andric if (!Offset->isUndef()) 1577fe6060f1SDimitry Andric report_fatal_error( 1578fe6060f1SDimitry Andric "unexpected offset when loading from webassembly global", false); 1579fe6060f1SDimitry Andric 1580fe6060f1SDimitry Andric SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other); 1581fe6060f1SDimitry Andric SDValue Ops[] = {LN->getChain(), Base}; 1582fe6060f1SDimitry Andric return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops, 1583fe6060f1SDimitry Andric LN->getMemoryVT(), LN->getMemOperand()); 1584fe6060f1SDimitry Andric } 1585fe6060f1SDimitry Andric 1586bdd1243dSDimitry Andric if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) { 1587fe6060f1SDimitry Andric if (!Offset->isUndef()) 1588fe6060f1SDimitry Andric report_fatal_error( 1589fe6060f1SDimitry Andric "unexpected offset when loading from webassembly local", false); 1590fe6060f1SDimitry Andric 1591fe6060f1SDimitry Andric SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32); 1592fe6060f1SDimitry Andric EVT LocalVT = LN->getValueType(0); 1593fe6060f1SDimitry Andric SDValue LocalGet = DAG.getNode(WebAssemblyISD::LOCAL_GET, DL, LocalVT, 1594fe6060f1SDimitry Andric {LN->getChain(), Idx}); 1595fe6060f1SDimitry Andric SDValue Result = DAG.getMergeValues({LocalGet, LN->getChain()}, DL); 1596fe6060f1SDimitry Andric assert(Result->getNumValues() == 2 && "Loads must carry a chain!"); 1597fe6060f1SDimitry Andric return Result; 1598fe6060f1SDimitry Andric } 1599fe6060f1SDimitry Andric 1600bdd1243dSDimitry Andric if (WebAssembly::isWasmVarAddressSpace(LN->getAddressSpace())) 1601bdd1243dSDimitry Andric report_fatal_error( 1602bdd1243dSDimitry Andric "Encountered an unlowerable load from the wasm_var address space", 1603bdd1243dSDimitry Andric false); 1604bdd1243dSDimitry Andric 1605fe6060f1SDimitry Andric return Op; 1606fe6060f1SDimitry Andric } 1607fe6060f1SDimitry Andric 16080b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, 16090b57cec5SDimitry Andric SelectionDAG &DAG) const { 16100b57cec5SDimitry Andric SDValue Src = Op.getOperand(2); 16110b57cec5SDimitry Andric if (isa<FrameIndexSDNode>(Src.getNode())) { 16120b57cec5SDimitry Andric // CopyToReg nodes don't support FrameIndex operands. Other targets select 16130b57cec5SDimitry Andric // the FI to some LEA-like instruction, but since we don't have that, we 16140b57cec5SDimitry Andric // need to insert some kind of instruction that can take an FI operand and 16150b57cec5SDimitry Andric // produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy 16160b57cec5SDimitry Andric // local.copy between Op and its FI operand. 16170b57cec5SDimitry Andric SDValue Chain = Op.getOperand(0); 16180b57cec5SDimitry Andric SDLoc DL(Op); 161904eeddc0SDimitry Andric Register Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg(); 16200b57cec5SDimitry Andric EVT VT = Src.getValueType(); 16210b57cec5SDimitry Andric SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32 16220b57cec5SDimitry Andric : WebAssembly::COPY_I64, 16230b57cec5SDimitry Andric DL, VT, Src), 16240b57cec5SDimitry Andric 0); 16250b57cec5SDimitry Andric return Op.getNode()->getNumValues() == 1 16260b57cec5SDimitry Andric ? DAG.getCopyToReg(Chain, DL, Reg, Copy) 16270b57cec5SDimitry Andric : DAG.getCopyToReg(Chain, DL, Reg, Copy, 16280b57cec5SDimitry Andric Op.getNumOperands() == 4 ? Op.getOperand(3) 16290b57cec5SDimitry Andric : SDValue()); 16300b57cec5SDimitry Andric } 16310b57cec5SDimitry Andric return SDValue(); 16320b57cec5SDimitry Andric } 16330b57cec5SDimitry Andric 16340b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op, 16350b57cec5SDimitry Andric SelectionDAG &DAG) const { 16360b57cec5SDimitry Andric int FI = cast<FrameIndexSDNode>(Op)->getIndex(); 16370b57cec5SDimitry Andric return DAG.getTargetFrameIndex(FI, Op.getValueType()); 16380b57cec5SDimitry Andric } 16390b57cec5SDimitry Andric 16400b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerRETURNADDR(SDValue Op, 16410b57cec5SDimitry Andric SelectionDAG &DAG) const { 16420b57cec5SDimitry Andric SDLoc DL(Op); 16430b57cec5SDimitry Andric 16440b57cec5SDimitry Andric if (!Subtarget->getTargetTriple().isOSEmscripten()) { 16450b57cec5SDimitry Andric fail(DL, DAG, 16460b57cec5SDimitry Andric "Non-Emscripten WebAssembly hasn't implemented " 16470b57cec5SDimitry Andric "__builtin_return_address"); 16480b57cec5SDimitry Andric return SDValue(); 16490b57cec5SDimitry Andric } 16500b57cec5SDimitry Andric 16510b57cec5SDimitry Andric if (verifyReturnAddressArgumentIsConstant(Op, DAG)) 16520b57cec5SDimitry Andric return SDValue(); 16530b57cec5SDimitry Andric 1654349cc55cSDimitry Andric unsigned Depth = Op.getConstantOperandVal(0); 16558bcb0991SDimitry Andric MakeLibCallOptions CallOptions; 16560b57cec5SDimitry Andric return makeLibCall(DAG, RTLIB::RETURN_ADDRESS, Op.getValueType(), 16578bcb0991SDimitry Andric {DAG.getConstant(Depth, DL, MVT::i32)}, CallOptions, DL) 16580b57cec5SDimitry Andric .first; 16590b57cec5SDimitry Andric } 16600b57cec5SDimitry Andric 16610b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, 16620b57cec5SDimitry Andric SelectionDAG &DAG) const { 16630b57cec5SDimitry Andric // Non-zero depths are not supported by WebAssembly currently. Use the 16640b57cec5SDimitry Andric // legalizer's default expansion, which is to return 0 (what this function is 16650b57cec5SDimitry Andric // documented to do). 16660b57cec5SDimitry Andric if (Op.getConstantOperandVal(0) > 0) 16670b57cec5SDimitry Andric return SDValue(); 16680b57cec5SDimitry Andric 16690b57cec5SDimitry Andric DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); 16700b57cec5SDimitry Andric EVT VT = Op.getValueType(); 16718bcb0991SDimitry Andric Register FP = 16720b57cec5SDimitry Andric Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); 16730b57cec5SDimitry Andric return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); 16740b57cec5SDimitry Andric } 16750b57cec5SDimitry Andric 1676e8d8bef9SDimitry Andric SDValue 1677e8d8bef9SDimitry Andric WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op, 1678e8d8bef9SDimitry Andric SelectionDAG &DAG) const { 1679e8d8bef9SDimitry Andric SDLoc DL(Op); 1680e8d8bef9SDimitry Andric const auto *GA = cast<GlobalAddressSDNode>(Op); 1681e8d8bef9SDimitry Andric 1682e8d8bef9SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 1683e8d8bef9SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) 1684e8d8bef9SDimitry Andric report_fatal_error("cannot use thread-local storage without bulk memory", 1685e8d8bef9SDimitry Andric false); 1686e8d8bef9SDimitry Andric 1687e8d8bef9SDimitry Andric const GlobalValue *GV = GA->getGlobal(); 1688e8d8bef9SDimitry Andric 1689972a253aSDimitry Andric // Currently only Emscripten supports dynamic linking with threads. Therefore, 1690972a253aSDimitry Andric // on other targets, if we have thread-local storage, only the local-exec 1691972a253aSDimitry Andric // model is possible. 1692972a253aSDimitry Andric auto model = Subtarget->getTargetTriple().isOSEmscripten() 1693972a253aSDimitry Andric ? GV->getThreadLocalMode() 1694972a253aSDimitry Andric : GlobalValue::LocalExecTLSModel; 1695349cc55cSDimitry Andric 1696349cc55cSDimitry Andric // Unsupported TLS modes 1697349cc55cSDimitry Andric assert(model != GlobalValue::NotThreadLocal); 1698349cc55cSDimitry Andric assert(model != GlobalValue::InitialExecTLSModel); 1699349cc55cSDimitry Andric 1700349cc55cSDimitry Andric if (model == GlobalValue::LocalExecTLSModel || 1701349cc55cSDimitry Andric model == GlobalValue::LocalDynamicTLSModel || 1702349cc55cSDimitry Andric (model == GlobalValue::GeneralDynamicTLSModel && 1703*0fca6ea1SDimitry Andric getTargetMachine().shouldAssumeDSOLocal(GV))) { 1704349cc55cSDimitry Andric // For DSO-local TLS variables we use offset from __tls_base 1705349cc55cSDimitry Andric 1706349cc55cSDimitry Andric MVT PtrVT = getPointerTy(DAG.getDataLayout()); 1707e8d8bef9SDimitry Andric auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 1708e8d8bef9SDimitry Andric : WebAssembly::GLOBAL_GET_I32; 1709e8d8bef9SDimitry Andric const char *BaseName = MF.createExternalSymbolName("__tls_base"); 1710e8d8bef9SDimitry Andric 1711e8d8bef9SDimitry Andric SDValue BaseAddr( 1712e8d8bef9SDimitry Andric DAG.getMachineNode(GlobalGet, DL, PtrVT, 1713e8d8bef9SDimitry Andric DAG.getTargetExternalSymbol(BaseName, PtrVT)), 1714e8d8bef9SDimitry Andric 0); 1715e8d8bef9SDimitry Andric 1716e8d8bef9SDimitry Andric SDValue TLSOffset = DAG.getTargetGlobalAddress( 1717e8d8bef9SDimitry Andric GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL); 1718349cc55cSDimitry Andric SDValue SymOffset = 1719349cc55cSDimitry Andric DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset); 1720e8d8bef9SDimitry Andric 1721349cc55cSDimitry Andric return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset); 1722349cc55cSDimitry Andric } 1723349cc55cSDimitry Andric 1724349cc55cSDimitry Andric assert(model == GlobalValue::GeneralDynamicTLSModel); 1725349cc55cSDimitry Andric 1726349cc55cSDimitry Andric EVT VT = Op.getValueType(); 1727349cc55cSDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 1728349cc55cSDimitry Andric DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, 1729349cc55cSDimitry Andric GA->getOffset(), 1730349cc55cSDimitry Andric WebAssemblyII::MO_GOT_TLS)); 1731e8d8bef9SDimitry Andric } 1732e8d8bef9SDimitry Andric 17330b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, 17340b57cec5SDimitry Andric SelectionDAG &DAG) const { 17350b57cec5SDimitry Andric SDLoc DL(Op); 17360b57cec5SDimitry Andric const auto *GA = cast<GlobalAddressSDNode>(Op); 17370b57cec5SDimitry Andric EVT VT = Op.getValueType(); 17380b57cec5SDimitry Andric assert(GA->getTargetFlags() == 0 && 17390b57cec5SDimitry Andric "Unexpected target flags on generic GlobalAddressSDNode"); 1740fe6060f1SDimitry Andric if (!WebAssembly::isValidAddressSpace(GA->getAddressSpace())) 1741fe6060f1SDimitry Andric fail(DL, DAG, "Invalid address space for WebAssembly target"); 17420b57cec5SDimitry Andric 17430b57cec5SDimitry Andric unsigned OperandFlags = 0; 17440b57cec5SDimitry Andric const GlobalValue *GV = GA->getGlobal(); 17455f757f3fSDimitry Andric // Since WebAssembly tables cannot yet be shared accross modules, we don't 17465f757f3fSDimitry Andric // need special treatment for tables in PIC mode. 17475f757f3fSDimitry Andric if (isPositionIndependent() && 17485f757f3fSDimitry Andric !WebAssembly::isWebAssemblyTableType(GV->getValueType())) { 1749*0fca6ea1SDimitry Andric if (getTargetMachine().shouldAssumeDSOLocal(GV)) { 17500b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 17510b57cec5SDimitry Andric MVT PtrVT = getPointerTy(MF.getDataLayout()); 17520b57cec5SDimitry Andric const char *BaseName; 17530b57cec5SDimitry Andric if (GV->getValueType()->isFunctionTy()) { 17540b57cec5SDimitry Andric BaseName = MF.createExternalSymbolName("__table_base"); 17550b57cec5SDimitry Andric OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL; 1756972a253aSDimitry Andric } else { 17570b57cec5SDimitry Andric BaseName = MF.createExternalSymbolName("__memory_base"); 17580b57cec5SDimitry Andric OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL; 17590b57cec5SDimitry Andric } 17600b57cec5SDimitry Andric SDValue BaseAddr = 17610b57cec5SDimitry Andric DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, 17620b57cec5SDimitry Andric DAG.getTargetExternalSymbol(BaseName, PtrVT)); 17630b57cec5SDimitry Andric 17640b57cec5SDimitry Andric SDValue SymAddr = DAG.getNode( 1765349cc55cSDimitry Andric WebAssemblyISD::WrapperREL, DL, VT, 17660b57cec5SDimitry Andric DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), 17670b57cec5SDimitry Andric OperandFlags)); 17680b57cec5SDimitry Andric 17690b57cec5SDimitry Andric return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr); 17700b57cec5SDimitry Andric } 1771349cc55cSDimitry Andric OperandFlags = WebAssemblyII::MO_GOT; 17720b57cec5SDimitry Andric } 17730b57cec5SDimitry Andric 17740b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 17750b57cec5SDimitry Andric DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, 17760b57cec5SDimitry Andric GA->getOffset(), OperandFlags)); 17770b57cec5SDimitry Andric } 17780b57cec5SDimitry Andric 17790b57cec5SDimitry Andric SDValue 17800b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, 17810b57cec5SDimitry Andric SelectionDAG &DAG) const { 17820b57cec5SDimitry Andric SDLoc DL(Op); 17830b57cec5SDimitry Andric const auto *ES = cast<ExternalSymbolSDNode>(Op); 17840b57cec5SDimitry Andric EVT VT = Op.getValueType(); 17850b57cec5SDimitry Andric assert(ES->getTargetFlags() == 0 && 17860b57cec5SDimitry Andric "Unexpected target flags on generic ExternalSymbolSDNode"); 17870b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 17880b57cec5SDimitry Andric DAG.getTargetExternalSymbol(ES->getSymbol(), VT)); 17890b57cec5SDimitry Andric } 17900b57cec5SDimitry Andric 17910b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, 17920b57cec5SDimitry Andric SelectionDAG &DAG) const { 17930b57cec5SDimitry Andric // There's no need for a Wrapper node because we always incorporate a jump 17940b57cec5SDimitry Andric // table operand into a BR_TABLE instruction, rather than ever 17950b57cec5SDimitry Andric // materializing it in a register. 17960b57cec5SDimitry Andric const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); 17970b57cec5SDimitry Andric return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), 17980b57cec5SDimitry Andric JT->getTargetFlags()); 17990b57cec5SDimitry Andric } 18000b57cec5SDimitry Andric 18010b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, 18020b57cec5SDimitry Andric SelectionDAG &DAG) const { 18030b57cec5SDimitry Andric SDLoc DL(Op); 18040b57cec5SDimitry Andric SDValue Chain = Op.getOperand(0); 18050b57cec5SDimitry Andric const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1)); 18060b57cec5SDimitry Andric SDValue Index = Op.getOperand(2); 18070b57cec5SDimitry Andric assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); 18080b57cec5SDimitry Andric 18090b57cec5SDimitry Andric SmallVector<SDValue, 8> Ops; 18100b57cec5SDimitry Andric Ops.push_back(Chain); 18110b57cec5SDimitry Andric Ops.push_back(Index); 18120b57cec5SDimitry Andric 18130b57cec5SDimitry Andric MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); 18140b57cec5SDimitry Andric const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; 18150b57cec5SDimitry Andric 18160b57cec5SDimitry Andric // Add an operand for each case. 1817bdd1243dSDimitry Andric for (auto *MBB : MBBs) 18180b57cec5SDimitry Andric Ops.push_back(DAG.getBasicBlock(MBB)); 18190b57cec5SDimitry Andric 18205ffd83dbSDimitry Andric // Add the first MBB as a dummy default target for now. This will be replaced 18215ffd83dbSDimitry Andric // with the proper default target (and the preceding range check eliminated) 18225ffd83dbSDimitry Andric // if possible by WebAssemblyFixBrTableDefaults. 18235ffd83dbSDimitry Andric Ops.push_back(DAG.getBasicBlock(*MBBs.begin())); 18240b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops); 18250b57cec5SDimitry Andric } 18260b57cec5SDimitry Andric 18270b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, 18280b57cec5SDimitry Andric SelectionDAG &DAG) const { 18290b57cec5SDimitry Andric SDLoc DL(Op); 18300b57cec5SDimitry Andric EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); 18310b57cec5SDimitry Andric 18320b57cec5SDimitry Andric auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>(); 18330b57cec5SDimitry Andric const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); 18340b57cec5SDimitry Andric 18350b57cec5SDimitry Andric SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, 18360b57cec5SDimitry Andric MFI->getVarargBufferVreg(), PtrVT); 18370b57cec5SDimitry Andric return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), 1838e8d8bef9SDimitry Andric MachinePointerInfo(SV)); 1839e8d8bef9SDimitry Andric } 1840e8d8bef9SDimitry Andric 18410b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, 18420b57cec5SDimitry Andric SelectionDAG &DAG) const { 18430b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 18440b57cec5SDimitry Andric unsigned IntNo; 18450b57cec5SDimitry Andric switch (Op.getOpcode()) { 18460b57cec5SDimitry Andric case ISD::INTRINSIC_VOID: 18470b57cec5SDimitry Andric case ISD::INTRINSIC_W_CHAIN: 1848349cc55cSDimitry Andric IntNo = Op.getConstantOperandVal(1); 18490b57cec5SDimitry Andric break; 18500b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: 1851349cc55cSDimitry Andric IntNo = Op.getConstantOperandVal(0); 18520b57cec5SDimitry Andric break; 18530b57cec5SDimitry Andric default: 18540b57cec5SDimitry Andric llvm_unreachable("Invalid intrinsic"); 18550b57cec5SDimitry Andric } 18560b57cec5SDimitry Andric SDLoc DL(Op); 18570b57cec5SDimitry Andric 18580b57cec5SDimitry Andric switch (IntNo) { 18590b57cec5SDimitry Andric default: 18600b57cec5SDimitry Andric return SDValue(); // Don't custom lower most intrinsics. 18610b57cec5SDimitry Andric 18620b57cec5SDimitry Andric case Intrinsic::wasm_lsda: { 1863349cc55cSDimitry Andric auto PtrVT = getPointerTy(MF.getDataLayout()); 1864349cc55cSDimitry Andric const char *SymName = MF.createExternalSymbolName( 1865349cc55cSDimitry Andric "GCC_except_table" + std::to_string(MF.getFunctionNumber())); 1866349cc55cSDimitry Andric if (isPositionIndependent()) { 1867349cc55cSDimitry Andric SDValue Node = DAG.getTargetExternalSymbol( 1868349cc55cSDimitry Andric SymName, PtrVT, WebAssemblyII::MO_MEMORY_BASE_REL); 1869349cc55cSDimitry Andric const char *BaseName = MF.createExternalSymbolName("__memory_base"); 1870349cc55cSDimitry Andric SDValue BaseAddr = 1871349cc55cSDimitry Andric DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, 1872349cc55cSDimitry Andric DAG.getTargetExternalSymbol(BaseName, PtrVT)); 1873349cc55cSDimitry Andric SDValue SymAddr = 1874349cc55cSDimitry Andric DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, Node); 1875349cc55cSDimitry Andric return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymAddr); 18760b57cec5SDimitry Andric } 1877349cc55cSDimitry Andric SDValue Node = DAG.getTargetExternalSymbol(SymName, PtrVT); 1878349cc55cSDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, Node); 1879e8d8bef9SDimitry Andric } 1880e8d8bef9SDimitry Andric 18815ffd83dbSDimitry Andric case Intrinsic::wasm_shuffle: { 18825ffd83dbSDimitry Andric // Drop in-chain and replace undefs, but otherwise pass through unchanged 18835ffd83dbSDimitry Andric SDValue Ops[18]; 18845ffd83dbSDimitry Andric size_t OpIdx = 0; 18855ffd83dbSDimitry Andric Ops[OpIdx++] = Op.getOperand(1); 18865ffd83dbSDimitry Andric Ops[OpIdx++] = Op.getOperand(2); 18875ffd83dbSDimitry Andric while (OpIdx < 18) { 18885ffd83dbSDimitry Andric const SDValue &MaskIdx = Op.getOperand(OpIdx + 1); 18891db9f3b2SDimitry Andric if (MaskIdx.isUndef() || MaskIdx.getNode()->getAsZExtVal() >= 32) { 189006c3fb27SDimitry Andric bool isTarget = MaskIdx.getNode()->getOpcode() == ISD::TargetConstant; 189106c3fb27SDimitry Andric Ops[OpIdx++] = DAG.getConstant(0, DL, MVT::i32, isTarget); 18925ffd83dbSDimitry Andric } else { 18935ffd83dbSDimitry Andric Ops[OpIdx++] = MaskIdx; 18945ffd83dbSDimitry Andric } 18955ffd83dbSDimitry Andric } 18965ffd83dbSDimitry Andric return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); 18975ffd83dbSDimitry Andric } 18980b57cec5SDimitry Andric } 18990b57cec5SDimitry Andric } 19000b57cec5SDimitry Andric 19010b57cec5SDimitry Andric SDValue 19020b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, 19030b57cec5SDimitry Andric SelectionDAG &DAG) const { 19040b57cec5SDimitry Andric SDLoc DL(Op); 19050b57cec5SDimitry Andric // If sign extension operations are disabled, allow sext_inreg only if operand 19065ffd83dbSDimitry Andric // is a vector extract of an i8 or i16 lane. SIMD does not depend on sign 19075ffd83dbSDimitry Andric // extension operations, but allowing sext_inreg in this context lets us have 19085ffd83dbSDimitry Andric // simple patterns to select extract_lane_s instructions. Expanding sext_inreg 19095ffd83dbSDimitry Andric // everywhere would be simpler in this file, but would necessitate large and 19105ffd83dbSDimitry Andric // brittle patterns to undo the expansion and select extract_lane_s 19115ffd83dbSDimitry Andric // instructions. 19120b57cec5SDimitry Andric assert(!Subtarget->hasSignExt() && Subtarget->hasSIMD128()); 19135ffd83dbSDimitry Andric if (Op.getOperand(0).getOpcode() != ISD::EXTRACT_VECTOR_ELT) 19145ffd83dbSDimitry Andric return SDValue(); 19155ffd83dbSDimitry Andric 19160b57cec5SDimitry Andric const SDValue &Extract = Op.getOperand(0); 19170b57cec5SDimitry Andric MVT VecT = Extract.getOperand(0).getSimpleValueType(); 19185ffd83dbSDimitry Andric if (VecT.getVectorElementType().getSizeInBits() > 32) 19195ffd83dbSDimitry Andric return SDValue(); 19205ffd83dbSDimitry Andric MVT ExtractedLaneT = 19215ffd83dbSDimitry Andric cast<VTSDNode>(Op.getOperand(1).getNode())->getVT().getSimpleVT(); 19220b57cec5SDimitry Andric MVT ExtractedVecT = 19230b57cec5SDimitry Andric MVT::getVectorVT(ExtractedLaneT, 128 / ExtractedLaneT.getSizeInBits()); 19240b57cec5SDimitry Andric if (ExtractedVecT == VecT) 19250b57cec5SDimitry Andric return Op; 19265ffd83dbSDimitry Andric 19270b57cec5SDimitry Andric // Bitcast vector to appropriate type to ensure ISel pattern coverage 19285ffd83dbSDimitry Andric const SDNode *Index = Extract.getOperand(1).getNode(); 19295ffd83dbSDimitry Andric if (!isa<ConstantSDNode>(Index)) 19305ffd83dbSDimitry Andric return SDValue(); 19311db9f3b2SDimitry Andric unsigned IndexVal = Index->getAsZExtVal(); 19320b57cec5SDimitry Andric unsigned Scale = 19330b57cec5SDimitry Andric ExtractedVecT.getVectorNumElements() / VecT.getVectorNumElements(); 19340b57cec5SDimitry Andric assert(Scale > 1); 19350b57cec5SDimitry Andric SDValue NewIndex = 19365ffd83dbSDimitry Andric DAG.getConstant(IndexVal * Scale, DL, Index->getValueType(0)); 19370b57cec5SDimitry Andric SDValue NewExtract = DAG.getNode( 19380b57cec5SDimitry Andric ISD::EXTRACT_VECTOR_ELT, DL, Extract.getValueType(), 19390b57cec5SDimitry Andric DAG.getBitcast(ExtractedVecT, Extract.getOperand(0)), NewIndex); 19405ffd83dbSDimitry Andric return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Op.getValueType(), NewExtract, 19415ffd83dbSDimitry Andric Op.getOperand(1)); 19420b57cec5SDimitry Andric } 19430b57cec5SDimitry Andric 194406c3fb27SDimitry Andric SDValue 194506c3fb27SDimitry Andric WebAssemblyTargetLowering::LowerEXTEND_VECTOR_INREG(SDValue Op, 194606c3fb27SDimitry Andric SelectionDAG &DAG) const { 194706c3fb27SDimitry Andric SDLoc DL(Op); 194806c3fb27SDimitry Andric EVT VT = Op.getValueType(); 194906c3fb27SDimitry Andric SDValue Src = Op.getOperand(0); 195006c3fb27SDimitry Andric EVT SrcVT = Src.getValueType(); 195106c3fb27SDimitry Andric 195206c3fb27SDimitry Andric if (SrcVT.getVectorElementType() == MVT::i1 || 195306c3fb27SDimitry Andric SrcVT.getVectorElementType() == MVT::i64) 195406c3fb27SDimitry Andric return SDValue(); 195506c3fb27SDimitry Andric 195606c3fb27SDimitry Andric assert(VT.getScalarSizeInBits() % SrcVT.getScalarSizeInBits() == 0 && 195706c3fb27SDimitry Andric "Unexpected extension factor."); 195806c3fb27SDimitry Andric unsigned Scale = VT.getScalarSizeInBits() / SrcVT.getScalarSizeInBits(); 195906c3fb27SDimitry Andric 196006c3fb27SDimitry Andric if (Scale != 2 && Scale != 4 && Scale != 8) 196106c3fb27SDimitry Andric return SDValue(); 196206c3fb27SDimitry Andric 196306c3fb27SDimitry Andric unsigned Ext; 196406c3fb27SDimitry Andric switch (Op.getOpcode()) { 196506c3fb27SDimitry Andric case ISD::ZERO_EXTEND_VECTOR_INREG: 196606c3fb27SDimitry Andric Ext = WebAssemblyISD::EXTEND_LOW_U; 196706c3fb27SDimitry Andric break; 196806c3fb27SDimitry Andric case ISD::SIGN_EXTEND_VECTOR_INREG: 196906c3fb27SDimitry Andric Ext = WebAssemblyISD::EXTEND_LOW_S; 197006c3fb27SDimitry Andric break; 197106c3fb27SDimitry Andric } 197206c3fb27SDimitry Andric 197306c3fb27SDimitry Andric SDValue Ret = Src; 197406c3fb27SDimitry Andric while (Scale != 1) { 197506c3fb27SDimitry Andric Ret = DAG.getNode(Ext, DL, 197606c3fb27SDimitry Andric Ret.getValueType() 197706c3fb27SDimitry Andric .widenIntegerVectorElementType(*DAG.getContext()) 197806c3fb27SDimitry Andric .getHalfNumVectorElementsVT(*DAG.getContext()), 197906c3fb27SDimitry Andric Ret); 198006c3fb27SDimitry Andric Scale /= 2; 198106c3fb27SDimitry Andric } 198206c3fb27SDimitry Andric assert(Ret.getValueType() == VT); 198306c3fb27SDimitry Andric return Ret; 198406c3fb27SDimitry Andric } 198506c3fb27SDimitry Andric 1986349cc55cSDimitry Andric static SDValue LowerConvertLow(SDValue Op, SelectionDAG &DAG) { 1987349cc55cSDimitry Andric SDLoc DL(Op); 1988349cc55cSDimitry Andric if (Op.getValueType() != MVT::v2f64) 1989349cc55cSDimitry Andric return SDValue(); 1990349cc55cSDimitry Andric 1991349cc55cSDimitry Andric auto GetConvertedLane = [](SDValue Op, unsigned &Opcode, SDValue &SrcVec, 1992349cc55cSDimitry Andric unsigned &Index) -> bool { 1993349cc55cSDimitry Andric switch (Op.getOpcode()) { 1994349cc55cSDimitry Andric case ISD::SINT_TO_FP: 1995349cc55cSDimitry Andric Opcode = WebAssemblyISD::CONVERT_LOW_S; 1996349cc55cSDimitry Andric break; 1997349cc55cSDimitry Andric case ISD::UINT_TO_FP: 1998349cc55cSDimitry Andric Opcode = WebAssemblyISD::CONVERT_LOW_U; 1999349cc55cSDimitry Andric break; 2000349cc55cSDimitry Andric case ISD::FP_EXTEND: 2001349cc55cSDimitry Andric Opcode = WebAssemblyISD::PROMOTE_LOW; 2002349cc55cSDimitry Andric break; 2003349cc55cSDimitry Andric default: 2004349cc55cSDimitry Andric return false; 2005349cc55cSDimitry Andric } 2006349cc55cSDimitry Andric 2007349cc55cSDimitry Andric auto ExtractVector = Op.getOperand(0); 2008349cc55cSDimitry Andric if (ExtractVector.getOpcode() != ISD::EXTRACT_VECTOR_ELT) 2009349cc55cSDimitry Andric return false; 2010349cc55cSDimitry Andric 2011349cc55cSDimitry Andric if (!isa<ConstantSDNode>(ExtractVector.getOperand(1).getNode())) 2012349cc55cSDimitry Andric return false; 2013349cc55cSDimitry Andric 2014349cc55cSDimitry Andric SrcVec = ExtractVector.getOperand(0); 2015349cc55cSDimitry Andric Index = ExtractVector.getConstantOperandVal(1); 2016349cc55cSDimitry Andric return true; 2017349cc55cSDimitry Andric }; 2018349cc55cSDimitry Andric 2019349cc55cSDimitry Andric unsigned LHSOpcode, RHSOpcode, LHSIndex, RHSIndex; 2020349cc55cSDimitry Andric SDValue LHSSrcVec, RHSSrcVec; 2021349cc55cSDimitry Andric if (!GetConvertedLane(Op.getOperand(0), LHSOpcode, LHSSrcVec, LHSIndex) || 2022349cc55cSDimitry Andric !GetConvertedLane(Op.getOperand(1), RHSOpcode, RHSSrcVec, RHSIndex)) 2023349cc55cSDimitry Andric return SDValue(); 2024349cc55cSDimitry Andric 2025349cc55cSDimitry Andric if (LHSOpcode != RHSOpcode) 2026349cc55cSDimitry Andric return SDValue(); 2027349cc55cSDimitry Andric 2028349cc55cSDimitry Andric MVT ExpectedSrcVT; 2029349cc55cSDimitry Andric switch (LHSOpcode) { 2030349cc55cSDimitry Andric case WebAssemblyISD::CONVERT_LOW_S: 2031349cc55cSDimitry Andric case WebAssemblyISD::CONVERT_LOW_U: 2032349cc55cSDimitry Andric ExpectedSrcVT = MVT::v4i32; 2033349cc55cSDimitry Andric break; 2034349cc55cSDimitry Andric case WebAssemblyISD::PROMOTE_LOW: 2035349cc55cSDimitry Andric ExpectedSrcVT = MVT::v4f32; 2036349cc55cSDimitry Andric break; 2037349cc55cSDimitry Andric } 2038349cc55cSDimitry Andric if (LHSSrcVec.getValueType() != ExpectedSrcVT) 2039349cc55cSDimitry Andric return SDValue(); 2040349cc55cSDimitry Andric 2041349cc55cSDimitry Andric auto Src = LHSSrcVec; 2042349cc55cSDimitry Andric if (LHSIndex != 0 || RHSIndex != 1 || LHSSrcVec != RHSSrcVec) { 2043349cc55cSDimitry Andric // Shuffle the source vector so that the converted lanes are the low lanes. 2044349cc55cSDimitry Andric Src = DAG.getVectorShuffle( 2045349cc55cSDimitry Andric ExpectedSrcVT, DL, LHSSrcVec, RHSSrcVec, 2046349cc55cSDimitry Andric {static_cast<int>(LHSIndex), static_cast<int>(RHSIndex) + 4, -1, -1}); 2047349cc55cSDimitry Andric } 2048349cc55cSDimitry Andric return DAG.getNode(LHSOpcode, DL, MVT::v2f64, Src); 2049349cc55cSDimitry Andric } 2050349cc55cSDimitry Andric 20510b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, 20520b57cec5SDimitry Andric SelectionDAG &DAG) const { 2053349cc55cSDimitry Andric if (auto ConvertLow = LowerConvertLow(Op, DAG)) 2054349cc55cSDimitry Andric return ConvertLow; 2055349cc55cSDimitry Andric 20560b57cec5SDimitry Andric SDLoc DL(Op); 20570b57cec5SDimitry Andric const EVT VecT = Op.getValueType(); 20580b57cec5SDimitry Andric const EVT LaneT = Op.getOperand(0).getValueType(); 20590b57cec5SDimitry Andric const size_t Lanes = Op.getNumOperands(); 20605ffd83dbSDimitry Andric bool CanSwizzle = VecT == MVT::v16i8; 20618bcb0991SDimitry Andric 20628bcb0991SDimitry Andric // BUILD_VECTORs are lowered to the instruction that initializes the highest 20638bcb0991SDimitry Andric // possible number of lanes at once followed by a sequence of replace_lane 20648bcb0991SDimitry Andric // instructions to individually initialize any remaining lanes. 20658bcb0991SDimitry Andric 20668bcb0991SDimitry Andric // TODO: Tune this. For example, lanewise swizzling is very expensive, so 20678bcb0991SDimitry Andric // swizzled lanes should be given greater weight. 20688bcb0991SDimitry Andric 2069fe6060f1SDimitry Andric // TODO: Investigate looping rather than always extracting/replacing specific 2070fe6060f1SDimitry Andric // lanes to fill gaps. 20718bcb0991SDimitry Andric 20720b57cec5SDimitry Andric auto IsConstant = [](const SDValue &V) { 20730b57cec5SDimitry Andric return V.getOpcode() == ISD::Constant || V.getOpcode() == ISD::ConstantFP; 20740b57cec5SDimitry Andric }; 20750b57cec5SDimitry Andric 20768bcb0991SDimitry Andric // Returns the source vector and index vector pair if they exist. Checks for: 20778bcb0991SDimitry Andric // (extract_vector_elt 20788bcb0991SDimitry Andric // $src, 20798bcb0991SDimitry Andric // (sign_extend_inreg (extract_vector_elt $indices, $i)) 20808bcb0991SDimitry Andric // ) 20818bcb0991SDimitry Andric auto GetSwizzleSrcs = [](size_t I, const SDValue &Lane) { 20828bcb0991SDimitry Andric auto Bail = std::make_pair(SDValue(), SDValue()); 20838bcb0991SDimitry Andric if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT) 20848bcb0991SDimitry Andric return Bail; 20858bcb0991SDimitry Andric const SDValue &SwizzleSrc = Lane->getOperand(0); 20868bcb0991SDimitry Andric const SDValue &IndexExt = Lane->getOperand(1); 20878bcb0991SDimitry Andric if (IndexExt->getOpcode() != ISD::SIGN_EXTEND_INREG) 20888bcb0991SDimitry Andric return Bail; 20898bcb0991SDimitry Andric const SDValue &Index = IndexExt->getOperand(0); 20908bcb0991SDimitry Andric if (Index->getOpcode() != ISD::EXTRACT_VECTOR_ELT) 20918bcb0991SDimitry Andric return Bail; 20928bcb0991SDimitry Andric const SDValue &SwizzleIndices = Index->getOperand(0); 20938bcb0991SDimitry Andric if (SwizzleSrc.getValueType() != MVT::v16i8 || 20948bcb0991SDimitry Andric SwizzleIndices.getValueType() != MVT::v16i8 || 20958bcb0991SDimitry Andric Index->getOperand(1)->getOpcode() != ISD::Constant || 20968bcb0991SDimitry Andric Index->getConstantOperandVal(1) != I) 20978bcb0991SDimitry Andric return Bail; 20988bcb0991SDimitry Andric return std::make_pair(SwizzleSrc, SwizzleIndices); 20998bcb0991SDimitry Andric }; 21008bcb0991SDimitry Andric 2101fe6060f1SDimitry Andric // If the lane is extracted from another vector at a constant index, return 2102fe6060f1SDimitry Andric // that vector. The source vector must not have more lanes than the dest 2103fe6060f1SDimitry Andric // because the shufflevector indices are in terms of the destination lanes and 2104fe6060f1SDimitry Andric // would not be able to address the smaller individual source lanes. 2105fe6060f1SDimitry Andric auto GetShuffleSrc = [&](const SDValue &Lane) { 2106fe6060f1SDimitry Andric if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT) 2107fe6060f1SDimitry Andric return SDValue(); 2108fe6060f1SDimitry Andric if (!isa<ConstantSDNode>(Lane->getOperand(1).getNode())) 2109fe6060f1SDimitry Andric return SDValue(); 2110fe6060f1SDimitry Andric if (Lane->getOperand(0).getValueType().getVectorNumElements() > 2111fe6060f1SDimitry Andric VecT.getVectorNumElements()) 2112fe6060f1SDimitry Andric return SDValue(); 2113fe6060f1SDimitry Andric return Lane->getOperand(0); 2114fe6060f1SDimitry Andric }; 2115fe6060f1SDimitry Andric 21168bcb0991SDimitry Andric using ValueEntry = std::pair<SDValue, size_t>; 21178bcb0991SDimitry Andric SmallVector<ValueEntry, 16> SplatValueCounts; 21188bcb0991SDimitry Andric 21198bcb0991SDimitry Andric using SwizzleEntry = std::pair<std::pair<SDValue, SDValue>, size_t>; 21208bcb0991SDimitry Andric SmallVector<SwizzleEntry, 16> SwizzleCounts; 21218bcb0991SDimitry Andric 2122fe6060f1SDimitry Andric using ShuffleEntry = std::pair<SDValue, size_t>; 2123fe6060f1SDimitry Andric SmallVector<ShuffleEntry, 16> ShuffleCounts; 2124fe6060f1SDimitry Andric 21258bcb0991SDimitry Andric auto AddCount = [](auto &Counts, const auto &Val) { 2126e8d8bef9SDimitry Andric auto CountIt = 2127e8d8bef9SDimitry Andric llvm::find_if(Counts, [&Val](auto E) { return E.first == Val; }); 21288bcb0991SDimitry Andric if (CountIt == Counts.end()) { 21298bcb0991SDimitry Andric Counts.emplace_back(Val, 1); 21300b57cec5SDimitry Andric } else { 21310b57cec5SDimitry Andric CountIt->second++; 21320b57cec5SDimitry Andric } 21338bcb0991SDimitry Andric }; 21340b57cec5SDimitry Andric 21358bcb0991SDimitry Andric auto GetMostCommon = [](auto &Counts) { 21368bcb0991SDimitry Andric auto CommonIt = 213781ad6265SDimitry Andric std::max_element(Counts.begin(), Counts.end(), llvm::less_second()); 21388bcb0991SDimitry Andric assert(CommonIt != Counts.end() && "Unexpected all-undef build_vector"); 21398bcb0991SDimitry Andric return *CommonIt; 21408bcb0991SDimitry Andric }; 21418bcb0991SDimitry Andric 21428bcb0991SDimitry Andric size_t NumConstantLanes = 0; 21438bcb0991SDimitry Andric 21448bcb0991SDimitry Andric // Count eligible lanes for each type of vector creation op 21458bcb0991SDimitry Andric for (size_t I = 0; I < Lanes; ++I) { 21468bcb0991SDimitry Andric const SDValue &Lane = Op->getOperand(I); 21478bcb0991SDimitry Andric if (Lane.isUndef()) 21488bcb0991SDimitry Andric continue; 21498bcb0991SDimitry Andric 21508bcb0991SDimitry Andric AddCount(SplatValueCounts, Lane); 21518bcb0991SDimitry Andric 2152fe6060f1SDimitry Andric if (IsConstant(Lane)) 21538bcb0991SDimitry Andric NumConstantLanes++; 2154fe6060f1SDimitry Andric if (auto ShuffleSrc = GetShuffleSrc(Lane)) 2155fe6060f1SDimitry Andric AddCount(ShuffleCounts, ShuffleSrc); 2156fe6060f1SDimitry Andric if (CanSwizzle) { 21578bcb0991SDimitry Andric auto SwizzleSrcs = GetSwizzleSrcs(I, Lane); 21588bcb0991SDimitry Andric if (SwizzleSrcs.first) 21598bcb0991SDimitry Andric AddCount(SwizzleCounts, SwizzleSrcs); 21608bcb0991SDimitry Andric } 21618bcb0991SDimitry Andric } 21628bcb0991SDimitry Andric 21638bcb0991SDimitry Andric SDValue SplatValue; 21648bcb0991SDimitry Andric size_t NumSplatLanes; 21658bcb0991SDimitry Andric std::tie(SplatValue, NumSplatLanes) = GetMostCommon(SplatValueCounts); 21668bcb0991SDimitry Andric 21678bcb0991SDimitry Andric SDValue SwizzleSrc; 21688bcb0991SDimitry Andric SDValue SwizzleIndices; 21698bcb0991SDimitry Andric size_t NumSwizzleLanes = 0; 21708bcb0991SDimitry Andric if (SwizzleCounts.size()) 21718bcb0991SDimitry Andric std::forward_as_tuple(std::tie(SwizzleSrc, SwizzleIndices), 21728bcb0991SDimitry Andric NumSwizzleLanes) = GetMostCommon(SwizzleCounts); 21738bcb0991SDimitry Andric 2174fe6060f1SDimitry Andric // Shuffles can draw from up to two vectors, so find the two most common 2175fe6060f1SDimitry Andric // sources. 2176fe6060f1SDimitry Andric SDValue ShuffleSrc1, ShuffleSrc2; 2177fe6060f1SDimitry Andric size_t NumShuffleLanes = 0; 2178fe6060f1SDimitry Andric if (ShuffleCounts.size()) { 2179fe6060f1SDimitry Andric std::tie(ShuffleSrc1, NumShuffleLanes) = GetMostCommon(ShuffleCounts); 2180349cc55cSDimitry Andric llvm::erase_if(ShuffleCounts, 2181349cc55cSDimitry Andric [&](const auto &Pair) { return Pair.first == ShuffleSrc1; }); 2182fe6060f1SDimitry Andric } 2183fe6060f1SDimitry Andric if (ShuffleCounts.size()) { 2184fe6060f1SDimitry Andric size_t AdditionalShuffleLanes; 2185fe6060f1SDimitry Andric std::tie(ShuffleSrc2, AdditionalShuffleLanes) = 2186fe6060f1SDimitry Andric GetMostCommon(ShuffleCounts); 2187fe6060f1SDimitry Andric NumShuffleLanes += AdditionalShuffleLanes; 2188fe6060f1SDimitry Andric } 2189fe6060f1SDimitry Andric 21908bcb0991SDimitry Andric // Predicate returning true if the lane is properly initialized by the 21918bcb0991SDimitry Andric // original instruction 21928bcb0991SDimitry Andric std::function<bool(size_t, const SDValue &)> IsLaneConstructed; 21938bcb0991SDimitry Andric SDValue Result; 2194fe6060f1SDimitry Andric // Prefer swizzles over shuffles over vector consts over splats 2195fe6060f1SDimitry Andric if (NumSwizzleLanes >= NumShuffleLanes && 2196fe6060f1SDimitry Andric NumSwizzleLanes >= NumConstantLanes && NumSwizzleLanes >= NumSplatLanes) { 21978bcb0991SDimitry Andric Result = DAG.getNode(WebAssemblyISD::SWIZZLE, DL, VecT, SwizzleSrc, 21988bcb0991SDimitry Andric SwizzleIndices); 21998bcb0991SDimitry Andric auto Swizzled = std::make_pair(SwizzleSrc, SwizzleIndices); 22008bcb0991SDimitry Andric IsLaneConstructed = [&, Swizzled](size_t I, const SDValue &Lane) { 22018bcb0991SDimitry Andric return Swizzled == GetSwizzleSrcs(I, Lane); 22028bcb0991SDimitry Andric }; 2203fe6060f1SDimitry Andric } else if (NumShuffleLanes >= NumConstantLanes && 2204fe6060f1SDimitry Andric NumShuffleLanes >= NumSplatLanes) { 2205fe6060f1SDimitry Andric size_t DestLaneSize = VecT.getVectorElementType().getFixedSizeInBits() / 8; 2206fe6060f1SDimitry Andric size_t DestLaneCount = VecT.getVectorNumElements(); 2207fe6060f1SDimitry Andric size_t Scale1 = 1; 2208fe6060f1SDimitry Andric size_t Scale2 = 1; 2209fe6060f1SDimitry Andric SDValue Src1 = ShuffleSrc1; 2210fe6060f1SDimitry Andric SDValue Src2 = ShuffleSrc2 ? ShuffleSrc2 : DAG.getUNDEF(VecT); 2211fe6060f1SDimitry Andric if (Src1.getValueType() != VecT) { 2212fe6060f1SDimitry Andric size_t LaneSize = 2213fe6060f1SDimitry Andric Src1.getValueType().getVectorElementType().getFixedSizeInBits() / 8; 2214fe6060f1SDimitry Andric assert(LaneSize > DestLaneSize); 2215fe6060f1SDimitry Andric Scale1 = LaneSize / DestLaneSize; 2216fe6060f1SDimitry Andric Src1 = DAG.getBitcast(VecT, Src1); 2217fe6060f1SDimitry Andric } 2218fe6060f1SDimitry Andric if (Src2.getValueType() != VecT) { 2219fe6060f1SDimitry Andric size_t LaneSize = 2220fe6060f1SDimitry Andric Src2.getValueType().getVectorElementType().getFixedSizeInBits() / 8; 2221fe6060f1SDimitry Andric assert(LaneSize > DestLaneSize); 2222fe6060f1SDimitry Andric Scale2 = LaneSize / DestLaneSize; 2223fe6060f1SDimitry Andric Src2 = DAG.getBitcast(VecT, Src2); 2224fe6060f1SDimitry Andric } 2225fe6060f1SDimitry Andric 2226fe6060f1SDimitry Andric int Mask[16]; 2227fe6060f1SDimitry Andric assert(DestLaneCount <= 16); 2228fe6060f1SDimitry Andric for (size_t I = 0; I < DestLaneCount; ++I) { 2229fe6060f1SDimitry Andric const SDValue &Lane = Op->getOperand(I); 2230fe6060f1SDimitry Andric SDValue Src = GetShuffleSrc(Lane); 2231fe6060f1SDimitry Andric if (Src == ShuffleSrc1) { 2232fe6060f1SDimitry Andric Mask[I] = Lane->getConstantOperandVal(1) * Scale1; 2233fe6060f1SDimitry Andric } else if (Src && Src == ShuffleSrc2) { 2234fe6060f1SDimitry Andric Mask[I] = DestLaneCount + Lane->getConstantOperandVal(1) * Scale2; 2235fe6060f1SDimitry Andric } else { 2236fe6060f1SDimitry Andric Mask[I] = -1; 2237fe6060f1SDimitry Andric } 2238fe6060f1SDimitry Andric } 2239fe6060f1SDimitry Andric ArrayRef<int> MaskRef(Mask, DestLaneCount); 2240fe6060f1SDimitry Andric Result = DAG.getVectorShuffle(VecT, DL, Src1, Src2, MaskRef); 2241fe6060f1SDimitry Andric IsLaneConstructed = [&](size_t, const SDValue &Lane) { 2242fe6060f1SDimitry Andric auto Src = GetShuffleSrc(Lane); 2243fe6060f1SDimitry Andric return Src == ShuffleSrc1 || (Src && Src == ShuffleSrc2); 2244fe6060f1SDimitry Andric }; 2245fe6060f1SDimitry Andric } else if (NumConstantLanes >= NumSplatLanes) { 22460b57cec5SDimitry Andric SmallVector<SDValue, 16> ConstLanes; 22470b57cec5SDimitry Andric for (const SDValue &Lane : Op->op_values()) { 22480b57cec5SDimitry Andric if (IsConstant(Lane)) { 2249349cc55cSDimitry Andric // Values may need to be fixed so that they will sign extend to be 2250349cc55cSDimitry Andric // within the expected range during ISel. Check whether the value is in 2251349cc55cSDimitry Andric // bounds based on the lane bit width and if it is out of bounds, lop 2252349cc55cSDimitry Andric // off the extra bits and subtract 2^n to reflect giving the high bit 2253349cc55cSDimitry Andric // value -2^(n-1) rather than +2^(n-1). Skip the i64 case because it 2254349cc55cSDimitry Andric // cannot possibly be out of range. 2255349cc55cSDimitry Andric auto *Const = dyn_cast<ConstantSDNode>(Lane.getNode()); 2256349cc55cSDimitry Andric int64_t Val = Const ? Const->getSExtValue() : 0; 2257349cc55cSDimitry Andric uint64_t LaneBits = 128 / Lanes; 2258349cc55cSDimitry Andric assert((LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && 2259349cc55cSDimitry Andric "Unexpected out of bounds negative value"); 2260349cc55cSDimitry Andric if (Const && LaneBits != 64 && Val > (1ll << (LaneBits - 1)) - 1) { 226106c3fb27SDimitry Andric uint64_t Mask = (1ll << LaneBits) - 1; 226206c3fb27SDimitry Andric auto NewVal = (((uint64_t)Val & Mask) - (1ll << LaneBits)) & Mask; 2263349cc55cSDimitry Andric ConstLanes.push_back(DAG.getConstant(NewVal, SDLoc(Lane), LaneT)); 2264349cc55cSDimitry Andric } else { 22650b57cec5SDimitry Andric ConstLanes.push_back(Lane); 2266349cc55cSDimitry Andric } 22670b57cec5SDimitry Andric } else if (LaneT.isFloatingPoint()) { 22680b57cec5SDimitry Andric ConstLanes.push_back(DAG.getConstantFP(0, DL, LaneT)); 22690b57cec5SDimitry Andric } else { 22700b57cec5SDimitry Andric ConstLanes.push_back(DAG.getConstant(0, DL, LaneT)); 22710b57cec5SDimitry Andric } 22720b57cec5SDimitry Andric } 22738bcb0991SDimitry Andric Result = DAG.getBuildVector(VecT, DL, ConstLanes); 2274e8d8bef9SDimitry Andric IsLaneConstructed = [&IsConstant](size_t _, const SDValue &Lane) { 22758bcb0991SDimitry Andric return IsConstant(Lane); 22768bcb0991SDimitry Andric }; 2277e8d8bef9SDimitry Andric } else { 2278bdd1243dSDimitry Andric // Use a splat (which might be selected as a load splat) 22798bcb0991SDimitry Andric Result = DAG.getSplatBuildVector(VecT, DL, SplatValue); 2280e8d8bef9SDimitry Andric IsLaneConstructed = [&SplatValue](size_t _, const SDValue &Lane) { 22818bcb0991SDimitry Andric return Lane == SplatValue; 22828bcb0991SDimitry Andric }; 22838bcb0991SDimitry Andric } 22848bcb0991SDimitry Andric 2285e8d8bef9SDimitry Andric assert(Result); 2286e8d8bef9SDimitry Andric assert(IsLaneConstructed); 2287e8d8bef9SDimitry Andric 22888bcb0991SDimitry Andric // Add replace_lane instructions for any unhandled values 22890b57cec5SDimitry Andric for (size_t I = 0; I < Lanes; ++I) { 22900b57cec5SDimitry Andric const SDValue &Lane = Op->getOperand(I); 22918bcb0991SDimitry Andric if (!Lane.isUndef() && !IsLaneConstructed(I, Lane)) 22920b57cec5SDimitry Andric Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane, 22930b57cec5SDimitry Andric DAG.getConstant(I, DL, MVT::i32)); 22940b57cec5SDimitry Andric } 22958bcb0991SDimitry Andric 22960b57cec5SDimitry Andric return Result; 22970b57cec5SDimitry Andric } 22980b57cec5SDimitry Andric 22990b57cec5SDimitry Andric SDValue 23000b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op, 23010b57cec5SDimitry Andric SelectionDAG &DAG) const { 23020b57cec5SDimitry Andric SDLoc DL(Op); 23030b57cec5SDimitry Andric ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask(); 23040b57cec5SDimitry Andric MVT VecType = Op.getOperand(0).getSimpleValueType(); 23050b57cec5SDimitry Andric assert(VecType.is128BitVector() && "Unexpected shuffle vector type"); 23060b57cec5SDimitry Andric size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8; 23070b57cec5SDimitry Andric 23080b57cec5SDimitry Andric // Space for two vector args and sixteen mask indices 23090b57cec5SDimitry Andric SDValue Ops[18]; 23100b57cec5SDimitry Andric size_t OpIdx = 0; 23110b57cec5SDimitry Andric Ops[OpIdx++] = Op.getOperand(0); 23120b57cec5SDimitry Andric Ops[OpIdx++] = Op.getOperand(1); 23130b57cec5SDimitry Andric 23140b57cec5SDimitry Andric // Expand mask indices to byte indices and materialize them as operands 23150b57cec5SDimitry Andric for (int M : Mask) { 23160b57cec5SDimitry Andric for (size_t J = 0; J < LaneBytes; ++J) { 2317bdd1243dSDimitry Andric // Lower undefs (represented by -1 in mask) to {0..J}, which use a 2318bdd1243dSDimitry Andric // whole lane of vector input, to allow further reduction at VM. E.g. 2319bdd1243dSDimitry Andric // match an 8x16 byte shuffle to an equivalent cheaper 32x4 shuffle. 2320bdd1243dSDimitry Andric uint64_t ByteIndex = M == -1 ? J : (uint64_t)M * LaneBytes + J; 23210b57cec5SDimitry Andric Ops[OpIdx++] = DAG.getConstant(ByteIndex, DL, MVT::i32); 23220b57cec5SDimitry Andric } 23230b57cec5SDimitry Andric } 23240b57cec5SDimitry Andric 23250b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); 23260b57cec5SDimitry Andric } 23270b57cec5SDimitry Andric 2328480093f4SDimitry Andric SDValue WebAssemblyTargetLowering::LowerSETCC(SDValue Op, 2329480093f4SDimitry Andric SelectionDAG &DAG) const { 2330480093f4SDimitry Andric SDLoc DL(Op); 2331fe6060f1SDimitry Andric // The legalizer does not know how to expand the unsupported comparison modes 2332fe6060f1SDimitry Andric // of i64x2 vectors, so we manually unroll them here. 2333480093f4SDimitry Andric assert(Op->getOperand(0)->getSimpleValueType(0) == MVT::v2i64); 2334480093f4SDimitry Andric SmallVector<SDValue, 2> LHS, RHS; 2335480093f4SDimitry Andric DAG.ExtractVectorElements(Op->getOperand(0), LHS); 2336480093f4SDimitry Andric DAG.ExtractVectorElements(Op->getOperand(1), RHS); 2337480093f4SDimitry Andric const SDValue &CC = Op->getOperand(2); 2338480093f4SDimitry Andric auto MakeLane = [&](unsigned I) { 2339480093f4SDimitry Andric return DAG.getNode(ISD::SELECT_CC, DL, MVT::i64, LHS[I], RHS[I], 2340480093f4SDimitry Andric DAG.getConstant(uint64_t(-1), DL, MVT::i64), 2341480093f4SDimitry Andric DAG.getConstant(uint64_t(0), DL, MVT::i64), CC); 2342480093f4SDimitry Andric }; 2343480093f4SDimitry Andric return DAG.getBuildVector(Op->getValueType(0), DL, 2344480093f4SDimitry Andric {MakeLane(0), MakeLane(1)}); 2345480093f4SDimitry Andric } 2346480093f4SDimitry Andric 23470b57cec5SDimitry Andric SDValue 23480b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerAccessVectorElement(SDValue Op, 23490b57cec5SDimitry Andric SelectionDAG &DAG) const { 23500b57cec5SDimitry Andric // Allow constant lane indices, expand variable lane indices 23510b57cec5SDimitry Andric SDNode *IdxNode = Op.getOperand(Op.getNumOperands() - 1).getNode(); 235206c3fb27SDimitry Andric if (isa<ConstantSDNode>(IdxNode)) { 2353bdd1243dSDimitry Andric // Ensure the index type is i32 to match the tablegen patterns 23541db9f3b2SDimitry Andric uint64_t Idx = IdxNode->getAsZExtVal(); 2355bdd1243dSDimitry Andric SmallVector<SDValue, 3> Ops(Op.getNode()->ops()); 2356bdd1243dSDimitry Andric Ops[Op.getNumOperands() - 1] = 2357bdd1243dSDimitry Andric DAG.getConstant(Idx, SDLoc(IdxNode), MVT::i32); 2358bdd1243dSDimitry Andric return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(), Ops); 2359bdd1243dSDimitry Andric } 23600b57cec5SDimitry Andric // Perform default expansion 23610b57cec5SDimitry Andric return SDValue(); 23620b57cec5SDimitry Andric } 23630b57cec5SDimitry Andric 23640b57cec5SDimitry Andric static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG) { 23650b57cec5SDimitry Andric EVT LaneT = Op.getSimpleValueType().getVectorElementType(); 23660b57cec5SDimitry Andric // 32-bit and 64-bit unrolled shifts will have proper semantics 23670b57cec5SDimitry Andric if (LaneT.bitsGE(MVT::i32)) 23680b57cec5SDimitry Andric return DAG.UnrollVectorOp(Op.getNode()); 23690b57cec5SDimitry Andric // Otherwise mask the shift value to get proper semantics from 32-bit shift 23700b57cec5SDimitry Andric SDLoc DL(Op); 23715ffd83dbSDimitry Andric size_t NumLanes = Op.getSimpleValueType().getVectorNumElements(); 23725ffd83dbSDimitry Andric SDValue Mask = DAG.getConstant(LaneT.getSizeInBits() - 1, DL, MVT::i32); 23735ffd83dbSDimitry Andric unsigned ShiftOpcode = Op.getOpcode(); 23745ffd83dbSDimitry Andric SmallVector<SDValue, 16> ShiftedElements; 23755ffd83dbSDimitry Andric DAG.ExtractVectorElements(Op.getOperand(0), ShiftedElements, 0, 0, MVT::i32); 23765ffd83dbSDimitry Andric SmallVector<SDValue, 16> ShiftElements; 23775ffd83dbSDimitry Andric DAG.ExtractVectorElements(Op.getOperand(1), ShiftElements, 0, 0, MVT::i32); 23785ffd83dbSDimitry Andric SmallVector<SDValue, 16> UnrolledOps; 23795ffd83dbSDimitry Andric for (size_t i = 0; i < NumLanes; ++i) { 23805ffd83dbSDimitry Andric SDValue MaskedShiftValue = 23815ffd83dbSDimitry Andric DAG.getNode(ISD::AND, DL, MVT::i32, ShiftElements[i], Mask); 23825ffd83dbSDimitry Andric SDValue ShiftedValue = ShiftedElements[i]; 23835ffd83dbSDimitry Andric if (ShiftOpcode == ISD::SRA) 23845ffd83dbSDimitry Andric ShiftedValue = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, MVT::i32, 23855ffd83dbSDimitry Andric ShiftedValue, DAG.getValueType(LaneT)); 23865ffd83dbSDimitry Andric UnrolledOps.push_back( 23875ffd83dbSDimitry Andric DAG.getNode(ShiftOpcode, DL, MVT::i32, ShiftedValue, MaskedShiftValue)); 23885ffd83dbSDimitry Andric } 23895ffd83dbSDimitry Andric return DAG.getBuildVector(Op.getValueType(), DL, UnrolledOps); 23900b57cec5SDimitry Andric } 23910b57cec5SDimitry Andric 23920b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op, 23930b57cec5SDimitry Andric SelectionDAG &DAG) const { 23940b57cec5SDimitry Andric SDLoc DL(Op); 23950b57cec5SDimitry Andric 23960b57cec5SDimitry Andric // Only manually lower vector shifts 23970b57cec5SDimitry Andric assert(Op.getSimpleValueType().isVector()); 23980b57cec5SDimitry Andric 239906c3fb27SDimitry Andric uint64_t LaneBits = Op.getValueType().getScalarSizeInBits(); 240006c3fb27SDimitry Andric auto ShiftVal = Op.getOperand(1); 240106c3fb27SDimitry Andric 240206c3fb27SDimitry Andric // Try to skip bitmask operation since it is implied inside shift instruction 240306c3fb27SDimitry Andric auto SkipImpliedMask = [](SDValue MaskOp, uint64_t MaskBits) { 240406c3fb27SDimitry Andric if (MaskOp.getOpcode() != ISD::AND) 240506c3fb27SDimitry Andric return MaskOp; 240606c3fb27SDimitry Andric SDValue LHS = MaskOp.getOperand(0); 240706c3fb27SDimitry Andric SDValue RHS = MaskOp.getOperand(1); 240806c3fb27SDimitry Andric if (MaskOp.getValueType().isVector()) { 240906c3fb27SDimitry Andric APInt MaskVal; 241006c3fb27SDimitry Andric if (!ISD::isConstantSplatVector(RHS.getNode(), MaskVal)) 241106c3fb27SDimitry Andric std::swap(LHS, RHS); 241206c3fb27SDimitry Andric 241306c3fb27SDimitry Andric if (ISD::isConstantSplatVector(RHS.getNode(), MaskVal) && 241406c3fb27SDimitry Andric MaskVal == MaskBits) 241506c3fb27SDimitry Andric MaskOp = LHS; 241606c3fb27SDimitry Andric } else { 241706c3fb27SDimitry Andric if (!isa<ConstantSDNode>(RHS.getNode())) 241806c3fb27SDimitry Andric std::swap(LHS, RHS); 241906c3fb27SDimitry Andric 242006c3fb27SDimitry Andric auto ConstantRHS = dyn_cast<ConstantSDNode>(RHS.getNode()); 242106c3fb27SDimitry Andric if (ConstantRHS && ConstantRHS->getAPIntValue() == MaskBits) 242206c3fb27SDimitry Andric MaskOp = LHS; 242306c3fb27SDimitry Andric } 242406c3fb27SDimitry Andric 242506c3fb27SDimitry Andric return MaskOp; 242606c3fb27SDimitry Andric }; 242706c3fb27SDimitry Andric 242806c3fb27SDimitry Andric // Skip vector and operation 242906c3fb27SDimitry Andric ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); 243006c3fb27SDimitry Andric ShiftVal = DAG.getSplatValue(ShiftVal); 24315ffd83dbSDimitry Andric if (!ShiftVal) 24320b57cec5SDimitry Andric return unrollVectorShift(Op, DAG); 24330b57cec5SDimitry Andric 243406c3fb27SDimitry Andric // Skip scalar and operation 243506c3fb27SDimitry Andric ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); 24365ffd83dbSDimitry Andric // Use anyext because none of the high bits can affect the shift 24375ffd83dbSDimitry Andric ShiftVal = DAG.getAnyExtOrTrunc(ShiftVal, DL, MVT::i32); 24380b57cec5SDimitry Andric 24390b57cec5SDimitry Andric unsigned Opcode; 24400b57cec5SDimitry Andric switch (Op.getOpcode()) { 24410b57cec5SDimitry Andric case ISD::SHL: 24420b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHL; 24430b57cec5SDimitry Andric break; 24440b57cec5SDimitry Andric case ISD::SRA: 24450b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHR_S; 24460b57cec5SDimitry Andric break; 24470b57cec5SDimitry Andric case ISD::SRL: 24480b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHR_U; 24490b57cec5SDimitry Andric break; 24500b57cec5SDimitry Andric default: 24510b57cec5SDimitry Andric llvm_unreachable("unexpected opcode"); 24520b57cec5SDimitry Andric } 24535ffd83dbSDimitry Andric 24545ffd83dbSDimitry Andric return DAG.getNode(Opcode, DL, Op.getValueType(), Op.getOperand(0), ShiftVal); 24550b57cec5SDimitry Andric } 24560b57cec5SDimitry Andric 2457fe6060f1SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFP_TO_INT_SAT(SDValue Op, 2458fe6060f1SDimitry Andric SelectionDAG &DAG) const { 2459fe6060f1SDimitry Andric SDLoc DL(Op); 2460fe6060f1SDimitry Andric EVT ResT = Op.getValueType(); 2461fe6060f1SDimitry Andric EVT SatVT = cast<VTSDNode>(Op.getOperand(1))->getVT(); 2462fe6060f1SDimitry Andric 2463fe6060f1SDimitry Andric if ((ResT == MVT::i32 || ResT == MVT::i64) && 2464fe6060f1SDimitry Andric (SatVT == MVT::i32 || SatVT == MVT::i64)) 2465fe6060f1SDimitry Andric return Op; 2466fe6060f1SDimitry Andric 2467fe6060f1SDimitry Andric if (ResT == MVT::v4i32 && SatVT == MVT::i32) 2468fe6060f1SDimitry Andric return Op; 2469fe6060f1SDimitry Andric 2470fe6060f1SDimitry Andric return SDValue(); 2471fe6060f1SDimitry Andric } 2472fe6060f1SDimitry Andric 24730b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 24745ffd83dbSDimitry Andric // Custom DAG combine hooks 24750b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 24765ffd83dbSDimitry Andric static SDValue 24775ffd83dbSDimitry Andric performVECTOR_SHUFFLECombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { 24785ffd83dbSDimitry Andric auto &DAG = DCI.DAG; 24795ffd83dbSDimitry Andric auto Shuffle = cast<ShuffleVectorSDNode>(N); 24805ffd83dbSDimitry Andric 24815ffd83dbSDimitry Andric // Hoist vector bitcasts that don't change the number of lanes out of unary 24825ffd83dbSDimitry Andric // shuffles, where they are less likely to get in the way of other combines. 24835ffd83dbSDimitry Andric // (shuffle (vNxT1 (bitcast (vNxT0 x))), undef, mask) -> 24845ffd83dbSDimitry Andric // (vNxT1 (bitcast (vNxT0 (shuffle x, undef, mask)))) 24855ffd83dbSDimitry Andric SDValue Bitcast = N->getOperand(0); 24865ffd83dbSDimitry Andric if (Bitcast.getOpcode() != ISD::BITCAST) 24875ffd83dbSDimitry Andric return SDValue(); 24885ffd83dbSDimitry Andric if (!N->getOperand(1).isUndef()) 24895ffd83dbSDimitry Andric return SDValue(); 24905ffd83dbSDimitry Andric SDValue CastOp = Bitcast.getOperand(0); 24915f757f3fSDimitry Andric EVT SrcType = CastOp.getValueType(); 24925f757f3fSDimitry Andric EVT DstType = Bitcast.getValueType(); 24935ffd83dbSDimitry Andric if (!SrcType.is128BitVector() || 24945ffd83dbSDimitry Andric SrcType.getVectorNumElements() != DstType.getVectorNumElements()) 24955ffd83dbSDimitry Andric return SDValue(); 24965ffd83dbSDimitry Andric SDValue NewShuffle = DAG.getVectorShuffle( 24975ffd83dbSDimitry Andric SrcType, SDLoc(N), CastOp, DAG.getUNDEF(SrcType), Shuffle->getMask()); 24985ffd83dbSDimitry Andric return DAG.getBitcast(DstType, NewShuffle); 24995ffd83dbSDimitry Andric } 25005ffd83dbSDimitry Andric 2501bdd1243dSDimitry Andric /// Convert ({u,s}itofp vec) --> ({u,s}itofp ({s,z}ext vec)) so it doesn't get 2502bdd1243dSDimitry Andric /// split up into scalar instructions during legalization, and the vector 2503bdd1243dSDimitry Andric /// extending instructions are selected in performVectorExtendCombine below. 2504bdd1243dSDimitry Andric static SDValue 2505bdd1243dSDimitry Andric performVectorExtendToFPCombine(SDNode *N, 2506bdd1243dSDimitry Andric TargetLowering::DAGCombinerInfo &DCI) { 2507bdd1243dSDimitry Andric auto &DAG = DCI.DAG; 2508bdd1243dSDimitry Andric assert(N->getOpcode() == ISD::UINT_TO_FP || 2509bdd1243dSDimitry Andric N->getOpcode() == ISD::SINT_TO_FP); 2510bdd1243dSDimitry Andric 2511bdd1243dSDimitry Andric EVT InVT = N->getOperand(0)->getValueType(0); 2512bdd1243dSDimitry Andric EVT ResVT = N->getValueType(0); 2513bdd1243dSDimitry Andric MVT ExtVT; 2514bdd1243dSDimitry Andric if (ResVT == MVT::v4f32 && (InVT == MVT::v4i16 || InVT == MVT::v4i8)) 2515bdd1243dSDimitry Andric ExtVT = MVT::v4i32; 2516bdd1243dSDimitry Andric else if (ResVT == MVT::v2f64 && (InVT == MVT::v2i16 || InVT == MVT::v2i8)) 2517bdd1243dSDimitry Andric ExtVT = MVT::v2i32; 2518bdd1243dSDimitry Andric else 2519bdd1243dSDimitry Andric return SDValue(); 2520bdd1243dSDimitry Andric 2521bdd1243dSDimitry Andric unsigned Op = 2522bdd1243dSDimitry Andric N->getOpcode() == ISD::UINT_TO_FP ? ISD::ZERO_EXTEND : ISD::SIGN_EXTEND; 2523bdd1243dSDimitry Andric SDValue Conv = DAG.getNode(Op, SDLoc(N), ExtVT, N->getOperand(0)); 2524bdd1243dSDimitry Andric return DAG.getNode(N->getOpcode(), SDLoc(N), ResVT, Conv); 2525bdd1243dSDimitry Andric } 2526bdd1243dSDimitry Andric 2527fe6060f1SDimitry Andric static SDValue 2528fe6060f1SDimitry Andric performVectorExtendCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { 2529e8d8bef9SDimitry Andric auto &DAG = DCI.DAG; 2530e8d8bef9SDimitry Andric assert(N->getOpcode() == ISD::SIGN_EXTEND || 2531e8d8bef9SDimitry Andric N->getOpcode() == ISD::ZERO_EXTEND); 2532e8d8bef9SDimitry Andric 2533e8d8bef9SDimitry Andric // Combine ({s,z}ext (extract_subvector src, i)) into a widening operation if 2534e8d8bef9SDimitry Andric // possible before the extract_subvector can be expanded. 2535e8d8bef9SDimitry Andric auto Extract = N->getOperand(0); 2536e8d8bef9SDimitry Andric if (Extract.getOpcode() != ISD::EXTRACT_SUBVECTOR) 2537e8d8bef9SDimitry Andric return SDValue(); 2538e8d8bef9SDimitry Andric auto Source = Extract.getOperand(0); 2539e8d8bef9SDimitry Andric auto *IndexNode = dyn_cast<ConstantSDNode>(Extract.getOperand(1)); 2540e8d8bef9SDimitry Andric if (IndexNode == nullptr) 2541e8d8bef9SDimitry Andric return SDValue(); 2542e8d8bef9SDimitry Andric auto Index = IndexNode->getZExtValue(); 2543e8d8bef9SDimitry Andric 2544fe6060f1SDimitry Andric // Only v8i8, v4i16, and v2i32 extracts can be widened, and only if the 2545fe6060f1SDimitry Andric // extracted subvector is the low or high half of its source. 2546e8d8bef9SDimitry Andric EVT ResVT = N->getValueType(0); 2547e8d8bef9SDimitry Andric if (ResVT == MVT::v8i16) { 2548e8d8bef9SDimitry Andric if (Extract.getValueType() != MVT::v8i8 || 2549e8d8bef9SDimitry Andric Source.getValueType() != MVT::v16i8 || (Index != 0 && Index != 8)) 2550e8d8bef9SDimitry Andric return SDValue(); 2551e8d8bef9SDimitry Andric } else if (ResVT == MVT::v4i32) { 2552e8d8bef9SDimitry Andric if (Extract.getValueType() != MVT::v4i16 || 2553e8d8bef9SDimitry Andric Source.getValueType() != MVT::v8i16 || (Index != 0 && Index != 4)) 2554e8d8bef9SDimitry Andric return SDValue(); 2555fe6060f1SDimitry Andric } else if (ResVT == MVT::v2i64) { 2556fe6060f1SDimitry Andric if (Extract.getValueType() != MVT::v2i32 || 2557fe6060f1SDimitry Andric Source.getValueType() != MVT::v4i32 || (Index != 0 && Index != 2)) 2558fe6060f1SDimitry Andric return SDValue(); 2559e8d8bef9SDimitry Andric } else { 2560e8d8bef9SDimitry Andric return SDValue(); 2561e8d8bef9SDimitry Andric } 2562e8d8bef9SDimitry Andric 2563e8d8bef9SDimitry Andric bool IsSext = N->getOpcode() == ISD::SIGN_EXTEND; 2564e8d8bef9SDimitry Andric bool IsLow = Index == 0; 2565e8d8bef9SDimitry Andric 2566fe6060f1SDimitry Andric unsigned Op = IsSext ? (IsLow ? WebAssemblyISD::EXTEND_LOW_S 2567fe6060f1SDimitry Andric : WebAssemblyISD::EXTEND_HIGH_S) 2568fe6060f1SDimitry Andric : (IsLow ? WebAssemblyISD::EXTEND_LOW_U 2569fe6060f1SDimitry Andric : WebAssemblyISD::EXTEND_HIGH_U); 2570e8d8bef9SDimitry Andric 2571e8d8bef9SDimitry Andric return DAG.getNode(Op, SDLoc(N), ResVT, Source); 2572e8d8bef9SDimitry Andric } 2573e8d8bef9SDimitry Andric 2574fe6060f1SDimitry Andric static SDValue 2575fe6060f1SDimitry Andric performVectorTruncZeroCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { 2576fe6060f1SDimitry Andric auto &DAG = DCI.DAG; 2577fe6060f1SDimitry Andric 2578fe6060f1SDimitry Andric auto GetWasmConversionOp = [](unsigned Op) { 2579fe6060f1SDimitry Andric switch (Op) { 2580fe6060f1SDimitry Andric case ISD::FP_TO_SINT_SAT: 2581fe6060f1SDimitry Andric return WebAssemblyISD::TRUNC_SAT_ZERO_S; 2582fe6060f1SDimitry Andric case ISD::FP_TO_UINT_SAT: 2583fe6060f1SDimitry Andric return WebAssemblyISD::TRUNC_SAT_ZERO_U; 2584fe6060f1SDimitry Andric case ISD::FP_ROUND: 2585fe6060f1SDimitry Andric return WebAssemblyISD::DEMOTE_ZERO; 2586fe6060f1SDimitry Andric } 2587fe6060f1SDimitry Andric llvm_unreachable("unexpected op"); 2588fe6060f1SDimitry Andric }; 2589fe6060f1SDimitry Andric 2590fe6060f1SDimitry Andric auto IsZeroSplat = [](SDValue SplatVal) { 2591fe6060f1SDimitry Andric auto *Splat = dyn_cast<BuildVectorSDNode>(SplatVal.getNode()); 2592fe6060f1SDimitry Andric APInt SplatValue, SplatUndef; 2593fe6060f1SDimitry Andric unsigned SplatBitSize; 2594fe6060f1SDimitry Andric bool HasAnyUndefs; 25955f757f3fSDimitry Andric // Endianness doesn't matter in this context because we are looking for 25965f757f3fSDimitry Andric // an all-zero value. 2597fe6060f1SDimitry Andric return Splat && 2598fe6060f1SDimitry Andric Splat->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, 2599fe6060f1SDimitry Andric HasAnyUndefs) && 2600fe6060f1SDimitry Andric SplatValue == 0; 2601fe6060f1SDimitry Andric }; 2602fe6060f1SDimitry Andric 2603fe6060f1SDimitry Andric if (N->getOpcode() == ISD::CONCAT_VECTORS) { 2604fe6060f1SDimitry Andric // Combine this: 2605fe6060f1SDimitry Andric // 2606fe6060f1SDimitry Andric // (concat_vectors (v2i32 (fp_to_{s,u}int_sat $x, 32)), (v2i32 (splat 0))) 2607fe6060f1SDimitry Andric // 2608fe6060f1SDimitry Andric // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x). 2609fe6060f1SDimitry Andric // 2610fe6060f1SDimitry Andric // Or this: 2611fe6060f1SDimitry Andric // 2612fe6060f1SDimitry Andric // (concat_vectors (v2f32 (fp_round (v2f64 $x))), (v2f32 (splat 0))) 2613fe6060f1SDimitry Andric // 2614fe6060f1SDimitry Andric // into (f32x4.demote_zero_f64x2 $x). 2615fe6060f1SDimitry Andric EVT ResVT; 2616fe6060f1SDimitry Andric EVT ExpectedConversionType; 2617fe6060f1SDimitry Andric auto Conversion = N->getOperand(0); 2618fe6060f1SDimitry Andric auto ConversionOp = Conversion.getOpcode(); 2619fe6060f1SDimitry Andric switch (ConversionOp) { 2620fe6060f1SDimitry Andric case ISD::FP_TO_SINT_SAT: 2621fe6060f1SDimitry Andric case ISD::FP_TO_UINT_SAT: 2622fe6060f1SDimitry Andric ResVT = MVT::v4i32; 2623fe6060f1SDimitry Andric ExpectedConversionType = MVT::v2i32; 2624fe6060f1SDimitry Andric break; 2625fe6060f1SDimitry Andric case ISD::FP_ROUND: 2626fe6060f1SDimitry Andric ResVT = MVT::v4f32; 2627fe6060f1SDimitry Andric ExpectedConversionType = MVT::v2f32; 2628fe6060f1SDimitry Andric break; 2629fe6060f1SDimitry Andric default: 2630fe6060f1SDimitry Andric return SDValue(); 2631fe6060f1SDimitry Andric } 2632fe6060f1SDimitry Andric 2633fe6060f1SDimitry Andric if (N->getValueType(0) != ResVT) 2634fe6060f1SDimitry Andric return SDValue(); 2635fe6060f1SDimitry Andric 2636fe6060f1SDimitry Andric if (Conversion.getValueType() != ExpectedConversionType) 2637fe6060f1SDimitry Andric return SDValue(); 2638fe6060f1SDimitry Andric 2639fe6060f1SDimitry Andric auto Source = Conversion.getOperand(0); 2640fe6060f1SDimitry Andric if (Source.getValueType() != MVT::v2f64) 2641fe6060f1SDimitry Andric return SDValue(); 2642fe6060f1SDimitry Andric 2643fe6060f1SDimitry Andric if (!IsZeroSplat(N->getOperand(1)) || 2644fe6060f1SDimitry Andric N->getOperand(1).getValueType() != ExpectedConversionType) 2645fe6060f1SDimitry Andric return SDValue(); 2646fe6060f1SDimitry Andric 2647fe6060f1SDimitry Andric unsigned Op = GetWasmConversionOp(ConversionOp); 2648fe6060f1SDimitry Andric return DAG.getNode(Op, SDLoc(N), ResVT, Source); 2649fe6060f1SDimitry Andric } 2650fe6060f1SDimitry Andric 2651fe6060f1SDimitry Andric // Combine this: 2652fe6060f1SDimitry Andric // 2653fe6060f1SDimitry Andric // (fp_to_{s,u}int_sat (concat_vectors $x, (v2f64 (splat 0))), 32) 2654fe6060f1SDimitry Andric // 2655fe6060f1SDimitry Andric // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x). 2656fe6060f1SDimitry Andric // 2657fe6060f1SDimitry Andric // Or this: 2658fe6060f1SDimitry Andric // 2659fe6060f1SDimitry Andric // (v4f32 (fp_round (concat_vectors $x, (v2f64 (splat 0))))) 2660fe6060f1SDimitry Andric // 2661fe6060f1SDimitry Andric // into (f32x4.demote_zero_f64x2 $x). 2662fe6060f1SDimitry Andric EVT ResVT; 2663fe6060f1SDimitry Andric auto ConversionOp = N->getOpcode(); 2664fe6060f1SDimitry Andric switch (ConversionOp) { 2665fe6060f1SDimitry Andric case ISD::FP_TO_SINT_SAT: 2666fe6060f1SDimitry Andric case ISD::FP_TO_UINT_SAT: 2667fe6060f1SDimitry Andric ResVT = MVT::v4i32; 2668fe6060f1SDimitry Andric break; 2669fe6060f1SDimitry Andric case ISD::FP_ROUND: 2670fe6060f1SDimitry Andric ResVT = MVT::v4f32; 2671fe6060f1SDimitry Andric break; 2672fe6060f1SDimitry Andric default: 2673fe6060f1SDimitry Andric llvm_unreachable("unexpected op"); 2674fe6060f1SDimitry Andric } 2675fe6060f1SDimitry Andric 2676fe6060f1SDimitry Andric if (N->getValueType(0) != ResVT) 2677fe6060f1SDimitry Andric return SDValue(); 2678fe6060f1SDimitry Andric 2679fe6060f1SDimitry Andric auto Concat = N->getOperand(0); 2680fe6060f1SDimitry Andric if (Concat.getValueType() != MVT::v4f64) 2681fe6060f1SDimitry Andric return SDValue(); 2682fe6060f1SDimitry Andric 2683fe6060f1SDimitry Andric auto Source = Concat.getOperand(0); 2684fe6060f1SDimitry Andric if (Source.getValueType() != MVT::v2f64) 2685fe6060f1SDimitry Andric return SDValue(); 2686fe6060f1SDimitry Andric 2687fe6060f1SDimitry Andric if (!IsZeroSplat(Concat.getOperand(1)) || 2688fe6060f1SDimitry Andric Concat.getOperand(1).getValueType() != MVT::v2f64) 2689fe6060f1SDimitry Andric return SDValue(); 2690fe6060f1SDimitry Andric 2691fe6060f1SDimitry Andric unsigned Op = GetWasmConversionOp(ConversionOp); 2692fe6060f1SDimitry Andric return DAG.getNode(Op, SDLoc(N), ResVT, Source); 2693fe6060f1SDimitry Andric } 2694fe6060f1SDimitry Andric 26950eae32dcSDimitry Andric // Helper to extract VectorWidth bits from Vec, starting from IdxVal. 26960eae32dcSDimitry Andric static SDValue extractSubVector(SDValue Vec, unsigned IdxVal, SelectionDAG &DAG, 26970eae32dcSDimitry Andric const SDLoc &DL, unsigned VectorWidth) { 26980eae32dcSDimitry Andric EVT VT = Vec.getValueType(); 26990eae32dcSDimitry Andric EVT ElVT = VT.getVectorElementType(); 27000eae32dcSDimitry Andric unsigned Factor = VT.getSizeInBits() / VectorWidth; 27010eae32dcSDimitry Andric EVT ResultVT = EVT::getVectorVT(*DAG.getContext(), ElVT, 27020eae32dcSDimitry Andric VT.getVectorNumElements() / Factor); 27030eae32dcSDimitry Andric 27040eae32dcSDimitry Andric // Extract the relevant VectorWidth bits. Generate an EXTRACT_SUBVECTOR 27050eae32dcSDimitry Andric unsigned ElemsPerChunk = VectorWidth / ElVT.getSizeInBits(); 27060eae32dcSDimitry Andric assert(isPowerOf2_32(ElemsPerChunk) && "Elements per chunk not power of 2"); 27070eae32dcSDimitry Andric 27080eae32dcSDimitry Andric // This is the index of the first element of the VectorWidth-bit chunk 27090eae32dcSDimitry Andric // we want. Since ElemsPerChunk is a power of 2 just need to clear bits. 27100eae32dcSDimitry Andric IdxVal &= ~(ElemsPerChunk - 1); 27110eae32dcSDimitry Andric 27120eae32dcSDimitry Andric // If the input is a buildvector just emit a smaller one. 27130eae32dcSDimitry Andric if (Vec.getOpcode() == ISD::BUILD_VECTOR) 27140eae32dcSDimitry Andric return DAG.getBuildVector(ResultVT, DL, 27150eae32dcSDimitry Andric Vec->ops().slice(IdxVal, ElemsPerChunk)); 27160eae32dcSDimitry Andric 27170eae32dcSDimitry Andric SDValue VecIdx = DAG.getIntPtrConstant(IdxVal, DL); 27180eae32dcSDimitry Andric return DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ResultVT, Vec, VecIdx); 27190eae32dcSDimitry Andric } 27200eae32dcSDimitry Andric 27210eae32dcSDimitry Andric // Helper to recursively truncate vector elements in half with NARROW_U. DstVT 27220eae32dcSDimitry Andric // is the expected destination value type after recursion. In is the initial 27230eae32dcSDimitry Andric // input. Note that the input should have enough leading zero bits to prevent 27240eae32dcSDimitry Andric // NARROW_U from saturating results. 27250eae32dcSDimitry Andric static SDValue truncateVectorWithNARROW(EVT DstVT, SDValue In, const SDLoc &DL, 27260eae32dcSDimitry Andric SelectionDAG &DAG) { 27270eae32dcSDimitry Andric EVT SrcVT = In.getValueType(); 27280eae32dcSDimitry Andric 27290eae32dcSDimitry Andric // No truncation required, we might get here due to recursive calls. 27300eae32dcSDimitry Andric if (SrcVT == DstVT) 27310eae32dcSDimitry Andric return In; 27320eae32dcSDimitry Andric 27330eae32dcSDimitry Andric unsigned SrcSizeInBits = SrcVT.getSizeInBits(); 27340eae32dcSDimitry Andric unsigned NumElems = SrcVT.getVectorNumElements(); 27350eae32dcSDimitry Andric if (!isPowerOf2_32(NumElems)) 27360eae32dcSDimitry Andric return SDValue(); 27370eae32dcSDimitry Andric assert(DstVT.getVectorNumElements() == NumElems && "Illegal truncation"); 27380eae32dcSDimitry Andric assert(SrcSizeInBits > DstVT.getSizeInBits() && "Illegal truncation"); 27390eae32dcSDimitry Andric 27400eae32dcSDimitry Andric LLVMContext &Ctx = *DAG.getContext(); 27410eae32dcSDimitry Andric EVT PackedSVT = EVT::getIntegerVT(Ctx, SrcVT.getScalarSizeInBits() / 2); 27420eae32dcSDimitry Andric 27430eae32dcSDimitry Andric // Narrow to the largest type possible: 27440eae32dcSDimitry Andric // vXi64/vXi32 -> i16x8.narrow_i32x4_u and vXi16 -> i8x16.narrow_i16x8_u. 27450eae32dcSDimitry Andric EVT InVT = MVT::i16, OutVT = MVT::i8; 27460eae32dcSDimitry Andric if (SrcVT.getScalarSizeInBits() > 16) { 27470eae32dcSDimitry Andric InVT = MVT::i32; 27480eae32dcSDimitry Andric OutVT = MVT::i16; 27490eae32dcSDimitry Andric } 27500eae32dcSDimitry Andric unsigned SubSizeInBits = SrcSizeInBits / 2; 27510eae32dcSDimitry Andric InVT = EVT::getVectorVT(Ctx, InVT, SubSizeInBits / InVT.getSizeInBits()); 27520eae32dcSDimitry Andric OutVT = EVT::getVectorVT(Ctx, OutVT, SubSizeInBits / OutVT.getSizeInBits()); 27530eae32dcSDimitry Andric 27540eae32dcSDimitry Andric // Split lower/upper subvectors. 27550eae32dcSDimitry Andric SDValue Lo = extractSubVector(In, 0, DAG, DL, SubSizeInBits); 27560eae32dcSDimitry Andric SDValue Hi = extractSubVector(In, NumElems / 2, DAG, DL, SubSizeInBits); 27570eae32dcSDimitry Andric 27580eae32dcSDimitry Andric // 256bit -> 128bit truncate - Narrow lower/upper 128-bit subvectors. 27590eae32dcSDimitry Andric if (SrcVT.is256BitVector() && DstVT.is128BitVector()) { 27600eae32dcSDimitry Andric Lo = DAG.getBitcast(InVT, Lo); 27610eae32dcSDimitry Andric Hi = DAG.getBitcast(InVT, Hi); 27620eae32dcSDimitry Andric SDValue Res = DAG.getNode(WebAssemblyISD::NARROW_U, DL, OutVT, Lo, Hi); 27630eae32dcSDimitry Andric return DAG.getBitcast(DstVT, Res); 27640eae32dcSDimitry Andric } 27650eae32dcSDimitry Andric 27660eae32dcSDimitry Andric // Recursively narrow lower/upper subvectors, concat result and narrow again. 27670eae32dcSDimitry Andric EVT PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems / 2); 27680eae32dcSDimitry Andric Lo = truncateVectorWithNARROW(PackedVT, Lo, DL, DAG); 27690eae32dcSDimitry Andric Hi = truncateVectorWithNARROW(PackedVT, Hi, DL, DAG); 27700eae32dcSDimitry Andric 27710eae32dcSDimitry Andric PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems); 27720eae32dcSDimitry Andric SDValue Res = DAG.getNode(ISD::CONCAT_VECTORS, DL, PackedVT, Lo, Hi); 27730eae32dcSDimitry Andric return truncateVectorWithNARROW(DstVT, Res, DL, DAG); 27740eae32dcSDimitry Andric } 27750eae32dcSDimitry Andric 27760eae32dcSDimitry Andric static SDValue performTruncateCombine(SDNode *N, 27770eae32dcSDimitry Andric TargetLowering::DAGCombinerInfo &DCI) { 27780eae32dcSDimitry Andric auto &DAG = DCI.DAG; 27790eae32dcSDimitry Andric 27800eae32dcSDimitry Andric SDValue In = N->getOperand(0); 27810eae32dcSDimitry Andric EVT InVT = In.getValueType(); 27820eae32dcSDimitry Andric if (!InVT.isSimple()) 27830eae32dcSDimitry Andric return SDValue(); 27840eae32dcSDimitry Andric 27850eae32dcSDimitry Andric EVT OutVT = N->getValueType(0); 27860eae32dcSDimitry Andric if (!OutVT.isVector()) 27870eae32dcSDimitry Andric return SDValue(); 27880eae32dcSDimitry Andric 27890eae32dcSDimitry Andric EVT OutSVT = OutVT.getVectorElementType(); 27900eae32dcSDimitry Andric EVT InSVT = InVT.getVectorElementType(); 27910eae32dcSDimitry Andric // Currently only cover truncate to v16i8 or v8i16. 27920eae32dcSDimitry Andric if (!((InSVT == MVT::i16 || InSVT == MVT::i32 || InSVT == MVT::i64) && 27930eae32dcSDimitry Andric (OutSVT == MVT::i8 || OutSVT == MVT::i16) && OutVT.is128BitVector())) 27940eae32dcSDimitry Andric return SDValue(); 27950eae32dcSDimitry Andric 27960eae32dcSDimitry Andric SDLoc DL(N); 27970eae32dcSDimitry Andric APInt Mask = APInt::getLowBitsSet(InVT.getScalarSizeInBits(), 27980eae32dcSDimitry Andric OutVT.getScalarSizeInBits()); 27990eae32dcSDimitry Andric In = DAG.getNode(ISD::AND, DL, InVT, In, DAG.getConstant(Mask, DL, InVT)); 28000eae32dcSDimitry Andric return truncateVectorWithNARROW(OutVT, In, DL, DAG); 28010eae32dcSDimitry Andric } 28020eae32dcSDimitry Andric 280306c3fb27SDimitry Andric static SDValue performBitcastCombine(SDNode *N, 280406c3fb27SDimitry Andric TargetLowering::DAGCombinerInfo &DCI) { 280506c3fb27SDimitry Andric auto &DAG = DCI.DAG; 280606c3fb27SDimitry Andric SDLoc DL(N); 280706c3fb27SDimitry Andric SDValue Src = N->getOperand(0); 280806c3fb27SDimitry Andric EVT VT = N->getValueType(0); 280906c3fb27SDimitry Andric EVT SrcVT = Src.getValueType(); 281006c3fb27SDimitry Andric 281106c3fb27SDimitry Andric // bitcast <N x i1> to iN 281206c3fb27SDimitry Andric // ==> bitmask 281306c3fb27SDimitry Andric if (DCI.isBeforeLegalize() && VT.isScalarInteger() && 281406c3fb27SDimitry Andric SrcVT.isFixedLengthVector() && SrcVT.getScalarType() == MVT::i1) { 281506c3fb27SDimitry Andric unsigned NumElts = SrcVT.getVectorNumElements(); 281606c3fb27SDimitry Andric if (NumElts != 2 && NumElts != 4 && NumElts != 8 && NumElts != 16) 281706c3fb27SDimitry Andric return SDValue(); 281806c3fb27SDimitry Andric EVT Width = MVT::getIntegerVT(128 / NumElts); 281906c3fb27SDimitry Andric return DAG.getZExtOrTrunc( 282006c3fb27SDimitry Andric DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, 282106c3fb27SDimitry Andric {DAG.getConstant(Intrinsic::wasm_bitmask, DL, MVT::i32), 282206c3fb27SDimitry Andric DAG.getSExtOrTrunc(N->getOperand(0), DL, 282306c3fb27SDimitry Andric SrcVT.changeVectorElementType(Width))}), 282406c3fb27SDimitry Andric DL, VT); 282506c3fb27SDimitry Andric } 282606c3fb27SDimitry Andric 282706c3fb27SDimitry Andric return SDValue(); 282806c3fb27SDimitry Andric } 282906c3fb27SDimitry Andric 283006c3fb27SDimitry Andric static SDValue performSETCCCombine(SDNode *N, 283106c3fb27SDimitry Andric TargetLowering::DAGCombinerInfo &DCI) { 283206c3fb27SDimitry Andric auto &DAG = DCI.DAG; 283306c3fb27SDimitry Andric 283406c3fb27SDimitry Andric SDValue LHS = N->getOperand(0); 283506c3fb27SDimitry Andric SDValue RHS = N->getOperand(1); 283606c3fb27SDimitry Andric ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(2))->get(); 283706c3fb27SDimitry Andric SDLoc DL(N); 283806c3fb27SDimitry Andric EVT VT = N->getValueType(0); 283906c3fb27SDimitry Andric 284006c3fb27SDimitry Andric // setcc (iN (bitcast (vNi1 X))), 0, ne 284106c3fb27SDimitry Andric // ==> any_true (vNi1 X) 284206c3fb27SDimitry Andric // setcc (iN (bitcast (vNi1 X))), 0, eq 284306c3fb27SDimitry Andric // ==> xor (any_true (vNi1 X)), -1 284406c3fb27SDimitry Andric // setcc (iN (bitcast (vNi1 X))), -1, eq 284506c3fb27SDimitry Andric // ==> all_true (vNi1 X) 284606c3fb27SDimitry Andric // setcc (iN (bitcast (vNi1 X))), -1, ne 284706c3fb27SDimitry Andric // ==> xor (all_true (vNi1 X)), -1 284806c3fb27SDimitry Andric if (DCI.isBeforeLegalize() && VT.isScalarInteger() && 284906c3fb27SDimitry Andric (Cond == ISD::SETEQ || Cond == ISD::SETNE) && 285006c3fb27SDimitry Andric (isNullConstant(RHS) || isAllOnesConstant(RHS)) && 285106c3fb27SDimitry Andric LHS->getOpcode() == ISD::BITCAST) { 285206c3fb27SDimitry Andric EVT FromVT = LHS->getOperand(0).getValueType(); 285306c3fb27SDimitry Andric if (FromVT.isFixedLengthVector() && 285406c3fb27SDimitry Andric FromVT.getVectorElementType() == MVT::i1) { 285506c3fb27SDimitry Andric int Intrin = isNullConstant(RHS) ? Intrinsic::wasm_anytrue 285606c3fb27SDimitry Andric : Intrinsic::wasm_alltrue; 285706c3fb27SDimitry Andric unsigned NumElts = FromVT.getVectorNumElements(); 285806c3fb27SDimitry Andric if (NumElts != 2 && NumElts != 4 && NumElts != 8 && NumElts != 16) 285906c3fb27SDimitry Andric return SDValue(); 286006c3fb27SDimitry Andric EVT Width = MVT::getIntegerVT(128 / NumElts); 286106c3fb27SDimitry Andric SDValue Ret = DAG.getZExtOrTrunc( 286206c3fb27SDimitry Andric DAG.getNode( 286306c3fb27SDimitry Andric ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, 286406c3fb27SDimitry Andric {DAG.getConstant(Intrin, DL, MVT::i32), 286506c3fb27SDimitry Andric DAG.getSExtOrTrunc(LHS->getOperand(0), DL, 286606c3fb27SDimitry Andric FromVT.changeVectorElementType(Width))}), 286706c3fb27SDimitry Andric DL, MVT::i1); 286806c3fb27SDimitry Andric if ((isNullConstant(RHS) && (Cond == ISD::SETEQ)) || 286906c3fb27SDimitry Andric (isAllOnesConstant(RHS) && (Cond == ISD::SETNE))) { 287006c3fb27SDimitry Andric Ret = DAG.getNOT(DL, Ret, MVT::i1); 287106c3fb27SDimitry Andric } 287206c3fb27SDimitry Andric return DAG.getZExtOrTrunc(Ret, DL, VT); 287306c3fb27SDimitry Andric } 287406c3fb27SDimitry Andric } 287506c3fb27SDimitry Andric 287606c3fb27SDimitry Andric return SDValue(); 287706c3fb27SDimitry Andric } 287806c3fb27SDimitry Andric 28795ffd83dbSDimitry Andric SDValue 28805ffd83dbSDimitry Andric WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N, 28815ffd83dbSDimitry Andric DAGCombinerInfo &DCI) const { 28825ffd83dbSDimitry Andric switch (N->getOpcode()) { 28835ffd83dbSDimitry Andric default: 28845ffd83dbSDimitry Andric return SDValue(); 288506c3fb27SDimitry Andric case ISD::BITCAST: 288606c3fb27SDimitry Andric return performBitcastCombine(N, DCI); 288706c3fb27SDimitry Andric case ISD::SETCC: 288806c3fb27SDimitry Andric return performSETCCCombine(N, DCI); 28895ffd83dbSDimitry Andric case ISD::VECTOR_SHUFFLE: 28905ffd83dbSDimitry Andric return performVECTOR_SHUFFLECombine(N, DCI); 2891e8d8bef9SDimitry Andric case ISD::SIGN_EXTEND: 2892e8d8bef9SDimitry Andric case ISD::ZERO_EXTEND: 2893fe6060f1SDimitry Andric return performVectorExtendCombine(N, DCI); 2894bdd1243dSDimitry Andric case ISD::UINT_TO_FP: 2895bdd1243dSDimitry Andric case ISD::SINT_TO_FP: 2896bdd1243dSDimitry Andric return performVectorExtendToFPCombine(N, DCI); 2897fe6060f1SDimitry Andric case ISD::FP_TO_SINT_SAT: 2898fe6060f1SDimitry Andric case ISD::FP_TO_UINT_SAT: 2899fe6060f1SDimitry Andric case ISD::FP_ROUND: 2900fe6060f1SDimitry Andric case ISD::CONCAT_VECTORS: 2901fe6060f1SDimitry Andric return performVectorTruncZeroCombine(N, DCI); 29020eae32dcSDimitry Andric case ISD::TRUNCATE: 29030eae32dcSDimitry Andric return performTruncateCombine(N, DCI); 29045ffd83dbSDimitry Andric } 29055ffd83dbSDimitry Andric } 2906