|
13 | 13 | //===----------------------------------------------------------------------===// |
14 | 14 |
|
15 | 15 | #include "WebAssemblyTargetTransformInfo.h" |
| 16 | +#include "llvm/IR/IntrinsicInst.h" |
| 17 | +#include "llvm/IR/IntrinsicsWebAssembly.h" |
| 18 | +#include "llvm/Transforms/InstCombine/InstCombiner.h" |
16 | 19 |
|
17 | 20 | #include "llvm/CodeGen/CostTable.h" |
18 | 21 | using namespace llvm; |
@@ -493,3 +496,86 @@ bool WebAssemblyTTIImpl::isProfitableToSinkOperands( |
493 | 496 |
|
494 | 497 | return false; |
495 | 498 | } |
| 499 | + |
| 500 | +/// Attempt to convert [relaxed_]swizzle to shufflevector if the mask is |
| 501 | +/// constant. |
| 502 | +static Value *simplifyWasmSwizzle(const IntrinsicInst &II, |
| 503 | + InstCombiner::BuilderTy &Builder, |
| 504 | + bool IsRelaxed) { |
| 505 | + auto *V = dyn_cast<Constant>(II.getArgOperand(1)); |
| 506 | + if (!V) |
| 507 | + return nullptr; |
| 508 | + |
| 509 | + auto *VecTy = cast<FixedVectorType>(II.getType()); |
| 510 | + unsigned NumElts = VecTy->getNumElements(); |
| 511 | + assert(NumElts == 16); |
| 512 | + |
| 513 | + // Construct a shuffle mask from constant integers or UNDEFs. |
| 514 | + int Indexes[16]; |
| 515 | + bool AnyOutOfBounds = false; |
| 516 | + |
| 517 | + for (unsigned I = 0; I < NumElts; ++I) { |
| 518 | + Constant *COp = V->getAggregateElement(I); |
| 519 | + if (!COp || (!isa<UndefValue>(COp) && !isa<ConstantInt>(COp))) |
| 520 | + return nullptr; |
| 521 | + |
| 522 | + if (isa<UndefValue>(COp)) { |
| 523 | + Indexes[I] = -1; |
| 524 | + continue; |
| 525 | + } |
| 526 | + |
| 527 | + int64_t Index = cast<ConstantInt>(COp)->getSExtValue(); |
| 528 | + |
| 529 | + if (Index >= NumElts && IsRelaxed) { |
| 530 | + // For lane indices above 15, the relaxed_swizzle operation can choose |
| 531 | + // between returning 0 or the lane at `Index % 16`. However, the choice |
| 532 | + // must be made consistently. As the WebAssembly spec states: |
| 533 | + // |
| 534 | + // "The result of relaxed operators are implementation-dependent, because |
| 535 | + // the set of possible results may depend on properties of the host |
| 536 | + // environment, such as its hardware. Technically, their behaviour is |
| 537 | + // controlled by a set of global parameters to the semantics that an |
| 538 | + // implementation can instantiate in different ways. These choices are |
| 539 | + // fixed, that is, parameters are constant during the execution of any |
| 540 | + // given program." |
| 541 | + // |
| 542 | + // The WebAssembly runtime may choose differently from us, so we can't |
| 543 | + // optimize a relaxed swizzle with lane indices above 15. |
| 544 | + return nullptr; |
| 545 | + } |
| 546 | + |
| 547 | + if (Index >= NumElts || Index < 0) { |
| 548 | + AnyOutOfBounds = true; |
| 549 | + // If there are out-of-bounds indices, the swizzle instruction returns |
| 550 | + // zeroes in those lanes. We'll provide an all-zeroes vector as the |
| 551 | + // second argument to shufflevector and read the first element from it. |
| 552 | + Indexes[I] = NumElts; |
| 553 | + continue; |
| 554 | + } |
| 555 | + |
| 556 | + Indexes[I] = Index; |
| 557 | + } |
| 558 | + |
| 559 | + auto *V1 = II.getArgOperand(0); |
| 560 | + auto *V2 = |
| 561 | + AnyOutOfBounds ? Constant::getNullValue(VecTy) : PoisonValue::get(VecTy); |
| 562 | + |
| 563 | + return Builder.CreateShuffleVector(V1, V2, ArrayRef(Indexes, NumElts)); |
| 564 | +} |
| 565 | + |
| 566 | +std::optional<Instruction *> |
| 567 | +WebAssemblyTTIImpl::instCombineIntrinsic(InstCombiner &IC, |
| 568 | + IntrinsicInst &II) const { |
| 569 | + Intrinsic::ID IID = II.getIntrinsicID(); |
| 570 | + switch (IID) { |
| 571 | + case Intrinsic::wasm_swizzle: |
| 572 | + case Intrinsic::wasm_relaxed_swizzle: |
| 573 | + if (Value *V = simplifyWasmSwizzle( |
| 574 | + II, IC.Builder, IID == Intrinsic::wasm_relaxed_swizzle)) { |
| 575 | + return IC.replaceInstUsesWith(II, V); |
| 576 | + } |
| 577 | + break; |
| 578 | + } |
| 579 | + |
| 580 | + return std::nullopt; |
| 581 | +} |
0 commit comments