496 lines · cpp
1//===-- WebAssemblyTargetTransformInfo.cpp - WebAssembly-specific TTI -----===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file defines the WebAssembly-specific TargetTransformInfo11/// implementation.12///13//===----------------------------------------------------------------------===//14 15#include "WebAssemblyTargetTransformInfo.h"16 17#include "llvm/CodeGen/CostTable.h"18using namespace llvm;19 20#define DEBUG_TYPE "wasmtti"21 22TargetTransformInfo::PopcntSupportKind23WebAssemblyTTIImpl::getPopcntSupport(unsigned TyWidth) const {24 assert(isPowerOf2_32(TyWidth) && "Ty width must be power of 2");25 return TargetTransformInfo::PSK_FastHardware;26}27 28unsigned WebAssemblyTTIImpl::getNumberOfRegisters(unsigned ClassID) const {29 unsigned Result = BaseT::getNumberOfRegisters(ClassID);30 31 // For SIMD, use at least 16 registers, as a rough guess.32 bool Vector = (ClassID == 1);33 if (Vector)34 Result = std::max(Result, 16u);35 36 return Result;37}38 39TypeSize WebAssemblyTTIImpl::getRegisterBitWidth(40 TargetTransformInfo::RegisterKind K) const {41 switch (K) {42 case TargetTransformInfo::RGK_Scalar:43 return TypeSize::getFixed(64);44 case TargetTransformInfo::RGK_FixedWidthVector:45 return TypeSize::getFixed(getST()->hasSIMD128() ? 128 : 64);46 case TargetTransformInfo::RGK_ScalableVector:47 return TypeSize::getScalable(0);48 }49 50 llvm_unreachable("Unsupported register kind");51}52 53InstructionCost WebAssemblyTTIImpl::getArithmeticInstrCost(54 unsigned Opcode, Type *Ty, TTI::TargetCostKind CostKind,55 TTI::OperandValueInfo Op1Info, TTI::OperandValueInfo Op2Info,56 ArrayRef<const Value *> Args, const Instruction *CxtI) const {57 58 InstructionCost Cost =59 BasicTTIImplBase<WebAssemblyTTIImpl>::getArithmeticInstrCost(60 Opcode, Ty, CostKind, Op1Info, Op2Info);61 62 if (auto *VTy = dyn_cast<VectorType>(Ty)) {63 switch (Opcode) {64 case Instruction::LShr:65 case Instruction::AShr:66 case Instruction::Shl:67 // SIMD128's shifts currently only accept a scalar shift count. For each68 // element, we'll need to extract, op, insert. The following is a rough69 // approximation.70 if (!Op2Info.isUniform())71 Cost =72 cast<FixedVectorType>(VTy)->getNumElements() *73 (TargetTransformInfo::TCC_Basic +74 getArithmeticInstrCost(Opcode, VTy->getElementType(), CostKind) +75 TargetTransformInfo::TCC_Basic);76 break;77 }78 }79 return Cost;80}81 82InstructionCost WebAssemblyTTIImpl::getCastInstrCost(83 unsigned Opcode, Type *Dst, Type *Src, TTI::CastContextHint CCH,84 TTI::TargetCostKind CostKind, const Instruction *I) const {85 int ISD = TLI->InstructionOpcodeToISD(Opcode);86 auto SrcTy = TLI->getValueType(DL, Src);87 auto DstTy = TLI->getValueType(DL, Dst);88 89 if (!SrcTy.isSimple() || !DstTy.isSimple()) {90 return BaseT::getCastInstrCost(Opcode, Dst, Src, CCH, CostKind, I);91 }92 93 if (!ST->hasSIMD128()) {94 return BaseT::getCastInstrCost(Opcode, Dst, Src, CCH, CostKind, I);95 }96 97 auto DstVT = DstTy.getSimpleVT();98 auto SrcVT = SrcTy.getSimpleVT();99 100 if (I && I->hasOneUser()) {101 auto *SingleUser = cast<Instruction>(*I->user_begin());102 int UserISD = TLI->InstructionOpcodeToISD(SingleUser->getOpcode());103 104 // extmul_low support105 if (UserISD == ISD::MUL &&106 (ISD == ISD::ZERO_EXTEND || ISD == ISD::SIGN_EXTEND)) {107 // Free low extensions.108 if ((SrcVT == MVT::v8i8 && DstVT == MVT::v8i16) ||109 (SrcVT == MVT::v4i16 && DstVT == MVT::v4i32) ||110 (SrcVT == MVT::v2i32 && DstVT == MVT::v2i64)) {111 return 0;112 }113 // Will require an additional extlow operation for the intermediate114 // i16/i32 value.115 if ((SrcVT == MVT::v4i8 && DstVT == MVT::v4i32) ||116 (SrcVT == MVT::v2i16 && DstVT == MVT::v2i64)) {117 return 1;118 }119 }120 }121 122 static constexpr TypeConversionCostTblEntry ConversionTbl[] = {123 // extend_low124 {ISD::SIGN_EXTEND, MVT::v2i64, MVT::v2i32, 1},125 {ISD::ZERO_EXTEND, MVT::v2i64, MVT::v2i32, 1},126 {ISD::SIGN_EXTEND, MVT::v4i32, MVT::v4i16, 1},127 {ISD::ZERO_EXTEND, MVT::v4i32, MVT::v4i16, 1},128 {ISD::SIGN_EXTEND, MVT::v8i16, MVT::v8i8, 1},129 {ISD::ZERO_EXTEND, MVT::v8i16, MVT::v8i8, 1},130 // 2 x extend_low131 {ISD::SIGN_EXTEND, MVT::v2i64, MVT::v2i16, 2},132 {ISD::ZERO_EXTEND, MVT::v2i64, MVT::v2i16, 2},133 {ISD::SIGN_EXTEND, MVT::v4i32, MVT::v4i8, 2},134 {ISD::ZERO_EXTEND, MVT::v4i32, MVT::v4i8, 2},135 // extend_low, extend_high136 {ISD::SIGN_EXTEND, MVT::v4i64, MVT::v4i32, 2},137 {ISD::ZERO_EXTEND, MVT::v4i64, MVT::v4i32, 2},138 {ISD::SIGN_EXTEND, MVT::v8i32, MVT::v8i16, 2},139 {ISD::ZERO_EXTEND, MVT::v8i32, MVT::v8i16, 2},140 {ISD::SIGN_EXTEND, MVT::v16i16, MVT::v16i8, 2},141 {ISD::ZERO_EXTEND, MVT::v16i16, MVT::v16i8, 2},142 // 2x extend_low, extend_high143 {ISD::SIGN_EXTEND, MVT::v8i64, MVT::v8i32, 4},144 {ISD::ZERO_EXTEND, MVT::v8i64, MVT::v8i32, 4},145 {ISD::SIGN_EXTEND, MVT::v16i32, MVT::v16i16, 4},146 {ISD::ZERO_EXTEND, MVT::v16i32, MVT::v16i16, 4},147 // shuffle148 {ISD::TRUNCATE, MVT::v2i16, MVT::v2i32, 2},149 {ISD::TRUNCATE, MVT::v2i8, MVT::v2i32, 4},150 {ISD::TRUNCATE, MVT::v4i16, MVT::v4i32, 2},151 {ISD::TRUNCATE, MVT::v4i8, MVT::v4i32, 4},152 // narrow, and153 {ISD::TRUNCATE, MVT::v8i16, MVT::v8i32, 2},154 {ISD::TRUNCATE, MVT::v8i8, MVT::v8i16, 2},155 // narrow, 2x and156 {ISD::TRUNCATE, MVT::v16i8, MVT::v16i16, 3},157 // 3x narrow, 4x and158 {ISD::TRUNCATE, MVT::v8i16, MVT::v8i64, 7},159 {ISD::TRUNCATE, MVT::v16i8, MVT::v16i32, 7},160 // 7x narrow, 8x and161 {ISD::TRUNCATE, MVT::v16i8, MVT::v16i64, 15},162 // convert_i32x4163 {ISD::SINT_TO_FP, MVT::v2f32, MVT::v2i32, 1},164 {ISD::UINT_TO_FP, MVT::v2f32, MVT::v2i32, 1},165 {ISD::SINT_TO_FP, MVT::v4f32, MVT::v4i32, 1},166 {ISD::UINT_TO_FP, MVT::v4f32, MVT::v4i32, 1},167 // extend_low, convert168 {ISD::SINT_TO_FP, MVT::v2f32, MVT::v2i16, 2},169 {ISD::UINT_TO_FP, MVT::v2f32, MVT::v2i16, 2},170 {ISD::SINT_TO_FP, MVT::v4f32, MVT::v4i16, 2},171 {ISD::UINT_TO_FP, MVT::v4f32, MVT::v4i16, 2},172 // extend_low x 2, convert173 {ISD::SINT_TO_FP, MVT::v2f32, MVT::v2i8, 3},174 {ISD::UINT_TO_FP, MVT::v2f32, MVT::v2i8, 3},175 {ISD::SINT_TO_FP, MVT::v4f32, MVT::v4i8, 3},176 {ISD::UINT_TO_FP, MVT::v4f32, MVT::v4i8, 3},177 // several shuffles178 {ISD::SINT_TO_FP, MVT::v8f32, MVT::v8i8, 10},179 {ISD::UINT_TO_FP, MVT::v8f32, MVT::v8i8, 10},180 {ISD::SINT_TO_FP, MVT::v8f32, MVT::v8i16, 10},181 {ISD::UINT_TO_FP, MVT::v8f32, MVT::v8i8, 10},182 /// trunc_sat, const, and, 3x narrow183 {ISD::FP_TO_SINT, MVT::v2i8, MVT::v2f32, 6},184 {ISD::FP_TO_UINT, MVT::v2i8, MVT::v2f32, 6},185 {ISD::FP_TO_SINT, MVT::v4i8, MVT::v4f32, 6},186 {ISD::FP_TO_UINT, MVT::v4i8, MVT::v4f32, 6},187 /// trunc_sat, const, and, narrow188 {ISD::FP_TO_UINT, MVT::v2i16, MVT::v2f32, 4},189 {ISD::FP_TO_SINT, MVT::v2i16, MVT::v2f32, 4},190 {ISD::FP_TO_SINT, MVT::v4i16, MVT::v4f32, 4},191 {ISD::FP_TO_UINT, MVT::v4i16, MVT::v4f32, 4},192 // 2x trunc_sat, const, 2x and, 3x narrow193 {ISD::FP_TO_SINT, MVT::v8i8, MVT::v8f32, 8},194 {ISD::FP_TO_UINT, MVT::v8i8, MVT::v8f32, 8},195 // 2x trunc_sat, const, 2x and, narrow196 {ISD::FP_TO_SINT, MVT::v8i16, MVT::v8f32, 6},197 {ISD::FP_TO_UINT, MVT::v8i16, MVT::v8f32, 6},198 };199 200 if (const auto *Entry =201 ConvertCostTableLookup(ConversionTbl, ISD, DstVT, SrcVT)) {202 return Entry->Cost;203 }204 205 return BaseT::getCastInstrCost(Opcode, Dst, Src, CCH, CostKind, I);206}207 208WebAssemblyTTIImpl::TTI::MemCmpExpansionOptions209WebAssemblyTTIImpl::enableMemCmpExpansion(bool OptSize, bool IsZeroCmp) const {210 TTI::MemCmpExpansionOptions Options;211 212 Options.AllowOverlappingLoads = true;213 214 if (ST->hasSIMD128())215 Options.LoadSizes.push_back(16);216 217 Options.LoadSizes.append({8, 4, 2, 1});218 Options.MaxNumLoads = TLI->getMaxExpandSizeMemcmp(OptSize);219 Options.NumLoadsPerBlock = Options.MaxNumLoads;220 221 return Options;222}223 224InstructionCost WebAssemblyTTIImpl::getMemoryOpCost(225 unsigned Opcode, Type *Ty, Align Alignment, unsigned AddressSpace,226 TTI::TargetCostKind CostKind, TTI::OperandValueInfo OpInfo,227 const Instruction *I) const {228 if (!ST->hasSIMD128() || !isa<FixedVectorType>(Ty)) {229 return BaseT::getMemoryOpCost(Opcode, Ty, Alignment, AddressSpace,230 CostKind);231 }232 233 EVT VT = TLI->getValueType(DL, Ty, true);234 // Type legalization can't handle structs235 if (VT == MVT::Other)236 return BaseT::getMemoryOpCost(Opcode, Ty, Alignment, AddressSpace,237 CostKind);238 239 auto LT = getTypeLegalizationCost(Ty);240 if (!LT.first.isValid())241 return InstructionCost::getInvalid();242 243 int ISD = TLI->InstructionOpcodeToISD(Opcode);244 unsigned width = VT.getSizeInBits();245 if (ISD == ISD::LOAD) {246 // 128-bit loads are a single instruction. 32-bit and 64-bit vector loads247 // can be lowered to load32_zero and load64_zero respectively. Assume SIMD248 // loads are twice as expensive as scalar.249 switch (width) {250 default:251 break;252 case 32:253 case 64:254 case 128:255 return 2;256 }257 } else if (ISD == ISD::STORE) {258 // For stores, we can use store lane operations.259 switch (width) {260 default:261 break;262 case 8:263 case 16:264 case 32:265 case 64:266 case 128:267 return 2;268 }269 }270 271 return BaseT::getMemoryOpCost(Opcode, Ty, Alignment, AddressSpace, CostKind);272}273 274InstructionCost WebAssemblyTTIImpl::getInterleavedMemoryOpCost(275 unsigned Opcode, Type *Ty, unsigned Factor, ArrayRef<unsigned> Indices,276 Align Alignment, unsigned AddressSpace, TTI::TargetCostKind CostKind,277 bool UseMaskForCond, bool UseMaskForGaps) const {278 assert(Factor >= 2 && "Invalid interleave factor");279 280 auto *VecTy = cast<VectorType>(Ty);281 if (!ST->hasSIMD128() || !isa<FixedVectorType>(VecTy)) {282 return InstructionCost::getInvalid();283 }284 285 if (UseMaskForCond || UseMaskForGaps)286 return BaseT::getInterleavedMemoryOpCost(Opcode, Ty, Factor, Indices,287 Alignment, AddressSpace, CostKind,288 UseMaskForCond, UseMaskForGaps);289 290 constexpr unsigned MaxInterleaveFactor = 4;291 if (Factor <= MaxInterleaveFactor) {292 unsigned MinElts = VecTy->getElementCount().getKnownMinValue();293 // Ensure the number of vector elements is greater than 1.294 if (MinElts < 2 || MinElts % Factor != 0)295 return InstructionCost::getInvalid();296 297 unsigned ElSize = DL.getTypeSizeInBits(VecTy->getElementType());298 // Ensure the element type is legal.299 if (ElSize != 8 && ElSize != 16 && ElSize != 32 && ElSize != 64)300 return InstructionCost::getInvalid();301 302 auto *SubVecTy =303 VectorType::get(VecTy->getElementType(),304 VecTy->getElementCount().divideCoefficientBy(Factor));305 InstructionCost MemCost =306 getMemoryOpCost(Opcode, SubVecTy, Alignment, AddressSpace, CostKind);307 308 unsigned VecSize = DL.getTypeSizeInBits(SubVecTy);309 unsigned MaxVecSize = 128;310 unsigned NumAccesses =311 std::max<unsigned>(1, (MinElts * ElSize + MaxVecSize - 1) / VecSize);312 313 // A stride of two is commonly supported via dedicated instructions, so it314 // should be relatively cheap for all element sizes. A stride of four is315 // more expensive as it will likely require more shuffles. Using two316 // simd128 inputs is considered more expensive and we mainly account for317 // shuffling two inputs (32 bytes), but we do model 4 x v4i32 to enable318 // arithmetic kernels.319 static const CostTblEntry ShuffleCostTbl[] = {320 // One reg.321 {2, MVT::v2i8, 1}, // interleave 2 x 2i8 into 4i8322 {2, MVT::v4i8, 1}, // interleave 2 x 4i8 into 8i8323 {2, MVT::v8i8, 1}, // interleave 2 x 8i8 into 16i8324 {2, MVT::v2i16, 1}, // interleave 2 x 2i16 into 4i16325 {2, MVT::v4i16, 1}, // interleave 2 x 4i16 into 8i16326 {2, MVT::v2i32, 1}, // interleave 2 x 2i32 into 4i32327 328 // Two regs.329 {2, MVT::v16i8, 2}, // interleave 2 x 16i8 into 32i8330 {2, MVT::v8i16, 2}, // interleave 2 x 8i16 into 16i16331 {2, MVT::v4i32, 2}, // interleave 2 x 4i32 into 8i32332 333 // One reg.334 {4, MVT::v2i8, 4}, // interleave 4 x 2i8 into 8i8335 {4, MVT::v4i8, 4}, // interleave 4 x 4i8 into 16i8336 {4, MVT::v2i16, 4}, // interleave 4 x 2i16 into 8i16337 338 // Two regs.339 {4, MVT::v8i8, 16}, // interleave 4 x 8i8 into 32i8340 {4, MVT::v4i16, 8}, // interleave 4 x 4i16 into 16i16341 {4, MVT::v2i32, 4}, // interleave 4 x 2i32 into 8i32342 343 // Four regs.344 {4, MVT::v4i32, 16}, // interleave 4 x 4i32 into 16i32345 };346 347 EVT ETy = TLI->getValueType(DL, SubVecTy);348 if (const auto *Entry =349 CostTableLookup(ShuffleCostTbl, Factor, ETy.getSimpleVT()))350 return Entry->Cost + (NumAccesses * MemCost);351 }352 353 return BaseT::getInterleavedMemoryOpCost(Opcode, VecTy, Factor, Indices,354 Alignment, AddressSpace, CostKind,355 UseMaskForCond, UseMaskForGaps);356}357 358InstructionCost WebAssemblyTTIImpl::getVectorInstrCost(359 unsigned Opcode, Type *Val, TTI::TargetCostKind CostKind, unsigned Index,360 const Value *Op0, const Value *Op1) const {361 InstructionCost Cost = BasicTTIImplBase::getVectorInstrCost(362 Opcode, Val, CostKind, Index, Op0, Op1);363 364 // SIMD128's insert/extract currently only take constant indices.365 if (Index == -1u)366 return Cost + 25 * TargetTransformInfo::TCC_Expensive;367 368 return Cost;369}370 371InstructionCost WebAssemblyTTIImpl::getPartialReductionCost(372 unsigned Opcode, Type *InputTypeA, Type *InputTypeB, Type *AccumType,373 ElementCount VF, TTI::PartialReductionExtendKind OpAExtend,374 TTI::PartialReductionExtendKind OpBExtend, std::optional<unsigned> BinOp,375 TTI::TargetCostKind CostKind) const {376 InstructionCost Invalid = InstructionCost::getInvalid();377 if (!VF.isFixed() || !ST->hasSIMD128())378 return Invalid;379 380 if (CostKind != TTI::TCK_RecipThroughput)381 return Invalid;382 383 if (Opcode != Instruction::Add)384 return Invalid;385 386 EVT AccumEVT = EVT::getEVT(AccumType);387 // TODO: Add i64 accumulator.388 if (AccumEVT != MVT::i32)389 return Invalid;390 391 // Possible options:392 // - i16x8.extadd_pairwise_i8x16_sx393 // - i32x4.extadd_pairwise_i16x8_sx394 // - i32x4.dot_i16x8_s395 // Only try to support dot, for now.396 397 EVT InputEVT = EVT::getEVT(InputTypeA);398 if (!((InputEVT == MVT::i16 && VF.getFixedValue() == 8) ||399 (InputEVT == MVT::i8 && VF.getFixedValue() == 16))) {400 return Invalid;401 }402 403 if (OpAExtend == TTI::PR_None)404 return Invalid;405 406 InstructionCost Cost(TTI::TCC_Basic);407 if (!BinOp)408 return Cost;409 410 if (OpAExtend != OpBExtend)411 return Invalid;412 413 if (*BinOp != Instruction::Mul)414 return Invalid;415 416 if (InputTypeA != InputTypeB)417 return Invalid;418 419 // Signed inputs can lower to dot420 if (InputEVT == MVT::i16 && VF.getFixedValue() == 8)421 return OpAExtend == TTI::PR_SignExtend ? Cost : Cost * 2;422 423 // Double the size of the lowered sequence.424 if (InputEVT == MVT::i8 && VF.getFixedValue() == 16)425 return OpAExtend == TTI::PR_SignExtend ? Cost * 2 : Cost * 4;426 427 return Invalid;428}429 430TTI::ReductionShuffle WebAssemblyTTIImpl::getPreferredExpandedReductionShuffle(431 const IntrinsicInst *II) const {432 433 switch (II->getIntrinsicID()) {434 default:435 break;436 case Intrinsic::vector_reduce_fadd:437 return TTI::ReductionShuffle::Pairwise;438 }439 return TTI::ReductionShuffle::SplitHalf;440}441 442void WebAssemblyTTIImpl::getUnrollingPreferences(443 Loop *L, ScalarEvolution &SE, TTI::UnrollingPreferences &UP,444 OptimizationRemarkEmitter *ORE) const {445 // Scan the loop: don't unroll loops with calls. This is a standard approach446 // for most (all?) targets.447 for (BasicBlock *BB : L->blocks())448 for (Instruction &I : *BB)449 if (isa<CallInst>(I) || isa<InvokeInst>(I))450 if (const Function *F = cast<CallBase>(I).getCalledFunction())451 if (isLoweredToCall(F))452 return;453 454 // The chosen threshold is within the range of 'LoopMicroOpBufferSize' of455 // the various microarchitectures that use the BasicTTI implementation and456 // has been selected through heuristics across multiple cores and runtimes.457 UP.Partial = UP.Runtime = UP.UpperBound = true;458 UP.PartialThreshold = 30;459 460 // Avoid unrolling when optimizing for size.461 UP.OptSizeThreshold = 0;462 UP.PartialOptSizeThreshold = 0;463 464 // Set number of instructions optimized when "back edge"465 // becomes "fall through" to default value of 2.466 UP.BEInsns = 2;467}468 469bool WebAssemblyTTIImpl::supportsTailCalls() const {470 return getST()->hasTailCall();471}472 473bool WebAssemblyTTIImpl::isProfitableToSinkOperands(474 Instruction *I, SmallVectorImpl<Use *> &Ops) const {475 using namespace llvm::PatternMatch;476 477 if (!I->getType()->isVectorTy() || !I->isShift())478 return false;479 480 Value *V = I->getOperand(1);481 // We dont need to sink constant splat.482 if (isa<Constant>(V))483 return false;484 485 if (match(V, m_Shuffle(m_InsertElt(m_Value(), m_Value(), m_ZeroInt()),486 m_Value(), m_ZeroMask()))) {487 // Sink insert488 Ops.push_back(&cast<Instruction>(V)->getOperandUse(0));489 // Sink shuffle490 Ops.push_back(&I->getOperandUse(1));491 return true;492 }493 494 return false;495}496