10b57cec5SDimitry Andric //=- WebAssemblyISelLowering.cpp - WebAssembly DAG Lowering Implementation -==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric /// This file implements the WebAssemblyTargetLowering class. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #include "WebAssemblyISelLowering.h" 150b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 160b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 170b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 180b57cec5SDimitry Andric #include "WebAssemblyTargetMachine.h" 190b57cec5SDimitry Andric #include "llvm/CodeGen/Analysis.h" 200b57cec5SDimitry Andric #include "llvm/CodeGen/CallingConvLower.h" 210b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 220b57cec5SDimitry Andric #include "llvm/CodeGen/MachineJumpTableInfo.h" 230b57cec5SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 240b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 250b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAG.h" 260b57cec5SDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h" 270b57cec5SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 280b57cec5SDimitry Andric #include "llvm/IR/DiagnosticPrinter.h" 290b57cec5SDimitry Andric #include "llvm/IR/Function.h" 300b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h" 310b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 320b57cec5SDimitry Andric #include "llvm/Support/ErrorHandling.h" 330b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 340b57cec5SDimitry Andric #include "llvm/Target/TargetOptions.h" 350b57cec5SDimitry Andric using namespace llvm; 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-lower" 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric WebAssemblyTargetLowering::WebAssemblyTargetLowering( 400b57cec5SDimitry Andric const TargetMachine &TM, const WebAssemblySubtarget &STI) 410b57cec5SDimitry Andric : TargetLowering(TM), Subtarget(&STI) { 420b57cec5SDimitry Andric auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric // Booleans always contain 0 or 1. 450b57cec5SDimitry Andric setBooleanContents(ZeroOrOneBooleanContent); 460b57cec5SDimitry Andric // Except in SIMD vectors 470b57cec5SDimitry Andric setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); 480b57cec5SDimitry Andric // We don't know the microarchitecture here, so just reduce register pressure. 490b57cec5SDimitry Andric setSchedulingPreference(Sched::RegPressure); 500b57cec5SDimitry Andric // Tell ISel that we have a stack pointer. 510b57cec5SDimitry Andric setStackPointerRegisterToSaveRestore( 520b57cec5SDimitry Andric Subtarget->hasAddr64() ? WebAssembly::SP64 : WebAssembly::SP32); 530b57cec5SDimitry Andric // Set up the register classes. 540b57cec5SDimitry Andric addRegisterClass(MVT::i32, &WebAssembly::I32RegClass); 550b57cec5SDimitry Andric addRegisterClass(MVT::i64, &WebAssembly::I64RegClass); 560b57cec5SDimitry Andric addRegisterClass(MVT::f32, &WebAssembly::F32RegClass); 570b57cec5SDimitry Andric addRegisterClass(MVT::f64, &WebAssembly::F64RegClass); 580b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 590b57cec5SDimitry Andric addRegisterClass(MVT::v16i8, &WebAssembly::V128RegClass); 600b57cec5SDimitry Andric addRegisterClass(MVT::v8i16, &WebAssembly::V128RegClass); 610b57cec5SDimitry Andric addRegisterClass(MVT::v4i32, &WebAssembly::V128RegClass); 620b57cec5SDimitry Andric addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass); 630b57cec5SDimitry Andric } 640b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) { 650b57cec5SDimitry Andric addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); 660b57cec5SDimitry Andric addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric // Compute derived properties from the register classes. 690b57cec5SDimitry Andric computeRegisterProperties(Subtarget->getRegisterInfo()); 700b57cec5SDimitry Andric 710b57cec5SDimitry Andric setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); 720b57cec5SDimitry Andric setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); 730b57cec5SDimitry Andric setOperationAction(ISD::JumpTable, MVTPtr, Custom); 740b57cec5SDimitry Andric setOperationAction(ISD::BlockAddress, MVTPtr, Custom); 750b57cec5SDimitry Andric setOperationAction(ISD::BRIND, MVT::Other, Custom); 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric // Take the default expansion for va_arg, va_copy, and va_end. There is no 780b57cec5SDimitry Andric // default action for va_start, so we do that custom. 790b57cec5SDimitry Andric setOperationAction(ISD::VASTART, MVT::Other, Custom); 800b57cec5SDimitry Andric setOperationAction(ISD::VAARG, MVT::Other, Expand); 810b57cec5SDimitry Andric setOperationAction(ISD::VACOPY, MVT::Other, Expand); 820b57cec5SDimitry Andric setOperationAction(ISD::VAEND, MVT::Other, Expand); 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric for (auto T : {MVT::f32, MVT::f64, MVT::v4f32, MVT::v2f64}) { 850b57cec5SDimitry Andric // Don't expand the floating-point types to constant pools. 860b57cec5SDimitry Andric setOperationAction(ISD::ConstantFP, T, Legal); 870b57cec5SDimitry Andric // Expand floating-point comparisons. 880b57cec5SDimitry Andric for (auto CC : {ISD::SETO, ISD::SETUO, ISD::SETUEQ, ISD::SETONE, 890b57cec5SDimitry Andric ISD::SETULT, ISD::SETULE, ISD::SETUGT, ISD::SETUGE}) 900b57cec5SDimitry Andric setCondCodeAction(CC, T, Expand); 910b57cec5SDimitry Andric // Expand floating-point library function operators. 920b57cec5SDimitry Andric for (auto Op : 930b57cec5SDimitry Andric {ISD::FSIN, ISD::FCOS, ISD::FSINCOS, ISD::FPOW, ISD::FREM, ISD::FMA}) 940b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 950b57cec5SDimitry Andric // Note supported floating-point library function operators that otherwise 960b57cec5SDimitry Andric // default to expand. 970b57cec5SDimitry Andric for (auto Op : 980b57cec5SDimitry Andric {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) 990b57cec5SDimitry Andric setOperationAction(Op, T, Legal); 1000b57cec5SDimitry Andric // Support minimum and maximum, which otherwise default to expand. 1010b57cec5SDimitry Andric setOperationAction(ISD::FMINIMUM, T, Legal); 1020b57cec5SDimitry Andric setOperationAction(ISD::FMAXIMUM, T, Legal); 1030b57cec5SDimitry Andric // WebAssembly currently has no builtin f16 support. 1040b57cec5SDimitry Andric setOperationAction(ISD::FP16_TO_FP, T, Expand); 1050b57cec5SDimitry Andric setOperationAction(ISD::FP_TO_FP16, T, Expand); 1060b57cec5SDimitry Andric setLoadExtAction(ISD::EXTLOAD, T, MVT::f16, Expand); 1070b57cec5SDimitry Andric setTruncStoreAction(T, MVT::f16, Expand); 1080b57cec5SDimitry Andric } 1090b57cec5SDimitry Andric 1100b57cec5SDimitry Andric // Expand unavailable integer operations. 1110b57cec5SDimitry Andric for (auto Op : 1120b57cec5SDimitry Andric {ISD::BSWAP, ISD::SMUL_LOHI, ISD::UMUL_LOHI, ISD::MULHS, ISD::MULHU, 1130b57cec5SDimitry Andric ISD::SDIVREM, ISD::UDIVREM, ISD::SHL_PARTS, ISD::SRA_PARTS, 1140b57cec5SDimitry Andric ISD::SRL_PARTS, ISD::ADDC, ISD::ADDE, ISD::SUBC, ISD::SUBE}) { 1150b57cec5SDimitry Andric for (auto T : {MVT::i32, MVT::i64}) 1160b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1170b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) 1180b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) 1190b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1200b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1210b57cec5SDimitry Andric setOperationAction(Op, MVT::v2i64, Expand); 1220b57cec5SDimitry Andric } 1230b57cec5SDimitry Andric 1240b57cec5SDimitry Andric // SIMD-specific configuration 1250b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 1260b57cec5SDimitry Andric // Support saturating add for i8x16 and i16x8 1270b57cec5SDimitry Andric for (auto Op : {ISD::SADDSAT, ISD::UADDSAT}) 1280b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16}) 1290b57cec5SDimitry Andric setOperationAction(Op, T, Legal); 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric // Custom lower BUILD_VECTORs to minimize number of replace_lanes 1320b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) 1330b57cec5SDimitry Andric setOperationAction(ISD::BUILD_VECTOR, T, Custom); 1340b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1350b57cec5SDimitry Andric for (auto T : {MVT::v2i64, MVT::v2f64}) 1360b57cec5SDimitry Andric setOperationAction(ISD::BUILD_VECTOR, T, Custom); 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric // We have custom shuffle lowering to expose the shuffle mask 1390b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) 1400b57cec5SDimitry Andric setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); 1410b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1420b57cec5SDimitry Andric for (auto T: {MVT::v2i64, MVT::v2f64}) 1430b57cec5SDimitry Andric setOperationAction(ISD::VECTOR_SHUFFLE, T, Custom); 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric // Custom lowering since wasm shifts must have a scalar shift amount 1460b57cec5SDimitry Andric for (auto Op : {ISD::SHL, ISD::SRA, ISD::SRL}) { 1470b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) 1480b57cec5SDimitry Andric setOperationAction(Op, T, Custom); 1490b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1500b57cec5SDimitry Andric setOperationAction(Op, MVT::v2i64, Custom); 1510b57cec5SDimitry Andric } 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric // Custom lower lane accesses to expand out variable indices 1540b57cec5SDimitry Andric for (auto Op : {ISD::EXTRACT_VECTOR_ELT, ISD::INSERT_VECTOR_ELT}) { 1550b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) 1560b57cec5SDimitry Andric setOperationAction(Op, T, Custom); 1570b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1580b57cec5SDimitry Andric for (auto T : {MVT::v2i64, MVT::v2f64}) 1590b57cec5SDimitry Andric setOperationAction(Op, T, Custom); 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric 1620b57cec5SDimitry Andric // There is no i64x2.mul instruction 1630b57cec5SDimitry Andric setOperationAction(ISD::MUL, MVT::v2i64, Expand); 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric // There are no vector select instructions 1660b57cec5SDimitry Andric for (auto Op : {ISD::VSELECT, ISD::SELECT_CC, ISD::SELECT}) { 1670b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32}) 1680b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1690b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1700b57cec5SDimitry Andric for (auto T : {MVT::v2i64, MVT::v2f64}) 1710b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1720b57cec5SDimitry Andric } 1730b57cec5SDimitry Andric 1740b57cec5SDimitry Andric // Expand integer operations supported for scalars but not SIMD 1750b57cec5SDimitry Andric for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP, ISD::SDIV, ISD::UDIV, 1760b57cec5SDimitry Andric ISD::SREM, ISD::UREM, ISD::ROTL, ISD::ROTR}) { 1770b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32}) 1780b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 1790b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1800b57cec5SDimitry Andric setOperationAction(Op, MVT::v2i64, Expand); 1810b57cec5SDimitry Andric } 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric // Expand float operations supported for scalars but not SIMD 1840b57cec5SDimitry Andric for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, 1850b57cec5SDimitry Andric ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, 1860b57cec5SDimitry Andric ISD::FEXP, ISD::FEXP2, ISD::FRINT}) { 1870b57cec5SDimitry Andric setOperationAction(Op, MVT::v4f32, Expand); 1880b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) 1890b57cec5SDimitry Andric setOperationAction(Op, MVT::v2f64, Expand); 1900b57cec5SDimitry Andric } 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric // Expand additional SIMD ops that V8 hasn't implemented yet 1930b57cec5SDimitry Andric if (!Subtarget->hasUnimplementedSIMD128()) { 1940b57cec5SDimitry Andric setOperationAction(ISD::FSQRT, MVT::v4f32, Expand); 1950b57cec5SDimitry Andric setOperationAction(ISD::FDIV, MVT::v4f32, Expand); 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric } 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric // As a special case, these operators use the type to mean the type to 2000b57cec5SDimitry Andric // sign-extend from. 2010b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); 2020b57cec5SDimitry Andric if (!Subtarget->hasSignExt()) { 2030b57cec5SDimitry Andric // Sign extends are legal only when extending a vector extract 2040b57cec5SDimitry Andric auto Action = Subtarget->hasSIMD128() ? Custom : Expand; 2050b57cec5SDimitry Andric for (auto T : {MVT::i8, MVT::i16, MVT::i32}) 2060b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, T, Action); 2070b57cec5SDimitry Andric } 208*8bcb0991SDimitry Andric for (auto T : MVT::integer_fixedlen_vector_valuetypes()) 2090b57cec5SDimitry Andric setOperationAction(ISD::SIGN_EXTEND_INREG, T, Expand); 2100b57cec5SDimitry Andric 2110b57cec5SDimitry Andric // Dynamic stack allocation: use the default expansion. 2120b57cec5SDimitry Andric setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); 2130b57cec5SDimitry Andric setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); 2140b57cec5SDimitry Andric setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand); 2150b57cec5SDimitry Andric 2160b57cec5SDimitry Andric setOperationAction(ISD::FrameIndex, MVT::i32, Custom); 2170b57cec5SDimitry Andric setOperationAction(ISD::CopyToReg, MVT::Other, Custom); 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric // Expand these forms; we pattern-match the forms that we can handle in isel. 2200b57cec5SDimitry Andric for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) 2210b57cec5SDimitry Andric for (auto Op : {ISD::BR_CC, ISD::SELECT_CC}) 2220b57cec5SDimitry Andric setOperationAction(Op, T, Expand); 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric // We have custom switch handling. 2250b57cec5SDimitry Andric setOperationAction(ISD::BR_JT, MVT::Other, Custom); 2260b57cec5SDimitry Andric 2270b57cec5SDimitry Andric // WebAssembly doesn't have: 2280b57cec5SDimitry Andric // - Floating-point extending loads. 2290b57cec5SDimitry Andric // - Floating-point truncating stores. 2300b57cec5SDimitry Andric // - i1 extending loads. 231*8bcb0991SDimitry Andric // - truncating SIMD stores and most extending loads 2320b57cec5SDimitry Andric setLoadExtAction(ISD::EXTLOAD, MVT::f64, MVT::f32, Expand); 2330b57cec5SDimitry Andric setTruncStoreAction(MVT::f64, MVT::f32, Expand); 2340b57cec5SDimitry Andric for (auto T : MVT::integer_valuetypes()) 2350b57cec5SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) 2360b57cec5SDimitry Andric setLoadExtAction(Ext, T, MVT::i1, Promote); 2370b57cec5SDimitry Andric if (Subtarget->hasSIMD128()) { 2380b57cec5SDimitry Andric for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v2i64, MVT::v4f32, 2390b57cec5SDimitry Andric MVT::v2f64}) { 240*8bcb0991SDimitry Andric for (auto MemT : MVT::fixedlen_vector_valuetypes()) { 2410b57cec5SDimitry Andric if (MVT(T) != MemT) { 2420b57cec5SDimitry Andric setTruncStoreAction(T, MemT, Expand); 2430b57cec5SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) 2440b57cec5SDimitry Andric setLoadExtAction(Ext, T, MemT, Expand); 2450b57cec5SDimitry Andric } 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric } 248*8bcb0991SDimitry Andric // But some vector extending loads are legal 249*8bcb0991SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) { 250*8bcb0991SDimitry Andric for (auto Ext : {ISD::EXTLOAD, ISD::SEXTLOAD, ISD::ZEXTLOAD}) { 251*8bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v8i16, MVT::v8i8, Legal); 252*8bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v4i32, MVT::v4i16, Legal); 253*8bcb0991SDimitry Andric setLoadExtAction(Ext, MVT::v2i64, MVT::v2i32, Legal); 254*8bcb0991SDimitry Andric } 255*8bcb0991SDimitry Andric } 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric // Don't do anything clever with build_pairs 2590b57cec5SDimitry Andric setOperationAction(ISD::BUILD_PAIR, MVT::i64, Expand); 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric // Trap lowers to wasm unreachable 2620b57cec5SDimitry Andric setOperationAction(ISD::TRAP, MVT::Other, Legal); 2630b57cec5SDimitry Andric 2640b57cec5SDimitry Andric // Exception handling intrinsics 2650b57cec5SDimitry Andric setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); 2660b57cec5SDimitry Andric setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); 2670b57cec5SDimitry Andric 2680b57cec5SDimitry Andric setMaxAtomicSizeInBitsSupported(64); 2690b57cec5SDimitry Andric 2700b57cec5SDimitry Andric // Override the __gnu_f2h_ieee/__gnu_h2f_ieee names so that the f32 name is 2710b57cec5SDimitry Andric // consistent with the f64 and f128 names. 2720b57cec5SDimitry Andric setLibcallName(RTLIB::FPEXT_F16_F32, "__extendhfsf2"); 2730b57cec5SDimitry Andric setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2"); 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric // Define the emscripten name for return address helper. 2760b57cec5SDimitry Andric // TODO: when implementing other WASM backends, make this generic or only do 2770b57cec5SDimitry Andric // this on emscripten depending on what they end up doing. 2780b57cec5SDimitry Andric setLibcallName(RTLIB::RETURN_ADDRESS, "emscripten_return_address"); 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric // Always convert switches to br_tables unless there is only one case, which 2810b57cec5SDimitry Andric // is equivalent to a simple branch. This reduces code size for wasm, and we 2820b57cec5SDimitry Andric // defer possible jump table optimizations to the VM. 2830b57cec5SDimitry Andric setMinimumJumpTableEntries(2); 2840b57cec5SDimitry Andric } 2850b57cec5SDimitry Andric 2860b57cec5SDimitry Andric TargetLowering::AtomicExpansionKind 2870b57cec5SDimitry Andric WebAssemblyTargetLowering::shouldExpandAtomicRMWInIR(AtomicRMWInst *AI) const { 2880b57cec5SDimitry Andric // We have wasm instructions for these 2890b57cec5SDimitry Andric switch (AI->getOperation()) { 2900b57cec5SDimitry Andric case AtomicRMWInst::Add: 2910b57cec5SDimitry Andric case AtomicRMWInst::Sub: 2920b57cec5SDimitry Andric case AtomicRMWInst::And: 2930b57cec5SDimitry Andric case AtomicRMWInst::Or: 2940b57cec5SDimitry Andric case AtomicRMWInst::Xor: 2950b57cec5SDimitry Andric case AtomicRMWInst::Xchg: 2960b57cec5SDimitry Andric return AtomicExpansionKind::None; 2970b57cec5SDimitry Andric default: 2980b57cec5SDimitry Andric break; 2990b57cec5SDimitry Andric } 3000b57cec5SDimitry Andric return AtomicExpansionKind::CmpXChg; 3010b57cec5SDimitry Andric } 3020b57cec5SDimitry Andric 3030b57cec5SDimitry Andric FastISel *WebAssemblyTargetLowering::createFastISel( 3040b57cec5SDimitry Andric FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const { 3050b57cec5SDimitry Andric return WebAssembly::createFastISel(FuncInfo, LibInfo); 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric 3080b57cec5SDimitry Andric MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout & /*DL*/, 3090b57cec5SDimitry Andric EVT VT) const { 3100b57cec5SDimitry Andric unsigned BitWidth = NextPowerOf2(VT.getSizeInBits() - 1); 3110b57cec5SDimitry Andric if (BitWidth > 1 && BitWidth < 8) 3120b57cec5SDimitry Andric BitWidth = 8; 3130b57cec5SDimitry Andric 3140b57cec5SDimitry Andric if (BitWidth > 64) { 3150b57cec5SDimitry Andric // The shift will be lowered to a libcall, and compiler-rt libcalls expect 3160b57cec5SDimitry Andric // the count to be an i32. 3170b57cec5SDimitry Andric BitWidth = 32; 3180b57cec5SDimitry Andric assert(BitWidth >= Log2_32_Ceil(VT.getSizeInBits()) && 3190b57cec5SDimitry Andric "32-bit shift counts ought to be enough for anyone"); 3200b57cec5SDimitry Andric } 3210b57cec5SDimitry Andric 3220b57cec5SDimitry Andric MVT Result = MVT::getIntegerVT(BitWidth); 3230b57cec5SDimitry Andric assert(Result != MVT::INVALID_SIMPLE_VALUE_TYPE && 3240b57cec5SDimitry Andric "Unable to represent scalar shift amount type"); 3250b57cec5SDimitry Andric return Result; 3260b57cec5SDimitry Andric } 3270b57cec5SDimitry Andric 3280b57cec5SDimitry Andric // Lower an fp-to-int conversion operator from the LLVM opcode, which has an 3290b57cec5SDimitry Andric // undefined result on invalid/overflow, to the WebAssembly opcode, which 3300b57cec5SDimitry Andric // traps on invalid/overflow. 3310b57cec5SDimitry Andric static MachineBasicBlock *LowerFPToInt(MachineInstr &MI, DebugLoc DL, 3320b57cec5SDimitry Andric MachineBasicBlock *BB, 3330b57cec5SDimitry Andric const TargetInstrInfo &TII, 3340b57cec5SDimitry Andric bool IsUnsigned, bool Int64, 3350b57cec5SDimitry Andric bool Float64, unsigned LoweredOpcode) { 3360b57cec5SDimitry Andric MachineRegisterInfo &MRI = BB->getParent()->getRegInfo(); 3370b57cec5SDimitry Andric 338*8bcb0991SDimitry Andric Register OutReg = MI.getOperand(0).getReg(); 339*8bcb0991SDimitry Andric Register InReg = MI.getOperand(1).getReg(); 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric unsigned Abs = Float64 ? WebAssembly::ABS_F64 : WebAssembly::ABS_F32; 3420b57cec5SDimitry Andric unsigned FConst = Float64 ? WebAssembly::CONST_F64 : WebAssembly::CONST_F32; 3430b57cec5SDimitry Andric unsigned LT = Float64 ? WebAssembly::LT_F64 : WebAssembly::LT_F32; 3440b57cec5SDimitry Andric unsigned GE = Float64 ? WebAssembly::GE_F64 : WebAssembly::GE_F32; 3450b57cec5SDimitry Andric unsigned IConst = Int64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32; 3460b57cec5SDimitry Andric unsigned Eqz = WebAssembly::EQZ_I32; 3470b57cec5SDimitry Andric unsigned And = WebAssembly::AND_I32; 3480b57cec5SDimitry Andric int64_t Limit = Int64 ? INT64_MIN : INT32_MIN; 3490b57cec5SDimitry Andric int64_t Substitute = IsUnsigned ? 0 : Limit; 3500b57cec5SDimitry Andric double CmpVal = IsUnsigned ? -(double)Limit * 2.0 : -(double)Limit; 3510b57cec5SDimitry Andric auto &Context = BB->getParent()->getFunction().getContext(); 3520b57cec5SDimitry Andric Type *Ty = Float64 ? Type::getDoubleTy(Context) : Type::getFloatTy(Context); 3530b57cec5SDimitry Andric 3540b57cec5SDimitry Andric const BasicBlock *LLVMBB = BB->getBasicBlock(); 3550b57cec5SDimitry Andric MachineFunction *F = BB->getParent(); 3560b57cec5SDimitry Andric MachineBasicBlock *TrueMBB = F->CreateMachineBasicBlock(LLVMBB); 3570b57cec5SDimitry Andric MachineBasicBlock *FalseMBB = F->CreateMachineBasicBlock(LLVMBB); 3580b57cec5SDimitry Andric MachineBasicBlock *DoneMBB = F->CreateMachineBasicBlock(LLVMBB); 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric MachineFunction::iterator It = ++BB->getIterator(); 3610b57cec5SDimitry Andric F->insert(It, FalseMBB); 3620b57cec5SDimitry Andric F->insert(It, TrueMBB); 3630b57cec5SDimitry Andric F->insert(It, DoneMBB); 3640b57cec5SDimitry Andric 3650b57cec5SDimitry Andric // Transfer the remainder of BB and its successor edges to DoneMBB. 3660b57cec5SDimitry Andric DoneMBB->splice(DoneMBB->begin(), BB, std::next(MI.getIterator()), BB->end()); 3670b57cec5SDimitry Andric DoneMBB->transferSuccessorsAndUpdatePHIs(BB); 3680b57cec5SDimitry Andric 3690b57cec5SDimitry Andric BB->addSuccessor(TrueMBB); 3700b57cec5SDimitry Andric BB->addSuccessor(FalseMBB); 3710b57cec5SDimitry Andric TrueMBB->addSuccessor(DoneMBB); 3720b57cec5SDimitry Andric FalseMBB->addSuccessor(DoneMBB); 3730b57cec5SDimitry Andric 3740b57cec5SDimitry Andric unsigned Tmp0, Tmp1, CmpReg, EqzReg, FalseReg, TrueReg; 3750b57cec5SDimitry Andric Tmp0 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 3760b57cec5SDimitry Andric Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 3770b57cec5SDimitry Andric CmpReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 3780b57cec5SDimitry Andric EqzReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 3790b57cec5SDimitry Andric FalseReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 3800b57cec5SDimitry Andric TrueReg = MRI.createVirtualRegister(MRI.getRegClass(OutReg)); 3810b57cec5SDimitry Andric 3820b57cec5SDimitry Andric MI.eraseFromParent(); 3830b57cec5SDimitry Andric // For signed numbers, we can do a single comparison to determine whether 3840b57cec5SDimitry Andric // fabs(x) is within range. 3850b57cec5SDimitry Andric if (IsUnsigned) { 3860b57cec5SDimitry Andric Tmp0 = InReg; 3870b57cec5SDimitry Andric } else { 3880b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(Abs), Tmp0).addReg(InReg); 3890b57cec5SDimitry Andric } 3900b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(FConst), Tmp1) 3910b57cec5SDimitry Andric .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, CmpVal))); 3920b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(LT), CmpReg).addReg(Tmp0).addReg(Tmp1); 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric // For unsigned numbers, we have to do a separate comparison with zero. 3950b57cec5SDimitry Andric if (IsUnsigned) { 3960b57cec5SDimitry Andric Tmp1 = MRI.createVirtualRegister(MRI.getRegClass(InReg)); 397*8bcb0991SDimitry Andric Register SecondCmpReg = 3980b57cec5SDimitry Andric MRI.createVirtualRegister(&WebAssembly::I32RegClass); 399*8bcb0991SDimitry Andric Register AndReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 4000b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(FConst), Tmp1) 4010b57cec5SDimitry Andric .addFPImm(cast<ConstantFP>(ConstantFP::get(Ty, 0.0))); 4020b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(GE), SecondCmpReg).addReg(Tmp0).addReg(Tmp1); 4030b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(And), AndReg).addReg(CmpReg).addReg(SecondCmpReg); 4040b57cec5SDimitry Andric CmpReg = AndReg; 4050b57cec5SDimitry Andric } 4060b57cec5SDimitry Andric 4070b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(Eqz), EqzReg).addReg(CmpReg); 4080b57cec5SDimitry Andric 4090b57cec5SDimitry Andric // Create the CFG diamond to select between doing the conversion or using 4100b57cec5SDimitry Andric // the substitute value. 4110b57cec5SDimitry Andric BuildMI(BB, DL, TII.get(WebAssembly::BR_IF)).addMBB(TrueMBB).addReg(EqzReg); 4120b57cec5SDimitry Andric BuildMI(FalseMBB, DL, TII.get(LoweredOpcode), FalseReg).addReg(InReg); 4130b57cec5SDimitry Andric BuildMI(FalseMBB, DL, TII.get(WebAssembly::BR)).addMBB(DoneMBB); 4140b57cec5SDimitry Andric BuildMI(TrueMBB, DL, TII.get(IConst), TrueReg).addImm(Substitute); 4150b57cec5SDimitry Andric BuildMI(*DoneMBB, DoneMBB->begin(), DL, TII.get(TargetOpcode::PHI), OutReg) 4160b57cec5SDimitry Andric .addReg(FalseReg) 4170b57cec5SDimitry Andric .addMBB(FalseMBB) 4180b57cec5SDimitry Andric .addReg(TrueReg) 4190b57cec5SDimitry Andric .addMBB(TrueMBB); 4200b57cec5SDimitry Andric 4210b57cec5SDimitry Andric return DoneMBB; 4220b57cec5SDimitry Andric } 4230b57cec5SDimitry Andric 4240b57cec5SDimitry Andric MachineBasicBlock *WebAssemblyTargetLowering::EmitInstrWithCustomInserter( 4250b57cec5SDimitry Andric MachineInstr &MI, MachineBasicBlock *BB) const { 4260b57cec5SDimitry Andric const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); 4270b57cec5SDimitry Andric DebugLoc DL = MI.getDebugLoc(); 4280b57cec5SDimitry Andric 4290b57cec5SDimitry Andric switch (MI.getOpcode()) { 4300b57cec5SDimitry Andric default: 4310b57cec5SDimitry Andric llvm_unreachable("Unexpected instr type to insert"); 4320b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I32_F32: 4330b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, false, false, 4340b57cec5SDimitry Andric WebAssembly::I32_TRUNC_S_F32); 4350b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I32_F32: 4360b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, false, false, 4370b57cec5SDimitry Andric WebAssembly::I32_TRUNC_U_F32); 4380b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I64_F32: 4390b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, true, false, 4400b57cec5SDimitry Andric WebAssembly::I64_TRUNC_S_F32); 4410b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I64_F32: 4420b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, true, false, 4430b57cec5SDimitry Andric WebAssembly::I64_TRUNC_U_F32); 4440b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I32_F64: 4450b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, false, true, 4460b57cec5SDimitry Andric WebAssembly::I32_TRUNC_S_F64); 4470b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I32_F64: 4480b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, false, true, 4490b57cec5SDimitry Andric WebAssembly::I32_TRUNC_U_F64); 4500b57cec5SDimitry Andric case WebAssembly::FP_TO_SINT_I64_F64: 4510b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, false, true, true, 4520b57cec5SDimitry Andric WebAssembly::I64_TRUNC_S_F64); 4530b57cec5SDimitry Andric case WebAssembly::FP_TO_UINT_I64_F64: 4540b57cec5SDimitry Andric return LowerFPToInt(MI, DL, BB, TII, true, true, true, 4550b57cec5SDimitry Andric WebAssembly::I64_TRUNC_U_F64); 4560b57cec5SDimitry Andric llvm_unreachable("Unexpected instruction to emit with custom inserter"); 4570b57cec5SDimitry Andric } 4580b57cec5SDimitry Andric } 4590b57cec5SDimitry Andric 4600b57cec5SDimitry Andric const char * 4610b57cec5SDimitry Andric WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { 4620b57cec5SDimitry Andric switch (static_cast<WebAssemblyISD::NodeType>(Opcode)) { 4630b57cec5SDimitry Andric case WebAssemblyISD::FIRST_NUMBER: 4640b57cec5SDimitry Andric break; 4650b57cec5SDimitry Andric #define HANDLE_NODETYPE(NODE) \ 4660b57cec5SDimitry Andric case WebAssemblyISD::NODE: \ 4670b57cec5SDimitry Andric return "WebAssemblyISD::" #NODE; 4680b57cec5SDimitry Andric #include "WebAssemblyISD.def" 4690b57cec5SDimitry Andric #undef HANDLE_NODETYPE 4700b57cec5SDimitry Andric } 4710b57cec5SDimitry Andric return nullptr; 4720b57cec5SDimitry Andric } 4730b57cec5SDimitry Andric 4740b57cec5SDimitry Andric std::pair<unsigned, const TargetRegisterClass *> 4750b57cec5SDimitry Andric WebAssemblyTargetLowering::getRegForInlineAsmConstraint( 4760b57cec5SDimitry Andric const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { 4770b57cec5SDimitry Andric // First, see if this is a constraint that directly corresponds to a 4780b57cec5SDimitry Andric // WebAssembly register class. 4790b57cec5SDimitry Andric if (Constraint.size() == 1) { 4800b57cec5SDimitry Andric switch (Constraint[0]) { 4810b57cec5SDimitry Andric case 'r': 4820b57cec5SDimitry Andric assert(VT != MVT::iPTR && "Pointer MVT not expected here"); 4830b57cec5SDimitry Andric if (Subtarget->hasSIMD128() && VT.isVector()) { 4840b57cec5SDimitry Andric if (VT.getSizeInBits() == 128) 4850b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::V128RegClass); 4860b57cec5SDimitry Andric } 4870b57cec5SDimitry Andric if (VT.isInteger() && !VT.isVector()) { 4880b57cec5SDimitry Andric if (VT.getSizeInBits() <= 32) 4890b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::I32RegClass); 4900b57cec5SDimitry Andric if (VT.getSizeInBits() <= 64) 4910b57cec5SDimitry Andric return std::make_pair(0U, &WebAssembly::I64RegClass); 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric break; 4940b57cec5SDimitry Andric default: 4950b57cec5SDimitry Andric break; 4960b57cec5SDimitry Andric } 4970b57cec5SDimitry Andric } 4980b57cec5SDimitry Andric 4990b57cec5SDimitry Andric return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); 5000b57cec5SDimitry Andric } 5010b57cec5SDimitry Andric 5020b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isCheapToSpeculateCttz() const { 5030b57cec5SDimitry Andric // Assume ctz is a relatively cheap operation. 5040b57cec5SDimitry Andric return true; 5050b57cec5SDimitry Andric } 5060b57cec5SDimitry Andric 5070b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isCheapToSpeculateCtlz() const { 5080b57cec5SDimitry Andric // Assume clz is a relatively cheap operation. 5090b57cec5SDimitry Andric return true; 5100b57cec5SDimitry Andric } 5110b57cec5SDimitry Andric 5120b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isLegalAddressingMode(const DataLayout &DL, 5130b57cec5SDimitry Andric const AddrMode &AM, 5140b57cec5SDimitry Andric Type *Ty, unsigned AS, 5150b57cec5SDimitry Andric Instruction *I) const { 5160b57cec5SDimitry Andric // WebAssembly offsets are added as unsigned without wrapping. The 5170b57cec5SDimitry Andric // isLegalAddressingMode gives us no way to determine if wrapping could be 5180b57cec5SDimitry Andric // happening, so we approximate this by accepting only non-negative offsets. 5190b57cec5SDimitry Andric if (AM.BaseOffs < 0) 5200b57cec5SDimitry Andric return false; 5210b57cec5SDimitry Andric 5220b57cec5SDimitry Andric // WebAssembly has no scale register operands. 5230b57cec5SDimitry Andric if (AM.Scale != 0) 5240b57cec5SDimitry Andric return false; 5250b57cec5SDimitry Andric 5260b57cec5SDimitry Andric // Everything else is legal. 5270b57cec5SDimitry Andric return true; 5280b57cec5SDimitry Andric } 5290b57cec5SDimitry Andric 5300b57cec5SDimitry Andric bool WebAssemblyTargetLowering::allowsMisalignedMemoryAccesses( 5310b57cec5SDimitry Andric EVT /*VT*/, unsigned /*AddrSpace*/, unsigned /*Align*/, 5320b57cec5SDimitry Andric MachineMemOperand::Flags /*Flags*/, bool *Fast) const { 5330b57cec5SDimitry Andric // WebAssembly supports unaligned accesses, though it should be declared 5340b57cec5SDimitry Andric // with the p2align attribute on loads and stores which do so, and there 5350b57cec5SDimitry Andric // may be a performance impact. We tell LLVM they're "fast" because 5360b57cec5SDimitry Andric // for the kinds of things that LLVM uses this for (merging adjacent stores 5370b57cec5SDimitry Andric // of constants, etc.), WebAssembly implementations will either want the 5380b57cec5SDimitry Andric // unaligned access or they'll split anyway. 5390b57cec5SDimitry Andric if (Fast) 5400b57cec5SDimitry Andric *Fast = true; 5410b57cec5SDimitry Andric return true; 5420b57cec5SDimitry Andric } 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, 5450b57cec5SDimitry Andric AttributeList Attr) const { 5460b57cec5SDimitry Andric // The current thinking is that wasm engines will perform this optimization, 5470b57cec5SDimitry Andric // so we can save on code size. 5480b57cec5SDimitry Andric return true; 5490b57cec5SDimitry Andric } 5500b57cec5SDimitry Andric 551*8bcb0991SDimitry Andric bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { 552*8bcb0991SDimitry Andric if (!Subtarget->hasUnimplementedSIMD128()) 553*8bcb0991SDimitry Andric return false; 554*8bcb0991SDimitry Andric MVT ExtT = ExtVal.getSimpleValueType(); 555*8bcb0991SDimitry Andric MVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getSimpleValueType(0); 556*8bcb0991SDimitry Andric return (ExtT == MVT::v8i16 && MemT == MVT::v8i8) || 557*8bcb0991SDimitry Andric (ExtT == MVT::v4i32 && MemT == MVT::v4i16) || 558*8bcb0991SDimitry Andric (ExtT == MVT::v2i64 && MemT == MVT::v2i32); 559*8bcb0991SDimitry Andric } 560*8bcb0991SDimitry Andric 5610b57cec5SDimitry Andric EVT WebAssemblyTargetLowering::getSetCCResultType(const DataLayout &DL, 5620b57cec5SDimitry Andric LLVMContext &C, 5630b57cec5SDimitry Andric EVT VT) const { 5640b57cec5SDimitry Andric if (VT.isVector()) 5650b57cec5SDimitry Andric return VT.changeVectorElementTypeToInteger(); 5660b57cec5SDimitry Andric 5670b57cec5SDimitry Andric return TargetLowering::getSetCCResultType(DL, C, VT); 5680b57cec5SDimitry Andric } 5690b57cec5SDimitry Andric 5700b57cec5SDimitry Andric bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, 5710b57cec5SDimitry Andric const CallInst &I, 5720b57cec5SDimitry Andric MachineFunction &MF, 5730b57cec5SDimitry Andric unsigned Intrinsic) const { 5740b57cec5SDimitry Andric switch (Intrinsic) { 5750b57cec5SDimitry Andric case Intrinsic::wasm_atomic_notify: 5760b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 5770b57cec5SDimitry Andric Info.memVT = MVT::i32; 5780b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 5790b57cec5SDimitry Andric Info.offset = 0; 580*8bcb0991SDimitry Andric Info.align = Align(4); 5810b57cec5SDimitry Andric // atomic.notify instruction does not really load the memory specified with 5820b57cec5SDimitry Andric // this argument, but MachineMemOperand should either be load or store, so 5830b57cec5SDimitry Andric // we set this to a load. 5840b57cec5SDimitry Andric // FIXME Volatile isn't really correct, but currently all LLVM atomic 5850b57cec5SDimitry Andric // instructions are treated as volatiles in the backend, so we should be 5860b57cec5SDimitry Andric // consistent. The same applies for wasm_atomic_wait intrinsics too. 5870b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 5880b57cec5SDimitry Andric return true; 5890b57cec5SDimitry Andric case Intrinsic::wasm_atomic_wait_i32: 5900b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 5910b57cec5SDimitry Andric Info.memVT = MVT::i32; 5920b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 5930b57cec5SDimitry Andric Info.offset = 0; 594*8bcb0991SDimitry Andric Info.align = Align(4); 5950b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 5960b57cec5SDimitry Andric return true; 5970b57cec5SDimitry Andric case Intrinsic::wasm_atomic_wait_i64: 5980b57cec5SDimitry Andric Info.opc = ISD::INTRINSIC_W_CHAIN; 5990b57cec5SDimitry Andric Info.memVT = MVT::i64; 6000b57cec5SDimitry Andric Info.ptrVal = I.getArgOperand(0); 6010b57cec5SDimitry Andric Info.offset = 0; 602*8bcb0991SDimitry Andric Info.align = Align(8); 6030b57cec5SDimitry Andric Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; 6040b57cec5SDimitry Andric return true; 6050b57cec5SDimitry Andric default: 6060b57cec5SDimitry Andric return false; 6070b57cec5SDimitry Andric } 6080b57cec5SDimitry Andric } 6090b57cec5SDimitry Andric 6100b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6110b57cec5SDimitry Andric // WebAssembly Lowering private implementation. 6120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6130b57cec5SDimitry Andric 6140b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6150b57cec5SDimitry Andric // Lowering Code 6160b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric static void fail(const SDLoc &DL, SelectionDAG &DAG, const char *Msg) { 6190b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 6200b57cec5SDimitry Andric DAG.getContext()->diagnose( 6210b57cec5SDimitry Andric DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); 6220b57cec5SDimitry Andric } 6230b57cec5SDimitry Andric 6240b57cec5SDimitry Andric // Test whether the given calling convention is supported. 6250b57cec5SDimitry Andric static bool callingConvSupported(CallingConv::ID CallConv) { 6260b57cec5SDimitry Andric // We currently support the language-independent target-independent 6270b57cec5SDimitry Andric // conventions. We don't yet have a way to annotate calls with properties like 6280b57cec5SDimitry Andric // "cold", and we don't have any call-clobbered registers, so these are mostly 6290b57cec5SDimitry Andric // all handled the same. 6300b57cec5SDimitry Andric return CallConv == CallingConv::C || CallConv == CallingConv::Fast || 6310b57cec5SDimitry Andric CallConv == CallingConv::Cold || 6320b57cec5SDimitry Andric CallConv == CallingConv::PreserveMost || 6330b57cec5SDimitry Andric CallConv == CallingConv::PreserveAll || 634*8bcb0991SDimitry Andric CallConv == CallingConv::CXX_FAST_TLS || 635*8bcb0991SDimitry Andric CallConv == CallingConv::WASM_EmscriptenInvoke; 6360b57cec5SDimitry Andric } 6370b57cec5SDimitry Andric 6380b57cec5SDimitry Andric SDValue 6390b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, 6400b57cec5SDimitry Andric SmallVectorImpl<SDValue> &InVals) const { 6410b57cec5SDimitry Andric SelectionDAG &DAG = CLI.DAG; 6420b57cec5SDimitry Andric SDLoc DL = CLI.DL; 6430b57cec5SDimitry Andric SDValue Chain = CLI.Chain; 6440b57cec5SDimitry Andric SDValue Callee = CLI.Callee; 6450b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 6460b57cec5SDimitry Andric auto Layout = MF.getDataLayout(); 6470b57cec5SDimitry Andric 6480b57cec5SDimitry Andric CallingConv::ID CallConv = CLI.CallConv; 6490b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 6500b57cec5SDimitry Andric fail(DL, DAG, 6510b57cec5SDimitry Andric "WebAssembly doesn't support language-specific or target-specific " 6520b57cec5SDimitry Andric "calling conventions yet"); 6530b57cec5SDimitry Andric if (CLI.IsPatchPoint) 6540b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support patch point yet"); 6550b57cec5SDimitry Andric 656*8bcb0991SDimitry Andric if (CLI.IsTailCall) { 657*8bcb0991SDimitry Andric bool MustTail = CLI.CS && CLI.CS.isMustTailCall(); 658*8bcb0991SDimitry Andric if (Subtarget->hasTailCall() && !CLI.IsVarArg) { 659*8bcb0991SDimitry Andric // Do not tail call unless caller and callee return types match 660*8bcb0991SDimitry Andric const Function &F = MF.getFunction(); 661*8bcb0991SDimitry Andric const TargetMachine &TM = getTargetMachine(); 662*8bcb0991SDimitry Andric Type *RetTy = F.getReturnType(); 663*8bcb0991SDimitry Andric SmallVector<MVT, 4> CallerRetTys; 664*8bcb0991SDimitry Andric SmallVector<MVT, 4> CalleeRetTys; 665*8bcb0991SDimitry Andric computeLegalValueVTs(F, TM, RetTy, CallerRetTys); 666*8bcb0991SDimitry Andric computeLegalValueVTs(F, TM, CLI.RetTy, CalleeRetTys); 667*8bcb0991SDimitry Andric bool TypesMatch = CallerRetTys.size() == CalleeRetTys.size() && 668*8bcb0991SDimitry Andric std::equal(CallerRetTys.begin(), CallerRetTys.end(), 669*8bcb0991SDimitry Andric CalleeRetTys.begin()); 670*8bcb0991SDimitry Andric if (!TypesMatch) { 671*8bcb0991SDimitry Andric // musttail in this case would be an LLVM IR validation failure 672*8bcb0991SDimitry Andric assert(!MustTail); 6730b57cec5SDimitry Andric CLI.IsTailCall = false; 6740b57cec5SDimitry Andric } 675*8bcb0991SDimitry Andric } else { 676*8bcb0991SDimitry Andric CLI.IsTailCall = false; 677*8bcb0991SDimitry Andric if (MustTail) { 678*8bcb0991SDimitry Andric if (CLI.IsVarArg) { 679*8bcb0991SDimitry Andric // The return would pop the argument buffer 680*8bcb0991SDimitry Andric fail(DL, DAG, "WebAssembly does not support varargs tail calls"); 681*8bcb0991SDimitry Andric } else { 682*8bcb0991SDimitry Andric fail(DL, DAG, "WebAssembly 'tail-call' feature not enabled"); 683*8bcb0991SDimitry Andric } 684*8bcb0991SDimitry Andric } 685*8bcb0991SDimitry Andric } 686*8bcb0991SDimitry Andric } 6870b57cec5SDimitry Andric 6880b57cec5SDimitry Andric SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins; 6890b57cec5SDimitry Andric if (Ins.size() > 1) 6900b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); 6910b57cec5SDimitry Andric 6920b57cec5SDimitry Andric SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs; 6930b57cec5SDimitry Andric SmallVectorImpl<SDValue> &OutVals = CLI.OutVals; 694*8bcb0991SDimitry Andric 695*8bcb0991SDimitry Andric // The generic code may have added an sret argument. If we're lowering an 696*8bcb0991SDimitry Andric // invoke function, the ABI requires that the function pointer be the first 697*8bcb0991SDimitry Andric // argument, so we may have to swap the arguments. 698*8bcb0991SDimitry Andric if (CallConv == CallingConv::WASM_EmscriptenInvoke && Outs.size() >= 2 && 699*8bcb0991SDimitry Andric Outs[0].Flags.isSRet()) { 700*8bcb0991SDimitry Andric std::swap(Outs[0], Outs[1]); 701*8bcb0991SDimitry Andric std::swap(OutVals[0], OutVals[1]); 702*8bcb0991SDimitry Andric } 703*8bcb0991SDimitry Andric 7040b57cec5SDimitry Andric unsigned NumFixedArgs = 0; 7050b57cec5SDimitry Andric for (unsigned I = 0; I < Outs.size(); ++I) { 7060b57cec5SDimitry Andric const ISD::OutputArg &Out = Outs[I]; 7070b57cec5SDimitry Andric SDValue &OutVal = OutVals[I]; 7080b57cec5SDimitry Andric if (Out.Flags.isNest()) 7090b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 7100b57cec5SDimitry Andric if (Out.Flags.isInAlloca()) 7110b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 7120b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegs()) 7130b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 7140b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegsLast()) 7150b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 7160b57cec5SDimitry Andric if (Out.Flags.isByVal() && Out.Flags.getByValSize() != 0) { 7170b57cec5SDimitry Andric auto &MFI = MF.getFrameInfo(); 7180b57cec5SDimitry Andric int FI = MFI.CreateStackObject(Out.Flags.getByValSize(), 7190b57cec5SDimitry Andric Out.Flags.getByValAlign(), 7200b57cec5SDimitry Andric /*isSS=*/false); 7210b57cec5SDimitry Andric SDValue SizeNode = 7220b57cec5SDimitry Andric DAG.getConstant(Out.Flags.getByValSize(), DL, MVT::i32); 7230b57cec5SDimitry Andric SDValue FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 7240b57cec5SDimitry Andric Chain = DAG.getMemcpy( 7250b57cec5SDimitry Andric Chain, DL, FINode, OutVal, SizeNode, Out.Flags.getByValAlign(), 7260b57cec5SDimitry Andric /*isVolatile*/ false, /*AlwaysInline=*/false, 7270b57cec5SDimitry Andric /*isTailCall*/ false, MachinePointerInfo(), MachinePointerInfo()); 7280b57cec5SDimitry Andric OutVal = FINode; 7290b57cec5SDimitry Andric } 7300b57cec5SDimitry Andric // Count the number of fixed args *after* legalization. 7310b57cec5SDimitry Andric NumFixedArgs += Out.IsFixed; 7320b57cec5SDimitry Andric } 7330b57cec5SDimitry Andric 7340b57cec5SDimitry Andric bool IsVarArg = CLI.IsVarArg; 7350b57cec5SDimitry Andric auto PtrVT = getPointerTy(Layout); 7360b57cec5SDimitry Andric 7370b57cec5SDimitry Andric // Analyze operands of the call, assigning locations to each operand. 7380b57cec5SDimitry Andric SmallVector<CCValAssign, 16> ArgLocs; 7390b57cec5SDimitry Andric CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); 7400b57cec5SDimitry Andric 7410b57cec5SDimitry Andric if (IsVarArg) { 7420b57cec5SDimitry Andric // Outgoing non-fixed arguments are placed in a buffer. First 7430b57cec5SDimitry Andric // compute their offsets and the total amount of buffer space needed. 7440b57cec5SDimitry Andric for (unsigned I = NumFixedArgs; I < Outs.size(); ++I) { 7450b57cec5SDimitry Andric const ISD::OutputArg &Out = Outs[I]; 7460b57cec5SDimitry Andric SDValue &Arg = OutVals[I]; 7470b57cec5SDimitry Andric EVT VT = Arg.getValueType(); 7480b57cec5SDimitry Andric assert(VT != MVT::iPTR && "Legalized args should be concrete"); 7490b57cec5SDimitry Andric Type *Ty = VT.getTypeForEVT(*DAG.getContext()); 7500b57cec5SDimitry Andric unsigned Align = std::max(Out.Flags.getOrigAlign(), 7510b57cec5SDimitry Andric Layout.getABITypeAlignment(Ty)); 7520b57cec5SDimitry Andric unsigned Offset = CCInfo.AllocateStack(Layout.getTypeAllocSize(Ty), 7530b57cec5SDimitry Andric Align); 7540b57cec5SDimitry Andric CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(), 7550b57cec5SDimitry Andric Offset, VT.getSimpleVT(), 7560b57cec5SDimitry Andric CCValAssign::Full)); 7570b57cec5SDimitry Andric } 7580b57cec5SDimitry Andric } 7590b57cec5SDimitry Andric 7600b57cec5SDimitry Andric unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); 7610b57cec5SDimitry Andric 7620b57cec5SDimitry Andric SDValue FINode; 7630b57cec5SDimitry Andric if (IsVarArg && NumBytes) { 7640b57cec5SDimitry Andric // For non-fixed arguments, next emit stores to store the argument values 7650b57cec5SDimitry Andric // to the stack buffer at the offsets computed above. 7660b57cec5SDimitry Andric int FI = MF.getFrameInfo().CreateStackObject(NumBytes, 7670b57cec5SDimitry Andric Layout.getStackAlignment(), 7680b57cec5SDimitry Andric /*isSS=*/false); 7690b57cec5SDimitry Andric unsigned ValNo = 0; 7700b57cec5SDimitry Andric SmallVector<SDValue, 8> Chains; 7710b57cec5SDimitry Andric for (SDValue Arg : 7720b57cec5SDimitry Andric make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { 7730b57cec5SDimitry Andric assert(ArgLocs[ValNo].getValNo() == ValNo && 7740b57cec5SDimitry Andric "ArgLocs should remain in order and only hold varargs args"); 7750b57cec5SDimitry Andric unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); 7760b57cec5SDimitry Andric FINode = DAG.getFrameIndex(FI, getPointerTy(Layout)); 7770b57cec5SDimitry Andric SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode, 7780b57cec5SDimitry Andric DAG.getConstant(Offset, DL, PtrVT)); 7790b57cec5SDimitry Andric Chains.push_back( 7800b57cec5SDimitry Andric DAG.getStore(Chain, DL, Arg, Add, 7810b57cec5SDimitry Andric MachinePointerInfo::getFixedStack(MF, FI, Offset), 0)); 7820b57cec5SDimitry Andric } 7830b57cec5SDimitry Andric if (!Chains.empty()) 7840b57cec5SDimitry Andric Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); 7850b57cec5SDimitry Andric } else if (IsVarArg) { 7860b57cec5SDimitry Andric FINode = DAG.getIntPtrConstant(0, DL); 7870b57cec5SDimitry Andric } 7880b57cec5SDimitry Andric 7890b57cec5SDimitry Andric if (Callee->getOpcode() == ISD::GlobalAddress) { 7900b57cec5SDimitry Andric // If the callee is a GlobalAddress node (quite common, every direct call 7910b57cec5SDimitry Andric // is) turn it into a TargetGlobalAddress node so that LowerGlobalAddress 7920b57cec5SDimitry Andric // doesn't at MO_GOT which is not needed for direct calls. 7930b57cec5SDimitry Andric GlobalAddressSDNode* GA = cast<GlobalAddressSDNode>(Callee); 7940b57cec5SDimitry Andric Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, 7950b57cec5SDimitry Andric getPointerTy(DAG.getDataLayout()), 7960b57cec5SDimitry Andric GA->getOffset()); 7970b57cec5SDimitry Andric Callee = DAG.getNode(WebAssemblyISD::Wrapper, DL, 7980b57cec5SDimitry Andric getPointerTy(DAG.getDataLayout()), Callee); 7990b57cec5SDimitry Andric } 8000b57cec5SDimitry Andric 8010b57cec5SDimitry Andric // Compute the operands for the CALLn node. 8020b57cec5SDimitry Andric SmallVector<SDValue, 16> Ops; 8030b57cec5SDimitry Andric Ops.push_back(Chain); 8040b57cec5SDimitry Andric Ops.push_back(Callee); 8050b57cec5SDimitry Andric 8060b57cec5SDimitry Andric // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs 8070b57cec5SDimitry Andric // isn't reliable. 8080b57cec5SDimitry Andric Ops.append(OutVals.begin(), 8090b57cec5SDimitry Andric IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); 8100b57cec5SDimitry Andric // Add a pointer to the vararg buffer. 8110b57cec5SDimitry Andric if (IsVarArg) 8120b57cec5SDimitry Andric Ops.push_back(FINode); 8130b57cec5SDimitry Andric 8140b57cec5SDimitry Andric SmallVector<EVT, 8> InTys; 8150b57cec5SDimitry Andric for (const auto &In : Ins) { 8160b57cec5SDimitry Andric assert(!In.Flags.isByVal() && "byval is not valid for return values"); 8170b57cec5SDimitry Andric assert(!In.Flags.isNest() && "nest is not valid for return values"); 8180b57cec5SDimitry Andric if (In.Flags.isInAlloca()) 8190b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca return values"); 8200b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegs()) 8210b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs return values"); 8220b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegsLast()) 8230b57cec5SDimitry Andric fail(DL, DAG, 8240b57cec5SDimitry Andric "WebAssembly hasn't implemented cons regs last return values"); 8250b57cec5SDimitry Andric // Ignore In.getOrigAlign() because all our arguments are passed in 8260b57cec5SDimitry Andric // registers. 8270b57cec5SDimitry Andric InTys.push_back(In.VT); 8280b57cec5SDimitry Andric } 8290b57cec5SDimitry Andric 8300b57cec5SDimitry Andric if (CLI.IsTailCall) { 8310b57cec5SDimitry Andric // ret_calls do not return values to the current frame 8320b57cec5SDimitry Andric SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); 8330b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::RET_CALL, DL, NodeTys, Ops); 8340b57cec5SDimitry Andric } 8350b57cec5SDimitry Andric 8360b57cec5SDimitry Andric InTys.push_back(MVT::Other); 8370b57cec5SDimitry Andric SDVTList InTyList = DAG.getVTList(InTys); 8380b57cec5SDimitry Andric SDValue Res = 8390b57cec5SDimitry Andric DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1, 8400b57cec5SDimitry Andric DL, InTyList, Ops); 8410b57cec5SDimitry Andric if (Ins.empty()) { 8420b57cec5SDimitry Andric Chain = Res; 8430b57cec5SDimitry Andric } else { 8440b57cec5SDimitry Andric InVals.push_back(Res); 8450b57cec5SDimitry Andric Chain = Res.getValue(1); 8460b57cec5SDimitry Andric } 8470b57cec5SDimitry Andric 8480b57cec5SDimitry Andric return Chain; 8490b57cec5SDimitry Andric } 8500b57cec5SDimitry Andric 8510b57cec5SDimitry Andric bool WebAssemblyTargetLowering::CanLowerReturn( 8520b57cec5SDimitry Andric CallingConv::ID /*CallConv*/, MachineFunction & /*MF*/, bool /*IsVarArg*/, 8530b57cec5SDimitry Andric const SmallVectorImpl<ISD::OutputArg> &Outs, 8540b57cec5SDimitry Andric LLVMContext & /*Context*/) const { 855*8bcb0991SDimitry Andric // WebAssembly can only handle returning tuples with multivalue enabled 856*8bcb0991SDimitry Andric return Subtarget->hasMultivalue() || Outs.size() <= 1; 8570b57cec5SDimitry Andric } 8580b57cec5SDimitry Andric 8590b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerReturn( 8600b57cec5SDimitry Andric SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, 8610b57cec5SDimitry Andric const SmallVectorImpl<ISD::OutputArg> &Outs, 8620b57cec5SDimitry Andric const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL, 8630b57cec5SDimitry Andric SelectionDAG &DAG) const { 864*8bcb0991SDimitry Andric assert((Subtarget->hasMultivalue() || Outs.size() <= 1) && 865*8bcb0991SDimitry Andric "MVP WebAssembly can only return up to one value"); 8660b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 8670b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 8680b57cec5SDimitry Andric 8690b57cec5SDimitry Andric SmallVector<SDValue, 4> RetOps(1, Chain); 8700b57cec5SDimitry Andric RetOps.append(OutVals.begin(), OutVals.end()); 8710b57cec5SDimitry Andric Chain = DAG.getNode(WebAssemblyISD::RETURN, DL, MVT::Other, RetOps); 8720b57cec5SDimitry Andric 8730b57cec5SDimitry Andric // Record the number and types of the return values. 8740b57cec5SDimitry Andric for (const ISD::OutputArg &Out : Outs) { 8750b57cec5SDimitry Andric assert(!Out.Flags.isByVal() && "byval is not valid for return values"); 8760b57cec5SDimitry Andric assert(!Out.Flags.isNest() && "nest is not valid for return values"); 8770b57cec5SDimitry Andric assert(Out.IsFixed && "non-fixed return value is not valid"); 8780b57cec5SDimitry Andric if (Out.Flags.isInAlloca()) 8790b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca results"); 8800b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegs()) 8810b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs results"); 8820b57cec5SDimitry Andric if (Out.Flags.isInConsecutiveRegsLast()) 8830b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results"); 8840b57cec5SDimitry Andric } 8850b57cec5SDimitry Andric 8860b57cec5SDimitry Andric return Chain; 8870b57cec5SDimitry Andric } 8880b57cec5SDimitry Andric 8890b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFormalArguments( 8900b57cec5SDimitry Andric SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, 8910b57cec5SDimitry Andric const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL, 8920b57cec5SDimitry Andric SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const { 8930b57cec5SDimitry Andric if (!callingConvSupported(CallConv)) 8940b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); 8950b57cec5SDimitry Andric 8960b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 8970b57cec5SDimitry Andric auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>(); 8980b57cec5SDimitry Andric 8990b57cec5SDimitry Andric // Set up the incoming ARGUMENTS value, which serves to represent the liveness 9000b57cec5SDimitry Andric // of the incoming values before they're represented by virtual registers. 9010b57cec5SDimitry Andric MF.getRegInfo().addLiveIn(WebAssembly::ARGUMENTS); 9020b57cec5SDimitry Andric 9030b57cec5SDimitry Andric for (const ISD::InputArg &In : Ins) { 9040b57cec5SDimitry Andric if (In.Flags.isInAlloca()) 9050b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); 9060b57cec5SDimitry Andric if (In.Flags.isNest()) 9070b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); 9080b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegs()) 9090b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); 9100b57cec5SDimitry Andric if (In.Flags.isInConsecutiveRegsLast()) 9110b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); 9120b57cec5SDimitry Andric // Ignore In.getOrigAlign() because all our arguments are passed in 9130b57cec5SDimitry Andric // registers. 9140b57cec5SDimitry Andric InVals.push_back(In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, 9150b57cec5SDimitry Andric DAG.getTargetConstant(InVals.size(), 9160b57cec5SDimitry Andric DL, MVT::i32)) 9170b57cec5SDimitry Andric : DAG.getUNDEF(In.VT)); 9180b57cec5SDimitry Andric 9190b57cec5SDimitry Andric // Record the number and types of arguments. 9200b57cec5SDimitry Andric MFI->addParam(In.VT); 9210b57cec5SDimitry Andric } 9220b57cec5SDimitry Andric 9230b57cec5SDimitry Andric // Varargs are copied into a buffer allocated by the caller, and a pointer to 9240b57cec5SDimitry Andric // the buffer is passed as an argument. 9250b57cec5SDimitry Andric if (IsVarArg) { 9260b57cec5SDimitry Andric MVT PtrVT = getPointerTy(MF.getDataLayout()); 927*8bcb0991SDimitry Andric Register VarargVreg = 9280b57cec5SDimitry Andric MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); 9290b57cec5SDimitry Andric MFI->setVarargBufferVreg(VarargVreg); 9300b57cec5SDimitry Andric Chain = DAG.getCopyToReg( 9310b57cec5SDimitry Andric Chain, DL, VarargVreg, 9320b57cec5SDimitry Andric DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, 9330b57cec5SDimitry Andric DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); 9340b57cec5SDimitry Andric MFI->addParam(PtrVT); 9350b57cec5SDimitry Andric } 9360b57cec5SDimitry Andric 9370b57cec5SDimitry Andric // Record the number and types of arguments and results. 9380b57cec5SDimitry Andric SmallVector<MVT, 4> Params; 9390b57cec5SDimitry Andric SmallVector<MVT, 4> Results; 9400b57cec5SDimitry Andric computeSignatureVTs(MF.getFunction().getFunctionType(), MF.getFunction(), 9410b57cec5SDimitry Andric DAG.getTarget(), Params, Results); 9420b57cec5SDimitry Andric for (MVT VT : Results) 9430b57cec5SDimitry Andric MFI->addResult(VT); 9440b57cec5SDimitry Andric // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify 9450b57cec5SDimitry Andric // the param logic here with ComputeSignatureVTs 9460b57cec5SDimitry Andric assert(MFI->getParams().size() == Params.size() && 9470b57cec5SDimitry Andric std::equal(MFI->getParams().begin(), MFI->getParams().end(), 9480b57cec5SDimitry Andric Params.begin())); 9490b57cec5SDimitry Andric 9500b57cec5SDimitry Andric return Chain; 9510b57cec5SDimitry Andric } 9520b57cec5SDimitry Andric 9530b57cec5SDimitry Andric void WebAssemblyTargetLowering::ReplaceNodeResults( 9540b57cec5SDimitry Andric SDNode *N, SmallVectorImpl<SDValue> &Results, SelectionDAG &DAG) const { 9550b57cec5SDimitry Andric switch (N->getOpcode()) { 9560b57cec5SDimitry Andric case ISD::SIGN_EXTEND_INREG: 9570b57cec5SDimitry Andric // Do not add any results, signifying that N should not be custom lowered 9580b57cec5SDimitry Andric // after all. This happens because simd128 turns on custom lowering for 9590b57cec5SDimitry Andric // SIGN_EXTEND_INREG, but for non-vector sign extends the result might be an 9600b57cec5SDimitry Andric // illegal type. 9610b57cec5SDimitry Andric break; 9620b57cec5SDimitry Andric default: 9630b57cec5SDimitry Andric llvm_unreachable( 9640b57cec5SDimitry Andric "ReplaceNodeResults not implemented for this op for WebAssembly!"); 9650b57cec5SDimitry Andric } 9660b57cec5SDimitry Andric } 9670b57cec5SDimitry Andric 9680b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9690b57cec5SDimitry Andric // Custom lowering hooks. 9700b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 9710b57cec5SDimitry Andric 9720b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, 9730b57cec5SDimitry Andric SelectionDAG &DAG) const { 9740b57cec5SDimitry Andric SDLoc DL(Op); 9750b57cec5SDimitry Andric switch (Op.getOpcode()) { 9760b57cec5SDimitry Andric default: 9770b57cec5SDimitry Andric llvm_unreachable("unimplemented operation lowering"); 9780b57cec5SDimitry Andric return SDValue(); 9790b57cec5SDimitry Andric case ISD::FrameIndex: 9800b57cec5SDimitry Andric return LowerFrameIndex(Op, DAG); 9810b57cec5SDimitry Andric case ISD::GlobalAddress: 9820b57cec5SDimitry Andric return LowerGlobalAddress(Op, DAG); 9830b57cec5SDimitry Andric case ISD::ExternalSymbol: 9840b57cec5SDimitry Andric return LowerExternalSymbol(Op, DAG); 9850b57cec5SDimitry Andric case ISD::JumpTable: 9860b57cec5SDimitry Andric return LowerJumpTable(Op, DAG); 9870b57cec5SDimitry Andric case ISD::BR_JT: 9880b57cec5SDimitry Andric return LowerBR_JT(Op, DAG); 9890b57cec5SDimitry Andric case ISD::VASTART: 9900b57cec5SDimitry Andric return LowerVASTART(Op, DAG); 9910b57cec5SDimitry Andric case ISD::BlockAddress: 9920b57cec5SDimitry Andric case ISD::BRIND: 9930b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly hasn't implemented computed gotos"); 9940b57cec5SDimitry Andric return SDValue(); 9950b57cec5SDimitry Andric case ISD::RETURNADDR: 9960b57cec5SDimitry Andric return LowerRETURNADDR(Op, DAG); 9970b57cec5SDimitry Andric case ISD::FRAMEADDR: 9980b57cec5SDimitry Andric return LowerFRAMEADDR(Op, DAG); 9990b57cec5SDimitry Andric case ISD::CopyToReg: 10000b57cec5SDimitry Andric return LowerCopyToReg(Op, DAG); 10010b57cec5SDimitry Andric case ISD::EXTRACT_VECTOR_ELT: 10020b57cec5SDimitry Andric case ISD::INSERT_VECTOR_ELT: 10030b57cec5SDimitry Andric return LowerAccessVectorElement(Op, DAG); 10040b57cec5SDimitry Andric case ISD::INTRINSIC_VOID: 10050b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: 10060b57cec5SDimitry Andric case ISD::INTRINSIC_W_CHAIN: 10070b57cec5SDimitry Andric return LowerIntrinsic(Op, DAG); 10080b57cec5SDimitry Andric case ISD::SIGN_EXTEND_INREG: 10090b57cec5SDimitry Andric return LowerSIGN_EXTEND_INREG(Op, DAG); 10100b57cec5SDimitry Andric case ISD::BUILD_VECTOR: 10110b57cec5SDimitry Andric return LowerBUILD_VECTOR(Op, DAG); 10120b57cec5SDimitry Andric case ISD::VECTOR_SHUFFLE: 10130b57cec5SDimitry Andric return LowerVECTOR_SHUFFLE(Op, DAG); 10140b57cec5SDimitry Andric case ISD::SHL: 10150b57cec5SDimitry Andric case ISD::SRA: 10160b57cec5SDimitry Andric case ISD::SRL: 10170b57cec5SDimitry Andric return LowerShift(Op, DAG); 10180b57cec5SDimitry Andric } 10190b57cec5SDimitry Andric } 10200b57cec5SDimitry Andric 10210b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, 10220b57cec5SDimitry Andric SelectionDAG &DAG) const { 10230b57cec5SDimitry Andric SDValue Src = Op.getOperand(2); 10240b57cec5SDimitry Andric if (isa<FrameIndexSDNode>(Src.getNode())) { 10250b57cec5SDimitry Andric // CopyToReg nodes don't support FrameIndex operands. Other targets select 10260b57cec5SDimitry Andric // the FI to some LEA-like instruction, but since we don't have that, we 10270b57cec5SDimitry Andric // need to insert some kind of instruction that can take an FI operand and 10280b57cec5SDimitry Andric // produces a value usable by CopyToReg (i.e. in a vreg). So insert a dummy 10290b57cec5SDimitry Andric // local.copy between Op and its FI operand. 10300b57cec5SDimitry Andric SDValue Chain = Op.getOperand(0); 10310b57cec5SDimitry Andric SDLoc DL(Op); 10320b57cec5SDimitry Andric unsigned Reg = cast<RegisterSDNode>(Op.getOperand(1))->getReg(); 10330b57cec5SDimitry Andric EVT VT = Src.getValueType(); 10340b57cec5SDimitry Andric SDValue Copy(DAG.getMachineNode(VT == MVT::i32 ? WebAssembly::COPY_I32 10350b57cec5SDimitry Andric : WebAssembly::COPY_I64, 10360b57cec5SDimitry Andric DL, VT, Src), 10370b57cec5SDimitry Andric 0); 10380b57cec5SDimitry Andric return Op.getNode()->getNumValues() == 1 10390b57cec5SDimitry Andric ? DAG.getCopyToReg(Chain, DL, Reg, Copy) 10400b57cec5SDimitry Andric : DAG.getCopyToReg(Chain, DL, Reg, Copy, 10410b57cec5SDimitry Andric Op.getNumOperands() == 4 ? Op.getOperand(3) 10420b57cec5SDimitry Andric : SDValue()); 10430b57cec5SDimitry Andric } 10440b57cec5SDimitry Andric return SDValue(); 10450b57cec5SDimitry Andric } 10460b57cec5SDimitry Andric 10470b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFrameIndex(SDValue Op, 10480b57cec5SDimitry Andric SelectionDAG &DAG) const { 10490b57cec5SDimitry Andric int FI = cast<FrameIndexSDNode>(Op)->getIndex(); 10500b57cec5SDimitry Andric return DAG.getTargetFrameIndex(FI, Op.getValueType()); 10510b57cec5SDimitry Andric } 10520b57cec5SDimitry Andric 10530b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerRETURNADDR(SDValue Op, 10540b57cec5SDimitry Andric SelectionDAG &DAG) const { 10550b57cec5SDimitry Andric SDLoc DL(Op); 10560b57cec5SDimitry Andric 10570b57cec5SDimitry Andric if (!Subtarget->getTargetTriple().isOSEmscripten()) { 10580b57cec5SDimitry Andric fail(DL, DAG, 10590b57cec5SDimitry Andric "Non-Emscripten WebAssembly hasn't implemented " 10600b57cec5SDimitry Andric "__builtin_return_address"); 10610b57cec5SDimitry Andric return SDValue(); 10620b57cec5SDimitry Andric } 10630b57cec5SDimitry Andric 10640b57cec5SDimitry Andric if (verifyReturnAddressArgumentIsConstant(Op, DAG)) 10650b57cec5SDimitry Andric return SDValue(); 10660b57cec5SDimitry Andric 10670b57cec5SDimitry Andric unsigned Depth = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); 1068*8bcb0991SDimitry Andric MakeLibCallOptions CallOptions; 10690b57cec5SDimitry Andric return makeLibCall(DAG, RTLIB::RETURN_ADDRESS, Op.getValueType(), 1070*8bcb0991SDimitry Andric {DAG.getConstant(Depth, DL, MVT::i32)}, CallOptions, DL) 10710b57cec5SDimitry Andric .first; 10720b57cec5SDimitry Andric } 10730b57cec5SDimitry Andric 10740b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, 10750b57cec5SDimitry Andric SelectionDAG &DAG) const { 10760b57cec5SDimitry Andric // Non-zero depths are not supported by WebAssembly currently. Use the 10770b57cec5SDimitry Andric // legalizer's default expansion, which is to return 0 (what this function is 10780b57cec5SDimitry Andric // documented to do). 10790b57cec5SDimitry Andric if (Op.getConstantOperandVal(0) > 0) 10800b57cec5SDimitry Andric return SDValue(); 10810b57cec5SDimitry Andric 10820b57cec5SDimitry Andric DAG.getMachineFunction().getFrameInfo().setFrameAddressIsTaken(true); 10830b57cec5SDimitry Andric EVT VT = Op.getValueType(); 1084*8bcb0991SDimitry Andric Register FP = 10850b57cec5SDimitry Andric Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); 10860b57cec5SDimitry Andric return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); 10870b57cec5SDimitry Andric } 10880b57cec5SDimitry Andric 10890b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, 10900b57cec5SDimitry Andric SelectionDAG &DAG) const { 10910b57cec5SDimitry Andric SDLoc DL(Op); 10920b57cec5SDimitry Andric const auto *GA = cast<GlobalAddressSDNode>(Op); 10930b57cec5SDimitry Andric EVT VT = Op.getValueType(); 10940b57cec5SDimitry Andric assert(GA->getTargetFlags() == 0 && 10950b57cec5SDimitry Andric "Unexpected target flags on generic GlobalAddressSDNode"); 10960b57cec5SDimitry Andric if (GA->getAddressSpace() != 0) 10970b57cec5SDimitry Andric fail(DL, DAG, "WebAssembly only expects the 0 address space"); 10980b57cec5SDimitry Andric 10990b57cec5SDimitry Andric unsigned OperandFlags = 0; 11000b57cec5SDimitry Andric if (isPositionIndependent()) { 11010b57cec5SDimitry Andric const GlobalValue *GV = GA->getGlobal(); 11020b57cec5SDimitry Andric if (getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)) { 11030b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 11040b57cec5SDimitry Andric MVT PtrVT = getPointerTy(MF.getDataLayout()); 11050b57cec5SDimitry Andric const char *BaseName; 11060b57cec5SDimitry Andric if (GV->getValueType()->isFunctionTy()) { 11070b57cec5SDimitry Andric BaseName = MF.createExternalSymbolName("__table_base"); 11080b57cec5SDimitry Andric OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL; 11090b57cec5SDimitry Andric } 11100b57cec5SDimitry Andric else { 11110b57cec5SDimitry Andric BaseName = MF.createExternalSymbolName("__memory_base"); 11120b57cec5SDimitry Andric OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL; 11130b57cec5SDimitry Andric } 11140b57cec5SDimitry Andric SDValue BaseAddr = 11150b57cec5SDimitry Andric DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, 11160b57cec5SDimitry Andric DAG.getTargetExternalSymbol(BaseName, PtrVT)); 11170b57cec5SDimitry Andric 11180b57cec5SDimitry Andric SDValue SymAddr = DAG.getNode( 11190b57cec5SDimitry Andric WebAssemblyISD::WrapperPIC, DL, VT, 11200b57cec5SDimitry Andric DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), 11210b57cec5SDimitry Andric OperandFlags)); 11220b57cec5SDimitry Andric 11230b57cec5SDimitry Andric return DAG.getNode(ISD::ADD, DL, VT, BaseAddr, SymAddr); 11240b57cec5SDimitry Andric } else { 11250b57cec5SDimitry Andric OperandFlags = WebAssemblyII::MO_GOT; 11260b57cec5SDimitry Andric } 11270b57cec5SDimitry Andric } 11280b57cec5SDimitry Andric 11290b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 11300b57cec5SDimitry Andric DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, 11310b57cec5SDimitry Andric GA->getOffset(), OperandFlags)); 11320b57cec5SDimitry Andric } 11330b57cec5SDimitry Andric 11340b57cec5SDimitry Andric SDValue 11350b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerExternalSymbol(SDValue Op, 11360b57cec5SDimitry Andric SelectionDAG &DAG) const { 11370b57cec5SDimitry Andric SDLoc DL(Op); 11380b57cec5SDimitry Andric const auto *ES = cast<ExternalSymbolSDNode>(Op); 11390b57cec5SDimitry Andric EVT VT = Op.getValueType(); 11400b57cec5SDimitry Andric assert(ES->getTargetFlags() == 0 && 11410b57cec5SDimitry Andric "Unexpected target flags on generic ExternalSymbolSDNode"); 11420b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 11430b57cec5SDimitry Andric DAG.getTargetExternalSymbol(ES->getSymbol(), VT)); 11440b57cec5SDimitry Andric } 11450b57cec5SDimitry Andric 11460b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerJumpTable(SDValue Op, 11470b57cec5SDimitry Andric SelectionDAG &DAG) const { 11480b57cec5SDimitry Andric // There's no need for a Wrapper node because we always incorporate a jump 11490b57cec5SDimitry Andric // table operand into a BR_TABLE instruction, rather than ever 11500b57cec5SDimitry Andric // materializing it in a register. 11510b57cec5SDimitry Andric const JumpTableSDNode *JT = cast<JumpTableSDNode>(Op); 11520b57cec5SDimitry Andric return DAG.getTargetJumpTable(JT->getIndex(), Op.getValueType(), 11530b57cec5SDimitry Andric JT->getTargetFlags()); 11540b57cec5SDimitry Andric } 11550b57cec5SDimitry Andric 11560b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerBR_JT(SDValue Op, 11570b57cec5SDimitry Andric SelectionDAG &DAG) const { 11580b57cec5SDimitry Andric SDLoc DL(Op); 11590b57cec5SDimitry Andric SDValue Chain = Op.getOperand(0); 11600b57cec5SDimitry Andric const auto *JT = cast<JumpTableSDNode>(Op.getOperand(1)); 11610b57cec5SDimitry Andric SDValue Index = Op.getOperand(2); 11620b57cec5SDimitry Andric assert(JT->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); 11630b57cec5SDimitry Andric 11640b57cec5SDimitry Andric SmallVector<SDValue, 8> Ops; 11650b57cec5SDimitry Andric Ops.push_back(Chain); 11660b57cec5SDimitry Andric Ops.push_back(Index); 11670b57cec5SDimitry Andric 11680b57cec5SDimitry Andric MachineJumpTableInfo *MJTI = DAG.getMachineFunction().getJumpTableInfo(); 11690b57cec5SDimitry Andric const auto &MBBs = MJTI->getJumpTables()[JT->getIndex()].MBBs; 11700b57cec5SDimitry Andric 11710b57cec5SDimitry Andric // Add an operand for each case. 11720b57cec5SDimitry Andric for (auto MBB : MBBs) 11730b57cec5SDimitry Andric Ops.push_back(DAG.getBasicBlock(MBB)); 11740b57cec5SDimitry Andric 11750b57cec5SDimitry Andric // TODO: For now, we just pick something arbitrary for a default case for now. 11760b57cec5SDimitry Andric // We really want to sniff out the guard and put in the real default case (and 11770b57cec5SDimitry Andric // delete the guard). 11780b57cec5SDimitry Andric Ops.push_back(DAG.getBasicBlock(MBBs[0])); 11790b57cec5SDimitry Andric 11800b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::BR_TABLE, DL, MVT::Other, Ops); 11810b57cec5SDimitry Andric } 11820b57cec5SDimitry Andric 11830b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, 11840b57cec5SDimitry Andric SelectionDAG &DAG) const { 11850b57cec5SDimitry Andric SDLoc DL(Op); 11860b57cec5SDimitry Andric EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout()); 11870b57cec5SDimitry Andric 11880b57cec5SDimitry Andric auto *MFI = DAG.getMachineFunction().getInfo<WebAssemblyFunctionInfo>(); 11890b57cec5SDimitry Andric const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue(); 11900b57cec5SDimitry Andric 11910b57cec5SDimitry Andric SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, 11920b57cec5SDimitry Andric MFI->getVarargBufferVreg(), PtrVT); 11930b57cec5SDimitry Andric return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), 11940b57cec5SDimitry Andric MachinePointerInfo(SV), 0); 11950b57cec5SDimitry Andric } 11960b57cec5SDimitry Andric 11970b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, 11980b57cec5SDimitry Andric SelectionDAG &DAG) const { 11990b57cec5SDimitry Andric MachineFunction &MF = DAG.getMachineFunction(); 12000b57cec5SDimitry Andric unsigned IntNo; 12010b57cec5SDimitry Andric switch (Op.getOpcode()) { 12020b57cec5SDimitry Andric case ISD::INTRINSIC_VOID: 12030b57cec5SDimitry Andric case ISD::INTRINSIC_W_CHAIN: 12040b57cec5SDimitry Andric IntNo = cast<ConstantSDNode>(Op.getOperand(1))->getZExtValue(); 12050b57cec5SDimitry Andric break; 12060b57cec5SDimitry Andric case ISD::INTRINSIC_WO_CHAIN: 12070b57cec5SDimitry Andric IntNo = cast<ConstantSDNode>(Op.getOperand(0))->getZExtValue(); 12080b57cec5SDimitry Andric break; 12090b57cec5SDimitry Andric default: 12100b57cec5SDimitry Andric llvm_unreachable("Invalid intrinsic"); 12110b57cec5SDimitry Andric } 12120b57cec5SDimitry Andric SDLoc DL(Op); 12130b57cec5SDimitry Andric 12140b57cec5SDimitry Andric switch (IntNo) { 12150b57cec5SDimitry Andric default: 12160b57cec5SDimitry Andric return SDValue(); // Don't custom lower most intrinsics. 12170b57cec5SDimitry Andric 12180b57cec5SDimitry Andric case Intrinsic::wasm_lsda: { 12190b57cec5SDimitry Andric EVT VT = Op.getValueType(); 12200b57cec5SDimitry Andric const TargetLowering &TLI = DAG.getTargetLoweringInfo(); 12210b57cec5SDimitry Andric MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout()); 12220b57cec5SDimitry Andric auto &Context = MF.getMMI().getContext(); 12230b57cec5SDimitry Andric MCSymbol *S = Context.getOrCreateSymbol(Twine("GCC_except_table") + 12240b57cec5SDimitry Andric Twine(MF.getFunctionNumber())); 12250b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, 12260b57cec5SDimitry Andric DAG.getMCSymbol(S, PtrVT)); 12270b57cec5SDimitry Andric } 12280b57cec5SDimitry Andric 12290b57cec5SDimitry Andric case Intrinsic::wasm_throw: { 12300b57cec5SDimitry Andric // We only support C++ exceptions for now 12310b57cec5SDimitry Andric int Tag = cast<ConstantSDNode>(Op.getOperand(2).getNode())->getZExtValue(); 12320b57cec5SDimitry Andric if (Tag != CPP_EXCEPTION) 12330b57cec5SDimitry Andric llvm_unreachable("Invalid tag!"); 12340b57cec5SDimitry Andric const TargetLowering &TLI = DAG.getTargetLoweringInfo(); 12350b57cec5SDimitry Andric MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout()); 12360b57cec5SDimitry Andric const char *SymName = MF.createExternalSymbolName("__cpp_exception"); 12370b57cec5SDimitry Andric SDValue SymNode = DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, 12380b57cec5SDimitry Andric DAG.getTargetExternalSymbol(SymName, PtrVT)); 12390b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::THROW, DL, 12400b57cec5SDimitry Andric MVT::Other, // outchain type 12410b57cec5SDimitry Andric { 12420b57cec5SDimitry Andric Op.getOperand(0), // inchain 12430b57cec5SDimitry Andric SymNode, // exception symbol 12440b57cec5SDimitry Andric Op.getOperand(3) // thrown value 12450b57cec5SDimitry Andric }); 12460b57cec5SDimitry Andric } 12470b57cec5SDimitry Andric } 12480b57cec5SDimitry Andric } 12490b57cec5SDimitry Andric 12500b57cec5SDimitry Andric SDValue 12510b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerSIGN_EXTEND_INREG(SDValue Op, 12520b57cec5SDimitry Andric SelectionDAG &DAG) const { 12530b57cec5SDimitry Andric SDLoc DL(Op); 12540b57cec5SDimitry Andric // If sign extension operations are disabled, allow sext_inreg only if operand 12550b57cec5SDimitry Andric // is a vector extract. SIMD does not depend on sign extension operations, but 12560b57cec5SDimitry Andric // allowing sext_inreg in this context lets us have simple patterns to select 12570b57cec5SDimitry Andric // extract_lane_s instructions. Expanding sext_inreg everywhere would be 12580b57cec5SDimitry Andric // simpler in this file, but would necessitate large and brittle patterns to 12590b57cec5SDimitry Andric // undo the expansion and select extract_lane_s instructions. 12600b57cec5SDimitry Andric assert(!Subtarget->hasSignExt() && Subtarget->hasSIMD128()); 12610b57cec5SDimitry Andric if (Op.getOperand(0).getOpcode() == ISD::EXTRACT_VECTOR_ELT) { 12620b57cec5SDimitry Andric const SDValue &Extract = Op.getOperand(0); 12630b57cec5SDimitry Andric MVT VecT = Extract.getOperand(0).getSimpleValueType(); 12640b57cec5SDimitry Andric MVT ExtractedLaneT = static_cast<VTSDNode *>(Op.getOperand(1).getNode()) 12650b57cec5SDimitry Andric ->getVT() 12660b57cec5SDimitry Andric .getSimpleVT(); 12670b57cec5SDimitry Andric MVT ExtractedVecT = 12680b57cec5SDimitry Andric MVT::getVectorVT(ExtractedLaneT, 128 / ExtractedLaneT.getSizeInBits()); 12690b57cec5SDimitry Andric if (ExtractedVecT == VecT) 12700b57cec5SDimitry Andric return Op; 12710b57cec5SDimitry Andric // Bitcast vector to appropriate type to ensure ISel pattern coverage 12720b57cec5SDimitry Andric const SDValue &Index = Extract.getOperand(1); 12730b57cec5SDimitry Andric unsigned IndexVal = 12740b57cec5SDimitry Andric static_cast<ConstantSDNode *>(Index.getNode())->getZExtValue(); 12750b57cec5SDimitry Andric unsigned Scale = 12760b57cec5SDimitry Andric ExtractedVecT.getVectorNumElements() / VecT.getVectorNumElements(); 12770b57cec5SDimitry Andric assert(Scale > 1); 12780b57cec5SDimitry Andric SDValue NewIndex = 12790b57cec5SDimitry Andric DAG.getConstant(IndexVal * Scale, DL, Index.getValueType()); 12800b57cec5SDimitry Andric SDValue NewExtract = DAG.getNode( 12810b57cec5SDimitry Andric ISD::EXTRACT_VECTOR_ELT, DL, Extract.getValueType(), 12820b57cec5SDimitry Andric DAG.getBitcast(ExtractedVecT, Extract.getOperand(0)), NewIndex); 12830b57cec5SDimitry Andric return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, Op.getValueType(), 12840b57cec5SDimitry Andric NewExtract, Op.getOperand(1)); 12850b57cec5SDimitry Andric } 12860b57cec5SDimitry Andric // Otherwise expand 12870b57cec5SDimitry Andric return SDValue(); 12880b57cec5SDimitry Andric } 12890b57cec5SDimitry Andric 12900b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, 12910b57cec5SDimitry Andric SelectionDAG &DAG) const { 12920b57cec5SDimitry Andric SDLoc DL(Op); 12930b57cec5SDimitry Andric const EVT VecT = Op.getValueType(); 12940b57cec5SDimitry Andric const EVT LaneT = Op.getOperand(0).getValueType(); 12950b57cec5SDimitry Andric const size_t Lanes = Op.getNumOperands(); 1296*8bcb0991SDimitry Andric bool CanSwizzle = Subtarget->hasUnimplementedSIMD128() && VecT == MVT::v16i8; 1297*8bcb0991SDimitry Andric 1298*8bcb0991SDimitry Andric // BUILD_VECTORs are lowered to the instruction that initializes the highest 1299*8bcb0991SDimitry Andric // possible number of lanes at once followed by a sequence of replace_lane 1300*8bcb0991SDimitry Andric // instructions to individually initialize any remaining lanes. 1301*8bcb0991SDimitry Andric 1302*8bcb0991SDimitry Andric // TODO: Tune this. For example, lanewise swizzling is very expensive, so 1303*8bcb0991SDimitry Andric // swizzled lanes should be given greater weight. 1304*8bcb0991SDimitry Andric 1305*8bcb0991SDimitry Andric // TODO: Investigate building vectors by shuffling together vectors built by 1306*8bcb0991SDimitry Andric // separately specialized means. 1307*8bcb0991SDimitry Andric 13080b57cec5SDimitry Andric auto IsConstant = [](const SDValue &V) { 13090b57cec5SDimitry Andric return V.getOpcode() == ISD::Constant || V.getOpcode() == ISD::ConstantFP; 13100b57cec5SDimitry Andric }; 13110b57cec5SDimitry Andric 1312*8bcb0991SDimitry Andric // Returns the source vector and index vector pair if they exist. Checks for: 1313*8bcb0991SDimitry Andric // (extract_vector_elt 1314*8bcb0991SDimitry Andric // $src, 1315*8bcb0991SDimitry Andric // (sign_extend_inreg (extract_vector_elt $indices, $i)) 1316*8bcb0991SDimitry Andric // ) 1317*8bcb0991SDimitry Andric auto GetSwizzleSrcs = [](size_t I, const SDValue &Lane) { 1318*8bcb0991SDimitry Andric auto Bail = std::make_pair(SDValue(), SDValue()); 1319*8bcb0991SDimitry Andric if (Lane->getOpcode() != ISD::EXTRACT_VECTOR_ELT) 1320*8bcb0991SDimitry Andric return Bail; 1321*8bcb0991SDimitry Andric const SDValue &SwizzleSrc = Lane->getOperand(0); 1322*8bcb0991SDimitry Andric const SDValue &IndexExt = Lane->getOperand(1); 1323*8bcb0991SDimitry Andric if (IndexExt->getOpcode() != ISD::SIGN_EXTEND_INREG) 1324*8bcb0991SDimitry Andric return Bail; 1325*8bcb0991SDimitry Andric const SDValue &Index = IndexExt->getOperand(0); 1326*8bcb0991SDimitry Andric if (Index->getOpcode() != ISD::EXTRACT_VECTOR_ELT) 1327*8bcb0991SDimitry Andric return Bail; 1328*8bcb0991SDimitry Andric const SDValue &SwizzleIndices = Index->getOperand(0); 1329*8bcb0991SDimitry Andric if (SwizzleSrc.getValueType() != MVT::v16i8 || 1330*8bcb0991SDimitry Andric SwizzleIndices.getValueType() != MVT::v16i8 || 1331*8bcb0991SDimitry Andric Index->getOperand(1)->getOpcode() != ISD::Constant || 1332*8bcb0991SDimitry Andric Index->getConstantOperandVal(1) != I) 1333*8bcb0991SDimitry Andric return Bail; 1334*8bcb0991SDimitry Andric return std::make_pair(SwizzleSrc, SwizzleIndices); 1335*8bcb0991SDimitry Andric }; 1336*8bcb0991SDimitry Andric 1337*8bcb0991SDimitry Andric using ValueEntry = std::pair<SDValue, size_t>; 1338*8bcb0991SDimitry Andric SmallVector<ValueEntry, 16> SplatValueCounts; 1339*8bcb0991SDimitry Andric 1340*8bcb0991SDimitry Andric using SwizzleEntry = std::pair<std::pair<SDValue, SDValue>, size_t>; 1341*8bcb0991SDimitry Andric SmallVector<SwizzleEntry, 16> SwizzleCounts; 1342*8bcb0991SDimitry Andric 1343*8bcb0991SDimitry Andric auto AddCount = [](auto &Counts, const auto &Val) { 1344*8bcb0991SDimitry Andric auto CountIt = std::find_if(Counts.begin(), Counts.end(), 1345*8bcb0991SDimitry Andric [&Val](auto E) { return E.first == Val; }); 1346*8bcb0991SDimitry Andric if (CountIt == Counts.end()) { 1347*8bcb0991SDimitry Andric Counts.emplace_back(Val, 1); 13480b57cec5SDimitry Andric } else { 13490b57cec5SDimitry Andric CountIt->second++; 13500b57cec5SDimitry Andric } 1351*8bcb0991SDimitry Andric }; 13520b57cec5SDimitry Andric 1353*8bcb0991SDimitry Andric auto GetMostCommon = [](auto &Counts) { 1354*8bcb0991SDimitry Andric auto CommonIt = 1355*8bcb0991SDimitry Andric std::max_element(Counts.begin(), Counts.end(), 1356*8bcb0991SDimitry Andric [](auto A, auto B) { return A.second < B.second; }); 1357*8bcb0991SDimitry Andric assert(CommonIt != Counts.end() && "Unexpected all-undef build_vector"); 1358*8bcb0991SDimitry Andric return *CommonIt; 1359*8bcb0991SDimitry Andric }; 1360*8bcb0991SDimitry Andric 1361*8bcb0991SDimitry Andric size_t NumConstantLanes = 0; 1362*8bcb0991SDimitry Andric 1363*8bcb0991SDimitry Andric // Count eligible lanes for each type of vector creation op 1364*8bcb0991SDimitry Andric for (size_t I = 0; I < Lanes; ++I) { 1365*8bcb0991SDimitry Andric const SDValue &Lane = Op->getOperand(I); 1366*8bcb0991SDimitry Andric if (Lane.isUndef()) 1367*8bcb0991SDimitry Andric continue; 1368*8bcb0991SDimitry Andric 1369*8bcb0991SDimitry Andric AddCount(SplatValueCounts, Lane); 1370*8bcb0991SDimitry Andric 1371*8bcb0991SDimitry Andric if (IsConstant(Lane)) { 1372*8bcb0991SDimitry Andric NumConstantLanes++; 1373*8bcb0991SDimitry Andric } else if (CanSwizzle) { 1374*8bcb0991SDimitry Andric auto SwizzleSrcs = GetSwizzleSrcs(I, Lane); 1375*8bcb0991SDimitry Andric if (SwizzleSrcs.first) 1376*8bcb0991SDimitry Andric AddCount(SwizzleCounts, SwizzleSrcs); 1377*8bcb0991SDimitry Andric } 1378*8bcb0991SDimitry Andric } 1379*8bcb0991SDimitry Andric 1380*8bcb0991SDimitry Andric SDValue SplatValue; 1381*8bcb0991SDimitry Andric size_t NumSplatLanes; 1382*8bcb0991SDimitry Andric std::tie(SplatValue, NumSplatLanes) = GetMostCommon(SplatValueCounts); 1383*8bcb0991SDimitry Andric 1384*8bcb0991SDimitry Andric SDValue SwizzleSrc; 1385*8bcb0991SDimitry Andric SDValue SwizzleIndices; 1386*8bcb0991SDimitry Andric size_t NumSwizzleLanes = 0; 1387*8bcb0991SDimitry Andric if (SwizzleCounts.size()) 1388*8bcb0991SDimitry Andric std::forward_as_tuple(std::tie(SwizzleSrc, SwizzleIndices), 1389*8bcb0991SDimitry Andric NumSwizzleLanes) = GetMostCommon(SwizzleCounts); 1390*8bcb0991SDimitry Andric 1391*8bcb0991SDimitry Andric // Predicate returning true if the lane is properly initialized by the 1392*8bcb0991SDimitry Andric // original instruction 1393*8bcb0991SDimitry Andric std::function<bool(size_t, const SDValue &)> IsLaneConstructed; 1394*8bcb0991SDimitry Andric SDValue Result; 13950b57cec5SDimitry Andric if (Subtarget->hasUnimplementedSIMD128()) { 1396*8bcb0991SDimitry Andric // Prefer swizzles over vector consts over splats 1397*8bcb0991SDimitry Andric if (NumSwizzleLanes >= NumSplatLanes && 1398*8bcb0991SDimitry Andric NumSwizzleLanes >= NumConstantLanes) { 1399*8bcb0991SDimitry Andric Result = DAG.getNode(WebAssemblyISD::SWIZZLE, DL, VecT, SwizzleSrc, 1400*8bcb0991SDimitry Andric SwizzleIndices); 1401*8bcb0991SDimitry Andric auto Swizzled = std::make_pair(SwizzleSrc, SwizzleIndices); 1402*8bcb0991SDimitry Andric IsLaneConstructed = [&, Swizzled](size_t I, const SDValue &Lane) { 1403*8bcb0991SDimitry Andric return Swizzled == GetSwizzleSrcs(I, Lane); 1404*8bcb0991SDimitry Andric }; 1405*8bcb0991SDimitry Andric } else if (NumConstantLanes >= NumSplatLanes) { 14060b57cec5SDimitry Andric SmallVector<SDValue, 16> ConstLanes; 14070b57cec5SDimitry Andric for (const SDValue &Lane : Op->op_values()) { 14080b57cec5SDimitry Andric if (IsConstant(Lane)) { 14090b57cec5SDimitry Andric ConstLanes.push_back(Lane); 14100b57cec5SDimitry Andric } else if (LaneT.isFloatingPoint()) { 14110b57cec5SDimitry Andric ConstLanes.push_back(DAG.getConstantFP(0, DL, LaneT)); 14120b57cec5SDimitry Andric } else { 14130b57cec5SDimitry Andric ConstLanes.push_back(DAG.getConstant(0, DL, LaneT)); 14140b57cec5SDimitry Andric } 14150b57cec5SDimitry Andric } 1416*8bcb0991SDimitry Andric Result = DAG.getBuildVector(VecT, DL, ConstLanes); 1417*8bcb0991SDimitry Andric IsLaneConstructed = [&](size_t _, const SDValue &Lane) { 1418*8bcb0991SDimitry Andric return IsConstant(Lane); 1419*8bcb0991SDimitry Andric }; 1420*8bcb0991SDimitry Andric } 1421*8bcb0991SDimitry Andric } 1422*8bcb0991SDimitry Andric if (!Result) { 1423*8bcb0991SDimitry Andric // Use a splat, but possibly a load_splat 1424*8bcb0991SDimitry Andric LoadSDNode *SplattedLoad; 1425*8bcb0991SDimitry Andric if (Subtarget->hasUnimplementedSIMD128() && 1426*8bcb0991SDimitry Andric (SplattedLoad = dyn_cast<LoadSDNode>(SplatValue)) && 1427*8bcb0991SDimitry Andric SplattedLoad->getMemoryVT() == VecT.getVectorElementType()) { 1428*8bcb0991SDimitry Andric Result = DAG.getNode(WebAssemblyISD::LOAD_SPLAT, DL, VecT, SplatValue); 1429*8bcb0991SDimitry Andric } else { 1430*8bcb0991SDimitry Andric Result = DAG.getSplatBuildVector(VecT, DL, SplatValue); 1431*8bcb0991SDimitry Andric } 1432*8bcb0991SDimitry Andric IsLaneConstructed = [&](size_t _, const SDValue &Lane) { 1433*8bcb0991SDimitry Andric return Lane == SplatValue; 1434*8bcb0991SDimitry Andric }; 1435*8bcb0991SDimitry Andric } 1436*8bcb0991SDimitry Andric 1437*8bcb0991SDimitry Andric // Add replace_lane instructions for any unhandled values 14380b57cec5SDimitry Andric for (size_t I = 0; I < Lanes; ++I) { 14390b57cec5SDimitry Andric const SDValue &Lane = Op->getOperand(I); 1440*8bcb0991SDimitry Andric if (!Lane.isUndef() && !IsLaneConstructed(I, Lane)) 14410b57cec5SDimitry Andric Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, VecT, Result, Lane, 14420b57cec5SDimitry Andric DAG.getConstant(I, DL, MVT::i32)); 14430b57cec5SDimitry Andric } 1444*8bcb0991SDimitry Andric 14450b57cec5SDimitry Andric return Result; 14460b57cec5SDimitry Andric } 14470b57cec5SDimitry Andric 14480b57cec5SDimitry Andric SDValue 14490b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerVECTOR_SHUFFLE(SDValue Op, 14500b57cec5SDimitry Andric SelectionDAG &DAG) const { 14510b57cec5SDimitry Andric SDLoc DL(Op); 14520b57cec5SDimitry Andric ArrayRef<int> Mask = cast<ShuffleVectorSDNode>(Op.getNode())->getMask(); 14530b57cec5SDimitry Andric MVT VecType = Op.getOperand(0).getSimpleValueType(); 14540b57cec5SDimitry Andric assert(VecType.is128BitVector() && "Unexpected shuffle vector type"); 14550b57cec5SDimitry Andric size_t LaneBytes = VecType.getVectorElementType().getSizeInBits() / 8; 14560b57cec5SDimitry Andric 14570b57cec5SDimitry Andric // Space for two vector args and sixteen mask indices 14580b57cec5SDimitry Andric SDValue Ops[18]; 14590b57cec5SDimitry Andric size_t OpIdx = 0; 14600b57cec5SDimitry Andric Ops[OpIdx++] = Op.getOperand(0); 14610b57cec5SDimitry Andric Ops[OpIdx++] = Op.getOperand(1); 14620b57cec5SDimitry Andric 14630b57cec5SDimitry Andric // Expand mask indices to byte indices and materialize them as operands 14640b57cec5SDimitry Andric for (int M : Mask) { 14650b57cec5SDimitry Andric for (size_t J = 0; J < LaneBytes; ++J) { 14660b57cec5SDimitry Andric // Lower undefs (represented by -1 in mask) to zero 14670b57cec5SDimitry Andric uint64_t ByteIndex = M == -1 ? 0 : (uint64_t)M * LaneBytes + J; 14680b57cec5SDimitry Andric Ops[OpIdx++] = DAG.getConstant(ByteIndex, DL, MVT::i32); 14690b57cec5SDimitry Andric } 14700b57cec5SDimitry Andric } 14710b57cec5SDimitry Andric 14720b57cec5SDimitry Andric return DAG.getNode(WebAssemblyISD::SHUFFLE, DL, Op.getValueType(), Ops); 14730b57cec5SDimitry Andric } 14740b57cec5SDimitry Andric 14750b57cec5SDimitry Andric SDValue 14760b57cec5SDimitry Andric WebAssemblyTargetLowering::LowerAccessVectorElement(SDValue Op, 14770b57cec5SDimitry Andric SelectionDAG &DAG) const { 14780b57cec5SDimitry Andric // Allow constant lane indices, expand variable lane indices 14790b57cec5SDimitry Andric SDNode *IdxNode = Op.getOperand(Op.getNumOperands() - 1).getNode(); 14800b57cec5SDimitry Andric if (isa<ConstantSDNode>(IdxNode) || IdxNode->isUndef()) 14810b57cec5SDimitry Andric return Op; 14820b57cec5SDimitry Andric else 14830b57cec5SDimitry Andric // Perform default expansion 14840b57cec5SDimitry Andric return SDValue(); 14850b57cec5SDimitry Andric } 14860b57cec5SDimitry Andric 14870b57cec5SDimitry Andric static SDValue unrollVectorShift(SDValue Op, SelectionDAG &DAG) { 14880b57cec5SDimitry Andric EVT LaneT = Op.getSimpleValueType().getVectorElementType(); 14890b57cec5SDimitry Andric // 32-bit and 64-bit unrolled shifts will have proper semantics 14900b57cec5SDimitry Andric if (LaneT.bitsGE(MVT::i32)) 14910b57cec5SDimitry Andric return DAG.UnrollVectorOp(Op.getNode()); 14920b57cec5SDimitry Andric // Otherwise mask the shift value to get proper semantics from 32-bit shift 14930b57cec5SDimitry Andric SDLoc DL(Op); 14940b57cec5SDimitry Andric SDValue ShiftVal = Op.getOperand(1); 14950b57cec5SDimitry Andric uint64_t MaskVal = LaneT.getSizeInBits() - 1; 14960b57cec5SDimitry Andric SDValue MaskedShiftVal = DAG.getNode( 14970b57cec5SDimitry Andric ISD::AND, // mask opcode 14980b57cec5SDimitry Andric DL, ShiftVal.getValueType(), // masked value type 14990b57cec5SDimitry Andric ShiftVal, // original shift value operand 15000b57cec5SDimitry Andric DAG.getConstant(MaskVal, DL, ShiftVal.getValueType()) // mask operand 15010b57cec5SDimitry Andric ); 15020b57cec5SDimitry Andric 15030b57cec5SDimitry Andric return DAG.UnrollVectorOp( 15040b57cec5SDimitry Andric DAG.getNode(Op.getOpcode(), // original shift opcode 15050b57cec5SDimitry Andric DL, Op.getValueType(), // original return type 15060b57cec5SDimitry Andric Op.getOperand(0), // original vector operand, 15070b57cec5SDimitry Andric MaskedShiftVal // new masked shift value operand 15080b57cec5SDimitry Andric ) 15090b57cec5SDimitry Andric .getNode()); 15100b57cec5SDimitry Andric } 15110b57cec5SDimitry Andric 15120b57cec5SDimitry Andric SDValue WebAssemblyTargetLowering::LowerShift(SDValue Op, 15130b57cec5SDimitry Andric SelectionDAG &DAG) const { 15140b57cec5SDimitry Andric SDLoc DL(Op); 15150b57cec5SDimitry Andric 15160b57cec5SDimitry Andric // Only manually lower vector shifts 15170b57cec5SDimitry Andric assert(Op.getSimpleValueType().isVector()); 15180b57cec5SDimitry Andric 15190b57cec5SDimitry Andric // Unroll non-splat vector shifts 15200b57cec5SDimitry Andric BuildVectorSDNode *ShiftVec; 15210b57cec5SDimitry Andric SDValue SplatVal; 15220b57cec5SDimitry Andric if (!(ShiftVec = dyn_cast<BuildVectorSDNode>(Op.getOperand(1).getNode())) || 15230b57cec5SDimitry Andric !(SplatVal = ShiftVec->getSplatValue())) 15240b57cec5SDimitry Andric return unrollVectorShift(Op, DAG); 15250b57cec5SDimitry Andric 15260b57cec5SDimitry Andric // All splats except i64x2 const splats are handled by patterns 15270b57cec5SDimitry Andric auto *SplatConst = dyn_cast<ConstantSDNode>(SplatVal); 15280b57cec5SDimitry Andric if (!SplatConst || Op.getSimpleValueType() != MVT::v2i64) 15290b57cec5SDimitry Andric return Op; 15300b57cec5SDimitry Andric 15310b57cec5SDimitry Andric // i64x2 const splats are custom lowered to avoid unnecessary wraps 15320b57cec5SDimitry Andric unsigned Opcode; 15330b57cec5SDimitry Andric switch (Op.getOpcode()) { 15340b57cec5SDimitry Andric case ISD::SHL: 15350b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHL; 15360b57cec5SDimitry Andric break; 15370b57cec5SDimitry Andric case ISD::SRA: 15380b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHR_S; 15390b57cec5SDimitry Andric break; 15400b57cec5SDimitry Andric case ISD::SRL: 15410b57cec5SDimitry Andric Opcode = WebAssemblyISD::VEC_SHR_U; 15420b57cec5SDimitry Andric break; 15430b57cec5SDimitry Andric default: 15440b57cec5SDimitry Andric llvm_unreachable("unexpected opcode"); 15450b57cec5SDimitry Andric } 15460b57cec5SDimitry Andric APInt Shift = SplatConst->getAPIntValue().zextOrTrunc(32); 15470b57cec5SDimitry Andric return DAG.getNode(Opcode, DL, Op.getValueType(), Op.getOperand(0), 15480b57cec5SDimitry Andric DAG.getConstant(Shift, DL, MVT::i32)); 15490b57cec5SDimitry Andric } 15500b57cec5SDimitry Andric 15510b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 15520b57cec5SDimitry Andric // WebAssembly Optimization Hooks 15530b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 1554