| File: | build/source/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp |
| Warning: | line 2152, column 9 The result of the left shift is undefined due to shifting by '127', which is greater or equal to the width of type 'long long' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | //=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==// | |||
| 2 | // | |||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
| 4 | // See https://llvm.org/LICENSE.txt for license information. | |||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
| 6 | // | |||
| 7 | //===----------------------------------------------------------------------===// | |||
| 8 | /// | |||
| 9 | /// \file | |||
| 10 | /// This file implements the WebAssemblyTargetLowering class. | |||
| 11 | /// | |||
| 12 | //===----------------------------------------------------------------------===// | |||
| 13 | ||||
| 14 | #include "WebAssemblyISelLowering.h" | |||
| 15 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" | |||
| 16 | #include "Utils/WebAssemblyTypeUtilities.h" | |||
| 17 | #include "Utils/WebAssemblyUtilities.h" | |||
| 18 | #include "WebAssemblyMachineFunctionInfo.h" | |||
| 19 | #include "WebAssemblySubtarget.h" | |||
| 20 | #include "WebAssemblyTargetMachine.h" | |||
| 21 | #include "llvm/CodeGen/CallingConvLower.h" | |||
| 22 | #include "llvm/CodeGen/MachineFrameInfo.h" | |||
| 23 | #include "llvm/CodeGen/MachineFunctionPass.h" | |||
| 24 | #include "llvm/CodeGen/MachineInstrBuilder.h" | |||
| 25 | #include "llvm/CodeGen/MachineJumpTableInfo.h" | |||
| 26 | #include "llvm/CodeGen/MachineModuleInfo.h" | |||
| 27 | #include "llvm/CodeGen/MachineRegisterInfo.h" | |||
| 28 | #include "llvm/CodeGen/SelectionDAG.h" | |||
| 29 | #include "llvm/CodeGen/SelectionDAGNodes.h" | |||
| 30 | #include "llvm/IR/DiagnosticInfo.h" | |||
| 31 | #include "llvm/IR/DiagnosticPrinter.h" | |||
| 32 | #include "llvm/IR/Function.h" | |||
| 33 | #include "llvm/IR/Intrinsics.h" | |||
| 34 | #include "llvm/IR/IntrinsicsWebAssembly.h" | |||
| 35 | #include "llvm/Support/Debug.h" | |||
| 36 | #include "llvm/Support/ErrorHandling.h" | |||
| 37 | #include "llvm/Support/KnownBits.h" | |||
| 38 | #include "llvm/Support/MathExtras.h" | |||
| 39 | #include "llvm/Support/raw_ostream.h" | |||
| 40 | #include "llvm/Target/TargetOptions.h" | |||
| 41 | using namespace llvm; | |||
| 42 | ||||
| 43 | #define DEBUG_TYPE"wasm-lower" "wasm-lower" | |||
| 44 | ||||
| 45 | WebAssemblyTargetLowering::WebAssemblyTargetLowering( | |||
| 46 | const TargetMachine &TM, const WebAssemblySubtarget &STI) | |||
| 47 | : TargetLowering(TM), Subtarget(&STI) { | |||
| 48 | auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; | |||
| 49 | ||||
| 50 | // Booleans always contain 0 or 1. | |||
| 51 | setBooleanContents(ZeroOrOneBooleanContent); | |||
| 52 | // Except in SIMD vectors | |||
| 53 | setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); | |||
| 54 | // We don't know the microarchitecture here, so just reduce register pressure. | |||
| 55 | setSchedulingPreference(Sched::RegPressure); | |||
| 56 | // Tell ISel that we have a stack pointer. | |||
| 57 | setStackPointerRegisterToSaveRestore( | |||
| 58 | Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32); | |||
| 59 | // Set up the register classes. | |||
| 60 | addRegisterClass(MVT::i32, &WebAssembly::I32RegClass); | |||
| 61 | addRegisterClass(MVT::i64, &WebAssembly::I64RegClass); | |||
| 62 | addRegisterClass(MVT::f32, &WebAssembly::F32RegClass); | |||
| 63 | addRegisterClass(MVT::f64, &WebAssembly::F64RegClass); | |||
| 64 | if (Subtarget->hasSIMD128()) { | |||
| 65 | addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass); | |||
| 66 | addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass); | |||
| 67 | addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass); | |||
| 68 | addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass); | |||
| 69 | addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); | |||
| 70 | addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); | |||
| 71 | } | |||
| 72 | if (Subtarget->hasReferenceTypes()) { | |||
| 73 | addRegisterClass(MVT::externref, &WebAssembly::EXTERNREFRegClass); | |||
| 74 | addRegisterClass(MVT::funcref, &WebAssembly::FUNCREFRegClass); | |||
| 75 | } | |||
| 76 | // Compute derived properties from the register classes. | |||
| 77 | computeRegisterProperties(Subtarget->getRegisterInfo()); | |||
| 78 | ||||
| 79 | // Transform loads and stores to pointers in address space 1 to loads and | |||
| 80 | // stores to WebAssembly global variables, outside linear memory. | |||
| 81 | for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) { | |||
| 82 | setOperationAction(ISD::LOAD, T, Custom); | |||
| 83 | setOperationAction(ISD::STORE, T, Custom); | |||
| 84 | } | |||
| 85 | if (Subtarget->hasSIMD128()) { | |||
| 86 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 87 | MVT::v2f64}) { | |||
| 88 | setOperationAction(ISD::LOAD, T, Custom); | |||
| 89 | setOperationAction(ISD::STORE, T, Custom); | |||
| 90 | } | |||
| 91 | } | |||
| 92 | if (Subtarget->hasReferenceTypes()) { | |||
| 93 | // We need custom load and store lowering for both externref, funcref and | |||
| 94 | // Other. The MVT::Other here represents tables of reference types. | |||
| 95 | for (auto T : {MVT::externref, MVT::funcref, MVT::Other}) { | |||
| 96 | setOperationAction(ISD::LOAD, T, Custom); | |||
| 97 | setOperationAction(ISD::STORE, T, Custom); | |||
| 98 | } | |||
| 99 | } | |||
| 100 | ||||
| 101 | setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); | |||
| 102 | setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); | |||
| 103 | setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); | |||
| 104 | setOperationAction(ISD::JumpTable, MVTPtr, Custom); | |||
| 105 | setOperationAction(ISD::BlockAddress, MVTPtr, Custom); | |||
| 106 | setOperationAction(ISD::BRIND, MVT::Other, Custom); | |||
| 107 | ||||
| 108 | // Take the default expansion for va_arg, va_copy, and va_end. There is no | |||
| 109 | // default action for va_start, so we do that custom. | |||
| 110 | setOperationAction(ISD::VASTART, MVT::Other, Custom); | |||
| 111 | setOperationAction(ISD::VAARG, MVT::Other, Expand); | |||
| 112 | setOperationAction(ISD::VACOPY, MVT::Other, Expand); | |||
| 113 | setOperationAction(ISD::VAEND, MVT::Other, Expand); | |||
| 114 | ||||
| 115 | for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64}) { | |||
| 116 | // Don't expand the floating-point types to constant pools. | |||
| 117 | setOperationAction(ISD::ConstantFP, T, Legal); | |||
| 118 | // Expand floating-point comparisons. | |||
| 119 | for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE, | |||
| 120 | ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE}) | |||
| 121 | setCondCodeAction(CC, T, Expand); | |||
| 122 | // Expand floating-point library function operators. | |||
| 123 | for (auto Op : | |||
| 124 | {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FMA}) | |||
| 125 | setOperationAction(Op, T, Expand); | |||
| 126 | // Note supported floating-point library function operators that otherwise | |||
| 127 | // default to expand. | |||
| 128 | for (auto Op : | |||
| 129 | {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) | |||
| 130 | setOperationAction(Op, T, Legal); | |||
| 131 | // Support minimum and maximum, which otherwise default to expand. | |||
| 132 | setOperationAction(ISD::FMINIMUM, T, Legal); | |||
| 133 | setOperationAction(ISD::FMAXIMUM, T, Legal); | |||
| 134 | // WebAssembly currently has no builtin f16 support. | |||
| 135 | setOperationAction(ISD::FP16_TO_FP, T, Expand); | |||
| 136 | setOperationAction(ISD::FP_TO_FP16, T, Expand); | |||
| 137 | setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand); | |||
| 138 | setTruncStoreAction(T, MVT::f16, Expand); | |||
| 139 | } | |||
| 140 | ||||
| 141 | // Expand unavailable integer operations. | |||
| 142 | for (auto Op : | |||
| 143 | {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI, ISD::MULHS, ISD::MULHU, | |||
| 144 | ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, ISD::SRA_PARTS, | |||
| 145 | ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) { | |||
| 146 | for (auto T : {MVT::i32, MVT::i64}) | |||
| 147 | setOperationAction(Op, T, Expand); | |||
| 148 | if (Subtarget->hasSIMD128()) | |||
| 149 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) | |||
| 150 | setOperationAction(Op, T, Expand); | |||
| 151 | } | |||
| 152 | ||||
| 153 | if (Subtarget->hasNontrappingFPToInt()) | |||
| 154 | for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}) | |||
| 155 | for (auto T : {MVT::i32, MVT::i64}) | |||
| 156 | setOperationAction(Op, T, Custom); | |||
| 157 | ||||
| 158 | // SIMD-specific configuration | |||
| 159 | if (Subtarget->hasSIMD128()) { | |||
| 160 | // Hoist bitcasts out of shuffles | |||
| 161 | setTargetDAGCombine(ISD::VECTOR_SHUFFLE); | |||
| 162 | ||||
| 163 | // Combine extends of extract_subvectors into widening ops | |||
| 164 | setTargetDAGCombine({ISD::SIGN_EXTEND, ISD::ZERO_EXTEND}); | |||
| 165 | ||||
| 166 | // Combine int_to_fp or fp_extend of extract_vectors and vice versa into | |||
| 167 | // conversions ops | |||
| 168 | setTargetDAGCombine({ISD::SINT_TO_FP, ISD::UINT_TO_FP, ISD::FP_EXTEND, | |||
| 169 | ISD::EXTRACT_SUBVECTOR}); | |||
| 170 | ||||
| 171 | // Combine fp_to_{s,u}int_sat or fp_round of concat_vectors or vice versa | |||
| 172 | // into conversion ops | |||
| 173 | setTargetDAGCombine({ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT, | |||
| 174 | ISD::FP_ROUND, ISD::CONCAT_VECTORS}); | |||
| 175 | ||||
| 176 | setTargetDAGCombine(ISD::TRUNCATE); | |||
| 177 | ||||
| 178 | // Support saturating add for i8x16 and i16x8 | |||
| 179 | for (auto Op : {ISD::SADDSAT, ISD::UADDSAT}) | |||
| 180 | for (auto T : {MVT::v16i8, MVT::v8i16}) | |||
| 181 | setOperationAction(Op, T, Legal); | |||
| 182 | ||||
| 183 | // Support integer abs | |||
| 184 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) | |||
| 185 | setOperationAction(ISD::ABS, T, Legal); | |||
| 186 | ||||
| 187 | // Custom lower BUILD_VECTORs to minimize number of replace_lanes | |||
| 188 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 189 | MVT::v2f64}) | |||
| 190 | setOperationAction(ISD::BUILD_VECTOR, T, Custom); | |||
| 191 | ||||
| 192 | // We have custom shuffle lowering to expose the shuffle mask | |||
| 193 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 194 | MVT::v2f64}) | |||
| 195 | setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); | |||
| 196 | ||||
| 197 | // Support splatting | |||
| 198 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 199 | MVT::v2f64}) | |||
| 200 | setOperationAction(ISD::SPLAT_VECTOR, T, Legal); | |||
| 201 | ||||
| 202 | // Custom lowering since wasm shifts must have a scalar shift amount | |||
| 203 | for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL}) | |||
| 204 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) | |||
| 205 | setOperationAction(Op, T, Custom); | |||
| 206 | ||||
| 207 | // Custom lower lane accesses to expand out variable indices | |||
| 208 | for (auto Op : {ISD::EXTRACT_VECTOR_ELT, ISD::INSERT_VECTOR_ELT}) | |||
| 209 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 210 | MVT::v2f64}) | |||
| 211 | setOperationAction(Op, T, Custom); | |||
| 212 | ||||
| 213 | // There is no i8x16.mul instruction | |||
| 214 | setOperationAction(ISD::MUL, MVT::v16i8, Expand); | |||
| 215 | ||||
| 216 | // There is no vector conditional select instruction | |||
| 217 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, | |||
| 218 | MVT::v2f64}) | |||
| 219 | setOperationAction(ISD::SELECT_CC, T, Expand); | |||
| 220 | ||||
| 221 | // Expand integer operations supported for scalars but not SIMD | |||
| 222 | for (auto Op : | |||
| 223 | {ISD::SDIV, ISD::UDIV, ISD::SREM, ISD::UREM, ISD::ROTL, ISD::ROTR}) | |||
| 224 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64}) | |||
| 225 | setOperationAction(Op, T, Expand); | |||
| 226 | ||||
| 227 | // But we do have integer min and max operations | |||
| 228 | for (auto Op : {ISD::SMIN, ISD::SMAX, ISD::UMIN, ISD::UMAX}) | |||
| 229 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) | |||
| 230 | setOperationAction(Op, T, Legal); | |||
| 231 | ||||
| 232 | // And we have popcnt for i8x16. It can be used to expand ctlz/cttz. | |||
| 233 | setOperationAction(ISD::CTPOP, MVT::v16i8, Legal); | |||
| 234 | setOperationAction(ISD::CTLZ, MVT::v16i8, Expand); | |||
| 235 | setOperationAction(ISD::CTTZ, MVT::v16i8, Expand); | |||
| 236 | ||||
| 237 | // Custom lower bit counting operations for other types to scalarize them. | |||
| 238 | for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP}) | |||
| 239 | for (auto T : {MVT::v8i16, MVT::v4i32, MVT::v2i64}) | |||
| 240 | setOperationAction(Op, T, Custom); | |||
| 241 | ||||
| 242 | // Expand float operations supported for scalars but not SIMD | |||
| 243 | for (auto Op : {ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, | |||
| 244 | ISD::FEXP, ISD::FEXP2, ISD::FRINT}) | |||
| 245 | for (auto T : {MVT::v4f32, MVT::v2f64}) | |||
| 246 | setOperationAction(Op, T, Expand); | |||
| 247 | ||||
| 248 | // Unsigned comparison operations are unavailable for i64x2 vectors. | |||
| 249 | for (auto CC : {ISD::SETUGT, ISD::SETUGE, ISD::SETULT, ISD::SETULE}) | |||
| 250 | setCondCodeAction(CC, MVT::v2i64, Custom); | |||
| 251 | ||||
| 252 | // 64x2 conversions are not in the spec | |||
| 253 | for (auto Op : | |||
| 254 | {ISD::SINT_TO_FP, ISD::UINT_TO_FP, ISD::FP_TO_SINT, ISD::FP_TO_UINT}) | |||
| 255 | for (auto T : {MVT::v2i64, MVT::v2f64}) | |||
| 256 | setOperationAction(Op, T, Expand); | |||
| 257 | ||||
| 258 | // But saturating fp_to_int converstions are | |||
| 259 | for (auto Op : {ISD::FP_TO_SINT_SAT, ISD::FP_TO_UINT_SAT}) | |||
| 260 | setOperationAction(Op, MVT::v4i32, Custom); | |||
| 261 | } | |||
| 262 | ||||
| 263 | // As a special case, these operators use the type to mean the type to | |||
| 264 | // sign-extend from. | |||
| 265 | setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); | |||
| 266 | if (!Subtarget->hasSignExt()) { | |||
| 267 | // Sign extends are legal only when extending a vector extract | |||
| 268 | auto Action = Subtarget->hasSIMD128() ? Custom : Expand; | |||
| 269 | for (auto T : {MVT::i8, MVT::i16, MVT::i32}) | |||
| 270 | setOperationAction(ISD::SIGN_EXTEND_INREG, T, Action); | |||
| 271 | } | |||
| 272 | for (auto T : MVT::integer_fixedlen_vector_valuetypes()) | |||
| 273 | setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand); | |||
| 274 | ||||
| 275 | // Dynamic stack allocation: use the default expansion. | |||
| 276 | setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); | |||
| 277 | setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); | |||
| 278 | setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); | |||
| 279 | ||||
| 280 | setOperationAction(ISD::FrameIndex, MVT::i32, Custom); | |||
| 281 | setOperationAction(ISD::FrameIndex, MVT::i64, Custom); | |||
| 282 | setOperationAction(ISD::CopyToReg, MVT::Other, Custom); | |||
| 283 | ||||
| 284 | // Expand these forms; we pattern-match the forms that we can handle in isel. | |||
| 285 | for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) | |||
| 286 | for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) | |||
| 287 | setOperationAction(Op, T, Expand); | |||
| 288 | ||||
| 289 | // We have custom switch handling. | |||
| 290 | setOperationAction(ISD::BR_JT, MVT::Other, Custom); | |||
| 291 | ||||
| 292 | // WebAssembly doesn't have: | |||
| 293 | // - Floating-point extending loads. | |||
| 294 | // - Floating-point truncating stores. | |||
| 295 | // - i1 extending loads. | |||
| 296 | // - truncating SIMD stores and most extending loads | |||
| 297 | setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); | |||
| 298 | setTruncStoreAction(MVT::f64, MVT::f32, Expand); | |||
| 299 | for (auto T : MVT::integer_valuetypes()) | |||
| 300 | for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) | |||
| 301 | setLoadExtAction(Ext, T, MVT::i1, Promote); | |||
| 302 | if (Subtarget->hasSIMD128()) { | |||
| 303 | for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64, MVT::v4f32, | |||
| 304 | MVT::v2f64}) { | |||
| 305 | for (auto MemT : MVT::fixedlen_vector_valuetypes()) { | |||
| 306 | if (MVT(T) != MemT) { | |||
| 307 | setTruncStoreAction(T, MemT, Expand); | |||
| 308 | for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) | |||
| 309 | setLoadExtAction(Ext, T, MemT, Expand); | |||
| 310 | } | |||
| 311 | } | |||
| 312 | } | |||
| 313 | // But some vector extending loads are legal | |||
| 314 | for (auto Ext : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) { | |||
| 315 | setLoadExtAction(Ext, MVT::v8i16, MVT::v8i8, Legal); | |||
| 316 | setLoadExtAction(Ext, MVT::v4i32, MVT::v4i16, Legal); | |||
| 317 | setLoadExtAction(Ext, MVT::v2i64, MVT::v2i32, Legal); | |||
| 318 | } | |||
| 319 | setLoadExtAction(ISD::EXTLOAD, MVT::v2f64, MVT::v2f32, Legal); | |||
| 320 | } | |||
| 321 | ||||
| 322 | // Don't do anything clever with build_pairs | |||
| 323 | setOperationAction(ISD::BUILD_PAIR, MVT::i64, Expand); | |||
| 324 | ||||
| 325 | // Trap lowers to wasm unreachable | |||
| 326 | setOperationAction(ISD::TRAP, MVT::Other, Legal); | |||
| 327 | setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); | |||
| 328 | ||||
| 329 | // Exception handling intrinsics | |||
| 330 | setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); | |||
| 331 | setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); | |||
| 332 | setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); | |||
| 333 | ||||
| 334 | setMaxAtomicSizeInBitsSupported(64); | |||
| 335 | ||||
| 336 | // Override the __gnu_f2h_ieee/__gnu_h2f_ieee names so that the f32 name is | |||
| 337 | // consistent with the f64 and f128 names. | |||
| 338 | setLibcallName(RTLIB::FPEXT_F16_F32, "__extendhfsf2"); | |||
| 339 | setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2"); | |||
| 340 | ||||
| 341 | // Define the emscripten name for return address helper. | |||
| 342 | // TODO: when implementing other Wasm backends, make this generic or only do | |||
| 343 | // this on emscripten depending on what they end up doing. | |||
| 344 | setLibcallName(RTLIB::RETURN_ADDRESS, "emscripten_return_address"); | |||
| 345 | ||||
| 346 | // Always convert switches to br_tables unless there is only one case, which | |||
| 347 | // is equivalent to a simple branch. This reduces code size for wasm, and we | |||
| 348 | // defer possible jump table optimizations to the VM. | |||
| 349 | setMinimumJumpTableEntries(2); | |||
| 350 | } | |||
| 351 | ||||
| 352 | MVT WebAssemblyTargetLowering::getPointerTy(const DataLayout &DL, | |||
| 353 | uint32_t AS) const { | |||
| 354 | if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) | |||
| 355 | return MVT::externref; | |||
| 356 | if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) | |||
| 357 | return MVT::funcref; | |||
| 358 | return TargetLowering::getPointerTy(DL, AS); | |||
| 359 | } | |||
| 360 | ||||
| 361 | MVT WebAssemblyTargetLowering::getPointerMemTy(const DataLayout &DL, | |||
| 362 | uint32_t AS) const { | |||
| 363 | if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF) | |||
| 364 | return MVT::externref; | |||
| 365 | if (AS == WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF) | |||
| 366 | return MVT::funcref; | |||
| 367 | return TargetLowering::getPointerMemTy(DL, AS); | |||
| 368 | } | |||
| 369 | ||||
| 370 | TargetLowering::AtomicExpansionKind | |||
| 371 | WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { | |||
| 372 | // We have wasm instructions for these | |||
| 373 | switch (AI->getOperation()) { | |||
| 374 | case AtomicRMWInst::Add: | |||
| 375 | case AtomicRMWInst::Sub: | |||
| 376 | case AtomicRMWInst::And: | |||
| 377 | case AtomicRMWInst::Or: | |||
| 378 | case AtomicRMWInst::Xor: | |||
| 379 | case AtomicRMWInst::Xchg: | |||
| 380 | return AtomicExpansionKind::None; | |||
| 381 | default: | |||
| 382 | break; | |||
| 383 | } | |||
| 384 | return AtomicExpansionKind::CmpXChg; | |||
| 385 | } | |||
| 386 | ||||
| 387 | bool WebAssemblyTargetLowering::shouldScalarizeBinop(SDValue VecOp) const { | |||
| 388 | // Implementation copied from X86TargetLowering. | |||
| 389 | unsigned Opc = VecOp.getOpcode(); | |||
| 390 | ||||
| 391 | // Assume target opcodes can't be scalarized. | |||
| 392 | // TODO - do we have any exceptions? | |||
| 393 | if (Opc >= ISD::BUILTIN_OP_END) | |||
| 394 | return false; | |||
| 395 | ||||
| 396 | // If the vector op is not supported, try to convert to scalar. | |||
| 397 | EVT VecVT = VecOp.getValueType(); | |||
| 398 | if (!isOperationLegalOrCustomOrPromote(Opc, VecVT)) | |||
| 399 | return true; | |||
| 400 | ||||
| 401 | // If the vector op is supported, but the scalar op is not, the transform may | |||
| 402 | // not be worthwhile. | |||
| 403 | EVT ScalarVT = VecVT.getScalarType(); | |||
| 404 | return isOperationLegalOrCustomOrPromote(Opc, ScalarVT); | |||
| 405 | } | |||
| 406 | ||||
| 407 | FastISel *WebAssemblyTargetLowering::createFastISel( | |||
| 408 | FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const { | |||
| 409 | return WebAssembly::createFastISel(FuncInfo, LibInfo); | |||
| 410 | } | |||
| 411 | ||||
| 412 | MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, | |||
| 413 | EVT VT) const { | |||
| 414 | unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1); | |||
| 415 | if (BitWidth > 1 && BitWidth < 8) | |||
| 416 | BitWidth = 8; | |||
| 417 | ||||
| 418 | if (BitWidth > 64) { | |||
| 419 | // The shift will be lowered to a libcall, and compiler-rt libcalls expect | |||
| 420 | // the count to be an i32. | |||
| 421 | BitWidth = 32; | |||
| 422 | assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) &&(static_cast <bool> (BitWidth >= Log2_32_Ceil(VT.getSizeInBits ()) && "32-bit shift counts ought to be enough for anyone" ) ? void (0) : __assert_fail ("BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && \"32-bit shift counts ought to be enough for anyone\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 423 , __extension__ __PRETTY_FUNCTION__)) | |||
| 423 | "32-bit shift counts ought to be enough for anyone")(static_cast <bool> (BitWidth >= Log2_32_Ceil(VT.getSizeInBits ()) && "32-bit shift counts ought to be enough for anyone" ) ? void (0) : __assert_fail ("BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && \"32-bit shift counts ought to be enough for anyone\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 423 , __extension__ __PRETTY_FUNCTION__)); | |||
| 424 | } | |||
| 425 | ||||
| 426 | MVT Result = MVT::getIntegerVT(BitWidth); | |||
| 427 | assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE &&(static_cast <bool> (Result != MVT::INVALID_SIMPLE_VALUE_TYPE && "Unable to represent scalar shift amount type") ? void (0) : __assert_fail ("Result != MVT::INVALID_SIMPLE_VALUE_TYPE && \"Unable to represent scalar shift amount type\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 428 , __extension__ __PRETTY_FUNCTION__)) | |||
| 428 | "Unable to represent scalar shift amount type")(static_cast <bool> (Result != MVT::INVALID_SIMPLE_VALUE_TYPE && "Unable to represent scalar shift amount type") ? void (0) : __assert_fail ("Result != MVT::INVALID_SIMPLE_VALUE_TYPE && \"Unable to represent scalar shift amount type\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 428 , __extension__ __PRETTY_FUNCTION__)); | |||
| 429 | return Result; | |||
| 430 | } | |||
| 431 | ||||
| 432 | // Lower an fp-to-int conversion operator from the LLVM opcode, which has an | |||
| 433 | // undefined result on invalid/overflow, to the WebAssembly opcode, which | |||
| 434 | // traps on invalid/overflow. | |||
| 435 | static MachineBasicBlock *LowerFPToInt(MachineInstr &MI, DebugLoc DL, | |||
| 436 | MachineBasicBlock *BB, | |||
| 437 | const TargetInstrInfo &TII, | |||
| 438 | bool IsUnsigned, bool Int64, | |||
| 439 | bool Float64, unsigned LoweredOpcode) { | |||
| 440 | MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); | |||
| 441 | ||||
| 442 | Register OutReg = MI.getOperand(0).getReg(); | |||
| 443 | Register InReg = MI.getOperand(1).getReg(); | |||
| 444 | ||||
| 445 | unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; | |||
| 446 | unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; | |||
| 447 | unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; | |||
| 448 | unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; | |||
| 449 | unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; | |||
| 450 | unsigned Eqz = WebAssembly::EQZ_I32; | |||
| 451 | unsigned And = WebAssembly::AND_I32; | |||
| 452 | int64_t Limit = Int64 ? INT64_MIN(-9223372036854775807L -1) : INT32_MIN(-2147483647-1); | |||
| 453 | int64_t Substitute = IsUnsigned ? 0 : Limit; | |||
| 454 | double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; | |||
| 455 | auto &Context = BB->getParent()->getFunction().getContext(); | |||
| 456 | Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); | |||
| 457 | ||||
| 458 | const BasicBlock *LLVMBB = BB->getBasicBlock(); | |||
| 459 | MachineFunction *F = BB->getParent(); | |||
| 460 | MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB); | |||
| 461 | MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVMBB); | |||
| 462 | MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB); | |||
| 463 | ||||
| 464 | MachineFunction::iterator It = ++BB->getIterator(); | |||
| 465 | F->insert(It, FalseMBB); | |||
| 466 | F->insert(It, TrueMBB); | |||
| 467 | F->insert(It, DoneMBB); | |||
| 468 | ||||
| 469 | // Transfer the remainder of BB and its successor edges to DoneMBB. | |||
| 470 | DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end()); | |||
| 471 | DoneMBB->transferSuccessorsAndUpdatePHIs(BB); | |||
| 472 | ||||
| 473 | BB->addSuccessor(TrueMBB); | |||
| 474 | BB->addSuccessor(FalseMBB); | |||
| 475 | TrueMBB->addSuccessor(DoneMBB); | |||
| 476 | FalseMBB->addSuccessor(DoneMBB); | |||
| 477 | ||||
| 478 | unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; | |||
| 479 | Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); | |||
| 480 | Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); | |||
| 481 | CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 482 | EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 483 | FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); | |||
| 484 | TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); | |||
| 485 | ||||
| 486 | MI.eraseFromParent(); | |||
| 487 | // For signed numbers, we can do a single comparison to determine whether | |||
| 488 | // fabs(x) is within range. | |||
| 489 | if (IsUnsigned) { | |||
| 490 | Tmp0 = InReg; | |||
| 491 | } else { | |||
| 492 | BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg); | |||
| 493 | } | |||
| 494 | BuildMI(BB, DL, TII.get(FConst), Tmp1) | |||
| 495 | .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal))); | |||
| 496 | BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1); | |||
| 497 | ||||
| 498 | // For unsigned numbers, we have to do a separate comparison with zero. | |||
| 499 | if (IsUnsigned) { | |||
| 500 | Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); | |||
| 501 | Register SecondCmpReg = | |||
| 502 | MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 503 | Register AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 504 | BuildMI(BB, DL, TII.get(FConst), Tmp1) | |||
| 505 | .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0))); | |||
| 506 | BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1); | |||
| 507 | BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg); | |||
| 508 | CmpReg = AndReg; | |||
| 509 | } | |||
| 510 | ||||
| 511 | BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg); | |||
| 512 | ||||
| 513 | // Create the CFG diamond to select between doing the conversion or using | |||
| 514 | // the substitute value. | |||
| 515 | BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg); | |||
| 516 | BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg); | |||
| 517 | BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); | |||
| 518 | BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute); | |||
| 519 | BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) | |||
| 520 | .addReg(FalseReg) | |||
| 521 | .addMBB(FalseMBB) | |||
| 522 | .addReg(TrueReg) | |||
| 523 | .addMBB(TrueMBB); | |||
| 524 | ||||
| 525 | return DoneMBB; | |||
| 526 | } | |||
| 527 | ||||
| 528 | static MachineBasicBlock * | |||
| 529 | LowerCallResults(MachineInstr &CallResults, DebugLoc DL, MachineBasicBlock *BB, | |||
| 530 | const WebAssemblySubtarget *Subtarget, | |||
| 531 | const TargetInstrInfo &TII) { | |||
| 532 | MachineInstr &CallParams = *CallResults.getPrevNode(); | |||
| 533 | assert(CallParams.getOpcode() == WebAssembly::CALL_PARAMS)(static_cast <bool> (CallParams.getOpcode() == WebAssembly ::CALL_PARAMS) ? void (0) : __assert_fail ("CallParams.getOpcode() == WebAssembly::CALL_PARAMS" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 533 , __extension__ __PRETTY_FUNCTION__)); | |||
| 534 | assert(CallResults.getOpcode() == WebAssembly::CALL_RESULTS ||(static_cast <bool> (CallResults.getOpcode() == WebAssembly ::CALL_RESULTS || CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS ) ? void (0) : __assert_fail ("CallResults.getOpcode() == WebAssembly::CALL_RESULTS || CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 535 , __extension__ __PRETTY_FUNCTION__)) | |||
| 535 | CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS)(static_cast <bool> (CallResults.getOpcode() == WebAssembly ::CALL_RESULTS || CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS ) ? void (0) : __assert_fail ("CallResults.getOpcode() == WebAssembly::CALL_RESULTS || CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 535 , __extension__ __PRETTY_FUNCTION__)); | |||
| 536 | ||||
| 537 | bool IsIndirect = | |||
| 538 | CallParams.getOperand(0).isReg() || CallParams.getOperand(0).isFI(); | |||
| 539 | bool IsRetCall = CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS; | |||
| 540 | ||||
| 541 | bool IsFuncrefCall = false; | |||
| 542 | if (IsIndirect && CallParams.getOperand(0).isReg()) { | |||
| 543 | Register Reg = CallParams.getOperand(0).getReg(); | |||
| 544 | const MachineFunction *MF = BB->getParent(); | |||
| 545 | const MachineRegisterInfo &MRI = MF->getRegInfo(); | |||
| 546 | const TargetRegisterClass *TRC = MRI.getRegClass(Reg); | |||
| 547 | IsFuncrefCall = (TRC == &WebAssembly::FUNCREFRegClass); | |||
| 548 | assert(!IsFuncrefCall || Subtarget->hasReferenceTypes())(static_cast <bool> (!IsFuncrefCall || Subtarget->hasReferenceTypes ()) ? void (0) : __assert_fail ("!IsFuncrefCall || Subtarget->hasReferenceTypes()" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 548 , __extension__ __PRETTY_FUNCTION__)); | |||
| 549 | } | |||
| 550 | ||||
| 551 | unsigned CallOp; | |||
| 552 | if (IsIndirect && IsRetCall) { | |||
| 553 | CallOp = WebAssembly::RET_CALL_INDIRECT; | |||
| 554 | } else if (IsIndirect) { | |||
| 555 | CallOp = WebAssembly::CALL_INDIRECT; | |||
| 556 | } else if (IsRetCall) { | |||
| 557 | CallOp = WebAssembly::RET_CALL; | |||
| 558 | } else { | |||
| 559 | CallOp = WebAssembly::CALL; | |||
| 560 | } | |||
| 561 | ||||
| 562 | MachineFunction &MF = *BB->getParent(); | |||
| 563 | const MCInstrDesc &MCID = TII.get(CallOp); | |||
| 564 | MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); | |||
| 565 | ||||
| 566 | // See if we must truncate the function pointer. | |||
| 567 | // CALL_INDIRECT takes an i32, but in wasm64 we represent function pointers | |||
| 568 | // as 64-bit for uniformity with other pointer types. | |||
| 569 | // See also: WebAssemblyFastISel::selectCall | |||
| 570 | if (IsIndirect && MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()) { | |||
| 571 | Register Reg32 = | |||
| 572 | MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 573 | auto &FnPtr = CallParams.getOperand(0); | |||
| 574 | BuildMI(*BB, CallResults.getIterator(), DL, | |||
| 575 | TII.get(WebAssembly::I32_WRAP_I64), Reg32) | |||
| 576 | .addReg(FnPtr.getReg()); | |||
| 577 | FnPtr.setReg(Reg32); | |||
| 578 | } | |||
| 579 | ||||
| 580 | // Move the function pointer to the end of the arguments for indirect calls | |||
| 581 | if (IsIndirect) { | |||
| 582 | auto FnPtr = CallParams.getOperand(0); | |||
| 583 | CallParams.removeOperand(0); | |||
| 584 | ||||
| 585 | // For funcrefs, call_indirect is done through __funcref_call_table and the | |||
| 586 | // funcref is always installed in slot 0 of the table, therefore instead of | |||
| 587 | // having the function pointer added at the end of the params list, a zero | |||
| 588 | // (the index in | |||
| 589 | // __funcref_call_table is added). | |||
| 590 | if (IsFuncrefCall) { | |||
| 591 | Register RegZero = | |||
| 592 | MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 593 | MachineInstrBuilder MIBC0 = | |||
| 594 | BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0); | |||
| 595 | ||||
| 596 | BB->insert(CallResults.getIterator(), MIBC0); | |||
| 597 | MachineInstrBuilder(MF, CallParams).addReg(RegZero); | |||
| 598 | } else | |||
| 599 | CallParams.addOperand(FnPtr); | |||
| 600 | } | |||
| 601 | ||||
| 602 | for (auto Def : CallResults.defs()) | |||
| 603 | MIB.add(Def); | |||
| 604 | ||||
| 605 | if (IsIndirect) { | |||
| 606 | // Placeholder for the type index. | |||
| 607 | MIB.addImm(0); | |||
| 608 | // The table into which this call_indirect indexes. | |||
| 609 | MCSymbolWasm *Table = IsFuncrefCall | |||
| 610 | ? WebAssembly::getOrCreateFuncrefCallTableSymbol( | |||
| 611 | MF.getContext(), Subtarget) | |||
| 612 | : WebAssembly::getOrCreateFunctionTableSymbol( | |||
| 613 | MF.getContext(), Subtarget); | |||
| 614 | if (Subtarget->hasReferenceTypes()) { | |||
| 615 | MIB.addSym(Table); | |||
| 616 | } else { | |||
| 617 | // For the MVP there is at most one table whose number is 0, but we can't | |||
| 618 | // write a table symbol or issue relocations. Instead we just ensure the | |||
| 619 | // table is live and write a zero. | |||
| 620 | Table->setNoStrip(); | |||
| 621 | MIB.addImm(0); | |||
| 622 | } | |||
| 623 | } | |||
| 624 | ||||
| 625 | for (auto Use : CallParams.uses()) | |||
| 626 | MIB.add(Use); | |||
| 627 | ||||
| 628 | BB->insert(CallResults.getIterator(), MIB); | |||
| 629 | CallParams.eraseFromParent(); | |||
| 630 | CallResults.eraseFromParent(); | |||
| 631 | ||||
| 632 | // If this is a funcref call, to avoid hidden GC roots, we need to clear the | |||
| 633 | // table slot with ref.null upon call_indirect return. | |||
| 634 | // | |||
| 635 | // This generates the following code, which comes right after a call_indirect | |||
| 636 | // of a funcref: | |||
| 637 | // | |||
| 638 | // i32.const 0 | |||
| 639 | // ref.null func | |||
| 640 | // table.set __funcref_call_table | |||
| 641 | if (IsIndirect && IsFuncrefCall) { | |||
| 642 | MCSymbolWasm *Table = WebAssembly::getOrCreateFuncrefCallTableSymbol( | |||
| 643 | MF.getContext(), Subtarget); | |||
| 644 | Register RegZero = | |||
| 645 | MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); | |||
| 646 | MachineInstr *Const0 = | |||
| 647 | BuildMI(MF, DL, TII.get(WebAssembly::CONST_I32), RegZero).addImm(0); | |||
| 648 | BB->insertAfter(MIB.getInstr()->getIterator(), Const0); | |||
| 649 | ||||
| 650 | Register RegFuncref = | |||
| 651 | MF.getRegInfo().createVirtualRegister(&WebAssembly::FUNCREFRegClass); | |||
| 652 | MachineInstr *RefNull = | |||
| 653 | BuildMI(MF, DL, TII.get(WebAssembly::REF_NULL_FUNCREF), RegFuncref); | |||
| 654 | BB->insertAfter(Const0->getIterator(), RefNull); | |||
| 655 | ||||
| 656 | MachineInstr *TableSet = | |||
| 657 | BuildMI(MF, DL, TII.get(WebAssembly::TABLE_SET_FUNCREF)) | |||
| 658 | .addSym(Table) | |||
| 659 | .addReg(RegZero) | |||
| 660 | .addReg(RegFuncref); | |||
| 661 | BB->insertAfter(RefNull->getIterator(), TableSet); | |||
| 662 | } | |||
| 663 | ||||
| 664 | return BB; | |||
| 665 | } | |||
| 666 | ||||
| 667 | MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter( | |||
| 668 | MachineInstr &MI, MachineBasicBlock *BB) const { | |||
| 669 | const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); | |||
| 670 | DebugLoc DL = MI.getDebugLoc(); | |||
| 671 | ||||
| 672 | switch (MI.getOpcode()) { | |||
| 673 | default: | |||
| 674 | llvm_unreachable("Unexpected instr type to insert")::llvm::llvm_unreachable_internal("Unexpected instr type to insert" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 674 ); | |||
| 675 | case WebAssembly::FP_TO_SINT_I32_F32: | |||
| 676 | return LowerFPToInt(MI, DL, BB, TII, false, false, false, | |||
| 677 | WebAssembly::I32_TRUNC_S_F32); | |||
| 678 | case WebAssembly::FP_TO_UINT_I32_F32: | |||
| 679 | return LowerFPToInt(MI, DL, BB, TII, true, false, false, | |||
| 680 | WebAssembly::I32_TRUNC_U_F32); | |||
| 681 | case WebAssembly::FP_TO_SINT_I64_F32: | |||
| 682 | return LowerFPToInt(MI, DL, BB, TII, false, true, false, | |||
| 683 | WebAssembly::I64_TRUNC_S_F32); | |||
| 684 | case WebAssembly::FP_TO_UINT_I64_F32: | |||
| 685 | return LowerFPToInt(MI, DL, BB, TII, true, true, false, | |||
| 686 | WebAssembly::I64_TRUNC_U_F32); | |||
| 687 | case WebAssembly::FP_TO_SINT_I32_F64: | |||
| 688 | return LowerFPToInt(MI, DL, BB, TII, false, false, true, | |||
| 689 | WebAssembly::I32_TRUNC_S_F64); | |||
| 690 | case WebAssembly::FP_TO_UINT_I32_F64: | |||
| 691 | return LowerFPToInt(MI, DL, BB, TII, true, false, true, | |||
| 692 | WebAssembly::I32_TRUNC_U_F64); | |||
| 693 | case WebAssembly::FP_TO_SINT_I64_F64: | |||
| 694 | return LowerFPToInt(MI, DL, BB, TII, false, true, true, | |||
| 695 | WebAssembly::I64_TRUNC_S_F64); | |||
| 696 | case WebAssembly::FP_TO_UINT_I64_F64: | |||
| 697 | return LowerFPToInt(MI, DL, BB, TII, true, true, true, | |||
| 698 | WebAssembly::I64_TRUNC_U_F64); | |||
| 699 | case WebAssembly::CALL_RESULTS: | |||
| 700 | case WebAssembly::RET_CALL_RESULTS: | |||
| 701 | return LowerCallResults(MI, DL, BB, Subtarget, TII); | |||
| 702 | } | |||
| 703 | } | |||
| 704 | ||||
| 705 | const char * | |||
| 706 | WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { | |||
| 707 | switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) { | |||
| 708 | case WebAssemblyISD::FIRST_NUMBER: | |||
| 709 | case WebAssemblyISD::FIRST_MEM_OPCODE: | |||
| 710 | break; | |||
| 711 | #define HANDLE_NODETYPE(NODE) \ | |||
| 712 | case WebAssemblyISD::NODE: \ | |||
| 713 | return "WebAssemblyISD::" #NODE; | |||
| 714 | #define HANDLE_MEM_NODETYPE(NODE) HANDLE_NODETYPE(NODE) | |||
| 715 | #include "WebAssemblyISD.def" | |||
| 716 | #undef HANDLE_MEM_NODETYPE | |||
| 717 | #undef HANDLE_NODETYPE | |||
| 718 | } | |||
| 719 | return nullptr; | |||
| 720 | } | |||
| 721 | ||||
| 722 | std::pair<unsigned, const TargetRegisterClass *> | |||
| 723 | WebAssemblyTargetLowering::getRegForInlineAsmConstraint( | |||
| 724 | const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { | |||
| 725 | // First, see if this is a constraint that directly corresponds to a | |||
| 726 | // WebAssembly register class. | |||
| 727 | if (Constraint.size() == 1) { | |||
| 728 | switch (Constraint[0]) { | |||
| 729 | case 'r': | |||
| 730 | assert(VT != MVT::iPTR && "Pointer MVT not expected here")(static_cast <bool> (VT != MVT::iPTR && "Pointer MVT not expected here" ) ? void (0) : __assert_fail ("VT != MVT::iPTR && \"Pointer MVT not expected here\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 730 , __extension__ __PRETTY_FUNCTION__)); | |||
| 731 | if (Subtarget->hasSIMD128() && VT.isVector()) { | |||
| 732 | if (VT.getSizeInBits() == 128) | |||
| 733 | return std::make_pair(0U, &WebAssembly::V128RegClass); | |||
| 734 | } | |||
| 735 | if (VT.isInteger() && !VT.isVector()) { | |||
| 736 | if (VT.getSizeInBits() <= 32) | |||
| 737 | return std::make_pair(0U, &WebAssembly::I32RegClass); | |||
| 738 | if (VT.getSizeInBits() <= 64) | |||
| 739 | return std::make_pair(0U, &WebAssembly::I64RegClass); | |||
| 740 | } | |||
| 741 | if (VT.isFloatingPoint() && !VT.isVector()) { | |||
| 742 | switch (VT.getSizeInBits()) { | |||
| 743 | case 32: | |||
| 744 | return std::make_pair(0U, &WebAssembly::F32RegClass); | |||
| 745 | case 64: | |||
| 746 | return std::make_pair(0U, &WebAssembly::F64RegClass); | |||
| 747 | default: | |||
| 748 | break; | |||
| 749 | } | |||
| 750 | } | |||
| 751 | break; | |||
| 752 | default: | |||
| 753 | break; | |||
| 754 | } | |||
| 755 | } | |||
| 756 | ||||
| 757 | return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); | |||
| 758 | } | |||
| 759 | ||||
| 760 | bool WebAssemblyTargetLowering::isCheapToSpeculateCttz(Type *Ty) const { | |||
| 761 | // Assume ctz is a relatively cheap operation. | |||
| 762 | return true; | |||
| 763 | } | |||
| 764 | ||||
| 765 | bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz(Type *Ty) const { | |||
| 766 | // Assume clz is a relatively cheap operation. | |||
| 767 | return true; | |||
| 768 | } | |||
| 769 | ||||
| 770 | bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL, | |||
| 771 | const AddrMode &AM, | |||
| 772 | Type *Ty, unsigned AS, | |||
| 773 | Instruction *I) const { | |||
| 774 | // WebAssembly offsets are added as unsigned without wrapping. The | |||
| 775 | // isLegalAddressingMode gives us no way to determine if wrapping could be | |||
| 776 | // happening, so we approximate this by accepting only non-negative offsets. | |||
| 777 | if (AM.BaseOffs < 0) | |||
| 778 | return false; | |||
| 779 | ||||
| 780 | // WebAssembly has no scale register operands. | |||
| 781 | if (AM.Scale != 0) | |||
| 782 | return false; | |||
| 783 | ||||
| 784 | // Everything else is legal. | |||
| 785 | return true; | |||
| 786 | } | |||
| 787 | ||||
| 788 | bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses( | |||
| 789 | EVT /*VT*/, unsigned /*AddrSpace*/, Align /*Align*/, | |||
| 790 | MachineMemOperand::Flags /*Flags*/, unsigned *Fast) const { | |||
| 791 | // WebAssembly supports unaligned accesses, though it should be declared | |||
| 792 | // with the p2align attribute on loads and stores which do so, and there | |||
| 793 | // may be a performance impact. We tell LLVM they're "fast" because | |||
| 794 | // for the kinds of things that LLVM uses this for (merging adjacent stores | |||
| 795 | // of constants, etc.), WebAssembly implementations will either want the | |||
| 796 | // unaligned access or they'll split anyway. | |||
| 797 | if (Fast) | |||
| 798 | *Fast = 1; | |||
| 799 | return true; | |||
| 800 | } | |||
| 801 | ||||
| 802 | bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, | |||
| 803 | AttributeList Attr) const { | |||
| 804 | // The current thinking is that wasm engines will perform this optimization, | |||
| 805 | // so we can save on code size. | |||
| 806 | return true; | |||
| 807 | } | |||
| 808 | ||||
| 809 | bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { | |||
| 810 | EVT ExtT = ExtVal.getValueType(); | |||
| 811 | EVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getValueType(0); | |||
| 812 | return (ExtT == MVT::v8i16 && MemT == MVT::v8i8) || | |||
| 813 | (ExtT == MVT::v4i32 && MemT == MVT::v4i16) || | |||
| 814 | (ExtT == MVT::v2i64 && MemT == MVT::v2i32); | |||
| 815 | } | |||
| 816 | ||||
| 817 | bool WebAssemblyTargetLowering::isOffsetFoldingLegal( | |||
| 818 | const GlobalAddressSDNode *GA) const { | |||
| 819 | // Wasm doesn't support function addresses with offsets | |||
| 820 | const GlobalValue *GV = GA->getGlobal(); | |||
| 821 | return isa<Function>(GV) ? false : TargetLowering::isOffsetFoldingLegal(GA); | |||
| 822 | } | |||
| 823 | ||||
| 824 | EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL, | |||
| 825 | LLVMContext &C, | |||
| 826 | EVT VT) const { | |||
| 827 | if (VT.isVector()) | |||
| 828 | return VT.changeVectorElementTypeToInteger(); | |||
| 829 | ||||
| 830 | // So far, all branch instructions in Wasm take an I32 condition. | |||
| 831 | // The default TargetLowering::getSetCCResultType returns the pointer size, | |||
| 832 | // which would be useful to reduce instruction counts when testing | |||
| 833 | // against 64-bit pointers/values if at some point Wasm supports that. | |||
| 834 | return EVT::getIntegerVT(C, 32); | |||
| 835 | } | |||
| 836 | ||||
| 837 | bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, | |||
| 838 | const CallInst &I, | |||
| 839 | MachineFunction &MF, | |||
| 840 | unsigned Intrinsic) const { | |||
| 841 | switch (Intrinsic) { | |||
| 842 | case Intrinsic::wasm_memory_atomic_notify: | |||
| 843 | Info.opc = ISD::INTRINSIC_W_CHAIN; | |||
| 844 | Info.memVT = MVT::i32; | |||
| 845 | Info.ptrVal = I.getArgOperand(0); | |||
| 846 | Info.offset = 0; | |||
| 847 | Info.align = Align(4); | |||
| 848 | // atomic.notify instruction does not really load the memory specified with | |||
| 849 | // this argument, but MachineMemOperand should either be load or store, so | |||
| 850 | // we set this to a load. | |||
| 851 | // FIXME Volatile isn't really correct, but currently all LLVM atomic | |||
| 852 | // instructions are treated as volatiles in the backend, so we should be | |||
| 853 | // consistent. The same applies for wasm_atomic_wait intrinsics too. | |||
| 854 | Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; | |||
| 855 | return true; | |||
| 856 | case Intrinsic::wasm_memory_atomic_wait32: | |||
| 857 | Info.opc = ISD::INTRINSIC_W_CHAIN; | |||
| 858 | Info.memVT = MVT::i32; | |||
| 859 | Info.ptrVal = I.getArgOperand(0); | |||
| 860 | Info.offset = 0; | |||
| 861 | Info.align = Align(4); | |||
| 862 | Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; | |||
| 863 | return true; | |||
| 864 | case Intrinsic::wasm_memory_atomic_wait64: | |||
| 865 | Info.opc = ISD::INTRINSIC_W_CHAIN; | |||
| 866 | Info.memVT = MVT::i64; | |||
| 867 | Info.ptrVal = I.getArgOperand(0); | |||
| 868 | Info.offset = 0; | |||
| 869 | Info.align = Align(8); | |||
| 870 | Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; | |||
| 871 | return true; | |||
| 872 | default: | |||
| 873 | return false; | |||
| 874 | } | |||
| 875 | } | |||
| 876 | ||||
| 877 | void WebAssemblyTargetLowering::computeKnownBitsForTargetNode( | |||
| 878 | const SDValue Op, KnownBits &Known, const APInt &DemandedElts, | |||
| 879 | const SelectionDAG &DAG, unsigned Depth) const { | |||
| 880 | switch (Op.getOpcode()) { | |||
| 881 | default: | |||
| 882 | break; | |||
| 883 | case ISD::INTRINSIC_WO_CHAIN: { | |||
| 884 | unsigned IntNo = Op.getConstantOperandVal(0); | |||
| 885 | switch (IntNo) { | |||
| 886 | default: | |||
| 887 | break; | |||
| 888 | case Intrinsic::wasm_bitmask: { | |||
| 889 | unsigned BitWidth = Known.getBitWidth(); | |||
| 890 | EVT VT = Op.getOperand(1).getSimpleValueType(); | |||
| 891 | unsigned PossibleBits = VT.getVectorNumElements(); | |||
| 892 | APInt ZeroMask = APInt::getHighBitsSet(BitWidth, BitWidth - PossibleBits); | |||
| 893 | Known.Zero |= ZeroMask; | |||
| 894 | break; | |||
| 895 | } | |||
| 896 | } | |||
| 897 | } | |||
| 898 | } | |||
| 899 | } | |||
| 900 | ||||
| 901 | TargetLoweringBase::LegalizeTypeAction | |||
| 902 | WebAssemblyTargetLowering::getPreferredVectorAction(MVT VT) const { | |||
| 903 | if (VT.isFixedLengthVector()) { | |||
| 904 | MVT EltVT = VT.getVectorElementType(); | |||
| 905 | // We have legal vector types with these lane types, so widening the | |||
| 906 | // vector would let us use some of the lanes directly without having to | |||
| 907 | // extend or truncate values. | |||
| 908 | if (EltVT == MVT::i8 || EltVT == MVT::i16 || EltVT == MVT::i32 || | |||
| 909 | EltVT == MVT::i64 || EltVT == MVT::f32 || EltVT == MVT::f64) | |||
| 910 | return TypeWidenVector; | |||
| 911 | } | |||
| 912 | ||||
| 913 | return TargetLoweringBase::getPreferredVectorAction(VT); | |||
| 914 | } | |||
| 915 | ||||
| 916 | bool WebAssemblyTargetLowering::shouldSimplifyDemandedVectorElts( | |||
| 917 | SDValue Op, const TargetLoweringOpt &TLO) const { | |||
| 918 | // ISel process runs DAGCombiner after legalization; this step is called | |||
| 919 | // SelectionDAG optimization phase. This post-legalization combining process | |||
| 920 | // runs DAGCombiner on each node, and if there was a change to be made, | |||
| 921 | // re-runs legalization again on it and its user nodes to make sure | |||
| 922 | // everythiing is in a legalized state. | |||
| 923 | // | |||
| 924 | // The legalization calls lowering routines, and we do our custom lowering for | |||
| 925 | // build_vectors (LowerBUILD_VECTOR), which converts undef vector elements | |||
| 926 | // into zeros. But there is a set of routines in DAGCombiner that turns unused | |||
| 927 | // (= not demanded) nodes into undef, among which SimplifyDemandedVectorElts | |||
| 928 | // turns unused vector elements into undefs. But this routine does not work | |||
| 929 | // with our custom LowerBUILD_VECTOR, which turns undefs into zeros. This | |||
| 930 | // combination can result in a infinite loop, in which undefs are converted to | |||
| 931 | // zeros in legalization and back to undefs in combining. | |||
| 932 | // | |||
| 933 | // So after DAG is legalized, we prevent SimplifyDemandedVectorElts from | |||
| 934 | // running for build_vectors. | |||
| 935 | if (Op.getOpcode() == ISD::BUILD_VECTOR && TLO.LegalOps && TLO.LegalTys) | |||
| 936 | return false; | |||
| 937 | return true; | |||
| 938 | } | |||
| 939 | ||||
| 940 | //===----------------------------------------------------------------------===// | |||
| 941 | // WebAssembly Lowering private implementation. | |||
| 942 | //===----------------------------------------------------------------------===// | |||
| 943 | ||||
| 944 | //===----------------------------------------------------------------------===// | |||
| 945 | // Lowering Code | |||
| 946 | //===----------------------------------------------------------------------===// | |||
| 947 | ||||
| 948 | static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) { | |||
| 949 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 950 | DAG.getContext()->diagnose( | |||
| 951 | DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); | |||
| 952 | } | |||
| 953 | ||||
| 954 | // Test whether the given calling convention is supported. | |||
| 955 | static bool callingConvSupported(CallingConv::ID CallConv) { | |||
| 956 | // We currently support the language-independent target-independent | |||
| 957 | // conventions. We don't yet have a way to annotate calls with properties like | |||
| 958 | // "cold", and we don't have any call-clobbered registers, so these are mostly | |||
| 959 | // all handled the same. | |||
| 960 | return CallConv == CallingConv::C || CallConv == CallingConv::Fast || | |||
| 961 | CallConv == CallingConv::Cold || | |||
| 962 | CallConv == CallingConv::PreserveMost || | |||
| 963 | CallConv == CallingConv::PreserveAll || | |||
| 964 | CallConv == CallingConv::CXX_FAST_TLS || | |||
| 965 | CallConv == CallingConv::WASM_EmscriptenInvoke || | |||
| 966 | CallConv == CallingConv::Swift; | |||
| 967 | } | |||
| 968 | ||||
| 969 | SDValue | |||
| 970 | WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, | |||
| 971 | SmallVectorImpl<SDValue> &InVals) const { | |||
| 972 | SelectionDAG &DAG = CLI.DAG; | |||
| 973 | SDLoc DL = CLI.DL; | |||
| 974 | SDValue Chain = CLI.Chain; | |||
| 975 | SDValue Callee = CLI.Callee; | |||
| 976 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 977 | auto Layout = MF.getDataLayout(); | |||
| 978 | ||||
| 979 | CallingConv::ID CallConv = CLI.CallConv; | |||
| 980 | if (!callingConvSupported(CallConv)) | |||
| 981 | fail(DL, DAG, | |||
| 982 | "WebAssembly doesn't support language-specific or target-specific " | |||
| 983 | "calling conventions yet"); | |||
| 984 | if (CLI.IsPatchPoint) | |||
| 985 | fail(DL, DAG, "WebAssembly doesn't support patch point yet"); | |||
| 986 | ||||
| 987 | if (CLI.IsTailCall) { | |||
| 988 | auto NoTail = [&](const char *Msg) { | |||
| 989 | if (CLI.CB && CLI.CB->isMustTailCall()) | |||
| 990 | fail(DL, DAG, Msg); | |||
| 991 | CLI.IsTailCall = false; | |||
| 992 | }; | |||
| 993 | ||||
| 994 | if (!Subtarget->hasTailCall()) | |||
| 995 | NoTail("WebAssembly 'tail-call' feature not enabled"); | |||
| 996 | ||||
| 997 | // Varargs calls cannot be tail calls because the buffer is on the stack | |||
| 998 | if (CLI.IsVarArg) | |||
| 999 | NoTail("WebAssembly does not support varargs tail calls"); | |||
| 1000 | ||||
| 1001 | // Do not tail call unless caller and callee return types match | |||
| 1002 | const Function &F = MF.getFunction(); | |||
| 1003 | const TargetMachine &TM = getTargetMachine(); | |||
| 1004 | Type *RetTy = F.getReturnType(); | |||
| 1005 | SmallVector<MVT, 4> CallerRetTys; | |||
| 1006 | SmallVector<MVT, 4> CalleeRetTys; | |||
| 1007 | computeLegalValueVTs(F, TM, RetTy, CallerRetTys); | |||
| 1008 | computeLegalValueVTs(F, TM, CLI.RetTy, CalleeRetTys); | |||
| 1009 | bool TypesMatch = CallerRetTys.size() == CalleeRetTys.size() && | |||
| 1010 | std::equal(CallerRetTys.begin(), CallerRetTys.end(), | |||
| 1011 | CalleeRetTys.begin()); | |||
| 1012 | if (!TypesMatch) | |||
| 1013 | NoTail("WebAssembly tail call requires caller and callee return types to " | |||
| 1014 | "match"); | |||
| 1015 | ||||
| 1016 | // If pointers to local stack values are passed, we cannot tail call | |||
| 1017 | if (CLI.CB) { | |||
| 1018 | for (auto &Arg : CLI.CB->args()) { | |||
| 1019 | Value *Val = Arg.get(); | |||
| 1020 | // Trace the value back through pointer operations | |||
| 1021 | while (true) { | |||
| 1022 | Value *Src = Val->stripPointerCastsAndAliases(); | |||
| 1023 | if (auto *GEP = dyn_cast<GetElementPtrInst>(Src)) | |||
| 1024 | Src = GEP->getPointerOperand(); | |||
| 1025 | if (Val == Src) | |||
| 1026 | break; | |||
| 1027 | Val = Src; | |||
| 1028 | } | |||
| 1029 | if (isa<AllocaInst>(Val)) { | |||
| 1030 | NoTail( | |||
| 1031 | "WebAssembly does not support tail calling with stack arguments"); | |||
| 1032 | break; | |||
| 1033 | } | |||
| 1034 | } | |||
| 1035 | } | |||
| 1036 | } | |||
| 1037 | ||||
| 1038 | SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; | |||
| 1039 | SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; | |||
| 1040 | SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; | |||
| 1041 | ||||
| 1042 | // The generic code may have added an sret argument. If we're lowering an | |||
| 1043 | // invoke function, the ABI requires that the function pointer be the first | |||
| 1044 | // argument, so we may have to swap the arguments. | |||
| 1045 | if (CallConv == CallingConv::WASM_EmscriptenInvoke && Outs.size() >= 2 && | |||
| 1046 | Outs[0].Flags.isSRet()) { | |||
| 1047 | std::swap(Outs[0], Outs[1]); | |||
| 1048 | std::swap(OutVals[0], OutVals[1]); | |||
| 1049 | } | |||
| 1050 | ||||
| 1051 | bool HasSwiftSelfArg = false; | |||
| 1052 | bool HasSwiftErrorArg = false; | |||
| 1053 | unsigned NumFixedArgs = 0; | |||
| 1054 | for (unsigned I = 0; I < Outs.size(); ++I) { | |||
| 1055 | const ISD::OutputArg &Out = Outs[I]; | |||
| 1056 | SDValue &OutVal = OutVals[I]; | |||
| 1057 | HasSwiftSelfArg |= Out.Flags.isSwiftSelf(); | |||
| 1058 | HasSwiftErrorArg |= Out.Flags.isSwiftError(); | |||
| 1059 | if (Out.Flags.isNest()) | |||
| 1060 | fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); | |||
| 1061 | if (Out.Flags.isInAlloca()) | |||
| 1062 | fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); | |||
| 1063 | if (Out.Flags.isInConsecutiveRegs()) | |||
| 1064 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); | |||
| 1065 | if (Out.Flags.isInConsecutiveRegsLast()) | |||
| 1066 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); | |||
| 1067 | if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) { | |||
| 1068 | auto &MFI = MF.getFrameInfo(); | |||
| 1069 | int FI = MFI.CreateStackObject(Out.Flags.getByValSize(), | |||
| 1070 | Out.Flags.getNonZeroByValAlign(), | |||
| 1071 | /*isSS=*/false); | |||
| 1072 | SDValue SizeNode = | |||
| 1073 | DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); | |||
| 1074 | SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); | |||
| 1075 | Chain = DAG.getMemcpy( | |||
| 1076 | Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getNonZeroByValAlign(), | |||
| 1077 | /*isVolatile*/ false, /*AlwaysInline=*/false, | |||
| 1078 | /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo()); | |||
| 1079 | OutVal = FINode; | |||
| 1080 | } | |||
| 1081 | // Count the number of fixed args *after* legalization. | |||
| 1082 | NumFixedArgs += Out.IsFixed; | |||
| 1083 | } | |||
| 1084 | ||||
| 1085 | bool IsVarArg = CLI.IsVarArg; | |||
| 1086 | auto PtrVT = getPointerTy(Layout); | |||
| 1087 | ||||
| 1088 | // For swiftcc, emit additional swiftself and swifterror arguments | |||
| 1089 | // if there aren't. These additional arguments are also added for callee | |||
| 1090 | // signature They are necessary to match callee and caller signature for | |||
| 1091 | // indirect call. | |||
| 1092 | if (CallConv == CallingConv::Swift) { | |||
| 1093 | if (!HasSwiftSelfArg) { | |||
| 1094 | NumFixedArgs++; | |||
| 1095 | ISD::OutputArg Arg; | |||
| 1096 | Arg.Flags.setSwiftSelf(); | |||
| 1097 | CLI.Outs.push_back(Arg); | |||
| 1098 | SDValue ArgVal = DAG.getUNDEF(PtrVT); | |||
| 1099 | CLI.OutVals.push_back(ArgVal); | |||
| 1100 | } | |||
| 1101 | if (!HasSwiftErrorArg) { | |||
| 1102 | NumFixedArgs++; | |||
| 1103 | ISD::OutputArg Arg; | |||
| 1104 | Arg.Flags.setSwiftError(); | |||
| 1105 | CLI.Outs.push_back(Arg); | |||
| 1106 | SDValue ArgVal = DAG.getUNDEF(PtrVT); | |||
| 1107 | CLI.OutVals.push_back(ArgVal); | |||
| 1108 | } | |||
| 1109 | } | |||
| 1110 | ||||
| 1111 | // Analyze operands of the call, assigning locations to each operand. | |||
| 1112 | SmallVector<CCValAssign, 16> ArgLocs; | |||
| 1113 | CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); | |||
| 1114 | ||||
| 1115 | if (IsVarArg) { | |||
| 1116 | // Outgoing non-fixed arguments are placed in a buffer. First | |||
| 1117 | // compute their offsets and the total amount of buffer space needed. | |||
| 1118 | for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) { | |||
| 1119 | const ISD::OutputArg &Out = Outs[I]; | |||
| 1120 | SDValue &Arg = OutVals[I]; | |||
| 1121 | EVT VT = Arg.getValueType(); | |||
| 1122 | assert(VT != MVT::iPTR && "Legalized args should be concrete")(static_cast <bool> (VT != MVT::iPTR && "Legalized args should be concrete" ) ? void (0) : __assert_fail ("VT != MVT::iPTR && \"Legalized args should be concrete\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1122 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1123 | Type *Ty = VT.getTypeForEVT(*DAG.getContext()); | |||
| 1124 | Align Alignment = | |||
| 1125 | std::max(Out.Flags.getNonZeroOrigAlign(), Layout.getABITypeAlign(Ty)); | |||
| 1126 | unsigned Offset = | |||
| 1127 | CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), Alignment); | |||
| 1128 | CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), | |||
| 1129 | Offset, VT.getSimpleVT(), | |||
| 1130 | CCValAssign::Full)); | |||
| 1131 | } | |||
| 1132 | } | |||
| 1133 | ||||
| 1134 | unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); | |||
| 1135 | ||||
| 1136 | SDValue FINode; | |||
| 1137 | if (IsVarArg && NumBytes) { | |||
| 1138 | // For non-fixed arguments, next emit stores to store the argument values | |||
| 1139 | // to the stack buffer at the offsets computed above. | |||
| 1140 | int FI = MF.getFrameInfo().CreateStackObject(NumBytes, | |||
| 1141 | Layout.getStackAlignment(), | |||
| 1142 | /*isSS=*/false); | |||
| 1143 | unsigned ValNo = 0; | |||
| 1144 | SmallVector<SDValue, 8> Chains; | |||
| 1145 | for (SDValue Arg : drop_begin(OutVals, NumFixedArgs)) { | |||
| 1146 | assert(ArgLocs[ValNo].getValNo() == ValNo &&(static_cast <bool> (ArgLocs[ValNo].getValNo() == ValNo && "ArgLocs should remain in order and only hold varargs args" ) ? void (0) : __assert_fail ("ArgLocs[ValNo].getValNo() == ValNo && \"ArgLocs should remain in order and only hold varargs args\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1147 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1147 | "ArgLocs should remain in order and only hold varargs args")(static_cast <bool> (ArgLocs[ValNo].getValNo() == ValNo && "ArgLocs should remain in order and only hold varargs args" ) ? void (0) : __assert_fail ("ArgLocs[ValNo].getValNo() == ValNo && \"ArgLocs should remain in order and only hold varargs args\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1147 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1148 | unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); | |||
| 1149 | FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); | |||
| 1150 | SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode, | |||
| 1151 | DAG.getConstant(Offset, DL, PtrVT)); | |||
| 1152 | Chains.push_back( | |||
| 1153 | DAG.getStore(Chain, DL, Arg, Add, | |||
| 1154 | MachinePointerInfo::getFixedStack(MF, FI, Offset))); | |||
| 1155 | } | |||
| 1156 | if (!Chains.empty()) | |||
| 1157 | Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); | |||
| 1158 | } else if (IsVarArg) { | |||
| 1159 | FINode = DAG.getIntPtrConstant(0, DL); | |||
| 1160 | } | |||
| 1161 | ||||
| 1162 | if (Callee->getOpcode() == ISD::GlobalAddress) { | |||
| 1163 | // If the callee is a GlobalAddress node (quite common, every direct call | |||
| 1164 | // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress | |||
| 1165 | // doesn't at MO_GOT which is not needed for direct calls. | |||
| 1166 | GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Callee); | |||
| 1167 | Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, | |||
| 1168 | getPointerTy(DAG.getDataLayout()), | |||
| 1169 | GA->getOffset()); | |||
| 1170 | Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL, | |||
| 1171 | getPointerTy(DAG.getDataLayout()), Callee); | |||
| 1172 | } | |||
| 1173 | ||||
| 1174 | // Compute the operands for the CALLn node. | |||
| 1175 | SmallVector<SDValue, 16> Ops; | |||
| 1176 | Ops.push_back(Chain); | |||
| 1177 | Ops.push_back(Callee); | |||
| 1178 | ||||
| 1179 | // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs | |||
| 1180 | // isn't reliable. | |||
| 1181 | Ops.append(OutVals.begin(), | |||
| 1182 | IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); | |||
| 1183 | // Add a pointer to the vararg buffer. | |||
| 1184 | if (IsVarArg) | |||
| 1185 | Ops.push_back(FINode); | |||
| 1186 | ||||
| 1187 | SmallVector<EVT, 8> InTys; | |||
| 1188 | for (const auto &In : Ins) { | |||
| 1189 | assert(!In.Flags.isByVal() && "byval is not valid for return values")(static_cast <bool> (!In.Flags.isByVal() && "byval is not valid for return values" ) ? void (0) : __assert_fail ("!In.Flags.isByVal() && \"byval is not valid for return values\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1189 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1190 | assert(!In.Flags.isNest() && "nest is not valid for return values")(static_cast <bool> (!In.Flags.isNest() && "nest is not valid for return values" ) ? void (0) : __assert_fail ("!In.Flags.isNest() && \"nest is not valid for return values\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1190 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1191 | if (In.Flags.isInAlloca()) | |||
| 1192 | fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values"); | |||
| 1193 | if (In.Flags.isInConsecutiveRegs()) | |||
| 1194 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values"); | |||
| 1195 | if (In.Flags.isInConsecutiveRegsLast()) | |||
| 1196 | fail(DL, DAG, | |||
| 1197 | "WebAssembly hasn't implemented cons regs last return values"); | |||
| 1198 | // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in | |||
| 1199 | // registers. | |||
| 1200 | InTys.push_back(In.VT); | |||
| 1201 | } | |||
| 1202 | ||||
| 1203 | // Lastly, if this is a call to a funcref we need to add an instruction | |||
| 1204 | // table.set to the chain and transform the call. | |||
| 1205 | if (CLI.CB && | |||
| 1206 | WebAssembly::isFuncrefType(CLI.CB->getCalledOperand()->getType())) { | |||
| 1207 | // In the absence of function references proposal where a funcref call is | |||
| 1208 | // lowered to call_ref, using reference types we generate a table.set to set | |||
| 1209 | // the funcref to a special table used solely for this purpose, followed by | |||
| 1210 | // a call_indirect. Here we just generate the table set, and return the | |||
| 1211 | // SDValue of the table.set so that LowerCall can finalize the lowering by | |||
| 1212 | // generating the call_indirect. | |||
| 1213 | SDValue Chain = Ops[0]; | |||
| 1214 | ||||
| 1215 | MCSymbolWasm *Table = WebAssembly::getOrCreateFuncrefCallTableSymbol( | |||
| 1216 | MF.getContext(), Subtarget); | |||
| 1217 | SDValue Sym = DAG.getMCSymbol(Table, PtrVT); | |||
| 1218 | SDValue TableSlot = DAG.getConstant(0, DL, MVT::i32); | |||
| 1219 | SDValue TableSetOps[] = {Chain, Sym, TableSlot, Callee}; | |||
| 1220 | SDValue TableSet = DAG.getMemIntrinsicNode( | |||
| 1221 | WebAssemblyISD::TABLE_SET, DL, DAG.getVTList(MVT::Other), TableSetOps, | |||
| 1222 | MVT::funcref, | |||
| 1223 | // Machine Mem Operand args | |||
| 1224 | MachinePointerInfo( | |||
| 1225 | WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF), | |||
| 1226 | CLI.CB->getCalledOperand()->getPointerAlignment(DAG.getDataLayout()), | |||
| 1227 | MachineMemOperand::MOStore); | |||
| 1228 | ||||
| 1229 | Ops[0] = TableSet; // The new chain is the TableSet itself | |||
| 1230 | } | |||
| 1231 | ||||
| 1232 | if (CLI.IsTailCall) { | |||
| 1233 | // ret_calls do not return values to the current frame | |||
| 1234 | SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); | |||
| 1235 | return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops); | |||
| 1236 | } | |||
| 1237 | ||||
| 1238 | InTys.push_back(MVT::Other); | |||
| 1239 | SDVTList InTyList = DAG.getVTList(InTys); | |||
| 1240 | SDValue Res = DAG.getNode(WebAssemblyISD::CALL, DL, InTyList, Ops); | |||
| 1241 | ||||
| 1242 | for (size_t I = 0; I < Ins.size(); ++I) | |||
| 1243 | InVals.push_back(Res.getValue(I)); | |||
| 1244 | ||||
| 1245 | // Return the chain | |||
| 1246 | return Res.getValue(Ins.size()); | |||
| 1247 | } | |||
| 1248 | ||||
| 1249 | bool WebAssemblyTargetLowering::CanLowerReturn( | |||
| 1250 | CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, | |||
| 1251 | const SmallVectorImpl<ISD::OutputArg> &Outs, | |||
| 1252 | LLVMContext & /*Context*/) const { | |||
| 1253 | // WebAssembly can only handle returning tuples with multivalue enabled | |||
| 1254 | return Subtarget->hasMultivalue() || Outs.size() <= 1; | |||
| 1255 | } | |||
| 1256 | ||||
| 1257 | SDValue WebAssemblyTargetLowering::LowerReturn( | |||
| 1258 | SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, | |||
| 1259 | const SmallVectorImpl<ISD::OutputArg> &Outs, | |||
| 1260 | const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, | |||
| 1261 | SelectionDAG &DAG) const { | |||
| 1262 | assert((Subtarget->hasMultivalue() || Outs.size() <= 1) &&(static_cast <bool> ((Subtarget->hasMultivalue() || Outs .size() <= 1) && "MVP WebAssembly can only return up to one value" ) ? void (0) : __assert_fail ("(Subtarget->hasMultivalue() || Outs.size() <= 1) && \"MVP WebAssembly can only return up to one value\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1263 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1263 | "MVP WebAssembly can only return up to one value")(static_cast <bool> ((Subtarget->hasMultivalue() || Outs .size() <= 1) && "MVP WebAssembly can only return up to one value" ) ? void (0) : __assert_fail ("(Subtarget->hasMultivalue() || Outs.size() <= 1) && \"MVP WebAssembly can only return up to one value\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1263 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1264 | if (!callingConvSupported(CallConv)) | |||
| 1265 | fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); | |||
| 1266 | ||||
| 1267 | SmallVector<SDValue, 4> RetOps(1, Chain); | |||
| 1268 | RetOps.append(OutVals.begin(), OutVals.end()); | |||
| 1269 | Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps); | |||
| 1270 | ||||
| 1271 | // Record the number and types of the return values. | |||
| 1272 | for (const ISD::OutputArg &Out : Outs) { | |||
| 1273 | assert(!Out.Flags.isByVal() && "byval is not valid for return values")(static_cast <bool> (!Out.Flags.isByVal() && "byval is not valid for return values" ) ? void (0) : __assert_fail ("!Out.Flags.isByVal() && \"byval is not valid for return values\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1273 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1274 | assert(!Out.Flags.isNest() && "nest is not valid for return values")(static_cast <bool> (!Out.Flags.isNest() && "nest is not valid for return values" ) ? void (0) : __assert_fail ("!Out.Flags.isNest() && \"nest is not valid for return values\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1274 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1275 | assert(Out.IsFixed && "non-fixed return value is not valid")(static_cast <bool> (Out.IsFixed && "non-fixed return value is not valid" ) ? void (0) : __assert_fail ("Out.IsFixed && \"non-fixed return value is not valid\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1275 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1276 | if (Out.Flags.isInAlloca()) | |||
| 1277 | fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); | |||
| 1278 | if (Out.Flags.isInConsecutiveRegs()) | |||
| 1279 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); | |||
| 1280 | if (Out.Flags.isInConsecutiveRegsLast()) | |||
| 1281 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); | |||
| 1282 | } | |||
| 1283 | ||||
| 1284 | return Chain; | |||
| 1285 | } | |||
| 1286 | ||||
| 1287 | SDValue WebAssemblyTargetLowering::LowerFormalArguments( | |||
| 1288 | SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, | |||
| 1289 | const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, | |||
| 1290 | SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { | |||
| 1291 | if (!callingConvSupported(CallConv)) | |||
| 1292 | fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); | |||
| 1293 | ||||
| 1294 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 1295 | auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); | |||
| 1296 | ||||
| 1297 | // Set up the incoming ARGUMENTS value, which serves to represent the liveness | |||
| 1298 | // of the incoming values before they're represented by virtual registers. | |||
| 1299 | MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); | |||
| 1300 | ||||
| 1301 | bool HasSwiftErrorArg = false; | |||
| 1302 | bool HasSwiftSelfArg = false; | |||
| 1303 | for (const ISD::InputArg &In : Ins) { | |||
| 1304 | HasSwiftSelfArg |= In.Flags.isSwiftSelf(); | |||
| 1305 | HasSwiftErrorArg |= In.Flags.isSwiftError(); | |||
| 1306 | if (In.Flags.isInAlloca()) | |||
| 1307 | fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); | |||
| 1308 | if (In.Flags.isNest()) | |||
| 1309 | fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); | |||
| 1310 | if (In.Flags.isInConsecutiveRegs()) | |||
| 1311 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); | |||
| 1312 | if (In.Flags.isInConsecutiveRegsLast()) | |||
| 1313 | fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); | |||
| 1314 | // Ignore In.getNonZeroOrigAlign() because all our arguments are passed in | |||
| 1315 | // registers. | |||
| 1316 | InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, | |||
| 1317 | DAG.getTargetConstant(InVals.size(), | |||
| 1318 | DL, MVT::i32)) | |||
| 1319 | : DAG.getUNDEF(In.VT)); | |||
| 1320 | ||||
| 1321 | // Record the number and types of arguments. | |||
| 1322 | MFI->addParam(In.VT); | |||
| 1323 | } | |||
| 1324 | ||||
| 1325 | // For swiftcc, emit additional swiftself and swifterror arguments | |||
| 1326 | // if there aren't. These additional arguments are also added for callee | |||
| 1327 | // signature They are necessary to match callee and caller signature for | |||
| 1328 | // indirect call. | |||
| 1329 | auto PtrVT = getPointerTy(MF.getDataLayout()); | |||
| 1330 | if (CallConv == CallingConv::Swift) { | |||
| 1331 | if (!HasSwiftSelfArg) { | |||
| 1332 | MFI->addParam(PtrVT); | |||
| 1333 | } | |||
| 1334 | if (!HasSwiftErrorArg) { | |||
| 1335 | MFI->addParam(PtrVT); | |||
| 1336 | } | |||
| 1337 | } | |||
| 1338 | // Varargs are copied into a buffer allocated by the caller, and a pointer to | |||
| 1339 | // the buffer is passed as an argument. | |||
| 1340 | if (IsVarArg) { | |||
| 1341 | MVT PtrVT = getPointerTy(MF.getDataLayout()); | |||
| 1342 | Register VarargVreg = | |||
| 1343 | MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); | |||
| 1344 | MFI->setVarargBufferVreg(VarargVreg); | |||
| 1345 | Chain = DAG.getCopyToReg( | |||
| 1346 | Chain, DL, VarargVreg, | |||
| 1347 | DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, | |||
| 1348 | DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); | |||
| 1349 | MFI->addParam(PtrVT); | |||
| 1350 | } | |||
| 1351 | ||||
| 1352 | // Record the number and types of arguments and results. | |||
| 1353 | SmallVector<MVT, 4> Params; | |||
| 1354 | SmallVector<MVT, 4> Results; | |||
| 1355 | computeSignatureVTs(MF.getFunction().getFunctionType(), &MF.getFunction(), | |||
| 1356 | MF.getFunction(), DAG.getTarget(), Params, Results); | |||
| 1357 | for (MVT VT : Results) | |||
| 1358 | MFI->addResult(VT); | |||
| 1359 | // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify | |||
| 1360 | // the param logic here with ComputeSignatureVTs | |||
| 1361 | assert(MFI->getParams().size() == Params.size() &&(static_cast <bool> (MFI->getParams().size() == Params .size() && std::equal(MFI->getParams().begin(), MFI ->getParams().end(), Params.begin())) ? void (0) : __assert_fail ("MFI->getParams().size() == Params.size() && std::equal(MFI->getParams().begin(), MFI->getParams().end(), Params.begin())" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1363 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1362 | std::equal(MFI->getParams().begin(), MFI->getParams().end(),(static_cast <bool> (MFI->getParams().size() == Params .size() && std::equal(MFI->getParams().begin(), MFI ->getParams().end(), Params.begin())) ? void (0) : __assert_fail ("MFI->getParams().size() == Params.size() && std::equal(MFI->getParams().begin(), MFI->getParams().end(), Params.begin())" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1363 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1363 | Params.begin()))(static_cast <bool> (MFI->getParams().size() == Params .size() && std::equal(MFI->getParams().begin(), MFI ->getParams().end(), Params.begin())) ? void (0) : __assert_fail ("MFI->getParams().size() == Params.size() && std::equal(MFI->getParams().begin(), MFI->getParams().end(), Params.begin())" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1363 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1364 | ||||
| 1365 | return Chain; | |||
| 1366 | } | |||
| 1367 | ||||
| 1368 | void WebAssemblyTargetLowering::ReplaceNodeResults( | |||
| 1369 | SDNode *N, SmallVectorImpl<SDValue> &Results, SelectionDAG &DAG) const { | |||
| 1370 | switch (N->getOpcode()) { | |||
| 1371 | case ISD::SIGN_EXTEND_INREG: | |||
| 1372 | // Do not add any results, signifying that N should not be custom lowered | |||
| 1373 | // after all. This happens because simd128 turns on custom lowering for | |||
| 1374 | // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an | |||
| 1375 | // illegal type. | |||
| 1376 | break; | |||
| 1377 | default: | |||
| 1378 | llvm_unreachable(::llvm::llvm_unreachable_internal("ReplaceNodeResults not implemented for this op for WebAssembly!" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1379 ) | |||
| 1379 | "ReplaceNodeResults not implemented for this op for WebAssembly!")::llvm::llvm_unreachable_internal("ReplaceNodeResults not implemented for this op for WebAssembly!" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1379 ); | |||
| 1380 | } | |||
| 1381 | } | |||
| 1382 | ||||
| 1383 | //===----------------------------------------------------------------------===// | |||
| 1384 | // Custom lowering hooks. | |||
| 1385 | //===----------------------------------------------------------------------===// | |||
| 1386 | ||||
| 1387 | SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, | |||
| 1388 | SelectionDAG &DAG) const { | |||
| 1389 | SDLoc DL(Op); | |||
| 1390 | switch (Op.getOpcode()) { | |||
| ||||
| 1391 | default: | |||
| 1392 | llvm_unreachable("unimplemented operation lowering")::llvm::llvm_unreachable_internal("unimplemented operation lowering" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1392 ); | |||
| 1393 | return SDValue(); | |||
| 1394 | case ISD::FrameIndex: | |||
| 1395 | return LowerFrameIndex(Op, DAG); | |||
| 1396 | case ISD::GlobalAddress: | |||
| 1397 | return LowerGlobalAddress(Op, DAG); | |||
| 1398 | case ISD::GlobalTLSAddress: | |||
| 1399 | return LowerGlobalTLSAddress(Op, DAG); | |||
| 1400 | case ISD::ExternalSymbol: | |||
| 1401 | return LowerExternalSymbol(Op, DAG); | |||
| 1402 | case ISD::JumpTable: | |||
| 1403 | return LowerJumpTable(Op, DAG); | |||
| 1404 | case ISD::BR_JT: | |||
| 1405 | return LowerBR_JT(Op, DAG); | |||
| 1406 | case ISD::VASTART: | |||
| 1407 | return LowerVASTART(Op, DAG); | |||
| 1408 | case ISD::BlockAddress: | |||
| 1409 | case ISD::BRIND: | |||
| 1410 | fail(DL, DAG, "WebAssembly hasn't implemented computed gotos"); | |||
| 1411 | return SDValue(); | |||
| 1412 | case ISD::RETURNADDR: | |||
| 1413 | return LowerRETURNADDR(Op, DAG); | |||
| 1414 | case ISD::FRAMEADDR: | |||
| 1415 | return LowerFRAMEADDR(Op, DAG); | |||
| 1416 | case ISD::CopyToReg: | |||
| 1417 | return LowerCopyToReg(Op, DAG); | |||
| 1418 | case ISD::EXTRACT_VECTOR_ELT: | |||
| 1419 | case ISD::INSERT_VECTOR_ELT: | |||
| 1420 | return LowerAccessVectorElement(Op, DAG); | |||
| 1421 | case ISD::INTRINSIC_VOID: | |||
| 1422 | case ISD::INTRINSIC_WO_CHAIN: | |||
| 1423 | case ISD::INTRINSIC_W_CHAIN: | |||
| 1424 | return LowerIntrinsic(Op, DAG); | |||
| 1425 | case ISD::SIGN_EXTEND_INREG: | |||
| 1426 | return LowerSIGN_EXTEND_INREG(Op, DAG); | |||
| 1427 | case ISD::BUILD_VECTOR: | |||
| 1428 | return LowerBUILD_VECTOR(Op, DAG); | |||
| 1429 | case ISD::VECTOR_SHUFFLE: | |||
| 1430 | return LowerVECTOR_SHUFFLE(Op, DAG); | |||
| 1431 | case ISD::SETCC: | |||
| 1432 | return LowerSETCC(Op, DAG); | |||
| 1433 | case ISD::SHL: | |||
| 1434 | case ISD::SRA: | |||
| 1435 | case ISD::SRL: | |||
| 1436 | return LowerShift(Op, DAG); | |||
| 1437 | case ISD::FP_TO_SINT_SAT: | |||
| 1438 | case ISD::FP_TO_UINT_SAT: | |||
| 1439 | return LowerFP_TO_INT_SAT(Op, DAG); | |||
| 1440 | case ISD::LOAD: | |||
| 1441 | return LowerLoad(Op, DAG); | |||
| 1442 | case ISD::STORE: | |||
| 1443 | return LowerStore(Op, DAG); | |||
| 1444 | case ISD::CTPOP: | |||
| 1445 | case ISD::CTLZ: | |||
| 1446 | case ISD::CTTZ: | |||
| 1447 | return DAG.UnrollVectorOp(Op.getNode()); | |||
| 1448 | } | |||
| 1449 | } | |||
| 1450 | ||||
| 1451 | static bool IsWebAssemblyGlobal(SDValue Op) { | |||
| 1452 | if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Op)) | |||
| 1453 | return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace()); | |||
| 1454 | ||||
| 1455 | return false; | |||
| 1456 | } | |||
| 1457 | ||||
| 1458 | static std::optional<unsigned> IsWebAssemblyLocal(SDValue Op, | |||
| 1459 | SelectionDAG &DAG) { | |||
| 1460 | const FrameIndexSDNode *FI = dyn_cast<FrameIndexSDNode>(Op); | |||
| 1461 | if (!FI) | |||
| 1462 | return std::nullopt; | |||
| 1463 | ||||
| 1464 | auto &MF = DAG.getMachineFunction(); | |||
| 1465 | return WebAssemblyFrameLowering::getLocalForStackObject(MF, FI->getIndex()); | |||
| 1466 | } | |||
| 1467 | ||||
| 1468 | SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op, | |||
| 1469 | SelectionDAG &DAG) const { | |||
| 1470 | SDLoc DL(Op); | |||
| 1471 | StoreSDNode *SN = cast<StoreSDNode>(Op.getNode()); | |||
| 1472 | const SDValue &Value = SN->getValue(); | |||
| 1473 | const SDValue &Base = SN->getBasePtr(); | |||
| 1474 | const SDValue &Offset = SN->getOffset(); | |||
| 1475 | ||||
| 1476 | if (IsWebAssemblyGlobal(Base)) { | |||
| 1477 | if (!Offset->isUndef()) | |||
| 1478 | report_fatal_error("unexpected offset when storing to webassembly global", | |||
| 1479 | false); | |||
| 1480 | ||||
| 1481 | SDVTList Tys = DAG.getVTList(MVT::Other); | |||
| 1482 | SDValue Ops[] = {SN->getChain(), Value, Base}; | |||
| 1483 | return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops, | |||
| 1484 | SN->getMemoryVT(), SN->getMemOperand()); | |||
| 1485 | } | |||
| 1486 | ||||
| 1487 | if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) { | |||
| 1488 | if (!Offset->isUndef()) | |||
| 1489 | report_fatal_error("unexpected offset when storing to webassembly local", | |||
| 1490 | false); | |||
| 1491 | ||||
| 1492 | SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32); | |||
| 1493 | SDVTList Tys = DAG.getVTList(MVT::Other); // The chain. | |||
| 1494 | SDValue Ops[] = {SN->getChain(), Idx, Value}; | |||
| 1495 | return DAG.getNode(WebAssemblyISD::LOCAL_SET, DL, Tys, Ops); | |||
| 1496 | } | |||
| 1497 | ||||
| 1498 | if (WebAssembly::isWasmVarAddressSpace(SN->getAddressSpace())) | |||
| 1499 | report_fatal_error( | |||
| 1500 | "Encountered an unlowerable store to the wasm_var address space", | |||
| 1501 | false); | |||
| 1502 | ||||
| 1503 | return Op; | |||
| 1504 | } | |||
| 1505 | ||||
| 1506 | SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op, | |||
| 1507 | SelectionDAG &DAG) const { | |||
| 1508 | SDLoc DL(Op); | |||
| 1509 | LoadSDNode *LN = cast<LoadSDNode>(Op.getNode()); | |||
| 1510 | const SDValue &Base = LN->getBasePtr(); | |||
| 1511 | const SDValue &Offset = LN->getOffset(); | |||
| 1512 | ||||
| 1513 | if (IsWebAssemblyGlobal(Base)) { | |||
| 1514 | if (!Offset->isUndef()) | |||
| 1515 | report_fatal_error( | |||
| 1516 | "unexpected offset when loading from webassembly global", false); | |||
| 1517 | ||||
| 1518 | SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other); | |||
| 1519 | SDValue Ops[] = {LN->getChain(), Base}; | |||
| 1520 | return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops, | |||
| 1521 | LN->getMemoryVT(), LN->getMemOperand()); | |||
| 1522 | } | |||
| 1523 | ||||
| 1524 | if (std::optional<unsigned> Local = IsWebAssemblyLocal(Base, DAG)) { | |||
| 1525 | if (!Offset->isUndef()) | |||
| 1526 | report_fatal_error( | |||
| 1527 | "unexpected offset when loading from webassembly local", false); | |||
| 1528 | ||||
| 1529 | SDValue Idx = DAG.getTargetConstant(*Local, Base, MVT::i32); | |||
| 1530 | EVT LocalVT = LN->getValueType(0); | |||
| 1531 | SDValue LocalGet = DAG.getNode(WebAssemblyISD::LOCAL_GET, DL, LocalVT, | |||
| 1532 | {LN->getChain(), Idx}); | |||
| 1533 | SDValue Result = DAG.getMergeValues({LocalGet, LN->getChain()}, DL); | |||
| 1534 | assert(Result->getNumValues() == 2 && "Loads must carry a chain!")(static_cast <bool> (Result->getNumValues() == 2 && "Loads must carry a chain!") ? void (0) : __assert_fail ("Result->getNumValues() == 2 && \"Loads must carry a chain!\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1534 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1535 | return Result; | |||
| 1536 | } | |||
| 1537 | ||||
| 1538 | if (WebAssembly::isWasmVarAddressSpace(LN->getAddressSpace())) | |||
| 1539 | report_fatal_error( | |||
| 1540 | "Encountered an unlowerable load from the wasm_var address space", | |||
| 1541 | false); | |||
| 1542 | ||||
| 1543 | return Op; | |||
| 1544 | } | |||
| 1545 | ||||
| 1546 | SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, | |||
| 1547 | SelectionDAG &DAG) const { | |||
| 1548 | SDValue Src = Op.getOperand(2); | |||
| 1549 | if (isa<FrameIndexSDNode>(Src.getNode())) { | |||
| 1550 | // CopyToReg nodes don't support FrameIndex operands. Other targets select | |||
| 1551 | // the FI to some LEA-like instruction, but since we don't have that, we | |||
| 1552 | // need to insert some kind of instruction that can take an FI operand and | |||
| 1553 | // produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy | |||
| 1554 | // local.copy between Op and its FI operand. | |||
| 1555 | SDValue Chain = Op.getOperand(0); | |||
| 1556 | SDLoc DL(Op); | |||
| 1557 | Register Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg(); | |||
| 1558 | EVT VT = Src.getValueType(); | |||
| 1559 | SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32 | |||
| 1560 | : WebAssembly::COPY_I64, | |||
| 1561 | DL, VT, Src), | |||
| 1562 | 0); | |||
| 1563 | return Op.getNode()->getNumValues() == 1 | |||
| 1564 | ? DAG.getCopyToReg(Chain, DL, Reg, Copy) | |||
| 1565 | : DAG.getCopyToReg(Chain, DL, Reg, Copy, | |||
| 1566 | Op.getNumOperands() == 4 ? Op.getOperand(3) | |||
| 1567 | : SDValue()); | |||
| 1568 | } | |||
| 1569 | return SDValue(); | |||
| 1570 | } | |||
| 1571 | ||||
| 1572 | SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op, | |||
| 1573 | SelectionDAG &DAG) const { | |||
| 1574 | int FI = cast<FrameIndexSDNode>(Op)->getIndex(); | |||
| 1575 | return DAG.getTargetFrameIndex(FI, Op.getValueType()); | |||
| 1576 | } | |||
| 1577 | ||||
| 1578 | SDValue WebAssemblyTargetLowering::LowerRETURNADDR(SDValue Op, | |||
| 1579 | SelectionDAG &DAG) const { | |||
| 1580 | SDLoc DL(Op); | |||
| 1581 | ||||
| 1582 | if (!Subtarget->getTargetTriple().isOSEmscripten()) { | |||
| 1583 | fail(DL, DAG, | |||
| 1584 | "Non-Emscripten WebAssembly hasn't implemented " | |||
| 1585 | "__builtin_return_address"); | |||
| 1586 | return SDValue(); | |||
| 1587 | } | |||
| 1588 | ||||
| 1589 | if (verifyReturnAddressArgumentIsConstant(Op, DAG)) | |||
| 1590 | return SDValue(); | |||
| 1591 | ||||
| 1592 | unsigned Depth = Op.getConstantOperandVal(0); | |||
| 1593 | MakeLibCallOptions CallOptions; | |||
| 1594 | return makeLibCall(DAG, RTLIB::RETURN_ADDRESS, Op.getValueType(), | |||
| 1595 | {DAG.getConstant(Depth, DL, MVT::i32)}, CallOptions, DL) | |||
| 1596 | .first; | |||
| 1597 | } | |||
| 1598 | ||||
| 1599 | SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, | |||
| 1600 | SelectionDAG &DAG) const { | |||
| 1601 | // Non-zero depths are not supported by WebAssembly currently. Use the | |||
| 1602 | // legalizer's default expansion, which is to return 0 (what this function is | |||
| 1603 | // documented to do). | |||
| 1604 | if (Op.getConstantOperandVal(0) > 0) | |||
| 1605 | return SDValue(); | |||
| 1606 | ||||
| 1607 | DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); | |||
| 1608 | EVT VT = Op.getValueType(); | |||
| 1609 | Register FP = | |||
| 1610 | Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); | |||
| 1611 | return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); | |||
| 1612 | } | |||
| 1613 | ||||
| 1614 | SDValue | |||
| 1615 | WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op, | |||
| 1616 | SelectionDAG &DAG) const { | |||
| 1617 | SDLoc DL(Op); | |||
| 1618 | const auto *GA = cast<GlobalAddressSDNode>(Op); | |||
| 1619 | ||||
| 1620 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 1621 | if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) | |||
| 1622 | report_fatal_error("cannot use thread-local storage without bulk memory", | |||
| 1623 | false); | |||
| 1624 | ||||
| 1625 | const GlobalValue *GV = GA->getGlobal(); | |||
| 1626 | ||||
| 1627 | // Currently only Emscripten supports dynamic linking with threads. Therefore, | |||
| 1628 | // on other targets, if we have thread-local storage, only the local-exec | |||
| 1629 | // model is possible. | |||
| 1630 | auto model = Subtarget->getTargetTriple().isOSEmscripten() | |||
| 1631 | ? GV->getThreadLocalMode() | |||
| 1632 | : GlobalValue::LocalExecTLSModel; | |||
| 1633 | ||||
| 1634 | // Unsupported TLS modes | |||
| 1635 | assert(model != GlobalValue::NotThreadLocal)(static_cast <bool> (model != GlobalValue::NotThreadLocal ) ? void (0) : __assert_fail ("model != GlobalValue::NotThreadLocal" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1635 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1636 | assert(model != GlobalValue::InitialExecTLSModel)(static_cast <bool> (model != GlobalValue::InitialExecTLSModel ) ? void (0) : __assert_fail ("model != GlobalValue::InitialExecTLSModel" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1636 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1637 | ||||
| 1638 | if (model == GlobalValue::LocalExecTLSModel || | |||
| 1639 | model == GlobalValue::LocalDynamicTLSModel || | |||
| 1640 | (model == GlobalValue::GeneralDynamicTLSModel && | |||
| 1641 | getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV))) { | |||
| 1642 | // For DSO-local TLS variables we use offset from __tls_base | |||
| 1643 | ||||
| 1644 | MVT PtrVT = getPointerTy(DAG.getDataLayout()); | |||
| 1645 | auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 | |||
| 1646 | : WebAssembly::GLOBAL_GET_I32; | |||
| 1647 | const char *BaseName = MF.createExternalSymbolName("__tls_base"); | |||
| 1648 | ||||
| 1649 | SDValue BaseAddr( | |||
| 1650 | DAG.getMachineNode(GlobalGet, DL, PtrVT, | |||
| 1651 | DAG.getTargetExternalSymbol(BaseName, PtrVT)), | |||
| 1652 | 0); | |||
| 1653 | ||||
| 1654 | SDValue TLSOffset = DAG.getTargetGlobalAddress( | |||
| 1655 | GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL); | |||
| 1656 | SDValue SymOffset = | |||
| 1657 | DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, TLSOffset); | |||
| 1658 | ||||
| 1659 | return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymOffset); | |||
| 1660 | } | |||
| 1661 | ||||
| 1662 | assert(model == GlobalValue::GeneralDynamicTLSModel)(static_cast <bool> (model == GlobalValue::GeneralDynamicTLSModel ) ? void (0) : __assert_fail ("model == GlobalValue::GeneralDynamicTLSModel" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1662 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1663 | ||||
| 1664 | EVT VT = Op.getValueType(); | |||
| 1665 | return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | |||
| 1666 | DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, | |||
| 1667 | GA->getOffset(), | |||
| 1668 | WebAssemblyII::MO_GOT_TLS)); | |||
| 1669 | } | |||
| 1670 | ||||
| 1671 | SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, | |||
| 1672 | SelectionDAG &DAG) const { | |||
| 1673 | SDLoc DL(Op); | |||
| 1674 | const auto *GA = cast<GlobalAddressSDNode>(Op); | |||
| 1675 | EVT VT = Op.getValueType(); | |||
| 1676 | assert(GA->getTargetFlags() == 0 &&(static_cast <bool> (GA->getTargetFlags() == 0 && "Unexpected target flags on generic GlobalAddressSDNode") ? void (0) : __assert_fail ("GA->getTargetFlags() == 0 && \"Unexpected target flags on generic GlobalAddressSDNode\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1677 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1677 | "Unexpected target flags on generic GlobalAddressSDNode")(static_cast <bool> (GA->getTargetFlags() == 0 && "Unexpected target flags on generic GlobalAddressSDNode") ? void (0) : __assert_fail ("GA->getTargetFlags() == 0 && \"Unexpected target flags on generic GlobalAddressSDNode\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1677 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1678 | if (!WebAssembly::isValidAddressSpace(GA->getAddressSpace())) | |||
| 1679 | fail(DL, DAG, "Invalid address space for WebAssembly target"); | |||
| 1680 | ||||
| 1681 | unsigned OperandFlags = 0; | |||
| 1682 | if (isPositionIndependent()) { | |||
| 1683 | const GlobalValue *GV = GA->getGlobal(); | |||
| 1684 | if (getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) { | |||
| 1685 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 1686 | MVT PtrVT = getPointerTy(MF.getDataLayout()); | |||
| 1687 | const char *BaseName; | |||
| 1688 | if (GV->getValueType()->isFunctionTy()) { | |||
| 1689 | BaseName = MF.createExternalSymbolName("__table_base"); | |||
| 1690 | OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL; | |||
| 1691 | } else { | |||
| 1692 | BaseName = MF.createExternalSymbolName("__memory_base"); | |||
| 1693 | OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL; | |||
| 1694 | } | |||
| 1695 | SDValue BaseAddr = | |||
| 1696 | DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, | |||
| 1697 | DAG.getTargetExternalSymbol(BaseName, PtrVT)); | |||
| 1698 | ||||
| 1699 | SDValue SymAddr = DAG.getNode( | |||
| 1700 | WebAssemblyISD::WrapperREL, DL, VT, | |||
| 1701 | DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), | |||
| 1702 | OperandFlags)); | |||
| 1703 | ||||
| 1704 | return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr); | |||
| 1705 | } | |||
| 1706 | OperandFlags = WebAssemblyII::MO_GOT; | |||
| 1707 | } | |||
| 1708 | ||||
| 1709 | return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | |||
| 1710 | DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, | |||
| 1711 | GA->getOffset(), OperandFlags)); | |||
| 1712 | } | |||
| 1713 | ||||
| 1714 | SDValue | |||
| 1715 | WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, | |||
| 1716 | SelectionDAG &DAG) const { | |||
| 1717 | SDLoc DL(Op); | |||
| 1718 | const auto *ES = cast<ExternalSymbolSDNode>(Op); | |||
| 1719 | EVT VT = Op.getValueType(); | |||
| 1720 | assert(ES->getTargetFlags() == 0 &&(static_cast <bool> (ES->getTargetFlags() == 0 && "Unexpected target flags on generic ExternalSymbolSDNode") ? void (0) : __assert_fail ("ES->getTargetFlags() == 0 && \"Unexpected target flags on generic ExternalSymbolSDNode\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1721 , __extension__ __PRETTY_FUNCTION__)) | |||
| 1721 | "Unexpected target flags on generic ExternalSymbolSDNode")(static_cast <bool> (ES->getTargetFlags() == 0 && "Unexpected target flags on generic ExternalSymbolSDNode") ? void (0) : __assert_fail ("ES->getTargetFlags() == 0 && \"Unexpected target flags on generic ExternalSymbolSDNode\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1721 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1722 | return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, | |||
| 1723 | DAG.getTargetExternalSymbol(ES->getSymbol(), VT)); | |||
| 1724 | } | |||
| 1725 | ||||
| 1726 | SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, | |||
| 1727 | SelectionDAG &DAG) const { | |||
| 1728 | // There's no need for a Wrapper node because we always incorporate a jump | |||
| 1729 | // table operand into a BR_TABLE instruction, rather than ever | |||
| 1730 | // materializing it in a register. | |||
| 1731 | const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); | |||
| 1732 | return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), | |||
| 1733 | JT->getTargetFlags()); | |||
| 1734 | } | |||
| 1735 | ||||
| 1736 | SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, | |||
| 1737 | SelectionDAG &DAG) const { | |||
| 1738 | SDLoc DL(Op); | |||
| 1739 | SDValue Chain = Op.getOperand(0); | |||
| 1740 | const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1)); | |||
| 1741 | SDValue Index = Op.getOperand(2); | |||
| 1742 | assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags")(static_cast <bool> (JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags") ? void (0) : __assert_fail ("JT->getTargetFlags() == 0 && \"WebAssembly doesn't set target flags\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1742 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1743 | ||||
| 1744 | SmallVector<SDValue, 8> Ops; | |||
| 1745 | Ops.push_back(Chain); | |||
| 1746 | Ops.push_back(Index); | |||
| 1747 | ||||
| 1748 | MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); | |||
| 1749 | const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; | |||
| 1750 | ||||
| 1751 | // Add an operand for each case. | |||
| 1752 | for (auto *MBB : MBBs) | |||
| 1753 | Ops.push_back(DAG.getBasicBlock(MBB)); | |||
| 1754 | ||||
| 1755 | // Add the first MBB as a dummy default target for now. This will be replaced | |||
| 1756 | // with the proper default target (and the preceding range check eliminated) | |||
| 1757 | // if possible by WebAssemblyFixBrTableDefaults. | |||
| 1758 | Ops.push_back(DAG.getBasicBlock(*MBBs.begin())); | |||
| 1759 | return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops); | |||
| 1760 | } | |||
| 1761 | ||||
| 1762 | SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, | |||
| 1763 | SelectionDAG &DAG) const { | |||
| 1764 | SDLoc DL(Op); | |||
| 1765 | EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); | |||
| 1766 | ||||
| 1767 | auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>(); | |||
| 1768 | const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); | |||
| 1769 | ||||
| 1770 | SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, | |||
| 1771 | MFI->getVarargBufferVreg(), PtrVT); | |||
| 1772 | return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), | |||
| 1773 | MachinePointerInfo(SV)); | |||
| 1774 | } | |||
| 1775 | ||||
| 1776 | SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, | |||
| 1777 | SelectionDAG &DAG) const { | |||
| 1778 | MachineFunction &MF = DAG.getMachineFunction(); | |||
| 1779 | unsigned IntNo; | |||
| 1780 | switch (Op.getOpcode()) { | |||
| 1781 | case ISD::INTRINSIC_VOID: | |||
| 1782 | case ISD::INTRINSIC_W_CHAIN: | |||
| 1783 | IntNo = Op.getConstantOperandVal(1); | |||
| 1784 | break; | |||
| 1785 | case ISD::INTRINSIC_WO_CHAIN: | |||
| 1786 | IntNo = Op.getConstantOperandVal(0); | |||
| 1787 | break; | |||
| 1788 | default: | |||
| 1789 | llvm_unreachable("Invalid intrinsic")::llvm::llvm_unreachable_internal("Invalid intrinsic", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 1789); | |||
| 1790 | } | |||
| 1791 | SDLoc DL(Op); | |||
| 1792 | ||||
| 1793 | switch (IntNo) { | |||
| 1794 | default: | |||
| 1795 | return SDValue(); // Don't custom lower most intrinsics. | |||
| 1796 | ||||
| 1797 | case Intrinsic::wasm_lsda: { | |||
| 1798 | auto PtrVT = getPointerTy(MF.getDataLayout()); | |||
| 1799 | const char *SymName = MF.createExternalSymbolName( | |||
| 1800 | "GCC_except_table" + std::to_string(MF.getFunctionNumber())); | |||
| 1801 | if (isPositionIndependent()) { | |||
| 1802 | SDValue Node = DAG.getTargetExternalSymbol( | |||
| 1803 | SymName, PtrVT, WebAssemblyII::MO_MEMORY_BASE_REL); | |||
| 1804 | const char *BaseName = MF.createExternalSymbolName("__memory_base"); | |||
| 1805 | SDValue BaseAddr = | |||
| 1806 | DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, | |||
| 1807 | DAG.getTargetExternalSymbol(BaseName, PtrVT)); | |||
| 1808 | SDValue SymAddr = | |||
| 1809 | DAG.getNode(WebAssemblyISD::WrapperREL, DL, PtrVT, Node); | |||
| 1810 | return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymAddr); | |||
| 1811 | } | |||
| 1812 | SDValue Node = DAG.getTargetExternalSymbol(SymName, PtrVT); | |||
| 1813 | return DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, Node); | |||
| 1814 | } | |||
| 1815 | ||||
| 1816 | case Intrinsic::wasm_shuffle: { | |||
| 1817 | // Drop in-chain and replace undefs, but otherwise pass through unchanged | |||
| 1818 | SDValue Ops[18]; | |||
| 1819 | size_t OpIdx = 0; | |||
| 1820 | Ops[OpIdx++] = Op.getOperand(1); | |||
| 1821 | Ops[OpIdx++] = Op.getOperand(2); | |||
| 1822 | while (OpIdx < 18) { | |||
| 1823 | const SDValue &MaskIdx = Op.getOperand(OpIdx + 1); | |||
| 1824 | if (MaskIdx.isUndef() || | |||
| 1825 | cast<ConstantSDNode>(MaskIdx.getNode())->getZExtValue() >= 32) { | |||
| 1826 | bool isTarget = MaskIdx.getNode()->getOpcode() == ISD::TargetConstant; | |||
| 1827 | Ops[OpIdx++] = DAG.getConstant(0, DL, MVT::i32, isTarget); | |||
| 1828 | } else { | |||
| 1829 | Ops[OpIdx++] = MaskIdx; | |||
| 1830 | } | |||
| 1831 | } | |||
| 1832 | return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); | |||
| 1833 | } | |||
| 1834 | } | |||
| 1835 | } | |||
| 1836 | ||||
| 1837 | SDValue | |||
| 1838 | WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, | |||
| 1839 | SelectionDAG &DAG) const { | |||
| 1840 | SDLoc DL(Op); | |||
| 1841 | // If sign extension operations are disabled, allow sext_inreg only if operand | |||
| 1842 | // is a vector extract of an i8 or i16 lane. SIMD does not depend on sign | |||
| 1843 | // extension operations, but allowing sext_inreg in this context lets us have | |||
| 1844 | // simple patterns to select extract_lane_s instructions. Expanding sext_inreg | |||
| 1845 | // everywhere would be simpler in this file, but would necessitate large and | |||
| 1846 | // brittle patterns to undo the expansion and select extract_lane_s | |||
| 1847 | // instructions. | |||
| 1848 | assert(!Subtarget->hasSignExt() && Subtarget->hasSIMD128())(static_cast <bool> (!Subtarget->hasSignExt() && Subtarget->hasSIMD128()) ? void (0) : __assert_fail ("!Subtarget->hasSignExt() && Subtarget->hasSIMD128()" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 1848 , __extension__ __PRETTY_FUNCTION__)); | |||
| 1849 | if (Op.getOperand(0).getOpcode() != ISD::EXTRACT_VECTOR_ELT) | |||
| 1850 | return SDValue(); | |||
| 1851 | ||||
| 1852 | const SDValue &Extract = Op.getOperand(0); | |||
| 1853 | MVT VecT = Extract.getOperand(0).getSimpleValueType(); | |||
| 1854 | if (VecT.getVectorElementType().getSizeInBits() > 32) | |||
| 1855 | return SDValue(); | |||
| 1856 | MVT ExtractedLaneT = | |||
| 1857 | cast<VTSDNode>(Op.getOperand(1).getNode())->getVT().getSimpleVT(); | |||
| 1858 | MVT ExtractedVecT = | |||
| 1859 | MVT::getVectorVT(ExtractedLaneT, 128 / ExtractedLaneT.getSizeInBits()); | |||
| 1860 | if (ExtractedVecT == VecT) | |||
| 1861 | return Op; | |||
| 1862 | ||||
| 1863 | // Bitcast vector to appropriate type to ensure ISel pattern coverage | |||
| 1864 | const SDNode *Index = Extract.getOperand(1).getNode(); | |||
| 1865 | if (!isa<ConstantSDNode>(Index)) | |||
| 1866 | return SDValue(); | |||
| 1867 | unsigned IndexVal = cast<ConstantSDNode>(Index)->getZExtValue(); | |||
| 1868 | unsigned Scale = | |||
| 1869 | ExtractedVecT.getVectorNumElements() / VecT.getVectorNumElements(); | |||
| 1870 | assert(Scale > 1)(static_cast <bool> (Scale > 1) ? void (0) : __assert_fail ("Scale > 1", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 1870, __extension__ __PRETTY_FUNCTION__)); | |||
| 1871 | SDValue NewIndex = | |||
| 1872 | DAG.getConstant(IndexVal * Scale, DL, Index->getValueType(0)); | |||
| 1873 | SDValue NewExtract = DAG.getNode( | |||
| 1874 | ISD::EXTRACT_VECTOR_ELT, DL, Extract.getValueType(), | |||
| 1875 | DAG.getBitcast(ExtractedVecT, Extract.getOperand(0)), NewIndex); | |||
| 1876 | return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Op.getValueType(), NewExtract, | |||
| 1877 | Op.getOperand(1)); | |||
| 1878 | } | |||
| 1879 | ||||
| 1880 | static SDValue LowerConvertLow(SDValue Op, SelectionDAG &DAG) { | |||
| 1881 | SDLoc DL(Op); | |||
| 1882 | if (Op.getValueType() != MVT::v2f64) | |||
| 1883 | return SDValue(); | |||
| 1884 | ||||
| 1885 | auto GetConvertedLane = [](SDValue Op, unsigned &Opcode, SDValue &SrcVec, | |||
| 1886 | unsigned &Index) -> bool { | |||
| 1887 | switch (Op.getOpcode()) { | |||
| 1888 | case ISD::SINT_TO_FP: | |||
| 1889 | Opcode = WebAssemblyISD::CONVERT_LOW_S; | |||
| 1890 | break; | |||
| 1891 | case ISD::UINT_TO_FP: | |||
| 1892 | Opcode = WebAssemblyISD::CONVERT_LOW_U; | |||
| 1893 | break; | |||
| 1894 | case ISD::FP_EXTEND: | |||
| 1895 | Opcode = WebAssemblyISD::PROMOTE_LOW; | |||
| 1896 | break; | |||
| 1897 | default: | |||
| 1898 | return false; | |||
| 1899 | } | |||
| 1900 | ||||
| 1901 | auto ExtractVector = Op.getOperand(0); | |||
| 1902 | if (ExtractVector.getOpcode() != ISD::EXTRACT_VECTOR_ELT) | |||
| 1903 | return false; | |||
| 1904 | ||||
| 1905 | if (!isa<ConstantSDNode>(ExtractVector.getOperand(1).getNode())) | |||
| 1906 | return false; | |||
| 1907 | ||||
| 1908 | SrcVec = ExtractVector.getOperand(0); | |||
| 1909 | Index = ExtractVector.getConstantOperandVal(1); | |||
| 1910 | return true; | |||
| 1911 | }; | |||
| 1912 | ||||
| 1913 | unsigned LHSOpcode, RHSOpcode, LHSIndex, RHSIndex; | |||
| 1914 | SDValue LHSSrcVec, RHSSrcVec; | |||
| 1915 | if (!GetConvertedLane(Op.getOperand(0), LHSOpcode, LHSSrcVec, LHSIndex) || | |||
| 1916 | !GetConvertedLane(Op.getOperand(1), RHSOpcode, RHSSrcVec, RHSIndex)) | |||
| 1917 | return SDValue(); | |||
| 1918 | ||||
| 1919 | if (LHSOpcode != RHSOpcode) | |||
| 1920 | return SDValue(); | |||
| 1921 | ||||
| 1922 | MVT ExpectedSrcVT; | |||
| 1923 | switch (LHSOpcode) { | |||
| 1924 | case WebAssemblyISD::CONVERT_LOW_S: | |||
| 1925 | case WebAssemblyISD::CONVERT_LOW_U: | |||
| 1926 | ExpectedSrcVT = MVT::v4i32; | |||
| 1927 | break; | |||
| 1928 | case WebAssemblyISD::PROMOTE_LOW: | |||
| 1929 | ExpectedSrcVT = MVT::v4f32; | |||
| 1930 | break; | |||
| 1931 | } | |||
| 1932 | if (LHSSrcVec.getValueType() != ExpectedSrcVT) | |||
| 1933 | return SDValue(); | |||
| 1934 | ||||
| 1935 | auto Src = LHSSrcVec; | |||
| 1936 | if (LHSIndex != 0 || RHSIndex != 1 || LHSSrcVec != RHSSrcVec) { | |||
| 1937 | // Shuffle the source vector so that the converted lanes are the low lanes. | |||
| 1938 | Src = DAG.getVectorShuffle( | |||
| 1939 | ExpectedSrcVT, DL, LHSSrcVec, RHSSrcVec, | |||
| 1940 | {static_cast<int>(LHSIndex), static_cast<int>(RHSIndex) + 4, -1, -1}); | |||
| 1941 | } | |||
| 1942 | return DAG.getNode(LHSOpcode, DL, MVT::v2f64, Src); | |||
| 1943 | } | |||
| 1944 | ||||
| 1945 | SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, | |||
| 1946 | SelectionDAG &DAG) const { | |||
| 1947 | if (auto ConvertLow = LowerConvertLow(Op, DAG)) | |||
| 1948 | return ConvertLow; | |||
| 1949 | ||||
| 1950 | SDLoc DL(Op); | |||
| 1951 | const EVT VecT = Op.getValueType(); | |||
| 1952 | const EVT LaneT = Op.getOperand(0).getValueType(); | |||
| 1953 | const size_t Lanes = Op.getNumOperands(); | |||
| 1954 | bool CanSwizzle = VecT == MVT::v16i8; | |||
| 1955 | ||||
| 1956 | // BUILD_VECTORs are lowered to the instruction that initializes the highest | |||
| 1957 | // possible number of lanes at once followed by a sequence of replace_lane | |||
| 1958 | // instructions to individually initialize any remaining lanes. | |||
| 1959 | ||||
| 1960 | // TODO: Tune this. For example, lanewise swizzling is very expensive, so | |||
| 1961 | // swizzled lanes should be given greater weight. | |||
| 1962 | ||||
| 1963 | // TODO: Investigate looping rather than always extracting/replacing specific | |||
| 1964 | // lanes to fill gaps. | |||
| 1965 | ||||
| 1966 | auto IsConstant = [](const SDValue &V) { | |||
| 1967 | return V.getOpcode() == ISD::Constant || V.getOpcode() == ISD::ConstantFP; | |||
| 1968 | }; | |||
| 1969 | ||||
| 1970 | // Returns the source vector and index vector pair if they exist. Checks for: | |||
| 1971 | // (extract_vector_elt | |||
| 1972 | // $src, | |||
| 1973 | // (sign_extend_inreg (extract_vector_elt $indices, $i)) | |||
| 1974 | // ) | |||
| 1975 | auto GetSwizzleSrcs = [](size_t I, const SDValue &Lane) { | |||
| 1976 | auto Bail = std::make_pair(SDValue(), SDValue()); | |||
| 1977 | if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT) | |||
| 1978 | return Bail; | |||
| 1979 | const SDValue &SwizzleSrc = Lane->getOperand(0); | |||
| 1980 | const SDValue &IndexExt = Lane->getOperand(1); | |||
| 1981 | if (IndexExt->getOpcode() != ISD::SIGN_EXTEND_INREG) | |||
| 1982 | return Bail; | |||
| 1983 | const SDValue &Index = IndexExt->getOperand(0); | |||
| 1984 | if (Index->getOpcode() != ISD::EXTRACT_VECTOR_ELT) | |||
| 1985 | return Bail; | |||
| 1986 | const SDValue &SwizzleIndices = Index->getOperand(0); | |||
| 1987 | if (SwizzleSrc.getValueType() != MVT::v16i8 || | |||
| 1988 | SwizzleIndices.getValueType() != MVT::v16i8 || | |||
| 1989 | Index->getOperand(1)->getOpcode() != ISD::Constant || | |||
| 1990 | Index->getConstantOperandVal(1) != I) | |||
| 1991 | return Bail; | |||
| 1992 | return std::make_pair(SwizzleSrc, SwizzleIndices); | |||
| 1993 | }; | |||
| 1994 | ||||
| 1995 | // If the lane is extracted from another vector at a constant index, return | |||
| 1996 | // that vector. The source vector must not have more lanes than the dest | |||
| 1997 | // because the shufflevector indices are in terms of the destination lanes and | |||
| 1998 | // would not be able to address the smaller individual source lanes. | |||
| 1999 | auto GetShuffleSrc = [&](const SDValue &Lane) { | |||
| 2000 | if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT) | |||
| 2001 | return SDValue(); | |||
| 2002 | if (!isa<ConstantSDNode>(Lane->getOperand(1).getNode())) | |||
| 2003 | return SDValue(); | |||
| 2004 | if (Lane->getOperand(0).getValueType().getVectorNumElements() > | |||
| 2005 | VecT.getVectorNumElements()) | |||
| 2006 | return SDValue(); | |||
| 2007 | return Lane->getOperand(0); | |||
| 2008 | }; | |||
| 2009 | ||||
| 2010 | using ValueEntry = std::pair<SDValue, size_t>; | |||
| 2011 | SmallVector<ValueEntry, 16> SplatValueCounts; | |||
| 2012 | ||||
| 2013 | using SwizzleEntry = std::pair<std::pair<SDValue, SDValue>, size_t>; | |||
| 2014 | SmallVector<SwizzleEntry, 16> SwizzleCounts; | |||
| 2015 | ||||
| 2016 | using ShuffleEntry = std::pair<SDValue, size_t>; | |||
| 2017 | SmallVector<ShuffleEntry, 16> ShuffleCounts; | |||
| 2018 | ||||
| 2019 | auto AddCount = [](auto &Counts, const auto &Val) { | |||
| 2020 | auto CountIt = | |||
| 2021 | llvm::find_if(Counts, [&Val](auto E) { return E.first == Val; }); | |||
| 2022 | if (CountIt == Counts.end()) { | |||
| 2023 | Counts.emplace_back(Val, 1); | |||
| 2024 | } else { | |||
| 2025 | CountIt->second++; | |||
| 2026 | } | |||
| 2027 | }; | |||
| 2028 | ||||
| 2029 | auto GetMostCommon = [](auto &Counts) { | |||
| 2030 | auto CommonIt = | |||
| 2031 | std::max_element(Counts.begin(), Counts.end(), llvm::less_second()); | |||
| 2032 | assert(CommonIt != Counts.end() && "Unexpected all-undef build_vector")(static_cast <bool> (CommonIt != Counts.end() && "Unexpected all-undef build_vector") ? void (0) : __assert_fail ("CommonIt != Counts.end() && \"Unexpected all-undef build_vector\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2032 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2033 | return *CommonIt; | |||
| 2034 | }; | |||
| 2035 | ||||
| 2036 | size_t NumConstantLanes = 0; | |||
| 2037 | ||||
| 2038 | // Count eligible lanes for each type of vector creation op | |||
| 2039 | for (size_t I = 0; I
| |||
| 2040 | const SDValue &Lane = Op->getOperand(I); | |||
| 2041 | if (Lane.isUndef()) | |||
| 2042 | continue; | |||
| 2043 | ||||
| 2044 | AddCount(SplatValueCounts, Lane); | |||
| 2045 | ||||
| 2046 | if (IsConstant(Lane)) | |||
| 2047 | NumConstantLanes++; | |||
| 2048 | if (auto ShuffleSrc = GetShuffleSrc(Lane)) | |||
| 2049 | AddCount(ShuffleCounts, ShuffleSrc); | |||
| 2050 | if (CanSwizzle
| |||
| 2051 | auto SwizzleSrcs = GetSwizzleSrcs(I, Lane); | |||
| 2052 | if (SwizzleSrcs.first) | |||
| 2053 | AddCount(SwizzleCounts, SwizzleSrcs); | |||
| 2054 | } | |||
| 2055 | } | |||
| 2056 | ||||
| 2057 | SDValue SplatValue; | |||
| 2058 | size_t NumSplatLanes; | |||
| 2059 | std::tie(SplatValue, NumSplatLanes) = GetMostCommon(SplatValueCounts); | |||
| 2060 | ||||
| 2061 | SDValue SwizzleSrc; | |||
| 2062 | SDValue SwizzleIndices; | |||
| 2063 | size_t NumSwizzleLanes = 0; | |||
| 2064 | if (SwizzleCounts.size()) | |||
| 2065 | std::forward_as_tuple(std::tie(SwizzleSrc, SwizzleIndices), | |||
| 2066 | NumSwizzleLanes) = GetMostCommon(SwizzleCounts); | |||
| 2067 | ||||
| 2068 | // Shuffles can draw from up to two vectors, so find the two most common | |||
| 2069 | // sources. | |||
| 2070 | SDValue ShuffleSrc1, ShuffleSrc2; | |||
| 2071 | size_t NumShuffleLanes = 0; | |||
| 2072 | if (ShuffleCounts.size()) { | |||
| 2073 | std::tie(ShuffleSrc1, NumShuffleLanes) = GetMostCommon(ShuffleCounts); | |||
| 2074 | llvm::erase_if(ShuffleCounts, | |||
| 2075 | [&](const auto &Pair) { return Pair.first == ShuffleSrc1; }); | |||
| 2076 | } | |||
| 2077 | if (ShuffleCounts.size()) { | |||
| 2078 | size_t AdditionalShuffleLanes; | |||
| 2079 | std::tie(ShuffleSrc2, AdditionalShuffleLanes) = | |||
| 2080 | GetMostCommon(ShuffleCounts); | |||
| 2081 | NumShuffleLanes += AdditionalShuffleLanes; | |||
| 2082 | } | |||
| 2083 | ||||
| 2084 | // Predicate returning true if the lane is properly initialized by the | |||
| 2085 | // original instruction | |||
| 2086 | std::function<bool(size_t, const SDValue &)> IsLaneConstructed; | |||
| 2087 | SDValue Result; | |||
| 2088 | // Prefer swizzles over shuffles over vector consts over splats | |||
| 2089 | if (NumSwizzleLanes >= NumShuffleLanes && | |||
| 2090 | NumSwizzleLanes >= NumConstantLanes && NumSwizzleLanes >= NumSplatLanes) { | |||
| 2091 | Result = DAG.getNode(WebAssemblyISD::SWIZZLE, DL, VecT, SwizzleSrc, | |||
| 2092 | SwizzleIndices); | |||
| 2093 | auto Swizzled = std::make_pair(SwizzleSrc, SwizzleIndices); | |||
| 2094 | IsLaneConstructed = [&, Swizzled](size_t I, const SDValue &Lane) { | |||
| 2095 | return Swizzled == GetSwizzleSrcs(I, Lane); | |||
| 2096 | }; | |||
| 2097 | } else if (NumShuffleLanes
| |||
| 2098 | NumShuffleLanes >= NumSplatLanes) { | |||
| 2099 | size_t DestLaneSize = VecT.getVectorElementType().getFixedSizeInBits() / 8; | |||
| 2100 | size_t DestLaneCount = VecT.getVectorNumElements(); | |||
| 2101 | size_t Scale1 = 1; | |||
| 2102 | size_t Scale2 = 1; | |||
| 2103 | SDValue Src1 = ShuffleSrc1; | |||
| 2104 | SDValue Src2 = ShuffleSrc2 ? ShuffleSrc2 : DAG.getUNDEF(VecT); | |||
| 2105 | if (Src1.getValueType() != VecT) { | |||
| 2106 | size_t LaneSize = | |||
| 2107 | Src1.getValueType().getVectorElementType().getFixedSizeInBits() / 8; | |||
| 2108 | assert(LaneSize > DestLaneSize)(static_cast <bool> (LaneSize > DestLaneSize) ? void (0) : __assert_fail ("LaneSize > DestLaneSize", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2108, __extension__ __PRETTY_FUNCTION__)); | |||
| 2109 | Scale1 = LaneSize / DestLaneSize; | |||
| 2110 | Src1 = DAG.getBitcast(VecT, Src1); | |||
| 2111 | } | |||
| 2112 | if (Src2.getValueType() != VecT) { | |||
| 2113 | size_t LaneSize = | |||
| 2114 | Src2.getValueType().getVectorElementType().getFixedSizeInBits() / 8; | |||
| 2115 | assert(LaneSize > DestLaneSize)(static_cast <bool> (LaneSize > DestLaneSize) ? void (0) : __assert_fail ("LaneSize > DestLaneSize", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2115, __extension__ __PRETTY_FUNCTION__)); | |||
| 2116 | Scale2 = LaneSize / DestLaneSize; | |||
| 2117 | Src2 = DAG.getBitcast(VecT, Src2); | |||
| 2118 | } | |||
| 2119 | ||||
| 2120 | int Mask[16]; | |||
| 2121 | assert(DestLaneCount <= 16)(static_cast <bool> (DestLaneCount <= 16) ? void (0) : __assert_fail ("DestLaneCount <= 16", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2121, __extension__ __PRETTY_FUNCTION__)); | |||
| 2122 | for (size_t I = 0; I < DestLaneCount; ++I) { | |||
| 2123 | const SDValue &Lane = Op->getOperand(I); | |||
| 2124 | SDValue Src = GetShuffleSrc(Lane); | |||
| 2125 | if (Src == ShuffleSrc1) { | |||
| 2126 | Mask[I] = Lane->getConstantOperandVal(1) * Scale1; | |||
| 2127 | } else if (Src && Src == ShuffleSrc2) { | |||
| 2128 | Mask[I] = DestLaneCount + Lane->getConstantOperandVal(1) * Scale2; | |||
| 2129 | } else { | |||
| 2130 | Mask[I] = -1; | |||
| 2131 | } | |||
| 2132 | } | |||
| 2133 | ArrayRef<int> MaskRef(Mask, DestLaneCount); | |||
| 2134 | Result = DAG.getVectorShuffle(VecT, DL, Src1, Src2, MaskRef); | |||
| 2135 | IsLaneConstructed = [&](size_t, const SDValue &Lane) { | |||
| 2136 | auto Src = GetShuffleSrc(Lane); | |||
| 2137 | return Src == ShuffleSrc1 || (Src && Src == ShuffleSrc2); | |||
| 2138 | }; | |||
| 2139 | } else if (NumConstantLanes >= NumSplatLanes) { | |||
| 2140 | SmallVector<SDValue, 16> ConstLanes; | |||
| 2141 | for (const SDValue &Lane : Op->op_values()) { | |||
| 2142 | if (IsConstant(Lane)) { | |||
| 2143 | // Values may need to be fixed so that they will sign extend to be | |||
| 2144 | // within the expected range during ISel. Check whether the value is in | |||
| 2145 | // bounds based on the lane bit width and if it is out of bounds, lop | |||
| 2146 | // off the extra bits and subtract 2^n to reflect giving the high bit | |||
| 2147 | // value -2^(n-1) rather than +2^(n-1). Skip the i64 case because it | |||
| 2148 | // cannot possibly be out of range. | |||
| 2149 | auto *Const = dyn_cast<ConstantSDNode>(Lane.getNode()); | |||
| 2150 | int64_t Val = Const
| |||
| 2151 | uint64_t LaneBits = 128 / Lanes; | |||
| 2152 | assert((LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) &&(static_cast <bool> ((LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && "Unexpected out of bounds negative value" ) ? void (0) : __assert_fail ("(LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && \"Unexpected out of bounds negative value\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2153 , __extension__ __PRETTY_FUNCTION__)) | |||
| ||||
| 2153 | "Unexpected out of bounds negative value")(static_cast <bool> ((LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && "Unexpected out of bounds negative value" ) ? void (0) : __assert_fail ("(LaneBits == 64 || Val >= -(1ll << (LaneBits - 1))) && \"Unexpected out of bounds negative value\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2153 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2154 | if (Const && LaneBits != 64 && Val > (1ll << (LaneBits - 1)) - 1) { | |||
| 2155 | uint64_t Mask = (1ll << LaneBits) - 1; | |||
| 2156 | auto NewVal = (((uint64_t)Val & Mask) - (1ll << LaneBits)) & Mask; | |||
| 2157 | ConstLanes.push_back(DAG.getConstant(NewVal, SDLoc(Lane), LaneT)); | |||
| 2158 | } else { | |||
| 2159 | ConstLanes.push_back(Lane); | |||
| 2160 | } | |||
| 2161 | } else if (LaneT.isFloatingPoint()) { | |||
| 2162 | ConstLanes.push_back(DAG.getConstantFP(0, DL, LaneT)); | |||
| 2163 | } else { | |||
| 2164 | ConstLanes.push_back(DAG.getConstant(0, DL, LaneT)); | |||
| 2165 | } | |||
| 2166 | } | |||
| 2167 | Result = DAG.getBuildVector(VecT, DL, ConstLanes); | |||
| 2168 | IsLaneConstructed = [&IsConstant](size_t _, const SDValue &Lane) { | |||
| 2169 | return IsConstant(Lane); | |||
| 2170 | }; | |||
| 2171 | } else { | |||
| 2172 | // Use a splat (which might be selected as a load splat) | |||
| 2173 | Result = DAG.getSplatBuildVector(VecT, DL, SplatValue); | |||
| 2174 | IsLaneConstructed = [&SplatValue](size_t _, const SDValue &Lane) { | |||
| 2175 | return Lane == SplatValue; | |||
| 2176 | }; | |||
| 2177 | } | |||
| 2178 | ||||
| 2179 | assert(Result)(static_cast <bool> (Result) ? void (0) : __assert_fail ("Result", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2179, __extension__ __PRETTY_FUNCTION__)); | |||
| 2180 | assert(IsLaneConstructed)(static_cast <bool> (IsLaneConstructed) ? void (0) : __assert_fail ("IsLaneConstructed", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2180, __extension__ __PRETTY_FUNCTION__)); | |||
| 2181 | ||||
| 2182 | // Add replace_lane instructions for any unhandled values | |||
| 2183 | for (size_t I = 0; I < Lanes; ++I) { | |||
| 2184 | const SDValue &Lane = Op->getOperand(I); | |||
| 2185 | if (!Lane.isUndef() && !IsLaneConstructed(I, Lane)) | |||
| 2186 | Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane, | |||
| 2187 | DAG.getConstant(I, DL, MVT::i32)); | |||
| 2188 | } | |||
| 2189 | ||||
| 2190 | return Result; | |||
| 2191 | } | |||
| 2192 | ||||
| 2193 | SDValue | |||
| 2194 | WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op, | |||
| 2195 | SelectionDAG &DAG) const { | |||
| 2196 | SDLoc DL(Op); | |||
| 2197 | ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask(); | |||
| 2198 | MVT VecType = Op.getOperand(0).getSimpleValueType(); | |||
| 2199 | assert(VecType.is128BitVector() && "Unexpected shuffle vector type")(static_cast <bool> (VecType.is128BitVector() && "Unexpected shuffle vector type") ? void (0) : __assert_fail ("VecType.is128BitVector() && \"Unexpected shuffle vector type\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2199 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2200 | size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8; | |||
| 2201 | ||||
| 2202 | // Space for two vector args and sixteen mask indices | |||
| 2203 | SDValue Ops[18]; | |||
| 2204 | size_t OpIdx = 0; | |||
| 2205 | Ops[OpIdx++] = Op.getOperand(0); | |||
| 2206 | Ops[OpIdx++] = Op.getOperand(1); | |||
| 2207 | ||||
| 2208 | // Expand mask indices to byte indices and materialize them as operands | |||
| 2209 | for (int M : Mask) { | |||
| 2210 | for (size_t J = 0; J < LaneBytes; ++J) { | |||
| 2211 | // Lower undefs (represented by -1 in mask) to {0..J}, which use a | |||
| 2212 | // whole lane of vector input, to allow further reduction at VM. E.g. | |||
| 2213 | // match an 8x16 byte shuffle to an equivalent cheaper 32x4 shuffle. | |||
| 2214 | uint64_t ByteIndex = M == -1 ? J : (uint64_t)M * LaneBytes + J; | |||
| 2215 | Ops[OpIdx++] = DAG.getConstant(ByteIndex, DL, MVT::i32); | |||
| 2216 | } | |||
| 2217 | } | |||
| 2218 | ||||
| 2219 | return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); | |||
| 2220 | } | |||
| 2221 | ||||
| 2222 | SDValue WebAssemblyTargetLowering::LowerSETCC(SDValue Op, | |||
| 2223 | SelectionDAG &DAG) const { | |||
| 2224 | SDLoc DL(Op); | |||
| 2225 | // The legalizer does not know how to expand the unsupported comparison modes | |||
| 2226 | // of i64x2 vectors, so we manually unroll them here. | |||
| 2227 | assert(Op->getOperand(0)->getSimpleValueType(0) == MVT::v2i64)(static_cast <bool> (Op->getOperand(0)->getSimpleValueType (0) == MVT::v2i64) ? void (0) : __assert_fail ("Op->getOperand(0)->getSimpleValueType(0) == MVT::v2i64" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2227 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2228 | SmallVector<SDValue, 2> LHS, RHS; | |||
| 2229 | DAG.ExtractVectorElements(Op->getOperand(0), LHS); | |||
| 2230 | DAG.ExtractVectorElements(Op->getOperand(1), RHS); | |||
| 2231 | const SDValue &CC = Op->getOperand(2); | |||
| 2232 | auto MakeLane = [&](unsigned I) { | |||
| 2233 | return DAG.getNode(ISD::SELECT_CC, DL, MVT::i64, LHS[I], RHS[I], | |||
| 2234 | DAG.getConstant(uint64_t(-1), DL, MVT::i64), | |||
| 2235 | DAG.getConstant(uint64_t(0), DL, MVT::i64), CC); | |||
| 2236 | }; | |||
| 2237 | return DAG.getBuildVector(Op->getValueType(0), DL, | |||
| 2238 | {MakeLane(0), MakeLane(1)}); | |||
| 2239 | } | |||
| 2240 | ||||
| 2241 | SDValue | |||
| 2242 | WebAssemblyTargetLowering::LowerAccessVectorElement(SDValue Op, | |||
| 2243 | SelectionDAG &DAG) const { | |||
| 2244 | // Allow constant lane indices, expand variable lane indices | |||
| 2245 | SDNode *IdxNode = Op.getOperand(Op.getNumOperands() - 1).getNode(); | |||
| 2246 | if (isa<ConstantSDNode>(IdxNode)) { | |||
| 2247 | // Ensure the index type is i32 to match the tablegen patterns | |||
| 2248 | uint64_t Idx = cast<ConstantSDNode>(IdxNode)->getZExtValue(); | |||
| 2249 | SmallVector<SDValue, 3> Ops(Op.getNode()->ops()); | |||
| 2250 | Ops[Op.getNumOperands() - 1] = | |||
| 2251 | DAG.getConstant(Idx, SDLoc(IdxNode), MVT::i32); | |||
| 2252 | return DAG.getNode(Op.getOpcode(), SDLoc(Op), Op.getValueType(), Ops); | |||
| 2253 | } | |||
| 2254 | // Perform default expansion | |||
| 2255 | return SDValue(); | |||
| 2256 | } | |||
| 2257 | ||||
| 2258 | static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG) { | |||
| 2259 | EVT LaneT = Op.getSimpleValueType().getVectorElementType(); | |||
| 2260 | // 32-bit and 64-bit unrolled shifts will have proper semantics | |||
| 2261 | if (LaneT.bitsGE(MVT::i32)) | |||
| 2262 | return DAG.UnrollVectorOp(Op.getNode()); | |||
| 2263 | // Otherwise mask the shift value to get proper semantics from 32-bit shift | |||
| 2264 | SDLoc DL(Op); | |||
| 2265 | size_t NumLanes = Op.getSimpleValueType().getVectorNumElements(); | |||
| 2266 | SDValue Mask = DAG.getConstant(LaneT.getSizeInBits() - 1, DL, MVT::i32); | |||
| 2267 | unsigned ShiftOpcode = Op.getOpcode(); | |||
| 2268 | SmallVector<SDValue, 16> ShiftedElements; | |||
| 2269 | DAG.ExtractVectorElements(Op.getOperand(0), ShiftedElements, 0, 0, MVT::i32); | |||
| 2270 | SmallVector<SDValue, 16> ShiftElements; | |||
| 2271 | DAG.ExtractVectorElements(Op.getOperand(1), ShiftElements, 0, 0, MVT::i32); | |||
| 2272 | SmallVector<SDValue, 16> UnrolledOps; | |||
| 2273 | for (size_t i = 0; i < NumLanes; ++i) { | |||
| 2274 | SDValue MaskedShiftValue = | |||
| 2275 | DAG.getNode(ISD::AND, DL, MVT::i32, ShiftElements[i], Mask); | |||
| 2276 | SDValue ShiftedValue = ShiftedElements[i]; | |||
| 2277 | if (ShiftOpcode == ISD::SRA) | |||
| 2278 | ShiftedValue = DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, MVT::i32, | |||
| 2279 | ShiftedValue, DAG.getValueType(LaneT)); | |||
| 2280 | UnrolledOps.push_back( | |||
| 2281 | DAG.getNode(ShiftOpcode, DL, MVT::i32, ShiftedValue, MaskedShiftValue)); | |||
| 2282 | } | |||
| 2283 | return DAG.getBuildVector(Op.getValueType(), DL, UnrolledOps); | |||
| 2284 | } | |||
| 2285 | ||||
| 2286 | SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op, | |||
| 2287 | SelectionDAG &DAG) const { | |||
| 2288 | SDLoc DL(Op); | |||
| 2289 | ||||
| 2290 | // Only manually lower vector shifts | |||
| 2291 | assert(Op.getSimpleValueType().isVector())(static_cast <bool> (Op.getSimpleValueType().isVector() ) ? void (0) : __assert_fail ("Op.getSimpleValueType().isVector()" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2291 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2292 | ||||
| 2293 | uint64_t LaneBits = Op.getValueType().getScalarSizeInBits(); | |||
| 2294 | auto ShiftVal = Op.getOperand(1); | |||
| 2295 | ||||
| 2296 | // Try to skip bitmask operation since it is implied inside shift instruction | |||
| 2297 | auto SkipImpliedMask = [](SDValue MaskOp, uint64_t MaskBits) { | |||
| 2298 | if (MaskOp.getOpcode() != ISD::AND) | |||
| 2299 | return MaskOp; | |||
| 2300 | SDValue LHS = MaskOp.getOperand(0); | |||
| 2301 | SDValue RHS = MaskOp.getOperand(1); | |||
| 2302 | if (MaskOp.getValueType().isVector()) { | |||
| 2303 | APInt MaskVal; | |||
| 2304 | if (!ISD::isConstantSplatVector(RHS.getNode(), MaskVal)) | |||
| 2305 | std::swap(LHS, RHS); | |||
| 2306 | ||||
| 2307 | if (ISD::isConstantSplatVector(RHS.getNode(), MaskVal) && | |||
| 2308 | MaskVal == MaskBits) | |||
| 2309 | MaskOp = LHS; | |||
| 2310 | } else { | |||
| 2311 | if (!isa<ConstantSDNode>(RHS.getNode())) | |||
| 2312 | std::swap(LHS, RHS); | |||
| 2313 | ||||
| 2314 | auto ConstantRHS = dyn_cast<ConstantSDNode>(RHS.getNode()); | |||
| 2315 | if (ConstantRHS && ConstantRHS->getAPIntValue() == MaskBits) | |||
| 2316 | MaskOp = LHS; | |||
| 2317 | } | |||
| 2318 | ||||
| 2319 | return MaskOp; | |||
| 2320 | }; | |||
| 2321 | ||||
| 2322 | // Skip vector and operation | |||
| 2323 | ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); | |||
| 2324 | ShiftVal = DAG.getSplatValue(ShiftVal); | |||
| 2325 | if (!ShiftVal) | |||
| 2326 | return unrollVectorShift(Op, DAG); | |||
| 2327 | ||||
| 2328 | // Skip scalar and operation | |||
| 2329 | ShiftVal = SkipImpliedMask(ShiftVal, LaneBits - 1); | |||
| 2330 | // Use anyext because none of the high bits can affect the shift | |||
| 2331 | ShiftVal = DAG.getAnyExtOrTrunc(ShiftVal, DL, MVT::i32); | |||
| 2332 | ||||
| 2333 | unsigned Opcode; | |||
| 2334 | switch (Op.getOpcode()) { | |||
| 2335 | case ISD::SHL: | |||
| 2336 | Opcode = WebAssemblyISD::VEC_SHL; | |||
| 2337 | break; | |||
| 2338 | case ISD::SRA: | |||
| 2339 | Opcode = WebAssemblyISD::VEC_SHR_S; | |||
| 2340 | break; | |||
| 2341 | case ISD::SRL: | |||
| 2342 | Opcode = WebAssemblyISD::VEC_SHR_U; | |||
| 2343 | break; | |||
| 2344 | default: | |||
| 2345 | llvm_unreachable("unexpected opcode")::llvm::llvm_unreachable_internal("unexpected opcode", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2345); | |||
| 2346 | } | |||
| 2347 | ||||
| 2348 | return DAG.getNode(Opcode, DL, Op.getValueType(), Op.getOperand(0), ShiftVal); | |||
| 2349 | } | |||
| 2350 | ||||
| 2351 | SDValue WebAssemblyTargetLowering::LowerFP_TO_INT_SAT(SDValue Op, | |||
| 2352 | SelectionDAG &DAG) const { | |||
| 2353 | SDLoc DL(Op); | |||
| 2354 | EVT ResT = Op.getValueType(); | |||
| 2355 | EVT SatVT = cast<VTSDNode>(Op.getOperand(1))->getVT(); | |||
| 2356 | ||||
| 2357 | if ((ResT == MVT::i32 || ResT == MVT::i64) && | |||
| 2358 | (SatVT == MVT::i32 || SatVT == MVT::i64)) | |||
| 2359 | return Op; | |||
| 2360 | ||||
| 2361 | if (ResT == MVT::v4i32 && SatVT == MVT::i32) | |||
| 2362 | return Op; | |||
| 2363 | ||||
| 2364 | return SDValue(); | |||
| 2365 | } | |||
| 2366 | ||||
| 2367 | //===----------------------------------------------------------------------===// | |||
| 2368 | // Custom DAG combine hooks | |||
| 2369 | //===----------------------------------------------------------------------===// | |||
| 2370 | static SDValue | |||
| 2371 | performVECTOR_SHUFFLECombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { | |||
| 2372 | auto &DAG = DCI.DAG; | |||
| 2373 | auto Shuffle = cast<ShuffleVectorSDNode>(N); | |||
| 2374 | ||||
| 2375 | // Hoist vector bitcasts that don't change the number of lanes out of unary | |||
| 2376 | // shuffles, where they are less likely to get in the way of other combines. | |||
| 2377 | // (shuffle (vNxT1 (bitcast (vNxT0 x))), undef, mask) -> | |||
| 2378 | // (vNxT1 (bitcast (vNxT0 (shuffle x, undef, mask)))) | |||
| 2379 | SDValue Bitcast = N->getOperand(0); | |||
| 2380 | if (Bitcast.getOpcode() != ISD::BITCAST) | |||
| 2381 | return SDValue(); | |||
| 2382 | if (!N->getOperand(1).isUndef()) | |||
| 2383 | return SDValue(); | |||
| 2384 | SDValue CastOp = Bitcast.getOperand(0); | |||
| 2385 | MVT SrcType = CastOp.getSimpleValueType(); | |||
| 2386 | MVT DstType = Bitcast.getSimpleValueType(); | |||
| 2387 | if (!SrcType.is128BitVector() || | |||
| 2388 | SrcType.getVectorNumElements() != DstType.getVectorNumElements()) | |||
| 2389 | return SDValue(); | |||
| 2390 | SDValue NewShuffle = DAG.getVectorShuffle( | |||
| 2391 | SrcType, SDLoc(N), CastOp, DAG.getUNDEF(SrcType), Shuffle->getMask()); | |||
| 2392 | return DAG.getBitcast(DstType, NewShuffle); | |||
| 2393 | } | |||
| 2394 | ||||
| 2395 | /// Convert ({u,s}itofp vec) --> ({u,s}itofp ({s,z}ext vec)) so it doesn't get | |||
| 2396 | /// split up into scalar instructions during legalization, and the vector | |||
| 2397 | /// extending instructions are selected in performVectorExtendCombine below. | |||
| 2398 | static SDValue | |||
| 2399 | performVectorExtendToFPCombine(SDNode *N, | |||
| 2400 | TargetLowering::DAGCombinerInfo &DCI) { | |||
| 2401 | auto &DAG = DCI.DAG; | |||
| 2402 | assert(N->getOpcode() == ISD::UINT_TO_FP ||(static_cast <bool> (N->getOpcode() == ISD::UINT_TO_FP || N->getOpcode() == ISD::SINT_TO_FP) ? void (0) : __assert_fail ("N->getOpcode() == ISD::UINT_TO_FP || N->getOpcode() == ISD::SINT_TO_FP" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2403 , __extension__ __PRETTY_FUNCTION__)) | |||
| 2403 | N->getOpcode() == ISD::SINT_TO_FP)(static_cast <bool> (N->getOpcode() == ISD::UINT_TO_FP || N->getOpcode() == ISD::SINT_TO_FP) ? void (0) : __assert_fail ("N->getOpcode() == ISD::UINT_TO_FP || N->getOpcode() == ISD::SINT_TO_FP" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2403 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2404 | ||||
| 2405 | EVT InVT = N->getOperand(0)->getValueType(0); | |||
| 2406 | EVT ResVT = N->getValueType(0); | |||
| 2407 | MVT ExtVT; | |||
| 2408 | if (ResVT == MVT::v4f32 && (InVT == MVT::v4i16 || InVT == MVT::v4i8)) | |||
| 2409 | ExtVT = MVT::v4i32; | |||
| 2410 | else if (ResVT == MVT::v2f64 && (InVT == MVT::v2i16 || InVT == MVT::v2i8)) | |||
| 2411 | ExtVT = MVT::v2i32; | |||
| 2412 | else | |||
| 2413 | return SDValue(); | |||
| 2414 | ||||
| 2415 | unsigned Op = | |||
| 2416 | N->getOpcode() == ISD::UINT_TO_FP ? ISD::ZERO_EXTEND : ISD::SIGN_EXTEND; | |||
| 2417 | SDValue Conv = DAG.getNode(Op, SDLoc(N), ExtVT, N->getOperand(0)); | |||
| 2418 | return DAG.getNode(N->getOpcode(), SDLoc(N), ResVT, Conv); | |||
| 2419 | } | |||
| 2420 | ||||
| 2421 | static SDValue | |||
| 2422 | performVectorExtendCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { | |||
| 2423 | auto &DAG = DCI.DAG; | |||
| 2424 | assert(N->getOpcode() == ISD::SIGN_EXTEND ||(static_cast <bool> (N->getOpcode() == ISD::SIGN_EXTEND || N->getOpcode() == ISD::ZERO_EXTEND) ? void (0) : __assert_fail ("N->getOpcode() == ISD::SIGN_EXTEND || N->getOpcode() == ISD::ZERO_EXTEND" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2425 , __extension__ __PRETTY_FUNCTION__)) | |||
| 2425 | N->getOpcode() == ISD::ZERO_EXTEND)(static_cast <bool> (N->getOpcode() == ISD::SIGN_EXTEND || N->getOpcode() == ISD::ZERO_EXTEND) ? void (0) : __assert_fail ("N->getOpcode() == ISD::SIGN_EXTEND || N->getOpcode() == ISD::ZERO_EXTEND" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2425 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2426 | ||||
| 2427 | // Combine ({s,z}ext (extract_subvector src, i)) into a widening operation if | |||
| 2428 | // possible before the extract_subvector can be expanded. | |||
| 2429 | auto Extract = N->getOperand(0); | |||
| 2430 | if (Extract.getOpcode() != ISD::EXTRACT_SUBVECTOR) | |||
| 2431 | return SDValue(); | |||
| 2432 | auto Source = Extract.getOperand(0); | |||
| 2433 | auto *IndexNode = dyn_cast<ConstantSDNode>(Extract.getOperand(1)); | |||
| 2434 | if (IndexNode == nullptr) | |||
| 2435 | return SDValue(); | |||
| 2436 | auto Index = IndexNode->getZExtValue(); | |||
| 2437 | ||||
| 2438 | // Only v8i8, v4i16, and v2i32 extracts can be widened, and only if the | |||
| 2439 | // extracted subvector is the low or high half of its source. | |||
| 2440 | EVT ResVT = N->getValueType(0); | |||
| 2441 | if (ResVT == MVT::v8i16) { | |||
| 2442 | if (Extract.getValueType() != MVT::v8i8 || | |||
| 2443 | Source.getValueType() != MVT::v16i8 || (Index != 0 && Index != 8)) | |||
| 2444 | return SDValue(); | |||
| 2445 | } else if (ResVT == MVT::v4i32) { | |||
| 2446 | if (Extract.getValueType() != MVT::v4i16 || | |||
| 2447 | Source.getValueType() != MVT::v8i16 || (Index != 0 && Index != 4)) | |||
| 2448 | return SDValue(); | |||
| 2449 | } else if (ResVT == MVT::v2i64) { | |||
| 2450 | if (Extract.getValueType() != MVT::v2i32 || | |||
| 2451 | Source.getValueType() != MVT::v4i32 || (Index != 0 && Index != 2)) | |||
| 2452 | return SDValue(); | |||
| 2453 | } else { | |||
| 2454 | return SDValue(); | |||
| 2455 | } | |||
| 2456 | ||||
| 2457 | bool IsSext = N->getOpcode() == ISD::SIGN_EXTEND; | |||
| 2458 | bool IsLow = Index == 0; | |||
| 2459 | ||||
| 2460 | unsigned Op = IsSext ? (IsLow ? WebAssemblyISD::EXTEND_LOW_S | |||
| 2461 | : WebAssemblyISD::EXTEND_HIGH_S) | |||
| 2462 | : (IsLow ? WebAssemblyISD::EXTEND_LOW_U | |||
| 2463 | : WebAssemblyISD::EXTEND_HIGH_U); | |||
| 2464 | ||||
| 2465 | return DAG.getNode(Op, SDLoc(N), ResVT, Source); | |||
| 2466 | } | |||
| 2467 | ||||
| 2468 | static SDValue | |||
| 2469 | performVectorTruncZeroCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { | |||
| 2470 | auto &DAG = DCI.DAG; | |||
| 2471 | ||||
| 2472 | auto GetWasmConversionOp = [](unsigned Op) { | |||
| 2473 | switch (Op) { | |||
| 2474 | case ISD::FP_TO_SINT_SAT: | |||
| 2475 | return WebAssemblyISD::TRUNC_SAT_ZERO_S; | |||
| 2476 | case ISD::FP_TO_UINT_SAT: | |||
| 2477 | return WebAssemblyISD::TRUNC_SAT_ZERO_U; | |||
| 2478 | case ISD::FP_ROUND: | |||
| 2479 | return WebAssemblyISD::DEMOTE_ZERO; | |||
| 2480 | } | |||
| 2481 | llvm_unreachable("unexpected op")::llvm::llvm_unreachable_internal("unexpected op", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2481); | |||
| 2482 | }; | |||
| 2483 | ||||
| 2484 | auto IsZeroSplat = [](SDValue SplatVal) { | |||
| 2485 | auto *Splat = dyn_cast<BuildVectorSDNode>(SplatVal.getNode()); | |||
| 2486 | APInt SplatValue, SplatUndef; | |||
| 2487 | unsigned SplatBitSize; | |||
| 2488 | bool HasAnyUndefs; | |||
| 2489 | return Splat && | |||
| 2490 | Splat->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, | |||
| 2491 | HasAnyUndefs) && | |||
| 2492 | SplatValue == 0; | |||
| 2493 | }; | |||
| 2494 | ||||
| 2495 | if (N->getOpcode() == ISD::CONCAT_VECTORS) { | |||
| 2496 | // Combine this: | |||
| 2497 | // | |||
| 2498 | // (concat_vectors (v2i32 (fp_to_{s,u}int_sat $x, 32)), (v2i32 (splat 0))) | |||
| 2499 | // | |||
| 2500 | // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x). | |||
| 2501 | // | |||
| 2502 | // Or this: | |||
| 2503 | // | |||
| 2504 | // (concat_vectors (v2f32 (fp_round (v2f64 $x))), (v2f32 (splat 0))) | |||
| 2505 | // | |||
| 2506 | // into (f32x4.demote_zero_f64x2 $x). | |||
| 2507 | EVT ResVT; | |||
| 2508 | EVT ExpectedConversionType; | |||
| 2509 | auto Conversion = N->getOperand(0); | |||
| 2510 | auto ConversionOp = Conversion.getOpcode(); | |||
| 2511 | switch (ConversionOp) { | |||
| 2512 | case ISD::FP_TO_SINT_SAT: | |||
| 2513 | case ISD::FP_TO_UINT_SAT: | |||
| 2514 | ResVT = MVT::v4i32; | |||
| 2515 | ExpectedConversionType = MVT::v2i32; | |||
| 2516 | break; | |||
| 2517 | case ISD::FP_ROUND: | |||
| 2518 | ResVT = MVT::v4f32; | |||
| 2519 | ExpectedConversionType = MVT::v2f32; | |||
| 2520 | break; | |||
| 2521 | default: | |||
| 2522 | return SDValue(); | |||
| 2523 | } | |||
| 2524 | ||||
| 2525 | if (N->getValueType(0) != ResVT) | |||
| 2526 | return SDValue(); | |||
| 2527 | ||||
| 2528 | if (Conversion.getValueType() != ExpectedConversionType) | |||
| 2529 | return SDValue(); | |||
| 2530 | ||||
| 2531 | auto Source = Conversion.getOperand(0); | |||
| 2532 | if (Source.getValueType() != MVT::v2f64) | |||
| 2533 | return SDValue(); | |||
| 2534 | ||||
| 2535 | if (!IsZeroSplat(N->getOperand(1)) || | |||
| 2536 | N->getOperand(1).getValueType() != ExpectedConversionType) | |||
| 2537 | return SDValue(); | |||
| 2538 | ||||
| 2539 | unsigned Op = GetWasmConversionOp(ConversionOp); | |||
| 2540 | return DAG.getNode(Op, SDLoc(N), ResVT, Source); | |||
| 2541 | } | |||
| 2542 | ||||
| 2543 | // Combine this: | |||
| 2544 | // | |||
| 2545 | // (fp_to_{s,u}int_sat (concat_vectors $x, (v2f64 (splat 0))), 32) | |||
| 2546 | // | |||
| 2547 | // into (i32x4.trunc_sat_f64x2_zero_{s,u} $x). | |||
| 2548 | // | |||
| 2549 | // Or this: | |||
| 2550 | // | |||
| 2551 | // (v4f32 (fp_round (concat_vectors $x, (v2f64 (splat 0))))) | |||
| 2552 | // | |||
| 2553 | // into (f32x4.demote_zero_f64x2 $x). | |||
| 2554 | EVT ResVT; | |||
| 2555 | auto ConversionOp = N->getOpcode(); | |||
| 2556 | switch (ConversionOp) { | |||
| 2557 | case ISD::FP_TO_SINT_SAT: | |||
| 2558 | case ISD::FP_TO_UINT_SAT: | |||
| 2559 | ResVT = MVT::v4i32; | |||
| 2560 | break; | |||
| 2561 | case ISD::FP_ROUND: | |||
| 2562 | ResVT = MVT::v4f32; | |||
| 2563 | break; | |||
| 2564 | default: | |||
| 2565 | llvm_unreachable("unexpected op")::llvm::llvm_unreachable_internal("unexpected op", "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp" , 2565); | |||
| 2566 | } | |||
| 2567 | ||||
| 2568 | if (N->getValueType(0) != ResVT) | |||
| 2569 | return SDValue(); | |||
| 2570 | ||||
| 2571 | auto Concat = N->getOperand(0); | |||
| 2572 | if (Concat.getValueType() != MVT::v4f64) | |||
| 2573 | return SDValue(); | |||
| 2574 | ||||
| 2575 | auto Source = Concat.getOperand(0); | |||
| 2576 | if (Source.getValueType() != MVT::v2f64) | |||
| 2577 | return SDValue(); | |||
| 2578 | ||||
| 2579 | if (!IsZeroSplat(Concat.getOperand(1)) || | |||
| 2580 | Concat.getOperand(1).getValueType() != MVT::v2f64) | |||
| 2581 | return SDValue(); | |||
| 2582 | ||||
| 2583 | unsigned Op = GetWasmConversionOp(ConversionOp); | |||
| 2584 | return DAG.getNode(Op, SDLoc(N), ResVT, Source); | |||
| 2585 | } | |||
| 2586 | ||||
| 2587 | // Helper to extract VectorWidth bits from Vec, starting from IdxVal. | |||
| 2588 | static SDValue extractSubVector(SDValue Vec, unsigned IdxVal, SelectionDAG &DAG, | |||
| 2589 | const SDLoc &DL, unsigned VectorWidth) { | |||
| 2590 | EVT VT = Vec.getValueType(); | |||
| 2591 | EVT ElVT = VT.getVectorElementType(); | |||
| 2592 | unsigned Factor = VT.getSizeInBits() / VectorWidth; | |||
| 2593 | EVT ResultVT = EVT::getVectorVT(*DAG.getContext(), ElVT, | |||
| 2594 | VT.getVectorNumElements() / Factor); | |||
| 2595 | ||||
| 2596 | // Extract the relevant VectorWidth bits. Generate an EXTRACT_SUBVECTOR | |||
| 2597 | unsigned ElemsPerChunk = VectorWidth / ElVT.getSizeInBits(); | |||
| 2598 | assert(isPowerOf2_32(ElemsPerChunk) && "Elements per chunk not power of 2")(static_cast <bool> (isPowerOf2_32(ElemsPerChunk) && "Elements per chunk not power of 2") ? void (0) : __assert_fail ("isPowerOf2_32(ElemsPerChunk) && \"Elements per chunk not power of 2\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2598 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2599 | ||||
| 2600 | // This is the index of the first element of the VectorWidth-bit chunk | |||
| 2601 | // we want. Since ElemsPerChunk is a power of 2 just need to clear bits. | |||
| 2602 | IdxVal &= ~(ElemsPerChunk - 1); | |||
| 2603 | ||||
| 2604 | // If the input is a buildvector just emit a smaller one. | |||
| 2605 | if (Vec.getOpcode() == ISD::BUILD_VECTOR) | |||
| 2606 | return DAG.getBuildVector(ResultVT, DL, | |||
| 2607 | Vec->ops().slice(IdxVal, ElemsPerChunk)); | |||
| 2608 | ||||
| 2609 | SDValue VecIdx = DAG.getIntPtrConstant(IdxVal, DL); | |||
| 2610 | return DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, ResultVT, Vec, VecIdx); | |||
| 2611 | } | |||
| 2612 | ||||
| 2613 | // Helper to recursively truncate vector elements in half with NARROW_U. DstVT | |||
| 2614 | // is the expected destination value type after recursion. In is the initial | |||
| 2615 | // input. Note that the input should have enough leading zero bits to prevent | |||
| 2616 | // NARROW_U from saturating results. | |||
| 2617 | static SDValue truncateVectorWithNARROW(EVT DstVT, SDValue In, const SDLoc &DL, | |||
| 2618 | SelectionDAG &DAG) { | |||
| 2619 | EVT SrcVT = In.getValueType(); | |||
| 2620 | ||||
| 2621 | // No truncation required, we might get here due to recursive calls. | |||
| 2622 | if (SrcVT == DstVT) | |||
| 2623 | return In; | |||
| 2624 | ||||
| 2625 | unsigned SrcSizeInBits = SrcVT.getSizeInBits(); | |||
| 2626 | unsigned NumElems = SrcVT.getVectorNumElements(); | |||
| 2627 | if (!isPowerOf2_32(NumElems)) | |||
| 2628 | return SDValue(); | |||
| 2629 | assert(DstVT.getVectorNumElements() == NumElems && "Illegal truncation")(static_cast <bool> (DstVT.getVectorNumElements() == NumElems && "Illegal truncation") ? void (0) : __assert_fail ( "DstVT.getVectorNumElements() == NumElems && \"Illegal truncation\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2629 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2630 | assert(SrcSizeInBits > DstVT.getSizeInBits() && "Illegal truncation")(static_cast <bool> (SrcSizeInBits > DstVT.getSizeInBits () && "Illegal truncation") ? void (0) : __assert_fail ("SrcSizeInBits > DstVT.getSizeInBits() && \"Illegal truncation\"" , "llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp", 2630 , __extension__ __PRETTY_FUNCTION__)); | |||
| 2631 | ||||
| 2632 | LLVMContext &Ctx = *DAG.getContext(); | |||
| 2633 | EVT PackedSVT = EVT::getIntegerVT(Ctx, SrcVT.getScalarSizeInBits() / 2); | |||
| 2634 | ||||
| 2635 | // Narrow to the largest type possible: | |||
| 2636 | // vXi64/vXi32 -> i16x8.narrow_i32x4_u and vXi16 -> i8x16.narrow_i16x8_u. | |||
| 2637 | EVT InVT = MVT::i16, OutVT = MVT::i8; | |||
| 2638 | if (SrcVT.getScalarSizeInBits() > 16) { | |||
| 2639 | InVT = MVT::i32; | |||
| 2640 | OutVT = MVT::i16; | |||
| 2641 | } | |||
| 2642 | unsigned SubSizeInBits = SrcSizeInBits / 2; | |||
| 2643 | InVT = EVT::getVectorVT(Ctx, InVT, SubSizeInBits / InVT.getSizeInBits()); | |||
| 2644 | OutVT = EVT::getVectorVT(Ctx, OutVT, SubSizeInBits / OutVT.getSizeInBits()); | |||
| 2645 | ||||
| 2646 | // Split lower/upper subvectors. | |||
| 2647 | SDValue Lo = extractSubVector(In, 0, DAG, DL, SubSizeInBits); | |||
| 2648 | SDValue Hi = extractSubVector(In, NumElems / 2, DAG, DL, SubSizeInBits); | |||
| 2649 | ||||
| 2650 | // 256bit -> 128bit truncate - Narrow lower/upper 128-bit subvectors. | |||
| 2651 | if (SrcVT.is256BitVector() && DstVT.is128BitVector()) { | |||
| 2652 | Lo = DAG.getBitcast(InVT, Lo); | |||
| 2653 | Hi = DAG.getBitcast(InVT, Hi); | |||
| 2654 | SDValue Res = DAG.getNode(WebAssemblyISD::NARROW_U, DL, OutVT, Lo, Hi); | |||
| 2655 | return DAG.getBitcast(DstVT, Res); | |||
| 2656 | } | |||
| 2657 | ||||
| 2658 | // Recursively narrow lower/upper subvectors, concat result and narrow again. | |||
| 2659 | EVT PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems / 2); | |||
| 2660 | Lo = truncateVectorWithNARROW(PackedVT, Lo, DL, DAG); | |||
| 2661 | Hi = truncateVectorWithNARROW(PackedVT, Hi, DL, DAG); | |||
| 2662 | ||||
| 2663 | PackedVT = EVT::getVectorVT(Ctx, PackedSVT, NumElems); | |||
| 2664 | SDValue Res = DAG.getNode(ISD::CONCAT_VECTORS, DL, PackedVT, Lo, Hi); | |||
| 2665 | return truncateVectorWithNARROW(DstVT, Res, DL, DAG); | |||
| 2666 | } | |||
| 2667 | ||||
| 2668 | static SDValue performTruncateCombine(SDNode *N, | |||
| 2669 | TargetLowering::DAGCombinerInfo &DCI) { | |||
| 2670 | auto &DAG = DCI.DAG; | |||
| 2671 | ||||
| 2672 | SDValue In = N->getOperand(0); | |||
| 2673 | EVT InVT = In.getValueType(); | |||
| 2674 | if (!InVT.isSimple()) | |||
| 2675 | return SDValue(); | |||
| 2676 | ||||
| 2677 | EVT OutVT = N->getValueType(0); | |||
| 2678 | if (!OutVT.isVector()) | |||
| 2679 | return SDValue(); | |||
| 2680 | ||||
| 2681 | EVT OutSVT = OutVT.getVectorElementType(); | |||
| 2682 | EVT InSVT = InVT.getVectorElementType(); | |||
| 2683 | // Currently only cover truncate to v16i8 or v8i16. | |||
| 2684 | if (!((InSVT == MVT::i16 || InSVT == MVT::i32 || InSVT == MVT::i64) && | |||
| 2685 | (OutSVT == MVT::i8 || OutSVT == MVT::i16) && OutVT.is128BitVector())) | |||
| 2686 | return SDValue(); | |||
| 2687 | ||||
| 2688 | SDLoc DL(N); | |||
| 2689 | APInt Mask = APInt::getLowBitsSet(InVT.getScalarSizeInBits(), | |||
| 2690 | OutVT.getScalarSizeInBits()); | |||
| 2691 | In = DAG.getNode(ISD::AND, DL, InVT, In, DAG.getConstant(Mask, DL, InVT)); | |||
| 2692 | return truncateVectorWithNARROW(OutVT, In, DL, DAG); | |||
| 2693 | } | |||
| 2694 | ||||
| 2695 | SDValue | |||
| 2696 | WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N, | |||
| 2697 | DAGCombinerInfo &DCI) const { | |||
| 2698 | switch (N->getOpcode()) { | |||
| 2699 | default: | |||
| 2700 | return SDValue(); | |||
| 2701 | case ISD::VECTOR_SHUFFLE: | |||
| 2702 | return performVECTOR_SHUFFLECombine(N, DCI); | |||
| 2703 | case ISD::SIGN_EXTEND: | |||
| 2704 | case ISD::ZERO_EXTEND: | |||
| 2705 | return performVectorExtendCombine(N, DCI); | |||
| 2706 | case ISD::UINT_TO_FP: | |||
| 2707 | case ISD::SINT_TO_FP: | |||
| 2708 | return performVectorExtendToFPCombine(N, DCI); | |||
| 2709 | case ISD::FP_TO_SINT_SAT: | |||
| 2710 | case ISD::FP_TO_UINT_SAT: | |||
| 2711 | case ISD::FP_ROUND: | |||
| 2712 | case ISD::CONCAT_VECTORS: | |||
| 2713 | return performVectorTruncZeroCombine(N, DCI); | |||
| 2714 | case ISD::TRUNCATE: | |||
| 2715 | return performTruncateCombine(N, DCI); | |||
| 2716 | } | |||
| 2717 | } |