aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp')
-rw-r--r--llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp333
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);
}
}