diff options
Diffstat (limited to 'llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp')
| -rw-r--r-- | llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 333 |
1 files changed, 303 insertions, 30 deletions
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index a9b9eceb4130..e348bba2b04c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -16,6 +16,7 @@ #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" +#include "WebAssemblyUtilities.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -31,6 +32,7 @@ #include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetOptions.h" using namespace llvm; @@ -68,6 +70,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( computeRegisterProperties(Subtarget->getRegisterInfo()); setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); setOperationAction(ISD::JumpTable, MVTPtr, Custom); setOperationAction(ISD::BlockAddress, MVTPtr, Custom); @@ -123,6 +126,10 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // Hoist bitcasts out of shuffles setTargetDAGCombine(ISD::VECTOR_SHUFFLE); + // Combine extends of extract_subvectors into widening ops + setTargetDAGCombine(ISD::SIGN_EXTEND); + setTargetDAGCombine(ISD::ZERO_EXTEND); + // Support saturating add for i8x16 and i16x8 for (auto Op : {ISD::SADDSAT, ISD::UADDSAT}) for (auto T : {MVT::v16i8, MVT::v8i16}) @@ -156,11 +163,10 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // There is no i8x16.mul instruction setOperationAction(ISD::MUL, MVT::v16i8, Expand); - // There are no vector select instructions - for (auto Op : {ISD::VSELECT, ISD::SELECT_CC, ISD::SELECT}) - for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, - MVT::v2f64}) - setOperationAction(Op, T, Expand); + // There is no vector conditional select instruction + for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64, + MVT::v2f64}) + setOperationAction(ISD::SELECT_CC, T, Expand); // Expand integer operations supported for scalars but not SIMD for (auto Op : {ISD::CTLZ, ISD::CTTZ, ISD::CTPOP, ISD::SDIV, ISD::UDIV, @@ -247,6 +253,9 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setLoadExtAction(Ext, MVT::v4i32, MVT::v4i16, Legal); setLoadExtAction(Ext, MVT::v2i64, MVT::v2i32, Legal); } + // And some truncating stores are legal as well + setTruncStoreAction(MVT::v8i16, MVT::v8i8, Legal); + setTruncStoreAction(MVT::v4i32, MVT::v4i16, Legal); } // Don't do anything clever with build_pairs @@ -258,6 +267,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // Exception handling intrinsics setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); setMaxAtomicSizeInBitsSupported(64); @@ -268,7 +278,7 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( setLibcallName(RTLIB::FPROUND_F32_F16, "__truncsfhf2"); // Define the emscripten name for return address helper. - // TODO: when implementing other WASM backends, make this generic or only do + // TODO: when implementing other Wasm backends, make this generic or only do // this on emscripten depending on what they end up doing. setLibcallName(RTLIB::RETURN_ADDRESS, "emscripten_return_address"); @@ -442,6 +452,19 @@ static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults, const MCInstrDesc &MCID = TII.get(CallOp); MachineInstrBuilder MIB(MF, MF.CreateMachineInstr(MCID, DL)); + // See if we must truncate the function pointer. + // CALL_INDIRECT takes an i32, but in wasm64 we represent function pointers + // as 64-bit for uniformity with other pointer types. + if (IsIndirect && MF.getSubtarget<WebAssemblySubtarget>().hasAddr64()) { + Register Reg32 = + MF.getRegInfo().createVirtualRegister(&WebAssembly::I32RegClass); + auto &FnPtr = CallParams.getOperand(0); + BuildMI(*BB, CallResults.getIterator(), DL, + TII.get(WebAssembly::I32_WRAP_I64), Reg32) + .addReg(FnPtr.getReg()); + FnPtr.setReg(Reg32); + } + // Move the function pointer to the end of the arguments for indirect calls if (IsIndirect) { auto FnPtr = CallParams.getOperand(0); @@ -456,6 +479,15 @@ static MachineBasicBlock *LowerCallResults(MachineInstr &CallResults, if (IsIndirect) { MIB.addImm(0); MIB.addImm(0); + + // Ensure that the object file has a __indirect_function_table import, as we + // call_indirect against it. + MCSymbolWasm *Sym = WebAssembly::getOrCreateFunctionTableSymbol( + MF.getContext(), "__indirect_function_table"); + // Until call_indirect emits TABLE_NUMBER relocs against this symbol, mark + // it as NO_STRIP so as to ensure that the indirect function table makes it + // to linked output. + Sym->setNoStrip(); } for (auto Use : CallParams.uses()) @@ -542,6 +574,16 @@ WebAssemblyTargetLowering::getRegForInlineAsmConstraint( if (VT.getSizeInBits() <= 64) return std::make_pair(0U, &WebAssembly::I64RegClass); } + if (VT.isFloatingPoint() && !VT.isVector()) { + switch (VT.getSizeInBits()) { + case 32: + return std::make_pair(0U, &WebAssembly::F32RegClass); + case 64: + return std::make_pair(0U, &WebAssembly::F64RegClass); + default: + break; + } + } break; default: break; @@ -601,8 +643,8 @@ bool WebAssemblyTargetLowering::isIntDivCheap(EVT VT, } bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const { - MVT ExtT = ExtVal.getSimpleValueType(); - MVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getSimpleValueType(0); + EVT ExtT = ExtVal.getValueType(); + EVT MemT = cast<LoadSDNode>(ExtVal->getOperand(0))->getValueType(0); return (ExtT == MVT::v8i16 && MemT == MVT::v8i8) || (ExtT == MVT::v4i32 && MemT == MVT::v4i16) || (ExtT == MVT::v2i64 && MemT == MVT::v2i32); @@ -626,7 +668,7 @@ bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, MachineFunction &MF, unsigned Intrinsic) const { switch (Intrinsic) { - case Intrinsic::wasm_atomic_notify: + case Intrinsic::wasm_memory_atomic_notify: Info.opc = ISD::INTRINSIC_W_CHAIN; Info.memVT = MVT::i32; Info.ptrVal = I.getArgOperand(0); @@ -640,7 +682,7 @@ bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, // consistent. The same applies for wasm_atomic_wait intrinsics too. Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; return true; - case Intrinsic::wasm_atomic_wait_i32: + case Intrinsic::wasm_memory_atomic_wait32: Info.opc = ISD::INTRINSIC_W_CHAIN; Info.memVT = MVT::i32; Info.ptrVal = I.getArgOperand(0); @@ -648,7 +690,7 @@ bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, Info.align = Align(4); Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; return true; - case Intrinsic::wasm_atomic_wait_i64: + case Intrinsic::wasm_memory_atomic_wait64: Info.opc = ISD::INTRINSIC_W_CHAIN; Info.memVT = MVT::i64; Info.ptrVal = I.getArgOperand(0); @@ -656,6 +698,75 @@ bool WebAssemblyTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info, Info.align = Align(8); Info.flags = MachineMemOperand::MOVolatile | MachineMemOperand::MOLoad; return true; + case Intrinsic::wasm_load32_zero: + case Intrinsic::wasm_load64_zero: + Info.opc = ISD::INTRINSIC_W_CHAIN; + Info.memVT = Intrinsic == Intrinsic::wasm_load32_zero ? MVT::i32 : MVT::i64; + Info.ptrVal = I.getArgOperand(0); + Info.offset = 0; + Info.align = Info.memVT == MVT::i32 ? Align(4) : Align(8); + Info.flags = MachineMemOperand::MOLoad; + return true; + case Intrinsic::wasm_load8_lane: + case Intrinsic::wasm_load16_lane: + case Intrinsic::wasm_load32_lane: + case Intrinsic::wasm_load64_lane: + case Intrinsic::wasm_store8_lane: + case Intrinsic::wasm_store16_lane: + case Intrinsic::wasm_store32_lane: + case Intrinsic::wasm_store64_lane: { + MVT MemVT; + Align MemAlign; + switch (Intrinsic) { + case Intrinsic::wasm_load8_lane: + case Intrinsic::wasm_store8_lane: + MemVT = MVT::i8; + MemAlign = Align(1); + break; + case Intrinsic::wasm_load16_lane: + case Intrinsic::wasm_store16_lane: + MemVT = MVT::i16; + MemAlign = Align(2); + break; + case Intrinsic::wasm_load32_lane: + case Intrinsic::wasm_store32_lane: + MemVT = MVT::i32; + MemAlign = Align(4); + break; + case Intrinsic::wasm_load64_lane: + case Intrinsic::wasm_store64_lane: + MemVT = MVT::i64; + MemAlign = Align(8); + break; + default: + llvm_unreachable("unexpected intrinsic"); + } + if (Intrinsic == Intrinsic::wasm_load8_lane || + Intrinsic == Intrinsic::wasm_load16_lane || + Intrinsic == Intrinsic::wasm_load32_lane || + Intrinsic == Intrinsic::wasm_load64_lane) { + Info.opc = ISD::INTRINSIC_W_CHAIN; + Info.flags = MachineMemOperand::MOLoad; + } else { + Info.opc = ISD::INTRINSIC_VOID; + Info.flags = MachineMemOperand::MOStore; + } + Info.ptrVal = I.getArgOperand(0); + Info.memVT = MemVT; + Info.offset = 0; + Info.align = MemAlign; + return true; + } + case Intrinsic::wasm_prefetch_t: + case Intrinsic::wasm_prefetch_nt: { + Info.opc = ISD::INTRINSIC_VOID; + Info.memVT = MVT::i8; + Info.ptrVal = I.getArgOperand(0); + Info.offset = 0; + Info.align = Align(1); + Info.flags = MachineMemOperand::MOLoad; + return true; + } default: return false; } @@ -866,8 +977,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, /*isSS=*/false); unsigned ValNo = 0; SmallVector<SDValue, 8> Chains; - for (SDValue Arg : - make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { + for (SDValue Arg : drop_begin(OutVals, NumFixedArgs)) { assert(ArgLocs[ValNo].getValNo() == ValNo && "ArgLocs should remain in order and only hold varargs args"); unsigned Offset = ArgLocs[ValNo++].getLocMemOffset(); @@ -876,7 +986,7 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, DAG.getConstant(Offset, DL, PtrVT)); Chains.push_back( DAG.getStore(Chain, DL, Arg, Add, - MachinePointerInfo::getFixedStack(MF, FI, Offset), 0)); + MachinePointerInfo::getFixedStack(MF, FI, Offset))); } if (!Chains.empty()) Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); @@ -1091,6 +1201,8 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, return LowerFrameIndex(Op, DAG); case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG); + case ISD::GlobalTLSAddress: + return LowerGlobalTLSAddress(Op, DAG); case ISD::ExternalSymbol: return LowerExternalSymbol(Op, DAG); case ISD::JumpTable: @@ -1199,6 +1311,49 @@ SDValue WebAssemblyTargetLowering::LowerFRAMEADDR(SDValue Op, return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), FP, VT); } +SDValue +WebAssemblyTargetLowering::LowerGlobalTLSAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + const auto *GA = cast<GlobalAddressSDNode>(Op); + MVT PtrVT = getPointerTy(DAG.getDataLayout()); + + MachineFunction &MF = DAG.getMachineFunction(); + if (!MF.getSubtarget<WebAssemblySubtarget>().hasBulkMemory()) + report_fatal_error("cannot use thread-local storage without bulk memory", + false); + + const GlobalValue *GV = GA->getGlobal(); + + // Currently Emscripten does not support dynamic linking with threads. + // Therefore, if we have thread-local storage, only the local-exec model + // is possible. + // TODO: remove this and implement proper TLS models once Emscripten + // supports dynamic linking with threads. + if (GV->getThreadLocalMode() != GlobalValue::LocalExecTLSModel && + !Subtarget->getTargetTriple().isOSEmscripten()) { + report_fatal_error("only -ftls-model=local-exec is supported for now on " + "non-Emscripten OSes: variable " + + GV->getName(), + false); + } + + auto GlobalGet = PtrVT == MVT::i64 ? WebAssembly::GLOBAL_GET_I64 + : WebAssembly::GLOBAL_GET_I32; + const char *BaseName = MF.createExternalSymbolName("__tls_base"); + + SDValue BaseAddr( + DAG.getMachineNode(GlobalGet, DL, PtrVT, + DAG.getTargetExternalSymbol(BaseName, PtrVT)), + 0); + + SDValue TLSOffset = DAG.getTargetGlobalAddress( + GV, DL, PtrVT, GA->getOffset(), WebAssemblyII::MO_TLS_BASE_REL); + SDValue SymAddr = DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, TLSOffset); + + return DAG.getNode(ISD::ADD, DL, PtrVT, BaseAddr, SymAddr); +} + SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); @@ -1303,7 +1458,22 @@ SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, MFI->getVarargBufferVreg(), PtrVT); return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), - MachinePointerInfo(SV), 0); + MachinePointerInfo(SV)); +} + +static SDValue getCppExceptionSymNode(SDValue Op, unsigned TagIndex, + SelectionDAG &DAG) { + // We only support C++ exceptions for now + int Tag = + cast<ConstantSDNode>(Op.getOperand(TagIndex).getNode())->getZExtValue(); + if (Tag != WebAssembly::CPP_EXCEPTION) + llvm_unreachable("Invalid tag: We only support C++ exceptions for now"); + auto &MF = DAG.getMachineFunction(); + const auto &TLI = DAG.getTargetLoweringInfo(); + MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout()); + const char *SymName = MF.createExternalSymbolName("__cpp_exception"); + return DAG.getNode(WebAssemblyISD::Wrapper, SDLoc(Op), PtrVT, + DAG.getTargetExternalSymbol(SymName, PtrVT)); } SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, @@ -1339,15 +1509,7 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, } case Intrinsic::wasm_throw: { - // We only support C++ exceptions for now - int Tag = cast<ConstantSDNode>(Op.getOperand(2).getNode())->getZExtValue(); - if (Tag != CPP_EXCEPTION) - llvm_unreachable("Invalid tag!"); - const TargetLowering &TLI = DAG.getTargetLoweringInfo(); - MVT PtrVT = TLI.getPointerTy(DAG.getDataLayout()); - const char *SymName = MF.createExternalSymbolName("__cpp_exception"); - SDValue SymNode = DAG.getNode(WebAssemblyISD::Wrapper, DL, PtrVT, - DAG.getTargetExternalSymbol(SymName, PtrVT)); + SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG); return DAG.getNode(WebAssemblyISD::THROW, DL, MVT::Other, // outchain type { @@ -1357,6 +1519,19 @@ SDValue WebAssemblyTargetLowering::LowerIntrinsic(SDValue Op, }); } + case Intrinsic::wasm_catch: { + SDValue SymNode = getCppExceptionSymNode(Op, 2, DAG); + return DAG.getNode(WebAssemblyISD::CATCH, DL, + { + MVT::i32, // outchain type + MVT::Other // return value + }, + { + Op.getOperand(0), // inchain + SymNode // exception symbol + }); + } + case Intrinsic::wasm_shuffle: { // Drop in-chain and replace undefs, but otherwise pass through unchanged SDValue Ops[18]; @@ -1474,8 +1649,8 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, SmallVector<SwizzleEntry, 16> SwizzleCounts; auto AddCount = [](auto &Counts, const auto &Val) { - auto CountIt = std::find_if(Counts.begin(), Counts.end(), - [&Val](auto E) { return E.first == Val; }); + auto CountIt = + llvm::find_if(Counts, [&Val](auto E) { return E.first == Val; }); if (CountIt == Counts.end()) { Counts.emplace_back(Val, 1); } else { @@ -1537,6 +1712,7 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, }; } else if (NumConstantLanes >= NumSplatLanes && Subtarget->hasUnimplementedSIMD128()) { + // If we support v128.const, emit it directly SmallVector<SDValue, 16> ConstLanes; for (const SDValue &Lane : Op->op_values()) { if (IsConstant(Lane)) { @@ -1548,11 +1724,59 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, } } Result = DAG.getBuildVector(VecT, DL, ConstLanes); - IsLaneConstructed = [&](size_t _, const SDValue &Lane) { + IsLaneConstructed = [&IsConstant](size_t _, const SDValue &Lane) { return IsConstant(Lane); }; - } - if (!Result) { + } else if (NumConstantLanes >= NumSplatLanes && VecT.isInteger()) { + // Otherwise, if this is an integer vector, pack the lane values together so + // we can construct the 128-bit constant from a pair of i64s using a splat + // followed by at most one i64x2.replace_lane. Also keep track of the lanes + // that actually matter so we can avoid the replace_lane in more cases. + std::array<uint64_t, 2> I64s{{0, 0}}; + std::array<uint64_t, 2> ConstLaneMasks{{0, 0}}; + size_t LaneBits = 128 / Lanes; + size_t HalfLanes = Lanes / 2; + for (size_t I = 0; I < Lanes; ++I) { + const SDValue &Lane = Op.getOperand(I); + if (IsConstant(Lane)) { + // How much we need to shift Val to position it in an i64 + auto Shift = LaneBits * (I % HalfLanes); + auto Mask = maskTrailingOnes<uint64_t>(LaneBits); + auto Val = cast<ConstantSDNode>(Lane.getNode())->getZExtValue() & Mask; + I64s[I / HalfLanes] |= Val << Shift; + ConstLaneMasks[I / HalfLanes] |= Mask << Shift; + } + } + // Check whether all constant lanes in the second half of the vector are + // equivalent in the first half or vice versa to determine whether splatting + // either side will be sufficient to materialize the constant. As a special + // case, if the first and second halves have no constant lanes in common, we + // can just combine them. + bool FirstHalfSufficient = (I64s[0] & ConstLaneMasks[1]) == I64s[1]; + bool SecondHalfSufficient = (I64s[1] & ConstLaneMasks[0]) == I64s[0]; + bool CombinedSufficient = (ConstLaneMasks[0] & ConstLaneMasks[1]) == 0; + + uint64_t Splatted; + if (SecondHalfSufficient) { + Splatted = I64s[1]; + } else if (CombinedSufficient) { + Splatted = I64s[0] | I64s[1]; + } else { + Splatted = I64s[0]; + } + + Result = DAG.getSplatBuildVector(MVT::v2i64, DL, + DAG.getConstant(Splatted, DL, MVT::i64)); + if (!FirstHalfSufficient && !SecondHalfSufficient && !CombinedSufficient) { + Result = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, MVT::v2i64, Result, + DAG.getConstant(I64s[1], DL, MVT::i64), + DAG.getConstant(1, DL, MVT::i32)); + } + Result = DAG.getBitcast(VecT, Result); + IsLaneConstructed = [&IsConstant](size_t _, const SDValue &Lane) { + return IsConstant(Lane); + }; + } else { // Use a splat, but possibly a load_splat LoadSDNode *SplattedLoad; if ((SplattedLoad = dyn_cast<LoadSDNode>(SplatValue)) && @@ -1565,11 +1789,14 @@ SDValue WebAssemblyTargetLowering::LowerBUILD_VECTOR(SDValue Op, } else { Result = DAG.getSplatBuildVector(VecT, DL, SplatValue); } - IsLaneConstructed = [&](size_t _, const SDValue &Lane) { + IsLaneConstructed = [&SplatValue](size_t _, const SDValue &Lane) { return Lane == SplatValue; }; } + assert(Result); + assert(IsLaneConstructed); + // Add replace_lane instructions for any unhandled values for (size_t I = 0; I < Lanes; ++I) { const SDValue &Lane = Op->getOperand(I); @@ -1730,6 +1957,49 @@ performVECTOR_SHUFFLECombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI) { return DAG.getBitcast(DstType, NewShuffle); } +static SDValue performVectorWidenCombine(SDNode *N, + TargetLowering::DAGCombinerInfo &DCI) { + auto &DAG = DCI.DAG; + assert(N->getOpcode() == ISD::SIGN_EXTEND || + N->getOpcode() == ISD::ZERO_EXTEND); + + // Combine ({s,z}ext (extract_subvector src, i)) into a widening operation if + // possible before the extract_subvector can be expanded. + auto Extract = N->getOperand(0); + if (Extract.getOpcode() != ISD::EXTRACT_SUBVECTOR) + return SDValue(); + auto Source = Extract.getOperand(0); + auto *IndexNode = dyn_cast<ConstantSDNode>(Extract.getOperand(1)); + if (IndexNode == nullptr) + return SDValue(); + auto Index = IndexNode->getZExtValue(); + + // Only v8i8 and v4i16 extracts can be widened, and only if the extracted + // subvector is the low or high half of its source. + EVT ResVT = N->getValueType(0); + if (ResVT == MVT::v8i16) { + if (Extract.getValueType() != MVT::v8i8 || + Source.getValueType() != MVT::v16i8 || (Index != 0 && Index != 8)) + return SDValue(); + } else if (ResVT == MVT::v4i32) { + if (Extract.getValueType() != MVT::v4i16 || + Source.getValueType() != MVT::v8i16 || (Index != 0 && Index != 4)) + return SDValue(); + } else { + return SDValue(); + } + + bool IsSext = N->getOpcode() == ISD::SIGN_EXTEND; + bool IsLow = Index == 0; + + unsigned Op = IsSext ? (IsLow ? WebAssemblyISD::WIDEN_LOW_S + : WebAssemblyISD::WIDEN_HIGH_S) + : (IsLow ? WebAssemblyISD::WIDEN_LOW_U + : WebAssemblyISD::WIDEN_HIGH_U); + + return DAG.getNode(Op, SDLoc(N), ResVT, Source); +} + SDValue WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { @@ -1738,5 +2008,8 @@ WebAssemblyTargetLowering::PerformDAGCombine(SDNode *N, return SDValue(); case ISD::VECTOR_SHUFFLE: return performVECTOR_SHUFFLECombine(N, DCI); + case ISD::SIGN_EXTEND: + case ISD::ZERO_EXTEND: + return performVectorWidenCombine(N, DCI); } } |
